简体   繁体   中英

Whats wrong with LINQ to EF?

I am using EF. This is my LINQ query

 public List<Tuple<int, string>> GetList()
 {
     return (from c in DALContext.MST
             select new Tuple<int, string>(c.CD, c.NAME)).ToList();
 }

When i call GetList() it throws an exception : Only parameterless constructors and initializers are supported in LINQ to Entities

Instead when i rewrite this query:

List<Tuple<int, string>> lst = new List<Tuple<int, string>>();
var query= (from c in DALContext.MST
            select new{c.CD, c.NAME});
foreach (var item in query)
{
    lst.Add(new Tuple<int,string>(item.CD,item.NAME));
}
return lst;

It just works fine. Whats wrong with my first query???

The other answers are correct about what's going on, but I didn't see anyone mention the best way to make your code work: AsEnumerable()

    public List<Tuple<int, string>> GetList()
    {
        return (from c in DALContext.MST.AsEnumerable()
                select Tuple.Create(c.CD, c.NAME)).ToList();
    }

The AsEnumerable method acts as a boundary between the code that should be translated into SQL and executed in the database server, and the code that should be executed in memory after we've gotten a response from the database. Putting it right after the table name tells EF to get all the records from the MST table, and then run the following code that creates tuples from the values that are returned.

I changed your new Tuple<int, string> into Tuple.Create mostly because I don't like typing generic type parameters any more than I have to.

LINQ to EF deals with queries a bit differently than LINQ to SQL. In LINQ to EF, you can not put a constructor with parameters in a LINQ expression, like you did here in the first bit of code:

from c in DALContext.MST
select new Tuple<int, string>(c.CD, c.NAME)

The constructor of Tuple is taking two parameters, and that is not allowed in LINQ to EF.

The reason is explained here :

In part this is a matter of wanting LINQ to Entities to be more explicit about the boundary between what parts of your query execute on the server and what part execute on the client.

With LINQ to SQL, for instance, it is possible to write a LINQ query which not only involves data from the server and functions on the server but also functions that can only be executed on the client and to mix them in together. The LINQ to SQL provider will then do its best to untangle things and execute the parts that it can on the server and other parts on the client. This is nice because it is easy to just write whatever query you want and if at all possible it will work. On the other hand, it's not so nice if you accidentally write a query where the only part which can execute on the server is the most basic thing that returns all the data in one or more tables and then have all the filtering happen on the client (with very nasty perf consequences).

With LINQ to Entities, the boundaries are more explicit. When you write a LINQ query against a LINQ to Entities IQueryable implementation, the entire query executes on the server, and if some part of the query cannot be executed on the server, then an explicit boundary must be created with something like ToQueryable() or ToList(). Once that query is executed and the data retrieved, then you can use LINQ to Objects to further refine the query if you so choose. This way you explicitly know where your boundaries are, and it's easier to track down performance issues and the like. One of the related limitations is that the select statement in LINQ to Entities can create anonymous types or other types as long as they have a default constructor and settable parameters. This minimizes the chance that the select statement has major side effects.

Or you could just write

var query= (from c in DALContext.MST
            select new{c.CD, c.NAME}).ToList().Select(x=>new Tuple(x.CD, x.NAME));

This has the advantage it only brings from the DB the two columns you need.

Your class needs to have a parameterless constructor for Linq to EF and you have to instantiate it like this:

public List<Tuple<int, string>> GetList()
{
    return (from c in DALContext.MST
            select new Tuple<int, string>(){CD = c.CD, Name = c.NAME}).ToList();
}

EDIT:

If you are not in the position to add a parameterless constructor to TUPLE (which is the case here as Tuple is not an class per se) then you have no choice with Linq to EF but to do this as a two step process:

public List<Tuple<int, string>> GetList()
{
    List<MST> mstList = (from c in DALContext.MST
                         select c).ToList();

    List<Tuple<int, string>> tupleList = new List<Tuple<int, string>>();

    mstList.foreach(c => tupleList.add(new Tuple(c.CD, c.Name)));

    return tupleList;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM