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

前端 未结 2 898
感动是毒
感动是毒 2020-12-20 16:35

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

相关标签:
2条回答
  • 2020-12-20 17:01

    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!

    0 讨论(0)
  • 2020-12-20 17:21

    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();
    
    0 讨论(0)
提交回复
热议问题