Previously when I used Automapper v3.x ignoring unmapped properties could be done by simply adding a .IgnoreUnmappedProperties() extension which looked like this
public static class AutoMapperExtensions
{
public static IMappingExpression<TSource, TDestination> IgnoreUnmappedProperties<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var typeMap = Mapper.FindTypeMapFor<TSource, TDestination>();
if (typeMap != null)
{
foreach (var unmappedPropertyName in typeMap.GetUnmappedPropertyNames())
{
expression.ForMember(unmappedPropertyName, opt => opt.Ignore());
}
}
return expression;
}
}
How can this extension be rewritten to work with Version 5.x. I can of course add the following to each property.
.ForMember(dest => dest.LastUpdatedBy, opt => opt.Ignore())
or not call
Mapper.AssertConfigurationIsValid();
You can do that using the CreateMap method's memberList parameter to specify the validation that you want.
CreateMap<TSource, TDestination>(MemberList.None)
The MemberList.None should do the trick. You can also switch between the source or destination validations.
Automapper - Selecting members to validate
Related
For some reason AutoMapper gives me a list of the type I need, where all properties are 0 or null. When I debug on data I see my list with all object and properties containing data. But .Map() gives me a list with no data in the properties (but the correct amount of objects). I'm new to AutoMapper, but this does seem very weird. Any suggestions?
public static IMapper Initialize()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<PlantSelectResult, IPlant>()
.ForMember(
dest => dest.description,
opt => opt.MapFrom(src => src.description));
});
return new Mapper(config);
}
And my DataProvider:
public IEnumerable<IPlant> GetPlants()
{
using (var dbCtx = new DataClasses1DataContext(_connectionString.String))
{
var data = dbCtx.PlantSelect().ToList();
return automapper.Map<List<PlantSelectResult>, IPlant[]>(data);
}
}
I didn't realize I had removed set; on the properties. Fixing the interfaces so they were settable fixed the problem.
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());
});
}
}
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);
}
I have been using AutoMapper for some time now. I have a profile setup like so:
public class ViewModelAutoMapperConfiguration : Profile
{
protected override string ProfileName
{
get { return "ViewModel"; }
}
protected override void Configure()
{
AddFormatter<HtmlEncoderFormatter>();
CreateMap<IUser, UserViewModel>();
}
}
I add this to the mapper using the following call:
Mapper.Initialize(x => x.AddProfile<ViewModelAutoMapperConfiguration>());
However, I now want to pass a dependency into the ViewModelAutoMapperConfiguration constructor using IoC. I am using Autofac. I have been reading through the article here: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/05/11/automapper-and-ioc.aspx but I can't see how this would work with Profiles.
Any ideas?
Thanks
Well, I found a way of doing it by using an overload of AddProfile. There is an overload that takes an instance of a profile, so I can resolve the instance before passing it into the AddProfile method.
A customer of mine was wondering the same thing as DownChapel and his answer triggered me in writing some sample application.
What I've done is the following.
First retrieve all Profile types from the asseblies and register them in the IoC container (I'm using Autofac).
var loadedProfiles = RetrieveProfiles();
containerBuilder.RegisterTypes(loadedProfiles.ToArray());
While registering the AutoMapper configuration I'm resolving all of the Profile types and resolve an instance from them.
private static void RegisterAutoMapper(IContainer container, IEnumerable<Type> loadedProfiles)
{
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.ConstructServicesUsing(container.Resolve);
foreach (var profile in loadedProfiles)
{
var resolvedProfile = container.Resolve(profile) as Profile;
cfg.AddProfile(resolvedProfile);
}
});
}
This way your IoC-framework (Autofac) will resolve all dependencies of the Profile, so it can have dependencies.
public class MyProfile : Profile
{
public MyProfile(IConvertor convertor)
{
CreateMap<Model, ViewModel>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Identifier))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => convertor.Execute(src.SomeText)))
;
}
}
The complete sample application can be found on GitHub, but most of the important code is shared over here.
I have Article class with property
private IList<Tag> _tags;
public virtual IList<Tag> Tags
{
get{
if(_tags == null)
_tags = TagService.GetTags(this);
return _tags;
}
}
Since there is no SET for Tags automapper will not set tags when mapping from viewmodel to view. Any ideas?
Try using the UseDestinationValue option:
ForMember(dest => dest.Tags, opt => opt.UseDestinationValue());
In the latest DLL on the trunk, AutoMapper should pick up readonly list-type members.
You can Ignore then property using:
ForMember(dest => dest.Tags, opt => opt.Ignore());