我正在开发Azure移动服务,在我的模型中,一些关系是可选的,使得表示它的属性可以为空 .
例如,我的模型类中的Message实体是这样的:
public partial class Message
{
public Message()
{
this.Messages = new HashSet<Message>();
}
public int Id { get; set; }
public int CreatedById { get; set; }
public int RecipientId { get; set; }
public Nullable<int> ParentId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int MessageTypeId { get; set; }
public Nullable<MessageType> Type { get; set; }
public Nullable<bool> Draft { get; set; }
public Nullable<bool> Read { get; set; }
public Nullable<bool> Replied { get; set; }
public Nullable<bool> isDeleted { get; set; }
[JsonIgnore]
[ForeignKey("CreatedById")]
public virtual User CreatedBy { get; set; }
[JsonIgnore]
[ForeignKey("RecipientId")]
public virtual User Recipient { get; set; }
[JsonIgnore]
public virtual ICollection<Message> Messages { get; set; }
[JsonIgnore]
public virtual Message Parent { get; set; }
}
我的DTO看起来像这样:
public class MessageDTO : EntityData
{
public string CreatedById { get; set; }
public string RecipientId { get; set; }
public string ParentId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public string Type { get; set; }
public Nullable<bool> Draft { get; set; }
public Nullable<bool> Read { get; set; }
public Nullable<bool> Replied { get; set; }
public Nullable<bool> isDeleted { get; set; }
}
在我的AutoMapper配置中,我有这个:
/*
* Mapping for Message entity
*/
cfg.CreateMap<Message, MessageDTO>()
.ForMember(messageDTO => messageDTO.Id, map =>
map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.Id))))
.ForMember(messageDTO => messageDTO.ParentId, map =>
map.MapFrom(message => message.ParentId))
.ForMember(messageDTO => messageDTO.CreatedById, map =>
map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.CreatedById))))
.ForMember(messageDTO => messageDTO.RecipientId, map =>
map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.RecipientId))))
.ForMember(messageDTO => messageDTO.Type, map =>
map.MapFrom(message => Enum.GetName(typeof(MessageType), message.MessageTypeId)));
cfg.CreateMap<MessageDTO, Message>()
.ForMember(message => message.Id, map =>
map.MapFrom(messageDTO => MySqlFuncs.IntParse(messageDTO.Id)))
.ForMember(message => message.ParentId, map =>
map.MapFrom(messageDTO => messageDTO.ParentId))
.ForMember(message => message.CreatedById, map =>
map.MapFrom(messageDTO => MySqlFuncs.IntParse(messageDTO.CreatedById)))
.ForMember(message => message.RecipientId, map =>
map.MapFrom(messageDTO => MySqlFuncs.IntParse(messageDTO.RecipientId)));
作为参考,MySqlFuncs类具有此函数来处理从string到int和int到string的转换:
class MySqlFuncs
{
[DbFunction("SqlServer", "STR")]
public static string StringConvert(int number)
{
return number.ToString();
}
[DbFunction("SqlServer", "LTRIM")]
public static string LTRIM(string s)
{
return s == null ? null : s.TrimStart();
}
// Can only be used locally.
public static int IntParse(string s)
{
int ret;
int.TryParse(s, out ret);
return ret;
}
}
虽然我能够插入元素,但由于以下错误,我无法获取它们:
从Nullable
1到String缺少 Map . 使用Mapper.CreateMap <Nullable
1,String>创建
从这里我得到的是我在AutoMapper定义中对此错误负责的行是:
.ForMember(messageDTO => messageDTO.ParentId, map =>
map.MapFrom(message => message.ParentId))
AutoMapper需要知道如何将可居住的int从ParentId属性映射到DTO . 我试图使用MySqlFuncs类中的函数:
.ForMember(messageDTO => messageDTO.ParentId, map =>
map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.ParentId))))
但是这给出了错误:
无法从'int?'转换'int'
如果我们认为配置必须以某种方式完成LINQ可以正确读取转换它,我如何定义映射,以便任何可空属性都被映射到字符串中作为空字符串“”或只是值null到我的DTO?
编辑1
似乎要解决这个问题,我必须使用ValueResolver,我编写如下:
public class NullableIntToStringResolver : ValueResolver<int?, string>
{
protected override string ResolveCore(int? source)
{
return !source.HasValue ? "" : MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(source));
}
}
并将映射更改为:
cfg.CreateMap<Message, MessageDTO>()
.ForMember(messageDTO => messageDTO.ParentId,
map => map.ResolveUsing(
new NullableIntToStringResolver()).FromMember(message => message.ParentId))
但这给了我错误
对象引用未设置为对象的实例
而StackTrace就是这样的:
AutoMapper.QueryableExtensions中的AutoMapper.QueryableExtensions.Extensions.ResolveExpression(PropertyMap propertyMap,Type currentType,Expression instanceParameter)在AutoMapper.QueryableExtensions的AutoMapper.QueryableExtensions.Extensions.CreateMemberBindings(IMappingEngine mappingEngine,TypePair typePair,TypeMap typeMap,Expression instanceParameter,IDictionary
2 typePairCount) . Extensions.CreateMapExpression(IMappingEngine mappingEngine,TypePair typePair,Expression instanceParameter,IDictionary
2 typePairCount),位于AutoMapper.Queryable.Extensions.CreateMapExpression(IMappingEngine mappingEngine,TypePair typePair,IDictionary2 typePairCount),位于AutoMapper.QueryableExtensions.Extensions . <> c__DisplayClass1
2在AutoMapper的AutoMapper.Internal.DictionaryFactoryOverride.ConcurrentDictionaryImpl2.GetOrAdd(TKey键,Func
2 valueFactory)的System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey键,Func
2 valueFactory)中的.b__0(TypePair tp) .QueryableExtensions.Extensions.CreateMapExpre ssion [TSource,TDestination](IMappingEngine mappingEngine)在Microsoft.WindowsAzure.Mobile.Service.TableController1.Query的Microsoft.WindowsAzure.Mobile.Service.MappedEntityDomainManager
2.Query()上的AutoMapper.QueryableExtensions.ProjectionExpression`1.ToTResult ()
知道为什么我得到一个空引用?
注意
调试时,在 GetAllMessageDTO
方法的TableController类中抛出错误:
public class MessageController : TableController<MessageDTO>
{
.
.
.
// GET tables/Message
public IQueryable<MessageDTO> GetAllMessageDTO()
{
return Query(); // Error is triggering here
}
.
.
.
}
调试时,如果发生此错误,则不会访问映射的任何行,因为在我初步确定服务时,映射就完成了 .
1 回答
我想你可以简单地解决这个问题 .
请考虑以下示例:
以下映射语句将根据需要解决它们:
在这种情况下不需要ValueResolver,因为您的行为非常简单 - 如果没有值则为空字符串,如果存在则为值 . 您可以替换StringConvert()方法,而不是调用.ToString() . 这里重要的是在Nullable包装器上使用.HasValue属性,并在存在时访问.Value属性 . 这避免了需要从int转换的复杂性?到int .
为了将持久化的字符串值转换回枚举,我建议您探索这个问题:How should I convert a string to an enum in C#?您应该能够使用相同的映射逻辑 .
这是一个更详细的.NET小提琴:https://dotnetfiddle.net/Eq0lof