views:

127

answers:

3

I've been using linq2sql on a few projects, but i decided it was time to try out EF as its supposed to be more powerful and better. There are a few things that is really annoying tho. One of them is projecting a result into a list, this works well in l2sql but not in EF.

public class bo.Transaction
{
  public long Id { get; set; }
  public List<bo.Line> Lines { get; set; }
}

public class bo.Line
{
  public int RowNo { get; set; }
  public string Descripton{ get; set; }
  public double Amount{ get; set; }
}

return from t in Db.Transaction.Include("Lines")
       select new bo.Transaction {
         Id = t.Id,
         Lines = t.Lines.OrderBy(l => l.RowNo).ToList(),
       };

The ToList() call fails however, with a message "System.NotSupportedException: LINQ to Entities does not recognize the method 'List[bo.Line] ToListLine' method, and this method cannot be translated into a store expression..".

Any way to get around this? Or do i just have to use ienumerable instead of lists?

A: 

This is a stab in the dark, but does your project have a reference to the System.Core dll? Without this reference, the .ToList() extension method won't be available.

Also, as Workshop Alex points out, your .ToList() call is in the wrong place.

Paul Suart
Its the EF translator that don't support ToList(), the references are correct
AndreasN
+1  A: 
return (from t in Db.Transaction.Include("Lines")
       select new bo.Transaction {
         Id = t.Id,
         Lines = t.Lines.OrderBy(l => l.RowNo),
       }).ToList();

The Linq query just doesn't support list, so we execute this on the Enumeration result. It does force the Query to execute right at that moment, though. Returning the IEnumerable would delay the execution of the query a bit longer. (Basically until the moment when you're going to access the data.)

Workshop Alex
"Include" is completely unnecessary here. LINQ to Entities will sort that out for you, since you're projecting. Otherwise correct (+1)
Craig Stuntz
A: 

What wrote Workshop Alex is right, but let me explain the theory behind it:
as you probably know, when you write a LINQ-to-Entities query, you're writing something that will be executed against your database. In order for that to be done in an efficient way, instead of fetching each entity, testing it for the given criteria, sorting the result and such operations, the query will be translated to SQL and run against your database.
The fact, however, that any valid C# is accepted for your query, doesn't mean that all of the code you could write has a translation to SQL. Notable examples of code without translation is a query mixing data access and reflection on your entity classes, or - just like in your case - code using peculiar .NET data types. That kind of queries usually can be performed in two steps, one operating against the database and one against the entities in RAM. While that will not probably be as efficient and as clean as you'd like, since personally I dislike stored procedures I still find it much better than most other approaches you may take, provided that your constraints allow you to make the choice.

Edit:
the scenario can indeed be easily handled running part of the query on the db and a part in memory this way:


Db.Transaction.Include("Lines").Select(t => new bo.Transaction { t.Id, Lines = t.Lines.OrderBy(l => l.RowNo) }).AsEnumerable().Select(t => new bo.Transacton { t.Id, Lines = t.Lines.ToList()});

The AsEnumerable call helps making sure that the second Select is run against a set of objects already fetched from the DB and thus there's no longer the problem of translating ToList to SQL.

emaster70
I'm well aware of how and when linq queries are executed. Thats not the problem here.The problem is that i can't project a subquery into an list on an other object.It seems ef leans more twards throwing runtime exceptions than executing parts of the query in memory.
AndreasN
It's really about translation and what part of your stack runs the query, because your issue is really that you can't eagerly fetch a collection from within your LINQ query and that's because SQL really has no clue of resizable arrays. Sure the exception you're getting is not very clear about it, but you have to consider that the SQL generated from your queries will not be run or validated anywhere else than on your dbms, so if wrong SQL has been generated due to an expression which has no translations, that's the kind of exception you're going to get.
emaster70