AutoMapper with IList<Item> - automapper

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());

Related

Automapper v5 Ignore unmapped properties

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

Inject parameter into automapper custom ValueResolver using Ninject

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

Automapper expression must resolve to top-level member

I am using automapper to map source and destination objects. While I map them I get the below error.
Expression must resolve to top-level member. Parameter name: lambdaExpression
I am not able resolve the issue.
My source and destination objects are:
public partial class Source
{
private Car[] cars;
public Car[] Cars
{
get { return this.cars; }
set { this.cars = value; }
}
}
public partial class Destination
{
private OutputData output;
public OutputData Output
{
get { return this.output; }
set { this.output= value; }
}
}
public class OutputData
{
private List<Cars> cars;
public Car[] Cars
{
get { return this.cars; }
set { this.cars = value; }
}
}
I have to map Source.Cars with Destination.OutputData.Cars object. Could you please help me in this?
You are using :
Mapper.CreateMap<Source, Destination>()
.ForMember( dest => dest.OutputData.Cars,
input => input.MapFrom(i => i.Cars));
This won't work because you are using 2 level in the dest lambda.
With Automapper, you can only map to 1 level. To fix the problem you need to use a single level :
Mapper.CreateMap<Source, Destination>()
.ForMember( dest => dest.OutputData,
input => input.MapFrom(i => new OutputData{Cars=i.Cars}));
This way, you can set your cars to the destination.
Define mapping between Source and OutputData.
Mapper.CreateMap<Source, OutputData>();
Update your configuration to map Destination.Output with OutputData.
Mapper.CreateMap<Source, Destination>().ForMember( dest => dest.Output, input =>
input.MapFrom(s=>Mapper.Map<Source, OutputData>(s)));
You can do it that way:
// First: create mapping for the subtypes
Mapper.CreateMap<Source, OutputData>();
// Then: create the main mapping
Mapper.CreateMap<Source, Destination>().
// chose the destination-property and map the source itself
ForMember(dest => dest.Output, x => x.MapFrom(src => src));
That's my way to do that ;-)
ForPath works for this exact scenario.
Mapper.CreateMap<Destination, Source>().ForPath(dst => dst.OutputData.Cars, e => e.MapFrom(src => src.Cars));
This worked for me:
Mapper.CreateMap<Destination, Source>()
.ForMember(x => x.Cars, x => x.MapFrom(y => y.OutputData.Cars))
.ReverseMap();
The correct answer given by allrameest on this question should help: AutoMapper - Deep level mapping
This is what you need:
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.OutputData, opt => opt.MapFrom(i => i));
Mapper.CreateMap<Source, OutputData>()
.ForMember(dest => dest.Cars, opt => opt.MapFrom(i => i.Cars));
When using the mapper, use:
var destinationObj = Mapper.Map<Source, Destination>(sourceObj)
where destinationObj is an instance of Destination and sourceObj is an instance of Source.
NOTE: You should try to move away from using Mapper.CreateMap at this point, it is obsolete and will be unsupported soon.

Using AutoMapper on shared properties?

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

Automapper - Convert NameValueCollection to Strongly Typed Collection

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

Resources