Map List<> with Automapper? - c#

I have two classes:
public class CustomerDTO
{
public string Name {get;set;}
public List<Order> Orders {get;set;}
}
public class OrderDTO
{
public string Name {get;set;}
public string Description {get;set;}
public decimal Cost{get;set;}
}
I am using AutoMapper for .NET 3.5 and currently doing the following in my Application_StartUp:
Mapper.CreateMap<Customer, CustomerDTO>();
Mapper.CreateMap<Order,OrderDTO>();
This is a simplified example as I named my DTO properties different than my entity properties, so I used ForMember, but I am unclear on how to map Orders to Customer:
I tried:
Mapper.CreateMap<Customer, CustomerDTO()
.ForMember(dest => dest.Orders, opt=> opt.MapFrom(src=>src.Orders));
but it does not find src.Orders.
If I do indeed need to have both CreateMap statements, does AutoMapper "automatically" link the objects Customer to Orders?

Yes, you need to tell AutoMapper about each mapping. It will not guess for you. So, if an OrderDTO should map to an Order, you must tell AutoMapper that. You must also specify the reverse relationship if that's needed as well (i.e. Order should map to OrderDTO).
In other words, for bi-directional mapping you would need:
Mapper.CreateMap<Order, OrderDTO>();
Mapper.CreateMap<OrderDTO, Order>();
As far as Customer goes, if both Customer and CustomerDTO have a property named Orders, you don't need to do anything else. As long as you've told AutoMapper to map between Order and OrderDTO and Customer and CustomerDTO, it will automatically map your Order when you map Customer.

Related

When Configuring an entity for DbContext in .Net, how do I represent a collection of objects with a unidirectional relationship?

I am doing an object configuration for entity framework where object A has contains a ICollection and object B does not have any relationship to this object. How would I configure this?
Here is an example of my object setup
public class A {
public string id { get; set; }
public ICollection<B> itemsILike { get; set;}
}
public class B {
public string id { get; set; }
}
B doesn't need to know about A at all, but I want A to have a list of B that it can add or remove from (and can also be empty)
This is what I currently have, but I do not think this creates the correct relationship
public void Configure(EntityTypeBuilder<A> builder)
{
builder.HasKey(e => e.id);
builder.HasMany(e => e.itemsILike);
}
public void Configure(EntityTypeBuilder<B> builder)
{
builder.HasKey(e => e.id);
}
Any Idea as to how to configure?
In order to store information about which Bs belong to an A the database needs to store this information somewhere. Without it would not be able to establish which Bs to return when you are retrieving a record of A.
This can be either via a foreign key aId on B or via a many-to-many relationship with an intermediate/join table that contains at least aId and bId.
There also is the option of storing a collection of Bs in a column of A and deserialising it in code. An example would be to store a comma-separated list of ids as a single string and add an access property/function which deseralise the data. For complex types you could use json.
Please note I'm not advocating storing json,, but if you cannot/don't_want to store the relationship as a column of B this could be a solution.
BTW. For EF Core you may want to look at No Foreign Key Property and Shadow Properties mentioned there.

Automapper with composite DTO (many-to-many)

Not having more than a month's worth experience with EF and Automapper, I'm really battling to get the following done:
I've got 3 DB entities as follow: (unnecessary fields remove for simplicity)
Within my project i have these entities pulled in through EF, and I created 2 DTO classes as follow:
[DataContract]
public class StoreDTO
{
[DataMember]
public int StoreID {get; set;}
[DataMember]
public string StoreName{get; set;}
[DataMember]
public List<CategoryDTO> Categories {get; set;}
}
[DataContract]
public class CategoryDTO
{
[DataMember]
public int CategoryID {get; set;}
[DataMember]
public string CategoryName{get; set;}
[DataMember]
public List<StoreDTO> Stores{get; set;}
}
I'm using automapper to try and map the two classes but I'm really battling to get the composite relationship going.
My map creation looks something like this now (after numerous tries):
Mapper.CreateMap<Store, StoreDTO>(); // Not performing the category list mapping yet here
// This is where I attemped the mapping for a store list in each category:
// x.Store_StoreCategory = StoreCategory as composite entity above
Mapper.CreateMap<Category, CategoryDTO>()
.ForMember(dto => dto.Stores,
opt => opt.MapFrom(x => Mapper.Map<List<Store>, List<StoreDTO>>(x.Store_StoreCategory.Select(y => y.Store).ToList())));
This isn't working though. I read a few articles and the latest i came across is this one by Jimmy Bogard himself:
Avoid many-to-many mappings in ORMs
He suggests creating the composite DTO as well and work from there which seems like a good way to go but I need help with the mapping as I'm a TSQL guy, not yet a LINQ guy with automapper and DTO capabilities...
Other articles I tried were the following but with my lack of knowledge on Automapper and EF, I fear I might be missing the point somewhere as these articles don't refer to the composite DTO as suggested by Jimmy Bogard:
AutoMapper many to many relationship into collection
https://github.com/AutoMapper/AutoMapper/wiki/Custom-value-resolvers
Many-to-many to DTOs using Automapper
Would really appreciate some insight!

NHibernate load the nested complex object entity in automapper queryable extension

I am having class employee which contain complex property Department.
public Class Employee
{
public int Id { get; set;}
public Department {get; set;}
}
public Class Department
{
public int Id {get;set;}
public string Name { get;set;}
}
i create the map for both
(consider the above both class are available in two namespace 'source', 'destination')
Mapper.CreateMap<source.Employee,destination.Employee>()
Mapper.CreateMap<source.Department,destination.Department>()
when i project it.
empQueryable.Project().To<destination.Employee>();
If i saw the NHProfiler
I found that it loads the entity Department
and create the query
select ... from employee left outer join Department .....
i don't know why it loads entity Department, it should make just the projection.
I'm going to go out on a limb here and assume that "destination.Employee" contains references to the "destination.Department". When AutoMapper builds a projection, it does the same as it would as "Mapper.Map". It crawls the destination type, its properties, and its member's properties all the way down. In short, it will build a Select expression something like:
.Select(e => new destination.Employee {
Id = e.Id,
Department = new destination.Department {
Id = e.Department.Id,
Name = e.Department.Name
}
});
You have a few choices here:
Ignore members you don't want mapped in your employee configuration, namely the "Department" property
Create targeted destination types based on use case, and don't share destination types that require different hydrated data based on different needs.
Use explicit expansion (ForMember(d => d.Department, opt => opt.ExplicitExpansion()), then explicitly expand that member in your projection as needed, with the overload for "Project.To" that takes a list of members to expand.

Automapper Composite DTO to ViewModel conversion

In some cases there is a need to return composite DTOs from our repository, where the DTO just has a few properties that are Model properties and the function of the DTO is just to be a simple composite object (returning a Queryable is not enough because there is more information than T)
For example:
Model:
public class Job
{
int Id { get; set; }
//more properties
}
public class JobApplication
{
int Id { get; set; }
//more properties
}
Repository:
IQueryable<JobAndUserApplication> GetJobAndMatchingUserApplication(int userId):
public class JobAndUserApplication
{
public Job Job { get; set; }
public JobApplication JobApplication { get; set; }
}
Now - Id like to simply do (Project and To are Automapper functionality):
//this allows one efficient query to bring in the subproperties of the composite DTO
var jobVmList = jobRepository.GetAllJobsAndMatchingJobApplicationByUser(userId)
.Project()
.To<JobVM>()
.ToList();
So I need a mapping kind of like this:
Mapper.CreateMap<JobAndUserApplication, JobVM>()
.ForMember(jvm => jvm, opt => opt.ResolveUsing(src => src.Job));
//many other .ForMembers that are not relevant right now
I am attempting to map the Job property of the DTO directly on to the JobVM (which shares many of the same properties).
My mapping throws the following exception:
Custom configuration for members is only supported for top-level individual members on a type.
What am I doing wrong and how can I accomplish the mapping form the Job property of the DTO on the the JobVM itself?
Thanks
Automapper is telling you that you can only define custom actions on a member (property) of a class, not on the class itself. What you need to do is first create a Job to JobVM map:
Mapper.CreateMap<Job, JobVM>()
and
Mapper.CreateMap<JobAndUserApplication, JobVM>()
being sure to ignore and set any duplicate properties across the two types. Then run automapper twice, first from the child object:
var jobVM = Mapper.Map<Job, JobVM>(jobAndUserApplication.job);
then from the parent object
Mapper.Map<JobAndUserApplication, JobVM>(jobAndUserApplication, jobVM );
Or the other way around, depending on how your properties are laid out.
Quick side note: I have a feeling you might be mixing concerns, and my code smell alarm is going off. I'd take a second look at either your viewmodel or domain model, as this is not a typical issue I see come up. (just my $0.02 :-)

Entity Framework + AutoMapper ( Entity to DTO and DTO to Entity )

I've got some problems using EF with AutoMapper. =/
for example :
I've got 2 related entities ( Customers and Orders )
and they're DTO classes :
class CustomerDTO
{
public string CustomerID {get;set;}
public string CustomerName {get;set;}
public IList< OrderDTO > Orders {get;set;}
}
class OrderDTO
{
public string OrderID {get;set;}
public string OrderDetails {get;set;}
public CustomerDTO Customers {get;set;}
}
//when mapping Entity to DTO the code works
Customers cust = getCustomer(id);
Mapper.CreateMap< Customers, CustomerDTO >();
Mapper.CreateMap< Orders, OrderDTO >();
CustomerDTO custDTO = Mapper.Map(cust);
//but when i try to map back from DTO to Entity it fails with AutoMapperMappingException.
Mapper.Reset();
Mapper.CreateMap< CustomerDTO , Customers >();
Mapper.CreateMap< OrderDTO , Orders >();
Customers customerModel = Mapper.Map< CustomerDTO ,Customers >(custDTO); // exception is thrown here
Am I doing something wrong?
Thanks in Advance !
The problem I had was related to updates to EntityCollection references. AutoMapper creates a new instance of the relation when mapping from the DTO to the Entity, and that doesn't please the EF.
What solved my problem was configuring AutoMapper to use the destination value for my EntityCollection properties. In your case:
Mapper.CreateMap< CustomerDTO , Customers >().ForMember(c => c.Orders, o => o.UseDestinationValue());
That way AM will not create a new EntityCollection instance, and will use that wich came with the original Customer entity.
I'm still working for a way to automate this, but for now it solves my problem.
Try mapping to an existing object:
entity = Mapper.Map<MyDTO, NyEntity>(dto, entity);
And keep the Ignore()'s in place.
http://groups.google.com/group/automapper-users/browse_thread/thread/24a90f22323a27bc?fwc=1&pli=1
Your problem is because Automapper loses the EntityKey associated with the record. As the EntityFramework does not by default handle POCO's (Plain Old CLR Object)
Jay Zimmerman has a good example here of how to handle this from is. gd /4NIcj
Also from Jaroslaw Kowalski (part of the EF team I believe ) has this example for using POCO's within EF, which may translate well to use with Automapper (I've not yet had a chance to try it) : http://blogs.msdn.com/jkowalski/archive/2008/09/09/persistence-ignorance-poco-adapter-for-entity-framework-v1.aspx
I'm not sure what your problem is, but - when i wanted to use LINQToEntities (switched to NHibernate),
i managed to use automapper with success.
Take a look at code:
public class SimpleMapper<TFrom, TTo>
{
public static TTo Map(TFrom fromModel)
{
Mapper.CreateMap<TFrom, TTo>();
return Mapper.Map<TFrom, TTo>(fromModel);
}
public static IList<TTo> MapList(IList<TFrom> fromModel)
{
Mapper.CreateMap<TFrom, TTo>();
return Mapper.Map<IList<TFrom>, IList<TTo>>(fromModel);
}
}
public class RepositoryBase<TModel, TLINQModel>
{
public IList<TModel> Map<TCustom>(IList<TCustom> model)
{
return SimpleMapper<TCustom, TModel>.MapList(model);
}
public TModel Map(TLINQModel model)
{
return SimpleMapper<TLINQModel, TModel>.Map(model);
}
public TLINQModel Map(TModel model)
{
return SimpleMapper<TModel, TLINQModel>.Map(model);
}
public IList<TModel> Map(IList<TLINQModel> model)
{
return SimpleMapper<TLINQModel, TModel>.MapList(model);
}
public IList<TLINQModel> Map(IList<TModel> model)
{
return SimpleMapper<TModel, TLINQModel>.MapList(model);
}
}
It's quite cryptic, always recreates mappings, but it worked. I hope it helps somehow. :)
Now, with new version of AutoMapper, the recommended way is using Queryable-Extensions:
When using an ORM such as NHibernate or Entity Framework with
AutoMapper's standard Mapper.Map functions, you may notice that the
ORM will query all the fields of all the objects within a graph when
AutoMapper is attempting to map the results to a destination type.
If your ORM exposes IQueryables, you can use AutoMapper's
QueryableExtensions helper methods to address this key pain.
The .ProjectTo() will tell AutoMapper's mapping engine
to emit a select clause to the IQueryable that will inform entity
framework that it only needs to query the Name column of the Item
table, same as if you manually projected your IQueryable to an
OrderLineDTO with a Select clause.
Create a mapping:
Mapper.CreateMap<Customer, CustomerDto>();
And project query to dto:
var customerDto =
session.Query<Customer>().Where(customer => customer.Id == id)
.Project().To<CustomerDto>()
.Single();
AutoMapper is very expressive when it comes to mapping error. read the exception message carefully.
another important thing is to remember to call Mapper.AssertConfigurationIsValid(); after creating the mappings. it gives an error if the mapping is wrong, thus preventing an exception later in the application runtime.
You should ignore mapping of some entity properties like so:
Mapper.CreateMap<CustomerDto, Customer>()
.ForMember(dest => dest.EntityKey, opt => opt.Ignore())
.ForMember(dest => dest.Licenses, opt => opt.Ignore())
.ForMember(dest => dest.AccessCodes, opt => opt.Ignore());
If you examine the message from the exception thrown by Automapper, you should see the entity properties that cannot be mapped and ignore them as above.
As you can read here you need to do the following
You can update entities with AutoMapper. Here's how: pass both the DTO and the entity object to AutoMapper's Map method. That's what this code does:
custExisting = Mapper.Map(Of CustomerDTO, Customer)(custDTO, custExisting)
Also beware of mapping issues like the one described here
These tips worked for me.

Categories

Resources