2009-09-23 7 views
6

Tengo problemas con algunas clases generadas por dbml que no se resuelven hasta SQL eficiente. Imagine que tengo una tabla de Cuentas y una tabla de Transacciones donde cada transacción está asociada a una cuenta en particular. Cargué todo esto en dbml y aparece una clase de cuenta y una clase de transacción. La clase Cuenta tiene una referencia de EntitySet a una colección de Transacciones que representa todas las transacciones en esa cuenta. Lo suficientemente justo.Abstracción de fugas en LINQ to SQL EntitySet

Ahora supongamos que solo quiero las transacciones para el período contable actual. Así agrego un método como este:

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; 
    } 
} 

se ve bien y funciona, pero el SQL no es buena:

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

Es decir: se está tirando de todo el conjunto de transacciones hacia abajo y procesarla con LINQ para Objetos. Intenté eliminar la cláusula where, la cláusula orderby, reemplazando las fechas con constantes, todo sigue siendo del lado del cliente.

Para la comparación, he intentado llamar la colección transacciones directamente en el contexto de datos:

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; 

y funciona muy bien:

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] 

Así que después de todo eso, tengo dos preguntas:

  1. ¿Es correcto el comportamiento anterior de Transactions EntitySet y/o existe alguna forma de solucionarlo?
  2. ¿Cómo implemento el "arreglo" anterior como método en mi clase de Cuenta? Las clases de entidad generadas por dbml no tienen acceso a un DataContext.

Respuesta

6

Usted simplemente no puede hacer eso, por desgracia. Las propiedades de la colección generadas para las clases de entidades LINQ a SQL no son IQueryable; por lo tanto, cualquier consulta realizada en ellos utilizará LINQ to Objects. Esto es por diseño. Como se da cuenta con razón, para obtener una consulta eficiente debe consultar más de Transactions desde su DataContext, pero no tiene una en su propiedad.

En este punto, sus opciones son:

  • Que sea un método que toma un DataContext como argumento; o
  • Uso reflexión hackery retrive el contexto - propia entidad no lo almacena, pero EntitySet s en que lo hacen, aunque de manera indirecta -, naturalmente, esta es la versión específica, propenso a la rotura, etc.

Hasta ahora como sé, Entity Framework no tiene esta limitación, ya que sus propiedades de recopilación son ObjectQuery<T>, que es IQueryable.

+0

Las propiedades de colección de Entity Framework, al menos desde 2009 hasta ahora, no son del tipo 'ObjectQuery ' pero del tipo 'EntityCollection ' (que, sin embargo, tiene un método para transformarlas en 'ObjectQuery '). –

0

Cambie el uso de IEnumerable a IQueryable en su lugar y su SQL se optimizará para que solo obtenga bajo demanda lo que necesita.

+1

No lo hará. Su problema es que 'Transactions' no es' IQueryable', y no puede hacer nada al respecto (es un 'EntitySet', y debe seguir siendo uno). –

+0

Sí, ese es exactamente el problema. –

+0

Argh. Nunca es una solución fácil. :) –

3

¿Cuál es el tipo de Transacciones en el primer ejemplo?

Recuerde que está utilizando métodos de extensión. Los métodos de extensión LINQ que se utilizan son dependientes de la interfaz Transacciones implementos:

  • IQueryable <T> sería LINQ a SQL o LINQ a las entidades o ...
  • IEnumerable T < > le dará linq-to-objects.

Editar:

Esta es la huella digital del tipo EntitySet:

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

para responder a sus preguntas:

  1. Transacciones no implementa IQueryable <T> por lo es el comportamiento correcto
  2. Su clase cuenta tendrá que ser capaz de hacer referencia al objeto Transacciones Tabla
+0

Transactions es la propiedad EntitySet generada en dbml. \t \t [Asociación (Name = "Account_Transaction", Almacenamiento = "_ Transacciones", ThisKey = "id", OtherKey = "account_id")] \t \t públicas EntitySet

+0

Transacciones En cuanto a 2). Entiendo esto, el problema es ¿cómo obtengo esa referencia dado que los objetos de la Cuenta son típicamente creados por el marco de contexto/entidad de datos? ¿Dónde me engancharía para pasar el objeto de la cuenta una referencia a la tabla de transacciones? O dicho de otra manera, dado un objeto de entidad que está asociado con un contexto de datos particular, ¿cómo puedo obtener de esa entidad su contexto de datos? –

+0

Está extendiendo la funcionalidad del objeto Cuenta ¿verdad? Agregar una llamada a un método parametrizado? IEnumerable GetCurrentTransactions (DataContext ctx) –