Reputation: 11423
I want to make a projection as a performance wise but the select part returns an anonymous type and I can't to make required mapping.
var jobDegreesQuery = _context.JOBDEGREEs.AsQueryable().Select(d=> new {d.DEGREE_CODE,d.DEGREE_NAME });
if (!String.IsNullOrWhiteSpace(name))
jobDegreesQuery = jobDegreesQuery.Where(c => c.DEGREE_NAME.Contains(name));
var jobDegreeDTOs = jobDegreesQuery
.ToList()
.Select(Mapper.Map<JOBDEGREE, JobDegreeDTO>); //The error
The type arguments for method 'Enumerable.Select(IEnumerable, Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
How can I do the projection and map to DTO
Successfully ?
Upvotes: 2
Views: 7331
Reputation: 30492
What is the result of your ToList()
? It is a List of objects of some anonymous class, that contains data extracted from your sequence of JobDegrees
Whenever you want to use Enumerable.Select
on a sequence of objects, you'll first have to name an identifier that represents one element of your sequence. This identifier is the part before the =>
. After the =>
you'll write the code to return one object using this input identifier.
This is a difficult way to say something like:
IEnumerable<Person> myPersons = ...
var firstNames = myPersns.Select(person => person.FirstName);
Here the person
before the =>
represents one item of your collection of Persons
. Hence person
seems a proper name for this identifier.
If you want you can use any identifier to identify a person
, although not all identifiers will improve readability:
var firstNames = myPersns.Select(x => x.FirstName);
When using LINQ and entity framework it is good practice to identify collections with plural nouns and elements of collections with singular nouns.
After the =>
you write some code that uses this input person
to return exactly one object. In this example the FirstName
of the person
.
Back to your question
The result of your ToList
is a sequence of objects with a DegreeCode
and a DegreeName
.
If you want to convert every object in your sequence into one other object (this is called projection), you'll have to identify one object of your sequence before the '=>'.
For example
...ToList()
.Select(extractedDegreeData => ...)
Here, every extractedDegreeData
corresponds with one element of your list.
Now what do you want to do with one such extractedDegreeData
? You want to return the return value of Mapper.Map<JOBDEGREE, JobDegreeDTO>(extractedDegreeData)
.
Therefore your code should be like:
...ToList()
.Select(extractedDegreeData => Mapper.Map<JOBDEGREE, JobDegreeDTO>(extractedDegreeData));
While constructing your LINQ query, don't use functions like ToList
, or any other functions that does not return IEnumerable<TResult>
, it is a waste of processing power. What if after your Select
you would have put Take(2)
? What a waste to create the complete list of 1000 elements if you only wanted the first two!
Therefore functions like ToList
, FirstOrDefault
, Max
, Count
should always be the last in your linq query.
Finally: dbContext.JobDegrees
is a DbSet<JobDegree>
, which implements IQueryable<JobDegree>
, hence there is no need to use AsQueryable
.
Upvotes: 1
Reputation: 3070
As I understand you want to map JOBDEGREEs to JobDegreeDTO. You are first selecting it as anonymous type, so I think AutoMapper can not map because you are giving anon. type.
Change your code as below it will perform better:
IQueryable<JOBDEGREEs> jobDegreesQuery = _context.JOBDEGREEs; // it is already queryable
if (!String.IsNullOrWhiteSpace(name))
jobDegreesQuery = jobDegreesQuery.Where(c => c.DEGREE_NAME.Contains(name));
var jobDegreeDTOs = jobDegreesQuery
//.Select(d=> new {d.DEGREE_CODE,d.DEGREE_NAME }) // do you need this?
.Select(d => Mapper.Map<JOBDEGREE, JobDegreeDTO>(d)); // here you can give any expression
.ToList()
Upvotes: 2