Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The entity or complex type ... cannot be constructed in a LINQ to Entities query

Why does one method work but not the other, when it seems they are both doing the same thing, i.e. constructing an entity. My question then, is there a way to construct the entity in a L2E query instead of having to use just Linq or indeed both?

This works fine...

var queryToList = (from ac in ctx.AuthorisationChecks
                   where wedNumbers.Contains(ac.WedNo)
                   orderby ac.WedNo, ac.ExpAuthDate, ac.ActAuthDate
                   select new AuthorisationCheck
                   {
                       Blah = ac.Blah
                   }).ToList();

model.AuthorisationChecks = queryToList.Select(x => new AuthorisationCheck
    {
        Blah = x.Blah
    }).ToList();

However, if i change...

var queryToList

to

model.AuthorisationChecks queryToList // Of type List<AuthorisationCheck>

i get the error in the Title...

The entity or complex type 'Model.AuthorisationCheck' cannot be constructed in a LINQ to Entities query.

EDIT: In the model it is simply, nothing fancy here.

public List<AuthorisationCheck> AuthorisationChecks { get; set; }

EDIT2: Tidied this up a little to be (which works fine)...

model.AuthorisationChecks = (from ac in ctx.AuthorisationChecks
                             where wedNumbers.Contains(ac.WedNo)
                             orderby ac.WedNo, ac.ExpAuthDate, ac.ActAuthDate
                             select ac).ToList()
                             .Select(x => new AuthorisationCheck
                             {
                                 Blah = x.Blah
                             }).ToList();

EDIT2: My Solution I wasn't happy with the anonymous type method and so went ahead and created a simple model containing only the properties I required to be used in the viewmodel.

Changed the type of model.AuthorisationChecks

from

List<AuthorisationCheck> // List of Entities

to

List<AuthorisationCheckModel> // List of models

which allows the following code to work, and without profiling it seems a lot quicker than using an anonymous type (and of course I don't cast to a list twice!).

model.AuthorisationChecks = (from ac in ctx.AuthorisationChecks
                             where wedNumbers.Contains(ac.WedNo)
                             orderby ac.WedNo, ac.ExpAuthDate, ac.ActAuthDate
                             select new AuthorisationCheckModel
                             {
                                 Blah = x.Blah
                             }).ToList();

P.S. I was once warned by a coworker (who used to work for Microsoft) that it isn't a good idea to directly use Entities in this manner, maybe this was one of the reasons he was thinking of, I've also noticed some odd behavior using Entities directly in other cases (mainly corruptions).

like image 1000
Paul Zahra Avatar asked Mar 11 '15 11:03

Paul Zahra


2 Answers

Note: I generally use lambda expression instead of Fluent API, but the root issue should be the same.

I have historically noticed LINQ is unable to use C# classes for Select statements if the original datasource (i.e. ctx for you) is accessed by translating your query into SQL.

In other words, there are issues when getting something from the database and casting it to a custom class in the same chain.

LINQ is smart enough that it actually doesn't immediately execute your chained calls. It simply internally builds a query, and when you actually access your results (i.e. retrieve the value from memory), it executes the query.
I assume this is also the reason why you are faced with this error, because LINQ translates everything (including the Select) to SQL, and fails because there's no SQL-way to express it. In short, LINQ can't do a built query half-SQL, half-code. It's either all in SQL, or all in code.

You can usually confirm that this is the case by first making a List<> of the database table, then run the exact same query on it.

var myTable = db.AuthorizationCheck.ToList();

var myResult = myTable. //query here

Note: That is not a solution!
Taking the entire table in memory is an overly intensive way to work around this. It just proves the point that the problem isn't encountered if the datasource is in memory, but the error does occur if it's in a database.

There are ways I've fixed this, although I've never found a uniform way to approach this problem (generally depends on the opinion of my code reviewer, whether he likes the fix or not)

Using anonymous types, you can select what you want, and later cast it to the correct class. You can use the exact same fields, making a later cast easier to understand.

//Simpler query for clarity's sake
var myAnonymousResult = ctx.AuthorizationChecks
                                .Where(x => x.IsActive)
                                .Select(x => new { Name = x.Name, IsActive = x.IsActive })
                                .ToList();

var myCastResult = myAnonymousResult.Select(x => new Check() { Name = x.Name, IsActive = x.IsActive }).ToList();

If you use lambda expressions instead of the fluent API, you can call .ToList() after applying the filters but before calling the .Select() method. This ensures the current query will be executed, retrieved from the database, and put into an actual List<> in memory. At that point, you can call the .Select() statement without running into the same problem.

//Simpler query for clarity's sake
var myCastResult = ctx.AuthorizationChecks
                                .Where(x => x.IsActive)
                                .ToList()
                                .Select(x => new Check() { Name = x.Name, IsActive = x.IsActive });

Unfortunately though, my experience with this problem is anecdotal. I've never been able to officially confirm my suspicions as to the root cause of this issue; but the workarounds I mentioned should work as I've applied them numerous times in the past.

If anyone has an explanation of the root cause, I'd be very interested in hearing it!

like image 53
Flater Avatar answered Oct 01 '22 17:10

Flater


If this query works fine:

var queryToList = (from ac in ctx.AuthorisationChecks
                   where wedNumbers.Contains(ac.WedNo)
                   orderby ac.WedNo, ac.ExpAuthDate, ac.ActAuthDate
                   select new AuthorisationCheck
                   {
                       Blah = ac.Blah
                   }).ToList();

then this should also work:

model.AuthorisationChecks = (from ac in ctx.AuthorisationChecks
                   where wedNumbers.Contains(ac.WedNo)
                   orderby ac.WedNo, ac.ExpAuthDate, ac.ActAuthDate
                   select new AuthorisationCheck
                   {
                       Blah = ac.Blah
                   }).ToList();

and in your first case you don't need to project again, you can directly assign it to model propeerty:

model.AuthorisationChecks = queryToList;

UPDATE:

As it is Linq To Entities,you have to do something like this using anonymous type:

var queryToList = (from ac in ctx.AuthorisationChecks
                       where wedNumbers.Contains(ac.WedNo)
                       orderby ac.WedNo, ac.ExpAuthDate, ac.ActAuthDate
                       select new 
                       {
                           Blah = ac.Blah
                       }).ToList();

and then:

model.AuthorisationChecks = queryToList.Select(x => new AuthorisationCheck
    {
        Blah = x.Blah
    }).ToList();
like image 45
Ehsan Sajjad Avatar answered Oct 01 '22 17:10

Ehsan Sajjad