I am using AutoMapper to map DTOs to entities.
When creating the maps, I always end up ignoring the relationships of the entities, and that usually result in a long, long list. That is:
Mapper.CreateMap<CUSTOMER_DTO, CUSTOMER_ENTITY>()
.ForMember(m => m.ORDERS, o => o.Ignore())
.ForMember(m => m.PAYMENT_METHODS, o => o.Ignore());
// And on and on!
Is there a way I can just indicate AutoMapper to ignore every relationship in my entities? Many thanks.
I'm assuming here that your DTOs are simply missing the relationships and you have to ignore them all by hand. If it's the case, this other answer will be helpful. You could just do:
Mapper.CreateMap<CustomerDto, CustomerEntity>().IgnoreAllNonExisting();
And it would ignore every property in CustomerEntity that's not present in CustomerDto.
As a side note: you should probably change the all-caps names for classes and properties to pascal cased, to follow the general C# coding conventions.
Related
We recently implemented automapper and currently looking to optimise assigning values from the DTO to model. currently we are doing something like
model.Property1 = dto.Property1;
model.SomePropertyType = dto.PropertyType;
model.Property2 = dto.Property2;
Now this could go pretty long and repetitive task to all Mapper classes.
Is there a way to simplify this on AutoMapper?
If you are using Automapper then have you not defined the maps (profile)? I believe, you might have defined those so please use those to instruct Automapper how to map source object to Target.
Another point Automapper also works based on naming convention, so if you have same property name in both source and target then it will automap automatically. So you don't unnecessary define the mapping fort it. To override mapping for a property (or whose name/type does not match), you can use .ForMember method.
cfg.CreateMap<MyDTO, MyModel>()
.ForMember(destination => destination.PropertyType,
opts => opts.MapFrom(source => source.SomePropertyType ));
You can read about Automapper at here.
Now in the code to get the mapped object, use it like
Mapper.Map<MyModel>(object of MyDTO);
I'm learning about NHibernate, where the class mapping, I learned, is done with XML. I understand that Fluent NHibernate came about as a strongly-typed replacement for the XML-style of mapping. Indeed, here is the fluent-nhibernate tag description:
Fluent NHibernate lets you write NHibernate mappings in strongly typed
C# code. This allows for easy refactoring, improved readability and
more concise code.
Then later I was using NHibernate Mapping Generator to create mappings and domain classes from my existing database, and it generated mapping code like this:
using NHibernate.Mapping.ByCode.Conformist;
using NHibernate.Mapping.ByCode;
namespace MyNamespace.Infrastructure.Mappings
{
public class MyItemMapping : ClassMapping<MyItem>
{
public MyItemMapping()
{
Table("MyItems");
Schema("master");
Lazy(true);
Id(x => x.ID, map => map.Generator(Generators.Assigned));
Property(x => x.Status, map => map.NotNullable(true));
Property(x => x.DueDate, map => map.NotNullable(true));
Property(x => x.NextReminderDate);
Property(x => x.DatePaid);
Property(x => x.Notes);
}
}
}
Lo and behold, it's using a NHibernate.Mapping.ByCode.Conformist.ClassMapping<T> class. What gives? If NHibernate in fact does have it's own strongly-typed, non-XML mapping capabilities, then why do I need Fluent NHibernate?
I've noticed some differences between NHibernate.Mapping.ByCode.Conformist.ClassMapping<T> and FluentNHibernate.Mapping.ClassMap<T>. For example, the former doesn't support References, e.g. References(x => x.BillingItemID);, to relate entities via the foreign key. Maybe there's another way of doing that.
FluentNHibernate was around before NHibernate had MappingByCode, now that it does, FluentNHibernate is obsolete, it's also less efficient than Nhibernate's own MappingByCode because it generates normal XML mapping files at startup and uses them internally.
The only downside to NHibernate MappingByCode is that there isn't much documentation for it, the best I've found is here:
http://notherdev.blogspot.co.uk/2012/02/nhibernates-mapping-by-code-summary.html
But I would use NHibernate's version regardless. I'm under the impression that NHibernate's version actually supports more than FluentNhibernate does as well, the equivalent of that Reference would just be the opposite side a relationship, e.g. if the parent is mapped as OneToMany() then the equivalent child side map to Fluent's Reference would be a ManyToOne(). I think that's the case anyway.
I'm beginning AutoMapper and had a question. I came across sample code like this:
Automapper.Mapper.CreateMap<Book, BookViewModel>()
.ForMember(dest => dest.Author,
opts => opts.MapFrom(src => src.Author.Name));
So this would let me convert a book to a book model, and then map the src to the src.author.name property because there was not a 1-1 mapping.
To confirm, it does not work in reverse on its own, meaning I now need to explicitly do this:
Mapper.Mapper.CreateMap<BookViewModel, Book>()
.ForMember(dest => dest.Author.Name,
opts => opts.MapFrom(src => src.Author));
Is that correct? So to further confirm, if I had 50 view models and views, I'd literally need 100 (one for the way in, one for the way out, plus any additional lines of code in the .ForMember expression)
Is this true? Is this a common practice (i.e. you could potentially see hundreds of lines of code to handle the field mapping back and forth for multiple DTOs with properties that do not match up 1-1)?
CreateMap creates a mapping for AutoMapper to use later when required. This is required only once in the lifetime of your Application. In order to make use of this mapping you have defined, you just need to call Automapper.Map method. Thus you don't have to create the mapping again and again.
Example:
var myBookViewModel = Automapper.Map<Book, BookViewModel>(myBook);
You are correct saying that defining a mapper for Book to BookViewModel will not make Automapper to create a mapper for BookViewModel to Book automatically for you. You have to create the mapper.
In essence yes, you'd need basically to specify both back and forth configurations to handle each DTO mapping. Without a deterministic pattern it is impossible to guess how the mapping would work between complex object when members' names are different, or when the properties you want are further within other objects, like in your example.
However, Automapper is fairly smart if you follow some basic patterns. In your case, if you change the DTO property name to AuthorName instead of Author, the CreateMap will properly map without having to specify the mapping manually (they call this "Flattening"). Automapper will even map methods to properties if they follow a GetProperty() => Property { get; set; } pattern. Renaming your DTO properties to follow this pattern could probably save you a bunch of lines. Take a look at their starters guide for other ways to simplify your mappings.
I am using Entity Framework 6 DBFirst, MVC5 and AutoMapper.
I have 2 tables, Customers and CustomerContacts
For each class I have in Entity Framework EDMX (auto-generated) I have a Model class with the exact same properties. i.e. Customer => Model.Customer, CustomerContact => Model.CustomerContact. This is why I use AutoMapper.
Question 1: Since my Customer_Get stored procedure returns an auto-generated complex type (Customer_Get_Result) would most people make an additional model class for this also? (Model.Customer_Get_Result.cs) Or are all of the properties supposed to get combined adding everything to Model.Customer?
Question 2: Is the way I am handling mapping for Customer Contacts below correct? Mostly every property in Model.Customer is exactly the same as the AutoGenerated EF6 DBFirst file except for this which I placed in Model.Customer:
public List<CustomerContact> Contacts { get; set; }
In the end I want to be able to use Customer.Contacts to automatically get a list of all that Customer's contacts
In my AutoMapper I am trying to do something like this but don't think this is correct:
CreateMap<Customer, Model.Customer>()
.ForMember(dest => dest.Contacts, opt => opt.MapFrom(src => src.CustomerContact));
//map for customer getlist stored proc which will be returning several fields
//such as TotalCount, RowNumber, fields from a bunch of other tables
CreateMap<Customer_Get_Result, Model.Customer>();
For a stored procedure's complex type (Customer_Get_Result) would most
people make an additional model class for this also?
If it is exactly same properties as Customer, then I probably wouldn't create a model just for it, and just map it to the existing model.
Although it depends on what you are doing with this Model class, and whether the use case for the data mapped from the entity is any different than the data mapped from the stored procedure. Model's are just a fancy term for POCO. Models are often one of two things. A simplification of the entity, that is closer to a POCO than the EF entity is; such as DTO's you might use between your business layer and database layer. Or it is a specialization for a particular context, such as a ViewModel that has only properties needed for a particular view and often includes things like data annotations that are UI specific.
It depends alot on how your application is layered and what part of the application is retrieving data, and what it plans to do with that data. That said, I'd probably start with just using the same Model and see if I feel a need to rafactor later(but you'll still need 2 mappings, one from SP_Complex_Type -> Model, and one Entity -> Model).
Looks like your DB/EF model only has a single related contact from Customer.CustomerContact, but your model has a one-to-many relationship Model.Customer.Contacts. I'm basing this only on the plurality of the property name since you didn't give us any declarations of your entity. Either way there's a mismatch on the relationship your EF entity supports versus the relationship your Model supports. There's alot of different things you can do here with AutoMapper, but you can't make that decision until you figure why one has a list of related contacts, and the other only has a single related contact.
CreateMap<Customer, Model.Customer>()
.ForMember(dest => dest.Contacts, opt => opt.MapFrom(src => src.CustomerContact));
When mapping a list to a list, and the properties are named differently, then the above is exactly what you would do. Additionally if the types in each list are different, then you need to make sure your previously declared a map for those types as well. For example, if you are going from List<CustomerContactEntity> to List<CustomerContactModel> then you need to have already done a CreateMap<CustomerContactEntity,CustomerContactModel> to tell AutoMapper it can convert these types. This way as it encounters each item in the list, it will see that it has a mapping for that child type.
I'm getting a stack overflow for the following mapping:
Mapper.CreateMap<Parent, ParentViewModel>()
.ForMember(x => x.Children, o => o.MapFrom(x => x.Children.ConvertToChildrenViewModel()));
Mapper.CreateMap<Children, ChildrenViewModel>()
.ForMember(x => x.Parents, o => o.MapFrom(x => x.Parents.ConvertToParentViewModel()));
I understand why this is happening, clearly an infinite loop here. How am I supposed to get this to work in automapper? I need parents to know about their children and their children to know about their parents. Would I have to create another ViewModel for Children.Parents that doesn't contain the Parents.Children property?
Extension methods example, similarly for children:
public static IList<ParentViewModel> ConvertToParentViewModel(this IEnumerable<Parent> parents)
{
return Mapper.Map<IList<ParentViewModel>>(parents);
}
There's a MaxDepth setting you can use for recursive mappings. I've never used it before, but it may help you out. You set it on the type mappings:
Mapper.CreateMap(...).MaxDepth(5)
AutoMapper does keep track of what's mapped, but only in the context of a single Map call, not multiple external calls to Mapper.Map.
You shouldn't need the ForMember piece on either mapping configuration. If you remove that, AutoMapper will traverse the parent/child relationships and keep track of what has already been mapped.