Comment mapper un nullable propriété à un DTO à l'aide de AutoMapper?

Je suis l'élaboration d'un Azure Mobile Services, dans mon modèle de quelques de la les relations sont en option, rendant les propriétés qui représentent à accepter les valeurs null.

Par exemple, mon Message entité dans mon modèle est la classe comme ceci:

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; }
}

Et mon DTO pour qu'il ressemble à ceci:

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; }
}

Dans mon AutoMapper de configuration, j'ai ceci:

        /*
* 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)));

Pour référence, le MySqlFuncs classe a cette fonction pour gérer la conversion de string en int et int en 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;
}
}

Alors que je suis en mesure d'insérer des éléments, je ne suis pas en mesure de les obtenir en raison de l'erreur suivante:

Carte manquante de Nullable`1 ' à la Chaîne. Créez à l'aide de
Mappeur.CreateMap<Nullable`1, String>

De ce que je reçois que la ligne resonsible pour cette erreur dans mon AutoMapper définition est:

            .ForMember(messageDTO => messageDTO.ParentId, map => 
map.MapFrom(message => message.ParentId))

AutoMapper besoin de savoir comment mapper les valeurs null int de la ParentId propriété dans la DTO. J'ai essayé d'utiliser les fonctions de MySqlFuncs classe:

            .ForMember(messageDTO => messageDTO.ParentId, map => 
map.MapFrom(message => MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.ParentId))))

Mais qui donne montre l'erreur:

impossible de convertir de 'int?' int'

Si l'on considère la configuration doit être fait de manière à LINQ peut lire un convertir correctement, comment puis-je définir le mappage de sorte que toute nullable propriétés répertoriées dans la chaîne de caractères comme une chaîne vide ("" ou tout simplement la valeur null dans ma DTO?

EDIT 1

Il semble que pour que cela soit résolu, je dois utiliser un ValueResolver, dont j'ai codé comme suit:

public class NullableIntToStringResolver : ValueResolver<int?, string>
{
protected override string ResolveCore(int? source)
{
return !source.HasValue ? "" : MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(source));
}
}

Et changé la cartographie de cette:

cfg.CreateMap<Message, MessageDTO>()
.ForMember(messageDTO => messageDTO.ParentId, 
map => map.ResolveUsing(
new NullableIntToStringResolver()).FromMember(message => message.ParentId))

Mais ce qui me donne l'erreur

Objet de référence non définie à une instance d'un objet

Et la StackTrace est-ce:

à
AutoMapper.QueryableExtensions.Extensions.ResolveExpression(PropertyMap
propertyMap, Type currentType, Expression instanceParameter)

au AutoMapper.QueryableExtensions.Extensions.CreateMemberBindings(IMappingEngine
mappingEngine, TypePair typePair, TypeMap typeMap, Expression
instanceParameter, IDictionary`2 typePairCount)

au AutoMapper.QueryableExtensions.Extensions.CreateMapExpression(IMappingEngine
mappingEngine, TypePair typePair, Expression instanceParameter,
IDictionary`2 typePairCount)

au AutoMapper.QueryableExtensions.Extensions.CreateMapExpression(IMappingEngine
mappingEngine, TypePair typePair, IDictionary`2 typePairCount)

au AutoMapper.QueryableExtensions.Extensions.<>c__DisplayClass1`2.b__0(TypePair
tp)

au Système.Les Collections.De façon concomitante.ConcurrentDictionary`2.GetOrAdd(TKey
touche, touche Func`2 valueFactory)

au AutoMapper.Interne.DictionaryFactoryOverride.ConcurrentDictionaryImpl`2.GetOrAdd(TKey
touche, touche Func`2 valueFactory)

au AutoMapper.QueryableExtensions.Extensions.CreateMapExpression[TSource,TDestination](IMappingEngine
mappingEngine)

au AutoMapper.QueryableExtensions.ProjectionExpression`1.ToTResult

au
Microsoft.WindowsAzure.Mobile.Service.MappedEntityDomainManager`2.Query()
chez Microsoft.WindowsAzure.Mobile.Service.TableController`1.Query()

Aucune idée de pourquoi je me fais référence null?

Remarque

Lors du débogage de l'erreur est levée dans mon TableController classe dans le GetAllMessageDTO méthode:

public class MessageController : TableController<MessageDTO>
{
.
.
.
//GET tables/Message
public IQueryable<MessageDTO> GetAllMessageDTO()
{
return Query(); //Error is triggering here
}
.
.
.
}

Lors du débogage d'aucune des lignes de la cartographie sont accessibles lorsque cette erreur se produit, depuis la cartographie est effectuée lorsque le service est initialisé aussi loin que je peux voir.

  • Pour info, même si vous arrive d'être la création d'un Azure Mobile Service, il ne semble pas que votre question (ou les éventuelles réponses) rien avoir à faire avec Azure. Je vous recommande de supprimer cette balise, et de modifier votre texte question de supprimer la référence à Azure.
  • La raison pour laquelle vous obtenez le cannot convert from 'int?' to 'int' erreur, c'est que votre fonction StringConvert prend un int et pas un int?. Avez-vous essayé de créer une surcharge de cette méthode qui prend un int? ou éventuellement à l'aide d'un conditionnel (message.ParentId.HasValue ? : MySqlFuncs.LTRIM(MySqlFuncs.StringConvert(message.ParentId.Value)) : null)
  • Je l'ai fait, mais le problème est que lorsque la cartographie de la valeur null à partir de la classe du Modèle qui est un int? à la DTO de la chaîne de propriété, AutoMapper besoin d'une manière de résoudre ce problème. Je vérifie maintenant ValueResolver implémentations, mais j'obtiens le message d'erreur Object reference not set to an instance of an object. Vérifier mes mises à jour question.
  • Une façon simple de le faire ce serait .ConstructUsing: Mapper.CreateMap<int?, string>() .ConstructUsing(i => i.HasValue ? i.Value.ToString() : null);. Cela fonctionne pour vous?
  • J'ai eu à mettre en œuvre différemment, parce que votre code ne s'applique pas à ces valeurs, pour cette: cfg.CreateMap<int?, string>().ConstructUsing(i => i.SourceValue != null ? i.SourceValue.ToString() : ""); mais cela lève l'erreur Type 'System.String' does not have a default constructor, de ce que j'ai lu cela signifie que le code est de remplacer le mappage par défaut fournis par AutoMapper, de sorte qu'il shouldln pas être fait.
  • Débogage et de voir où l'objet est null, probablement message dans message.ParentId.
  • Im placer un point d'arrêt dans cette ligne, mais il n'est jamais atteint, l'exception est levée sur mon TableController de Message sur la GetAllMessageDTO méthode sur la ligne return Query();

InformationsquelleAutor Uriel Arvizu | 2015-01-20