2009-09-23 8 views
6

J'ai des problèmes avec certaines classes générées par dbml qui ne résolvent pas SQL efficacement. Imaginez que j'ai une table des comptes et une table des transactions où chaque transaction est associée à un compte particulier. Je charge tout cela dans dbml et sort une classe Account et une classe Transaction. La classe Account a une référence EntitySet à une collection de transactions qui représente toutes les transactions de ce compte. C'est suffisant.L'abstraction fuyante dans LINQ to SQL EntitySet

Supposons maintenant que je ne souhaite que les transactions pour la période comptable en cours. J'ajoute donc une méthode comme ceci:

public IEnumerable<Transaction> CurrentTransactions 
{ 
    get 
    { 
     DateTime dtStart = CurrentPeriod; 
     DateTime dtEnd = NextPeriod; 
     return 
      from t in Transactions 
      orderby t.date 
      where t.date >= CurrentPeriod && t.date <= NextPeriod 
      select t; 
    } 
} 

est joli et il fonctionne, mais le SQL est pas bon:

SELECT [t0].[id], [t0].[account_id], [t0].[date], [t0].[description], [t0].[amount], [t0].[sign] 
FROM [dbo].[transactions] AS [t0] 
WHERE [t0].[account_id] = @p0 

Ie: il est tirer l'ensemble des transactions vers le bas et le traitement avec LINQ pour les objets. J'ai essayé de sortir la clause where, la clause orderby, en remplaçant les dates par des constantes tout est encore fait côté client.

À titre de comparaison, j'ai essayé d'appeler la collecte des transactions directement sur le contexte de données:

DateTime dtStart = account.CurrentPeriod; 
DateTime dtEnd = account.NextPeriod; 
IEnumerable<Transaction> trans= 
       from t in MyDataContext.Transactions 
       orderby t.date 
       where t.date >= dtStart && t.date <= dtEnd && t.account_id==iAccountID 
       select t; 

et il fonctionne à merveille:

SELECT [t0].[id], [t0].[account_id], [t0].[date], [t0].[description], [t0].[amount], [t0].[sign] 
FROM [dbo].[transactions] AS [t0] 
WHERE ([t0].[date] >= @p0) AND ([t0].[date] <= @p1) AND ([t0].[account_id] = @p2) 
ORDER BY [t0].[date] 

Alors, après tout cela, j'ai deux questions:

  1. Le comportement ci-dessus de Transactions EntitySet est-il correct et/ou existe-t-il un moyen de le réparer?
  2. Comment implémenter le "fix" ci-dessus comme méthode sur ma classe de compte. Les classes d'entités générées par dbml n'ont pas accès à un DataContext.

Répondre

6

Vous ne pouvez malheureusement pas faire cela. Les propriétés de collection générées pour les classes d'entités LINQ to SQL ne sont pas IQueryable; par conséquent, toutes les requêtes effectuées sur eux utiliseront LINQ to Objects. C'est par conception. Comme vous le constatez à juste titre, pour obtenir une requête efficace, vous devez interroger Transactions à partir de votre DataContext, mais vous n'en avez pas dans votre gettor de propriété.

À ce stade, vos options sont soit:

  • en faire une méthode qui prend un DataContext comme argument; ou
  • Utilisez la réflexion retrive le hack contexte - entité elle-même ne stocke pas, mais EntitySet s sur le faire, bien qu'indirectement - naturellement c'est une version spécifique, sujette à la rupture, etc.

Jusqu'à présent comme je sais, Entity Framework n'a pas cette limitation, puisque ses propriétés de collection sont ObjectQuery<T> - qui est IQueryable.

+0

Les propriétés de collection d'Entity Framework, au moins de 2009 jusqu'à maintenant, ne sont pas de type 'ObjectQuery ' mais de type 'EntityCollection ' (qui a cependant une méthode pour les transformer en 'ObjectQuery '). –

0

Passez de l'utilisation de IEnumerable à IQueryable à la place et votre SQL sera optimisé pour tirer uniquement à la demande ce dont vous avez besoin.

+1

Ce ne sera pas. Son problème est que 'Transactions' n'est pas' IQueryable', et il ne peut rien y faire (c'est un 'EntitySet', et doit en rester un). –

+0

Oui, c'est exactement le problème. –

+0

Argh. Jamais une solution facile. :) –

3

Quel est le type de Transactions dans le premier exemple? N'oubliez pas que vous utilisez des méthodes d'extension. Les Linq Méthodes d'extension qui sont utilisés dépendent de l'interface Transactions met en œuvre:

  • IQueryable <T> serait LINQ-to-sql-à-LINQ entités ou ... ou
  • IEnumerable <T> vous donnera linq-to-objects.

Edit:

Ceci est l'empreinte digitale du type EntitySet:

public sealed class EntitySet<TEntity> : IList, 
    ICollection, IList<TEntity>, ICollection<TEntity>, IEnumerable<TEntity>, 
    IEnumerable, IListSource 
where TEntity : class 

Pour répondre à vos questions:

  1. Transactions ne met pas en œuvre IQueryable <T> donc c'est le comportement correct
  2. Votre classe de compte devra pouvoir référencer l'objet Table des transactions
+0

Transactions est la propriété EntitySet générée par dbml. \t \t [Association (Name = "Account_Transaction", stockage = "_ Transactions", ThisKey = "id", OtherKey = "account_id")] \t \t publics EntitySet Transactions –

+0

En ce qui concerne 2). Je comprends cela, le problème est comment puis-je obtenir une telle référence étant donné que les objets de compte sont généralement créés par le contexte de données/cadre de l'entité? Où est-ce que j'accrocherais pour passer l'objet de compte une référence à la table de transactions? Ou, en d'autres termes, étant donné un objet entité associé à un contexte de données particulier, comment puis-je revenir de cette entité dans son contexte de données? –

+0

Vous étendez la fonctionnalité de l'objet Compte à droite? Ajouter un appel de méthode paramétré? IEnumerable GetCurrentTransactions (DataContext ctx) –