AutoMapper - How to pass parameters into a Custom Resolver using ConstructedBy method? - c#

In my ASP.NET MVC 2 (RC) project - I'm using AutoMapper to map between a Linq to Sql class (Media) and a view model (MediaVM). The view model has a SelectList property for a drop down in the view. I have a custom value resolver to populate the SelectList property items from the db, but am wondering if there's a way to pass a couple values from the source model into the resolver (using ConstructedBy method?) to a) define the selected item and b) filter the items from the db. The source object gets passed into the custom resolver - but the resolver is used on several different view models with different types of source objects, so would rather define where to get the values from in my mapping config. Here is my view model:
public class MediaVM
{
public bool Active { get; set; }
public string Name { get; set; }
[UIHint("DropDownList")]
[DisplayName("Users")]
public SelectList slUsers { get; private set; }
}
The automapper mapping config:
Mapper.CreateMap<Media, MediaVM>()
.ForMember(dest => dest.slUsers, opt => opt.ResolveUsing<UsersSelectListResolver>());
It would be nice to be able to do something like this on the .ForMember mapping clause:
.ConstructedBy(src => new UsersSelectListResolver(src.UserID, src.FilterVal))
Is there a way to accomplish this?

I like that idea as a feature request. You can do something like that right now, with MapFrom:
ForMember(dest => dest.slUsers, opt => opt.MapFrom(src => new UsersSelectListResolver(src).Resolve(src));

I found your posting trying to do the same thing. I decided on a simple approach and skip trying to map to my select list directly via AutoMaper. I simply return an array into my ViewModel and reference that object for my select list. The array gets mapped, select list object does not. Simple, effective. And, IMHO each is doing it's intended task - the mapper maps, the ViewModel does the layout
View Model code:
[DisplayName("Criterion Type")]
public virtual CriterionType[] CriterionTypes { get; set; }
[DisplayName("Criterion Type")]
public SelectList CriterionTypeList
{
get
{
return new SelectList(CriterionTypes, "Id", "Key");
}
}
my mapper:
Mapper.CreateMap<Criterion, CriterionForm>()
.ForMember(dest => dest.CriterionTypeList, opt => opt.Ignore());

Related

Automapper - Mapping the index into a property of collection

I'm mapping a domain model to a DTO and vice versa. I'm trying to configure my API to accept a DTO with a collection, where the order of that collection will map to a int Sequence in my domain object for persistence.
public class Model {
public ICollection<Fields> Fields { get; set; }
}
public class Field {
public int Sequence { get; set; }
}
CreateMap<ModelView, Model>()
.ForMember(x => x.Fields, opt => opt...)
// here I want to specify that currentField.Sequence = Model.Fields.IndexOf(currentField)
// , or to set it equal to some counter++;
;
Is such a thing possible in Automapper, or would I have to write my own ConstructUsing() method to do this logic? I'm hesitant to use ConstructUsing() because I have a mapping specified for the Field DTO and I don't want to duplicate that logic.
I also would like to be able to configure it so that when I'm going back to my DTO (Model -> ModelView) that I can insert the Fields into the collection in the order specified by Sequence.
I think I found the solution I was looking for. Using AfterMap() I'm able to override these values from being mapped directly:
CreateMap<Model, ModelView>()
.AfterMap((m, v) =>
{
v.Fields = v.Fields?.OrderBy(x => x.Sequence).ToList();
//ensure that the DTO has the fields in the correct order
})
;
CreateMap<ModelView, Model>()
.AfterMap((v, m) =>
{
//override the sequence values based on the order they were provided in the DTO
var counter = 0;
foreach (var field in m.Fields)
{
field.Sequence = counter++;
}
})

Automapper unmapped properties error with simple objects mappings

I need to map a model object coming from API to my actual entity object on DbContext. It is used when creating a new machine object using a POST action.
As always, I created a simple map for the source/destination objects.
In this case we consider the source object as the API model and the destination object as the entity. Also the model has just a subset of properties of the entity.
Source/destination types
// Destination (entity on DbContext)
public class Machine
{
public long Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string MnmConfiguration { get; set; }
public MachineBootStatus Status { get; set; }
public long MachineDriverId { get; set; }
public MachineDriver MachineDriver { get; set; }
public string DriverConfiguration { get; set; }
public string DriverStatus { get; set; }
}
// Source (from API controller)
public class MachineCreateModel
{
public string Name { get; set; }
public string Description { get; set; }
public string MnmConfiguration { get; set; }
public long MachineDriverId { get; set; }
}
Mapping configuration
public class DomainProfile : Profile
{
public DomainProfile()
{
//CreateMap<MachineCreateModel, Machine>();
// Update 2019/01/30 with proposed solution
CreateMap<MachineCreateModel, Machine>(MemberList.Source);
}
}
I'm using Unity DI container and the configuration of AutoMapper is this:
container = new UnityContainer();
// ... some other configurations...
container.RegisterType<IMapper, Mapper>(new InjectionConstructor(new MapperConfiguration(cfg => cfg.AddProfile<DomainProfile>())));
Version
Using AutoMapper v8.0.0.
Expected behavior
I expect to obtain a simple automatic mapping without errors, since my source model is just a subset of properties of the destination model, with same names.
Actual behavior
I get this error about unmapped properties when I hit this line of code:
Machine entity = Mapper.Map<Machine>(request.Machine);
[14:08:34.363 8 2e62361a INF] Creating new machine: TEST M1
[14:08:36.205 8 bd577466 ERR] An unhandled exception has occurred while executing the request.
AutoMapper.AutoMapperConfigurationException:
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
=================================================================================================
AutoMapper created this type map for you, but your types cannot be mapped using the current configuration.
MachineCreateModel -> Machine (Destination member list)
MyApplication.Dcs.Application.Models.MachineCreateModel -> MyApplication.Dcs.Domain.Entities.Machine (Destination member list)
Unmapped properties:
Id
Status
MachineDriver
DriverConfiguration
DriverStatus
at AutoMapper.ConfigurationValidator.AssertConfigurationIsValid(IEnumerable`1 typeMaps)
at lambda_method(Closure , MachineCreateModel , Machine , ResolutionContext )
at lambda_method(Closure , Object , Object , ResolutionContext )
at AutoMapper.Mapper.AutoMapper.IMapper.Map[TDestination](Object source)
at MyApplication.Dcs.Application.Commands.MachineCreateCommandHandler.Handle(MachineCreateCommand request, CancellationToken cancellationToken) ..Commands\MachineCreateCommand.cs:line 28
Note
In my solution I've many projects and 3 of them are making use of AutoMapper (same version for all). There're 3 different DomainProfile.cs files (1 for each project) with the needed mappings.
In the other 2 DomainProfile classes I've some manual mappings (see example below) because I need to "translate" an object with italian property names to another one with english property names. So there're many lines for each object mapping, such as:
CreateMap<ArticleCreateUpdateModel, Articoli>()
.ForMember(d => d.Categoria, opt => opt.MapFrom(src => src.Category))
.ForMember(d => d.CodiceArticolo, opt => opt.MapFrom(src => src.Code))
.ForMember(d => d.Descrizione, opt => opt.MapFrom(src => src.Description))
.ForMember(d => d.Famiglia, opt => opt.MapFrom(src => src.Family))
.ForMember(d => d.Note, opt => opt.MapFrom(src => src.Note))
...
I don't know if the usage of those manual members mapping on one or more DomainProfile class, obliges me in some way to always explain all the subsequent mappings, even if they should be simple like those of this example.
By default, AutoMapper validates the destination properties. As there are neither matching properties nor ForMember constructs for a bunch of properties in your destination type you get the exception above.
Try to validate on the source properties instead:
CreateMap<ArticleCreateUpdateModel, Articoli>(MemberList.Source)
.ForMember(d => d.Categoria, opt => opt.MapFrom(src => src.Category))
// ...
Remark:
On the other hand, I have to mention that this is the typical case when AutoMapper is an overkill. Apart from trivial cases I would never use it anymore.
I had to use it in a project for more than a year but actually it is only good for making simple things more complicated than necessary. Some FromDto and ToDto [extension] methods are just simpler, faster, easier to debug and more reactive to code changes. Mapping between different class layouts or property names often results practically as much code (or even more with tons of lambdas) as simply writing the mapping manually. See also this link.

Automapper: Want to Reuse Code via Interface Mappings

I'm building CMS like application.
For example my BlogPost page contains several widget areas. Each widget hosts a serie of "related" blog posts.
All my views are pure presentational, i build urls, convert datetime and ints into strings in my service layer. I find this approach easier to maintain, since views have zer0 logic. All logic is consolidated into AutoMapper's resolvers, converters and custom transformation logic.
So lets come closer to the problem at hand.
To create Url i need 2 parameters: BlogId and BlogSlug, my urls look like b/{id}/{slug}.html
Im quite happy with that.
In my CSM i use so called "source models", a model that is not view model, but an intermediate representation of it. Why do i have to resort to such wicked solutions ?
Well, lets take a look how a typical data retrieval code can look like in my project:
.Select(x =>
{
Id = x.Id,
BlogId = x.Blog.Id,
BlogSlug = x.Blog.Slug,
// Here is the trap, LINQ provider will throw an exception, since he doesn't know how to translate function into expression
BlogUrl = Url.Action("RenderPost", "BlogController", new { Id = x.Blog.Id, slug = x.Blog.Slug })
}
So thats not an option.
Luckily, we can do this
.Select(x => new
{
Id = x.Id,
BlogId = x.Blog.Id,
BlogSlug = x.Blog.Slug
}
.ToList()
.Select(x => new
{
// This works
BlogUrl = Url.Action("RenderPost", "BlogController", new { Id = x.BlogId, slug = x.BlogSlug })
}
Copy paste this stuff into each and every action method that renders different "intresting blog" parts (they have different visual representation as well cant use same view model) ? Not a good way, so i came up with a solution.
I created "source model", so the code will be
.Select(x => new BlogPostSourceViewModel
{
Id = x.Id,
BlogId = x.Blog.Id,
BlogSlug = x.Blog.Slug
}
.ToList()
.Select(x => x.ToBlogPostViewModel()) // Extension method { return Mapper.Map<>() }
.ToList();
This surely looks better, but i have many different models like BlogPostSourceViewModel, BlogAuthorSourceViewModel, BlogCommentSourceViewModel. They all need this link building logic.
Ok, i extract the needed source data (BlogId, BlogSlug) into an interface
BlogPostSourceViewModel : IBlogPostUrl
BlogAuthorSourceViewModel: IBlogPostUrl
BlogCommentSourceViewModel : IBlogPostUrl
Then i define the mappings
Mapper.CreateMap<BlogPostSourceViewModel, BlogPostViewModel>
.ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())
Mapper.CreateMap<BlogAuthorSourceViewModel, BlogAuthorViewModel>
.ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())
Mapper.CreateMap<BlogCommentSourceViewModel, BlogCommentViewModel>
.ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())
Resolver:
BlogPostUrlResolver : ValueResolver<IBlogPostUrl, String>
// Here goes the url building logic
As you see the more models i have that need blog url the more identical mappings i have to add. This is ok for now, but as project grows it will be painful.
Ideally i would want to have it like this:
Mapper.CreateMap<IBlogPostUrl, SomeOtherInterfaceWithBlogUrlAsString>
.ForMember(dest => dest.BlogUrl, opt => opt.ResolveUsing<BlogPostUrlResolver>())
but Automapper doesn't understand it. And i dont know how if there is other way to do it.
Any ideas ?
If I understand the question correctly...
In order to use AutoMapper mapping capabilities across all of your classes you can provide base class that will use Generics:
public abstract class Base<Entity, ViewModel>
where Entity : EntityObject
where ViewModel : BaseViewModel
{
// you will call this method from your operations class using base
public SomeViewModel GetData()
{
public Entity entityObject = db.Entity.SingleOrDefault();
public ViewModel yourViewModelName = base.Map(entityObject);
return yourViewModelName;
}
....
// this will be defined only once for each mapping direction
// ie. there will be multiple of these.
public static Entity Map(ViewModel typeViewModel)
{
try
{
Mapper.CreateMap<ViewModel, Entity>();
Entity t = Mapper.Map<ViewModel, Entity>(typeViewModel);
return t;
}
catch (Exception exc)
{
throw exc;
}
}
}
// this will be your class for database operations on specific Entity
// that inherits generic base, with its AutoMapping setup.
public class DataBaseOperationsClass : Base<SomeEntity, SomeViewModel>
{
public SomeViewModel Get()
{
return base.GetData();
}
}
Hope this helps !

Automapper to create object from XML

If I have the following class:
class SPUser
{
public int ID { get; set; }
public string Name { get; set; }
public string LoginName { get; set; }
public string Email { get; set; }
public bool IsSiteAdmin { get; set; }
public bool IsSiteAuditor { get; set; }
public bool IsDomainGroup { get; set; }
public List<SPGroup> Groups { get; set; }
}
And I am using the sharepoint web services, which return an XML with an attribute for each property on my class, such as:
<Users>
<User Name="name" Description="desc" ..... />
</Users>
Is there any way to use AutoMapper to map the XML fragment to an SPUser class instance?
Blog has been deleted - here's the Bing archive of the post by #DannyDouglass
Simplify Using Xml Data With AutoMapper and Linq-to-Xml
I recently ran into a scenario at work that required manually consuming several SOAP web services, which I’m sure you can imagine was rather monotonous. A co-worker (Seth Carney) and I tried a few different approaches, but we finally settled on a solution that simplified consumption of the xml and ultimately made the code more testable. That solution centered around leveraging AutoMapper, an open source object-object mapping tool, to create a link between the XElements(http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement.aspx) returned in the SOAP messages and custom contracts we created – in a reusable manner.
I put together a quick demo that shows how you could use the same approach to consume and display the Twitter Public Timeline (http://api.twitter.com/1/statuses/public_timeline.xml) (using the API’s Xml response type).
Note: The source code for the following example can be found on my GitHub page: https://github.com/DannyDouglass/AutoMapperXmlMappingDemo
Getting the Project Setup
After creating a basic MVC3 (download beta) project and the associated test project, the first step was to get the AutoMapper package installed. I have been using NuGet, Microsoft’s recently announced package management system, to install any open source dependencies. The following command was all that was needed to setup AutoMapper in my MVC3 project (read more about NuGet here(http://weblogs.asp.net/scottgu/archive/2010/10/06/announcing-nupack-asp-net-mvc-3-beta-and-webmatrix-beta-2.aspx) and here(http://weblogs.asp.net/scottgu/archive/2010/10/06/announcing-nupack-asp-net-mvc-3-beta-and-webmatrix-beta-2.aspx)):
PM> add-package AutoMapper
Creating the Mapping
With AutoMapper installed I’m ready to get started creating the components necessary for the xml-to-object mapping. The first step is creating a quick contract used in my application to represent the Tweet object:
public interface ITweetContract
{
ulong Id { get; set; }
string Name { get; set; }
string UserName { get; set; }
string Body { get; set; }
string ProfileImageUrl { get; set; }
string Created { get; set; }
}
Nothing crazy here – just a simple entity. These are all fields that are provided in the response from the Twitter API using a different name for some fields. In simple cases where the source and destination objects have the same name you can setup a map very quickly using this syntax:
Mapper.CreateMap<SourceObj, DestinationObj>();
However, AutoMapper does not support Xml by default I have to specify the fields that I will be mapping. Using the Fluent API in AutoMapper I’m able to chain my field mappings. Take a look at one example field mapped in my example – the tweet’s Body:
Mapper.CreateMap<XElement, ITweetContract>()
.ForMember(
dest => dest.Body,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("text")))
It may look complicated at first, but all that is really happening here is that we are providing details to AutoMapper on what value to use in my source object and how to map it to the destination object’s property. There is one particular line I would like to focus on in the above Body field mapping:
options => options.ResolveUsing<XElementResolver<ulong>>()
.FromMember(source => source.Element("id")))
The XElementResolver is a custom value resolver (http://automapper.codeplex.com/wikipage?title=Custom%20Value%20Resolvers) that Seth came up with to handle parsing the XmlElement source object to retrieve a strongly-typed value for use in the mapping. I’ll detail that more in a moment, but before we move on take a look at my full mapping:
Mapper.CreateMap<XElement, ITweetContract>()
.ForMember(
dest => dest.Id,
options => options.ResolveUsing<XElementResolver<ulong>>()
.FromMember(source => source.Element("id")))
.ForMember(
dest => dest.Name,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("user")
.Descendants("name").Single()))
.ForMember(
dest => dest.UserName,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("user")
.Descendants("screen_name").Single()))
.ForMember(
dest => dest.Body,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("text")))
.ForMember(
dest => dest.ProfileImageUrl,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("user")
.Descendants("profile_image_url").Single()))
.ForMember(
dest => dest.Created,
options => options.ResolveUsing<XElementResolver<string>>()
.FromMember(source => source.Element("created_at")));
The Generic XElementResolver
This custom value resolver is the real key that allowed these XElement-to-Contract maps to work in the original solution. I’ve reused this resolver in this example as we saw above. This was all that was necessary to create the custom resolver class:
public class XElementResolver<T> : ValueResolver<XElement, T>
{
protected override T ResolveCore(XElement source)
{
if (source == null || string.IsNullOrEmpty(source.Value))
return default(T);
return (T)Convert.ChangeType(source.Value, typeof(T));
}
}
This generic XElementResolver allows use to easily pass the type of the value retrieved in our mapping above. For example, the following syntax is used to strongly type the value retrieved from the XmlElement in the Id field’s .ForMember() declaration above:
ResolveUsing<XElementResolver<ulong>>()
With my mapping completely configured and instantiated, I’m ready to invoke the Twitter API and leverage AutoMapper to display that latest Public Timeline.
Putting the Pieces Together
I created a simple class responsible for retrieving the Twitter API response:
public class TwitterTimelineRetriever
{
private readonly XDocument _twitterTimelineXml;
public TwitterTimelineRetriever()
{
_twitterTimelineXml = XDocument
.Load("http://api.twitter.com/1/statuses/public_timeline.xml");
}
public IEnumerable<ITweetContract> GetPublicTimeline(int numberOfTweets)
{
var tweets = _twitterTimelineXml.Descendants("status")
.Take(numberOfTweets);
return tweets.Select(Mapper.Map<XElement, ITweetContract>).ToList();
}
}
The GetPublicTimeline method is a simple method returning, you guessed it, the Twitter Public Timeline by leveraging the map we created earlier:
return tweets.Select(Mapper.Map<XElement, ITweetContract>).ToList();
In my MVC3 site’s HomeController I can make a quick call to the retrieval method, requesting the last 10 results:
public class HomeController : Controller
{
private TwitterTimelineRetriever _twitterTimelineRetriever;
public ActionResult Index()
{
_twitterTimelineRetriever = new TwitterTimelineRetriever();
ViewModel.Message = "Twitter Public Timeline";
return View(_twitterTimelineRetriever.GetPublicTimeline(10));
}
}
And finally, after a little formatting in my View using the new Razor view engine from Microsoft, I have my public timeline displaying!
You need to check out XML serialization in .NET for this - that's the way to serialize an object to XML or deserialize it from XML.
Automapper can be used to set properties between two objects - it doesn't deal with XML at all.
More resources:
C# Tutorial - XML Serialization
Bit late in the day, but someone has used AutoMapper to map XML to a POCO rather than going down the XMLSerialization route. I found the following blog entry:-
Simplify Using Xml Data with AutoMapper and Linq-to-Xml
This is enough to get you started on implementing a generic custom resolver of your own if the example one is not enough.
EDIT: fixed link
EDIT: really fixed link
You can user XML deserialization for this purposes. In .NET we have XmlSerializer and DataContractSerializer

How to use AutoMapper?

First time using AutoMapper and I'm have a hard time figuring out how to use it.
I'm trying to map a ViewModel to my Database Tables.
My ViewModel looks like this...
public class AddressEditViewModel
{
public AddressEdit GetOneAddressByDistrictGuid { get; private set; }
public IEnumerable<ZipCodeFind> GetZipCodes { get; private set; }
public AddressEditViewModel(AddressEdit editAddress, IEnumerable<ZipCodeFind> Zips)
{
this.GetOneAddressByDistrictGuid = editAddress;
this.GetZipCodes = Zips;
}
}
The Mapping I'm trying to use is...
CreateMap<Address, AddressEditViewModel>();
When I run this test...
public void Should_map_dtos()
{
AutoMapperConfiguration.Configure();
Mapper.AssertConfigurationIsValid();
}
I get this error...
AutoMapper.AutoMapperConfigurationException: The following 2 properties on JCIMS_MVC2.DomainModel.ViewModels.AddressEditViewModel
are not mapped:
GetOneAddressByDistrictGuid
GetZipCodes
Add a custom mapping expression, ignore, or rename the property on JCIMS_MVC2.DomainModel.Address.
I'm not sure how I am supposed to map those 2 properties. I would appreciate any direction. Thanks
Mark
Ok so I can see a few things you are doing that probably won't help.
Firstly this AutoMapper is used to copy Properties in one object to Properties in a diff object. Along the way it might interrogate or manipulate them to get the end result viewmodel in the correct state.
The properties are named 'Get...' which sounds more like a method to me.
The setters on your properties are private so AutoSetter won't be able to find them. Change these to minimum internal.
Use of a parametrized constructor is no longer needed when you use AutoMapper - as you are converting directly from one object to another. The parametised constructor is there mainly to show what is explicitly required by this object.
CreateMap<Address, AddressEditViewModel>()
.ForMember( x => x.GetOneAddressByDistrictGuid ,
o => o.MapFrom( m => m."GetOneAddressByDistrictGuid") )
.ForMember( x => x.GetZipCodes,
o => o.MapFrom( m => m."GetZipCodes" ) );
What Automapper is really good for is copying from DataObjects into POCO objects, or View Model objects.
public class AddressViewModel
{
public string FullAddress{get;set;}
}
public class Address
{
public string Street{get;set;}
public string Suburb{get;set;}
public string City{get;set;}
}
CreateMap<Address, AddressViewModel>()
.ForMember( x => x.FullAddress,
o => o.MapFrom( m => String.Format("{0},{1},{2}"), m.Street, m.Suburb, m.City ) );
Address address = new Address(){
Street = "My Street";
Suburb= "My Suburb";
City= "My City";
};
AddressViewModel addressViewModel = Mapper.Map(address, Address, AddressViewModel);

Categories