Defining both CreateProjection and CreateMap - c#

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.

Related

AutoMapper: What is the difference between CreateMap and CreateProjection?

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

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.

Whats the difference between IQueryable and DbQuery?

Following up on this question/answer
How to make Entity Framework Data Context Readonly
The solution is to make your DbContext collections of the type DbQuery, but that is a rather specialized type (it's buried down in the namespaces for EF).
So, what's the functional difference between have a DbContext with this:
public DbQuery<Customer> Customers
{
get { return Set<Customer>().AsNoTracking(); }
}
vs this:
public IQueryable<Customer> Customers
{
get { return Set<Customer>().AsNoTracking(); }
}
...the EF documentation is very light when it comes to the DbQuery class, but I prefer the idea of having DbContext made up of interfaces rather than classes so I'd like to avoid it. What additional benefits does the DbQuery class provide?
Update
After reading the answers and just looking at the code I realized my question was a little silly. I was too quick to ask before I thought! Obviously the underlying concrete object will be a DbQuery regardless, so the actually inner functionality will be the same. It seems to me that using IQueryable is the better choice. Thanks for your patience!
DBQuery is a non-generic LINQ to Entities query against a DbContext. Exposing this will give you LINQ functionality against Entities. If you don't need this, use the IQueryable interface abstraction.
IOrderedQueryable
Intended for implementation by query providers.
This interface represents the result of a sorting query that calls the method(s) OrderBy, OrderByDescending, ThenBy or ThenByDescending. When CreateQuery is called and passed an expression tree that represents a sorting query, the resulting IQueryable object must be of a type that implements IOrderedQueryable.
IListSource
Provides functionality to an object to return a list that can be bound to a data source.
IDbAsyncEnumerable
Asynchronous version of the IEnumerable interface that allows elements to be retrieved asynchronously. This interface is used to interact with Entity Framework queries and shouldn't be implemented by custom classes.
This is an old question, but it comes up in a google search on DbQuery, so just to update things a bit:
In EF Core 2.1, QueryTypes are now mapped to DbQuery types, as described in the documentation located at
https://learn.microsoft.com/en-us/ef/core/modeling/query-types
Here are the relevant bits:
Query types have many similarities with entity types...
...
However they are different from entity types in that they:
Do not require a key to be defined.
Are never tracked for changes on the DbContext and therefore are never inserted, updated or deleted on the database.
Are never discovered by convention.
Only support a subset of navigation mapping capabilities - Specifically:
They may never act as the principal end of a relationship.
They can only contain reference navigation properties pointing to entities.
Entities cannot contain navigation properties to query types.
Are addressed on the ModelBuilder using the Query method rather than the Entity method.
Are mapped on the DbContext through properties of type DbQuery rather than DbSet
Are mapped to database objects using the ToView method, rather than ToTable.
May be mapped to a defining query - A defining query is a secondary query declared in the model that acts a data source for a query type.
You define a QueryType in your DbContext like a DbSet, but using the DbQuery type:
public virtual DbQuery<CustomClassThatMapsYourQueryResults> QueryResults { get; set; }
Then in your OnModelCreating method you indicate that your custom results object is mapped to a stored proc in the database:
modelBuilder.Query<CustomClassThatMapsYourQueryResults>();
Then in your data access code somewhere:
var someVariable = 1;
var someOtherVariable = "someValue";
...
var myResults = _dbContext.FromSql($"spStoredProcName {someVariable} '{someOtherVariable}'");
As far as I know, the only way to populate the DbQuery< T > object is to use the FromSql() method. The DbQuery< T > results of the FromSql() method return IQueryable< T >.

Using AutoMapper with EF6 DbFirst and Stored Procedure (complex types)

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.

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