1
votes

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 ?

2
You should use ProjectTo here.Gert Arnold

2 Answers

2
votes

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()
1
votes

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));

Advice:

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.