AutoMapper: What is the difference between CreateMap and CreateProjection? - c#

I am looking for an explanation of the difference between CreateMap/CreateProjection in automapper and ProjectTo/MapTo by relation.
I just got started with the library and I am having trouble understanding what to use when. I partially understand that ProjectTo has some relation to LINQ and can be used for entire collections? I wish to use this library in a Blazor Server Side project.
I am also looking into these two libraries as I am using EF Core:
https://github.com/AutoMapper/AutoMapper.Collection
https://github.com/AutoMapper/AutoMapper.Collection.EFCore
But as I am new to the library I think it is a good idea to start with the base first before moving on.

TL;DR
If you don’t use Map, just ProjectTo, you should use CreateProjection instead of CreateMap. That way you’ll use only the API subset supported by ProjectTo and start-up should be faster.
CreateProjection explicitly disables Map. CreateMap allows both.
So if you need only entity -> DTO mapping via ORM (like Entity Framework) and ProjectTo then use CreateProjection, otherwise use CreateMap.
Details
As written in the docs if you are working with ORM based on IQueryable you can use ProjectTo so the AutoMapper+ORM pair can generate a proper SQL statement instead of fetching the whole entity into memory (if your are not mapping in one-to-one fashion this can have positive effect on performance) and mapping it client side:
var configuration = new MapperConfiguration(cfg =>
cfg.CreateProjection<OrderLine, OrderLineDTO>()
.ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name)));
public List<OrderLineDTO> GetLinesForOrder(int orderId)
{
using (var context = new orderEntities())
{
return context.OrderLines.Where(ol => ol.OrderId == orderId)
.ProjectTo<OrderLineDTO>(configuration).ToList();
}
}
The .ProjectTo<OrderLineDTO>() 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.
Due to the nature of the ProjectTo it is much more limited in what it can actually map/do (not everything can be turned into SQL).
Read more:
What is the difference between IQueryable and IEnumerable?
Defining both CreateProjection and CreateMap
CreateProjection explicitly disables Map. CreateMap allows both.
11.0 Upgrade Guide:
If you don’t use Map, just ProjectTo, you should use CreateProjection instead of CreateMap. That way you’ll use only the API subset supported by ProjectTo and start-up should be faster.
12.0 Upgrade Guide
You also cannot have for the same map/member separate configurations for Map and ProjectTo.
AutoMapper ConvertUsing is not called

The short answer is it's generally better to use CreateMap.
Create map defines mappings that can be used for both MapTo and ProjectTo whereas CreateProjection defines mappings that only works with ProjectTo and effectively disables the use of MapTo.
Using CreateMap will give you the freedom to map in-memory objects and database queries (that work with IQueryable) directly into your chosen object.
CreateProjection can be used if you will only be mapping straight from DB queries

Related

Defining both CreateProjection and CreateMap

In my project I use Entity Framework for ORM, and Dto classes for api responses. I use Automapper to map between the two.
When I need to directly fetch the Dtos from the EF queryables, I do a final ProjectTo<> on my query and everything is fine.
But there are many times that I have an already materialized object from entity framework and I need to Map it to its Dto equivalent. In this case I use Map<> instead of ProjectTo<> since I have an instance in memory and not an IQueryable that translates to SQL.
I have registered both Projection and Map when I created the Automapper profile like this:
CreateMap<UserSession, Models.UserSession>(); // used when mapping between instances
CreateProjection<UserSession, Models.UserSession>(); // used when mapping on EF LINQ expressions
That should be enough for Automapper to understand that when I use ProjectTo I want the Projection mapping and when I use Map I want the instance mapping.
But, instead, when I have a materialized UserSession object (the type registered with Entity Framework), and want to map it to a Models.UserSession object (my Dto) I get this response.
CreateProjection works with ProjectTo, not with Map.
So, how can I register both map and projection for the same types in Automapper?
In this case you have to use CreateMap. CreateProjection explicitly disables Map. CreateMap allows both.

Load explicit entity (not entities extending it) with EF

We've got a code first approach in our application. We have a simple hierarchy similar to this:
SuperSpecializedPerson extends SpecializedPerson extends (abstract) Person
We've got two repositories for SuperSpecializedPerson and SpecializedPerson.
When querying for SuperSpecializedPerson, the returned entities are the wanted ones.
When querying for SpecializedPerson, all SpecializedPerson as well as SuperSpecializedPerson (as an instances of SpecializedPerson) are being returned. This is my issue.
Checking the SQL query is see this part of code WHERE ([Extent1].[Discriminator] IN (N''SuperSpecializedPerson '',N''SpecializedPerson'')), where I'd like to have WHERE ([Extent1].[Discriminator] IN (N''SpecializedPerson'')).
How can I get only SpecializedPerson?
[edit] I'll give some more context to my issue to figure out if I am on the wrong track altogether:
I have to return a list of DTOs of the same type from the backend to the frontend. The DTOs are being created with Automapper according to the specified mapping profiles.
First I query for SuperSpecializedPerson, map them to DTO, then the same happens for SpecializedPerson and concatenate both lists. After merging, I get two instances of all SuperSpecializedPerson (once with only the SpecializedPerson properties).
The described model has been defined according to the current knowledge and will probably in future to have a second class extending SpecializedPerson.
This happens because SuperSpecializedPerson is also a SpecializedPerson. This a fundamental aspect of inheritance. A cat is an animal. The keyword is includes derived types.
Query with
context.SpecializedPersons.Where(p => !(p is SuperSpecializedPerson));
Another approach not as performing as this one but also working with additional derived types in future, is to filter with LINQ-to-Objects
context.SpecializedPersons
.Where(p => /* other filters go here */)
.AsEnumerable() // Switches to LINQ to Objects
.Where(p => p.GetType() == typeof(SpecializedPerson));
Your edit says that the problem is the mapping with automapper. See automapper documentation for Mapping Inheritance. Especially the part with Runtime polymorphism.

EF 5.0 Oracle Code First set Precision to number types

with Entity Framework 5.0 and ODP.NET I am trying to build a code first DbContext for my existing Oracle database.
I know this approach is not supported officially by ODP, but maybe there is a workaround for the only problem I still need to solve.
All my tables have keys which are of type NUMBER(18,0). This is a simple example:
Table
> DESCRIBE T_USER
KUSER NOT NULL NUMBER(18,0)
Domain Object
public class User
{
public long Id { get; set; }
/* ... */
}
Mapping configuration
modelBuilder.Entity<User>()
.ToTable("T_USER");
modelBuilder.Entity<User>()
.Property<long>(x => x.Id)
.IsRequired()
.HasColumnType("number")
.HasColumnName("KUSER");
I cannot specify the Precision attribute though, because only the DecimalPropertyConfiguration class (.Property()) exposes Precision and Scale properties.
The result is that all translated queries contain a CAST AS number with 19 digits precision (default mapping for Int64), like:
SELECT
CAST( "Extent1"."KUSER" AS number(19,0)) AS "C1",
/* ... */
The same casts are done in JOIN and WHERE clauses with heavy performance impacts on each query.
In the EDMX XML file - which I have deprecated since I hope to not have to use it ever in the future -
I had this line:
<Property Name="KUSER" Type="number" Nullable="false" Precision="18" />
Is it possible to manually set the Precision of a property after EF model creation?
Or maybe there is a way to extend EF configuration classes and add a custom NumberPropertyConfiguration
which exposes the Precision property.
The fact that the Edm namespace is internal has stopped me from pursuing this latter path.
Notes
I can not represent those properties with C# decimal type because I would have to rewrite pretty much all the domain layer.
I can not add a fake long property to wrap an hidden decimal one, because the long field needs to be used in LINQ-to-SQL queries joins and select
Updates
Added call to .HasColumnType("number") in the PrimitivePropertyConfiguration, but the result is the same.
Is it feasible to alter property facets by traversing the MetadataWorkspace?
(this as IObjectContextAdapter).ObjectContext.MetadataWorkspace
I will try this myself as soon as possible and update with results.
No, MetadataWorkspace is read only.
No you cannot do that. If you're using Code First the model is defined by your code. If your code says that a property is long, it won't be mapped as a NUMBER() column in the database.
The only direct workarounds I can think of are the ones that you mention, but you want to avoid.
You can still use a mapper, like AutoMapper (more info here), which allows you to map from your EF entities to your domain entities. Depending on how your code is implemented this could work, but, if you leak EF LINQ functionalities to your domain logic, then this wouldn't work. You could try using AutoMapper IQueryable Extensions but I'm not sure if they would work for your use case.

Automapper, MapFrom and EF dynamic proxies

I have been trying to map my domain objects to a report view model. Things all worked well in testing where I faked the entity framework code out and used a builder to return a fully populated pocco object. Now that I am actually hitting the database and returning data I am seeing some wierd dynamic proxy type errors.
Here is a sample of my code:
public class ContactMapping : Profile
{
protected override void Configure()
{
Mapper.CreateMap<Contact, ReportRowModel>()
.ForMember(dest => dest.Gender, opt => opt.MapFrom(src => src.Gender.Name));
}
}
And the mapping code is like this:
var contact = GetContactFor(clientPolicy);
Mapper.DynamicMap(contact, rowModel);
return rowModel;
The contact fields all populate correctly except for the rowModel.Gender field which is returning System.Data.Entity.DynamicProxies.Gender_3419AAE86B58120AA2983DA212CFFEC4E42296DA14DE0836B3E25D7C6252EF18
I have seen solutions where people have had problems using Map instead of DynamicMap, but I haven't found anything where a .ForMember mapping is failing like this.
Any suggestions.
Your EF query is not returning the Gender, it is returning a Proxy that can get Gender for you when evaluated, which is not of the type that AutoMapper built a mapping to handle.
You either need to eagerly fetch Gender in your query, or use AutoMapper's IQueryable Extention's Project method to have AutoMapper emit an anonymous projection (again, in your query), rather than try to apply the AutoMapping after the result has been returned from your EF context.
This is good practice in general to avoid Select N+1 issues.
I've got the same issue right now with version 4.x, reverting to 3.3.1 fixed the issue.

Map query expression from DTO to Entity Object

I have inherited a code base that uses DTOs in the business layer, these are populated via a set of mappers from Entity Framework. This has some quite serious limitation in terms of querying so I am working on a new "optimised" querying service.
My first issue is that I need to translate my LINQ query on my DTO to work with my Entity object but the calling context has no knowledge of the EF entities. Let's assume we can rely on the properties on each object having matching names.
This is where I have got to in terms of stubbing out what I want:
public static List<TDataObject> GetFiltered<TDataObject(Expression<Func<TDataObject, TDataObject>> projection, Func<TDataObject, bool> filter)
{
// 1. translate the filter parameter to work with my equivalent Entity object
// 2. build the EF query with the modified filter expression and also a Select() projection so we only return the properties we need. (this should generate an optimised SQL query under the hood)
// 3. map the results from the EF query back onto my TDataObject and return (I already have AutoMapper maps in place for this)
}
It is item 1 that I am struggling with so if anyone has any code examples for blogs posts for achieving this I'd appreciate it.
Also if anyone has any alternate suggestions I'd be happy to hear them.
One way to handle this is to build primitives around queries (instead of layers with repositories etc). Here's what we do:
http://lostechies.com/jimmybogard/2013/10/29/put-your-controllers-on-a-diet-gets-and-queries/
The calling code (controller) knows about a query and the result (DTO), but the piece doing the mapping knows exactly about EF Context/NHibernate ISession. Works very well for us and keeps our controllers light and thin.
Alternatively, get rid of your layers, and expose the EF objects directly:
var dtos = context.Employees.Where(e => e.IsActive)
.Project().ToArray<EmployeeShowViewModel>();
Put this in the controller because who cares, layers and abstractions are productivity preventers and time wasters.

Categories