Can AutoMapper map shared properties?
Entity
public class Entity
{
public static string SomeProperty {get; set;}
...
...
...
}
View Model
public class ViewModel
{
public string SomeProperty {get; set;}
...
...
...
}
Although I have not used AutoMapper yet, I see no reason why you would not be able to achieve what you are looking for. Based on the project's documentation on Projection, you can write a projector:
Mapper.CreateMap<Entity, ViewModel>()
.ForMember(dest => dest.SomeProperty, opt => opt.MapFrom(src => src.SomeProperty));
// Perform mapping
ViewModel form = Mapper.Map<Entity, ViewModel>(entity);
You should use a code like this:
Mapper.CreateMap<Entity, ViewModel>()
.ForMember(
dest => dest.SomeProperty,
opt => opt.MapFrom(src => Entity.SomeProperty));
Related
I'm using automapper with CQRS pattern. Below is my class which takes input from .net core API. The API takes collection as input and I'm sending collection in my Mediatr Command object. In Mediatr command, I'm mapping source collection to destination collection and while doing mapping, I'm getting following exception:
AutoMapper.AutoMapperMappingException
HResult=0x80131500
Message=Error mapping types.
Inner Exception 1:
AutoMapperMappingException: Missing type map configuration or unsupported mapping.
I'm using follwing code for mapping:
var insertData = _mapper.Map<List<Source>, List<Destination>>(request.Data.ToList());
In my class, I have following:
public class Source: ICustomMapping
{
public int? Prop1 { get; set; }
public string Prop2 { get; set; }
public void CreateMappings(Profile configuration)
{
configuration.CreateMap<Destination, Source>()
.ForMember(dto => dto.Prop1 , opt => opt.MapFrom(p => p.Prop1 ))
.ForMember(dto => dto.Prop2, opt => opt.MapFrom(p => p.Prop2))
;
}
}
This mapping works flawlessly when I have single object in both ways (forward and reverse). Now I need to pass collection of object for processing and save destination collection data in to database.
After looking in to the documentation I realize that I do not have the reverse mapping.
public void CreateMappings(Profile configuration)
{
configuration.CreateMap<Destination, Source>()
.ForMember(dto => dto.Prop1 , opt => opt.MapFrom(p => p.Prop1 ))
.ForMember(dto => dto.Prop2, opt => opt.MapFrom(p => p.Prop2))
.ReverseMap();
}
The ReverseMap() I was missing.
Thanks
I am using AutoMapper 6.2.2 to map a composite object (made up of two objects) into one, and it isn't working.
Source/destination types
Source type: a composite object (using suggestion from https://github.com/AutoMapper/AutoMapper/issues/389)
public class JoinedJob
{
public JobInfo job { get; set; }
public LocationInfo loc { get; set; }
}
Destination type:
public class JobDetailsModel
{
public int JobID { get; set; }
public string JobType { get; set; }
}
For testing purposes, I am using this very simple destination class, ignoring the source loc fields completely for now.
Mapping configuration
CreateMap<JoinedJob, JobDetailsModel>()
.ForMember(dest => dest.JobID, opt => opt.MapFrom(src => src.job.JobID))
.ForMember(dest => dest.JobType, opt => opt.MapFrom(src => src.job.JTName));
Expected behavior
I expect the JoinedJob object to get properly mapped to the JobDetailsModel object.
Actual behavior
This doesn't work at all when using ProjectTo<>. The objects I got back had null and zero values.
Steps to reproduce
So I tested a single instance:
var testDetail = _mapper.Map<JoinedJob, JobDetailsModel>(testJob);
and this exception is thrown:
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type.
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters.
JoinedJob -> JobDetailsModel (Destination member list)
NDSWebAPI.Modules.Core.BusinessLogic.JobRetrieval+JoinedJob -> NDSWebAPI.Models.Core.JobDetailsModel (Destination member list)
Unmapped properties:
JobID
JobType
It's as if AutoMapper doesn't recognize the .ForMember mappings I defined?
Should this work? I have no problems at all with mapping single objects (e.g. mapping JobInfo to JobDetailsModel works). The problem is introduced with the composite object.
I'm using automapper library to convert my Model into my ViewModel. For each Model, I create profile which inside i add my maps using CreateMap.
I want to use custom ValueResolver in which it will get the logged user ID from IContext, so i need to pass implementation of IContext using Ninject.
Inside my profile class:
Mapper.CreateMap<ViewModel, BusinessModel>()
.ForMember(dest => dest.ManagerId, opt => opt.ResolveUsing<GetManagerResolver>());
Then my GetManagerResolver:
public class GetManagerResolver : ValueResolver<BusinessModel, int>
{
private IContext context;
public GetManagerResolver(IContext context)
{
this.context = context;
}
protected override int GetManagerResolver(BusinessModel source)
{
return context.UserId;
}
}
But i get this exception message {"Type needs to have a constructor with 0 args or only optional args\r\nParameter name: type"}.
Any Ideas on how make automapper use ninject for object creation?
UPDATE
My code to add automapper configuration:
public static class AutoMapperWebConfiguration
{
public static void Configure()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile(new Profile1());
cfg.AddProfile(new Profile2());
// now i want to add this line, but how to get access to kernel in static class?
// cfg.ConstructServicesUsing(t => Kernel.Get(t));
});
}
}
You can use the ConstructedBy function to configure how Automapper should create your GetManagerResolver after calling ResolveUsing:
Mapper.CreateMap<ViewModel, BusinessModel>()
.ForMember(dest => dest.ManagerId,
opt => opt.ResolveUsing<GetManagerResolver>()
.ConstructedBy(() => kernel.Get<GetManagerResolver>());
Or you can globally sepecify your Ninject kernel to be used by Automapper when resolving any type with Mapper.Configuration.ConstructServicesUsing method:
Mapper.Configuration.ConstructServicesUsing((type) => kernel.Get(type));
What I ended up doing was to create NinjectModule for Automapper where I put all my automapper configuration and tell automapper to use Ninject Kernel to construct objects. Here is my code:
public class AutoMapperModule : NinjectModule
{
public override void Load()
{
Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(t => Kernel.Get(t));
cfg.AddProfile(new Profile1());
cfg.AddProfile(new Profile2());
});
}
}
I am trying to map using AutoMapper.
Source object is C# object
public class Source
{
public string comment { get; set; }
public string Subject { get; set; }
public Account Account{ get; set; }
}
public class Account
{
public string FirstName{ get; set; }
public string LastName{ get; set; }
}
My destination is a CRM entity named crm_destination which has fields like comment , Subject but it also has a LookUp field account of type Account.
But I don't know how to map LookUp field.
Following is my Automapper
AutoMapper.Mapper.CreateMap<Source, Destinaetion>()
.ForMember(dest => dest.comment, opt => opt.MapFrom(src => src.comment))
.ForMember(dest => dest.account, opt => opt.MapFrom(src => src.account));
.ForMember(dest => dest.account, opt => opt.MapFrom(src => src.account)) is throwing error of type mismatch..
basically my problem is I don't know how to map LookUp field where CRM entity is destination.
You need to use a custom resolver in AutoMapper. In your custom resolver you'll need to query CRM to get the ID of the account record in that matches your Account object and return an EntityReference to this object.
Lookups are stored as EntityReferences in CRM. So you'll need to convert your account to an EntityReference.
I have never used AutOMapper, but what Nicknow suggests makes sense. Just call the ToEntityReference() method on Entity.
I solved it. Lookup creates a relationship. my lookp created a relationship name cxrm_account_cxrm_source_account
so instead of .ForMember(dest => dest.account, opt => opt.MapFrom(src => src.account))
I did
.ForMember(dest => dest.cxrm_account_cxrm_source_account, opt => opt.MapFrom(src => src.Account))
now it is working
In automapper, how would I map a namevalue collection to a strongly typed collection?
Mapper.Map<NameValueCollection, List<MetaModel>>();
public class MetaModel
{
public string Name;
public string Value;
}
Piggybacking off of #dtryon's answer, the tough part about this is that there's no way to map the internal objects in NameValueCollection to your DTO type.
One thing you could do is write a custom converter that constructs KeyValuePair<string, string> objects from the items in the NameValueCollection. This would allow you to create a generic converter that leverages another mapping from KeyValuePair to a destination type of your choosing. Something like:
public class NameValueCollectionConverter<T> : ITypeConverter<NameValueCollection, List<T>>
{
public List<T> Convert(ResolutionContext ctx)
{
NameValueCollection source = ctx.SourceValue as NameValueCollection;
return source.Cast<string>()
.Select (v => MapKeyValuePair(new KeyValuePair<string, string>(v, source[v])))
.ToList();
}
private T MapKeyValuePair(KeyValuePair<string, string> source)
{
return Mapper.Map<KeyValuePair<string, string>, T>(source);
}
}
Then you would need to define a mapping from KeyValuePair<string, string> to MetaModel:
Mapper.CreateMap<KeyValuePair<string, string>, MetaModel>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Key))
.ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.Value));
And finally, create a mapping between NameValueCollection and List<MetaModel>, using the custom converter:
Mapper.CreateMap<NameValueCollection, List<MetaModel>>()
.ConvertUsing<NameValueCollectionConverter<MetaModel>>();
Well, since NameValueCollection is so special, I don't think there is a good way to do this. This is mostly due to the fact that you can't get a handle on a key/value object inside the NameValueCollection. Luckily the code to map to the List<MetaModel> is not that bad. I would just map it manually and continue working:
[TestMethod]
public void TestMethod2()
{
List<MetaModel> dest = new List<MetaModel>();
NameValueCollection src = new NameValueCollection();
src.Add("Key1", "Value1");
src.Add("Key2", "Value2");
src.Add("Key3", "Value3");
src.Add("Key4", "Value4");
src.Add("Key5", "Value5");
foreach (var srcItem in src.AllKeys)
{
dest.Add(new MetaModel() { Name = srcItem, Value = src[srcItem] });
}
Assert.AreEqual(5, dest.Count);
}