Getting Dapper to return an empty string instead of a null string - dapper

I know it's kind of the wrong thing to do, but I'm dealing with a legacy codebase that has NULLS when it means empty strings and vice versa.
I can't immediately see how it is possible, but is it possible to get (or modifiy dapper so it will) return an empty string instead of a null string when mapping back from the database.

Dapper doesn't call any setter when it sees a null, so options might include:
set the default value to "" in the constructor
check for null in the accessor
So:
public class SomeDto
{
public SomeDto()
{
Name = "";
}
public string Name {get;set;}
}
or:
public class SomeDto
{
private string name;
public string Name { get {return name ?? "";} set {name = value;} }
}
However, this only applies to reading values; I can't think of a nice way to get dapper to turn "" into null when passing the dto in as the parameter object; options include:
creating an anon-type, substituting "" to null (perhaps write a string NullIfBlank(this string s) extension method)
having a shim property on the type that returns null in place of "", and have your database query bind to #NameOrNull rather than #Name

You can control this with your queries, for example:
public class Data
{
public string Foo { get; set; }
}
var result = conn.Query<Data>("select Foo = coalesce(Foo, '') from MyTable");
So in the above example, coalesce will return an empty string when Foo is null.

In short: depending how you load the data to the dapper you may get two different scenarios.
First: Turn up your data provider layer, for example like in this post - How to return null from a Dapper query rather than default(T)?.
Second way to try: you may modify your GetTypeDeserializer like in the following post - Change Dapper so that it maps a database null value to double.NaN
Third and the last: it is my friendly advice to work on your previous questions acceptance rate. In this way you may increase chances of replies for your questions.
Hope all this will help.

I tend to use a global extension method on string called ConvertNull() which converts any null values to an empty string. You can then call this anywhere without your code looking cluttered. If you're using this directly on an aspx page, just make sure you've imported the namespace of the extension methods and then the method will be available to you:
namespace ExtensionMethods
{
using System;
public static class StringExtensionsClass
{
/// <summary>Converts null strings to empty strings</summary>
/// <param name="s">Input string</param>
/// <returns>Original string, or empty string if original string was null</returns>
public static string ConvertNull(this string s)
{
return s ?? "";
}
}
}
Then call this on an instance of a string.
Usage:
myStringInstance.ConvertNull().Replace("\r\n", "<br />");

Related

iBatis unable to read property from Map when using isEqual

I'm seeing a very bizarre issue with iBatis when trying to read a property from a Java map using isEqual, but not with other iBatis operators. For example it is able to read the map properties fine when using isNotNull and iterate. The xml:
<isNotNull property="filterCriteria.account">
AND
id
<isEqual property="filterCriteria.account.meetsCriteria" compareValue="false">
NOT
</isEqual>
IN
(SELECT DISTINCT id
FROM account
WHERE some other criteria....
)
</isNotNull>
The 2 java classes we're using here:
public class SearchProfile {
private Map<String, SearchProfileCriteria> filterCriteria;
public SAOSearchProfile() {
filterCriteria = new HashMap<>();
}
public Map<String, SAOSearchProfileCriteria> getFilterCriteria() {
return filterCriteria;
}
public void setFilterCriteria(Map<String, SAOSearchProfileCriteriaBase> filterCriteria) {
this.filterCriteria = filterCriteria;
}
}
Above is the container object that is passed to iBatis for the querying, and below is the criteria object that will be the value of the map. In this example it is keyed with the String "account"
public class SearchProfileCriteria {
boolean meetsCriteria;
public String getCriteriaAsString() {
return StringUtils.getStringValueFromBoolean(meetsCriteria);
}
public boolean isMeetsCriteria() {
return meetsCriteria;
}
public void setMeetsCriteria(boolean meetsCriteria) {
this.meetsCriteria = meetsCriteria;
}
public String getSQLString(){
return meetsCriteria ? "" : "NOT";
}
}
And the exception:
Cause: com.ibatis.common.beans.ProbeException: There is no READABLE property named 'account' in class 'java.util.Map'; nested exception is com.ibatis.common.jdbc.exception.NestedSQLException:
The getSQLString() method was my half baked attempt at a work around, the String gets escaped in the query and throws a syntax error.
When I remove the <isEqual> block the query executes find, which indicates it is able to read the "account" key when checking the to see if it is null. As I mentioned above, we're also able to use the map keys in <iterate> tags without issue. It seems <isEqual> and <isNotEqual> are the only tags causing issues. Does anyone have experience with this or know what may be going on?
Beware: Using isNotNull, isEqual, iterate is iBatis, they don't exist anymore in Mybatis, so referencing to Mybatis indifferently is confusing.
Reference documentation.
For your issue, how does it behave if replacing Map with a class (property will be known at compile time)?
Or try using <isPropertyAvailable>.
The work around could work with right syntax: $ instead of #: $filterCriteria.account.SQLString$ instead of #filterCriteria.account.SQLString#, then the value is just concatenated instead of bound as parameter.

Dapper can't ignore nested objects for parameter?

I am beginning to use Dapper and love it so far. However as i venture further into complexity, i have ran into a big issue with it. The fact that you can pass an entire custom object as a parameter is great. However, when i add another custom object a a property, it no longer works as it tries to map the object as a SQL parameter. Is there any way to have it ignore custom objects that are properties of the main object being passed thru? Example below
public class CarMaker
{
public string Name { get; set; }
public Car Mycar { get; set; }
}
propery Name maps fine but property MyCar fails because it is a custom object. I will have to restructure my entire project if Dapper can't handle this which...well blows haha
Dapper extensions has a way to create custom maps, which allows you to ignore properties:
public class MyModelMapper : ClassMapper<MyModel>
{
public MyModelMapper()
{
//use a custom schema
Schema("not_dbo_schema");
//have a custom primary key
Map(x => x.ThePrimaryKey).Key(KeyType.Assigned);
//Use a different name property from database column
Map(x=> x.Foo).Column("Bar");
//Ignore this property entirely
Map(x=> x.SecretDataMan).Ignore();
//optional, map all other columns
AutoMap();
}
}
Here is a link
There is a much simpler solution to this problem.
If the property MyCar is not in the database, and it is probably not, then simple remove the {get;set;} and the "property" becomes a field and is automatically ignored by DapperExtensions. If you are actually storing this information in a database and it is a multi-valued property that is not serialized into a JSON or similar format, I think you are probably asking for complexity that you don't want. There is no sql equivalent of the object "Car", and the properties in your model must map to something that sql recognizes.
UPDATE:
If "Car" is part of a table in your database, then you can read it into the CarMaker object using Dapper's QueryMultiple.
I use it in this fashion:
dynamic reader = dbConnection.QueryMultiple("Request_s", param: new { id = id }, commandType: CommandType.StoredProcedure);
if (reader != null)
{
result = reader.Read<Models.Request>()[0] as Models.Request;
result.reviews = reader.Read<Models.Review>() as IEnumerable<Models.Review>;
}
The Request Class has a field as such:
public IEnumerable<Models.Review> reviews;
The stored procedure looks like this:
ALTER PROCEDURE [dbo].[Request_s]
(
#id int = null
)
AS
BEGIN
SELECT *
FROM [biospecimen].requests as bn
where bn.id=coalesce(#id, bn.id)
order by bn.id desc;
if #id is not null
begin
SELECT
*
FROM [biospecimen].reviews as bn
where bn.request_id = #id;
end
END
In the first read, Dapper ignores the field reviews, and in the second read, Dapper loads the information into the field. If a null set is returned, Dapper will load the field with a null set just like it will load the parent class with null contents.
The second select statement then reads the collection needed to complete the object, and Dapper stores the output as shown.
I have been implementing this in my Repository classes in situations where a target parent class has several child classes that are being displayed at the same time.
This prevents multiple trips to the database.
You can also use this approach when the target class is a child class and you need information about the parent class it is related to.

Parameter must be an entity type exposed by the DomainService?

Trying to implement a domain service in a SL app and getting the following error:
Parameter 'spFolderCreate' of domain method 'CreateSharePointFolder' must be an entity type exposed by the DomainService.
[EnableClientAccess()]
public class FileUploadService : DomainService
{
public void CreateSharePointFolder(SharePointFolderCreate spFolderCreate)
{
SharePointFolder spf = new SharePointFolder();
spf.CreateFolder_ClientOM(spFolderCreate.listName, spFolderCreate.fileName);
}
[OperationContract]
void CreateSharePointFolder(SharePointFolderCreate spFolderCreate);
[DataContract]
public class SharePointFolderCreate
{
private string m_listName;
private string m_fileName;
[DataMember]
public string listName
{
get { return m_listName; }
set { m_listName = value; }
}
[DataMember]
public string fileName
{
get { return m_fileName; }
set { m_fileName = value; }
}
}
So am I missing something simple here to make this all work?
It may be that the framework is inferring the intended operation because you have the word "Create" prefixing the function name (CreateSharePointFolder). Details of this behaviour can be found here
Although that is all fine for DomainServices and EntityFramework, following the information in that article, it can be inferred that methods beginning "Delete" will be performing a delete of an entity, so must accept an entity as a parameter. The same is true for "Create" or "Insert" prefixed methods. Only "Get" or "Select" methods can take non-entity parameters, making it possible to pass a numeric id (for example) to a "Get" method.
Try changing your method name temporarily to "BlahSharePointFolder" to see if it is this convention of inferrance that's causing your problem.
Also, as there is no metadata defined for your SharePointFolderCreate DC, you might need to decorate the class (in addition to the [DataContract] attribute) with the [MetadataType] attribute. You will see how to implement this if you used the DomainServiceClass wizard and point to an EF model. There is a checkbox at the bottom for generating metadata. Somewhere in your solution.Web project you should find a domainservice.metadata.cs file. In this file, you will find examples of how to use the [MetadataType] attribute.
For the RIA WCF service to work correctly with your own methods, you need to ensure that all entities existing on the parameter list have at least one member with a [Key] attribute defined in their metadata class, and that the entity is returned somewhere on your DomainService in a "Get" method.
HTH
Lee

RIA/EF4 Entity property mapped to NOT NULL nvarchar - empty string

Background:
Entity Framework 4
Silverlight 4
RIA services
MSSQL Server 2008
I have an entity that has a String property named Description.
In database it maps to the NOT NULL NVARCHAR(200).
Problem:
When I try to insert a new row of that entity, this is what I do:
MyExampleEntity entity = new MyExampleEntity()
{
Name = "example",
Description = "" // NOTE THIS LINE!
};
DatabaseContext db = new DatabaseContext();
db.MyExampleEntities.Add(entity);
db.SubmitChanges();
This, however, causes an exception saying "The Description field is required."
Question:
Should not the "empty string" be simply that - a string with zero characters?
I believe only Description = null should be treated as providing no value.
Why is my string, which has a value (although its length is 0), considered to be as if I have omitted the value?
On what level does this conversion happen? On RIA, on EF or in MSSQL?
Is there a way to make a description have zero-length value when I set the Description to "" and cause an exception when Description = null (having no value)?
This appears to be a symptom of Entity Framework.
Related Article
Some data annotations can be used to overcome this:
[MetadataType(typeof(Report_META))]
public partial class Report
{
}
public partial class Report_META
{
[Required(AllowEmptyStrings = true)]
[DisplayFormat(ConvertEmptyStringToNull = false)]
public object Note { get; set; }
}

Why can I return a string from RIA Services, but not IEnumerable<string>?

I am trying to create a method in my DomainService to return a list of strings. The reason why I am doing this is because I don't want to return whole entities of information. I can create a method to return a single string, as follows:
public string GetDestinations()
{
return "Hello world";
}
This works ok from the client via a InvokeOperation<string> call. However, when I turn the DomainService method into Ienumerable, as follows;
public IEnumerable<string> GetDestinations()
{
List<String> h = new List<string>();
h.Add("HELLO");
h.Add("WORLD");
return h;
}
And then obviously use an InvokeOperation<IEnumerable<string>> call I get a compiler error:
Type 'String' is not a valid entity type. Entity types cannot be a primitive type or a simple type like string or Guid.
Huh? This doesn't make sense given that I can return a single string. How do I return a list of strings from my DomainService?
Try returning an array of strings and marking the method as an InvokeOperation.
[Invoke]
public string[] GetDestinations()
{
...
}
By convention the method that returns IEnumerable counted as Query method. Query method can return only collection of entities. Add Invoke attribute to your method.

Resources