2010-11-27 12 views
7

Acabo de cambiar de Linq 2 SQL a Entity Framework, y estoy viendo comportamientos extraños en EF con los que espero que alguien pueda ayudar. Intenté buscar en Google, pero no pude encontrar otras personas con este mismo problema. Me he burlado de un escenario para explicar la situación.Entity Framework y Repository Pattern (problema con IQueryable)

Si trabajo directamente con un contexto EF, puedo hacer una selección dentro de una selección. Por ejemplo, esto se ejecuta perfectamente bien:

 // this is an Entity Framework context that inherits from ObjectContext 
     var dc = new MyContext(); 

     var companies1 = (from c in dc.Companies 
          select new { 
           Company = c, 
           UserCount = (from u in dc.CompanyUsers 
              where u.CompanyId == c.Id 
              select u).Count() 
          }).ToList(); 

Sin embargo, si uso un patrón repositorio en el repositorio está volviendo IQueryable (o incluso ObjectSet o ObjectQuery), aparece un NotSupportedException (LINQ a Entidades no reconoce el método 'System.Linq.IQueryable`1) ...

Aquí está un ejemplo de mi repositorio:

public class Repository { 
    private MyContext _dc; 

    public Repository() { 
     _dc = new MyContext(); 
    } 

    public IQueryable<Company> GetCompanies() { 
     return _dc.Companies; 
    } 

    public IQueryable<CompanyUser> GetCompanyUsers() { 
     return _dc.CompanyUsers; 
    } 
} 

// estoy usando el repositorio dentro de otra clase (por ejemplo, en mi capa de Servicios)

 var repository = new Repository(); 

     var companies2 = (from c in repository.GetCompanies() 
          select new { 
           Company = c, 
           UserCount = (from u in repository.GetCompanyUsers() 
              where u.CompanyId == c.Id 
              select u).Count() 
          }).ToList(); 

El código anterior arroja una NotSupportedException.

que se dan cuenta de que si hay una asociación entre las empresas y CompanyUsers, entonces puedo simplemente hacer esto y no tendrán ningún problema:

 var companies3 = (from c in repository.GetCompanies() 
          select new { 
           Company = c, 
           UserCount = (from u in c.CompanyUsers 
              select u).Count() 
          }).ToList(); 

... pero mi ejemplo es sólo una versión simplificada de una más complicada escenario donde no tengo una asociación entre las entidades.

Así que estoy muy confundido por qué Entity Framework está lanzando la excepción NotSupportedException. ¿Cómo es que la consulta funciona perfectamente bien cuando estoy trabajando con el contexto EF directamente, pero no es compatible si estoy trabajando con IQueryable devuelto por otro método. Esto funcionó perfectamente bien con Linq 2 SQL, pero no parece funcionar en Entity Framework.

Cualquier observación sería muy apreciada.

Gracias de antemano.

Respuesta

7

sospecho que lo que está sucediendo es que EF ve la expresión de repository.GetCompanyUsers() dentro de la lambda por primera select y no sabe qué hacer con él porque repository no es un contexto EF. Creo que si pasa el IQueryable directamente en lugar de una expresión que lo devuelve, debería funcionar.

¿Qué tal si haces esto:

var companyUsers = repository.GetCompanyUsers(); 
    var companies2 = (from c in repository.GetCompanies() 
         select new { 
          Company = c, 
          UserCount = (from u in companyUsers 
             where u.CompanyId == c.Id 
             select u).Count() 
         }).ToList(); 
+0

¿Por qué necesitamos hacer eso, quiero decir que tenemos asociación aquí? – paragy

+0

+1 U también lo contestó ... – paragy

+0

Esto requiere dos viajes redondos al servidor ... espera, ¿o sí? –

3

Ésta es una de esas extrañas peculiaridades con LINQ a SQL/EF. Al parecer, implementaron una forma de traducir desde una propiedad getter a SQL, pero no una forma de traducir desde una función getter a SQL.

Si en lugar de una función GetCompanyUsers() utiliza una propiedad como CompanyUsers, debería funcionar.

Weird eh?

Así que en lugar de

public IQueryable<CompanyUser> GetCompanyUsers() { 
    return _dc.CompanyUsers; 
} 

Es posible hacer

public IQueryable<CompanyUser> CompanyUsers { 
    get { return _dc.CompanyUsers; } 
} 

Por lo consultas con parámetros como van (que no se puede hacer con una propiedad, obviamente), véase mi pregunta respondida aquí: Custom function in Entity Framework query sometimes translates properly, sometimes doesn't

También puede tener wheres y selects en la propiedad también; ellos traducirán bien Por ejemplo, si tuviera un blog con una tabla de los artículos que contiene algunos artículos que no están en línea:

public IQueryable<Article> LiveArticles { 
    get { return _dc.Articles.Where(a => !a.IsDraft); } 
} 

Eso también va a reducir el número de consultas con parámetros que necesita.

+0

Muy raro, pero tienes toda la razón. Las propiedades que devuelven IQueryable funcionan con EF. Linq to SQL también trabajó con métodos, pero supongo que EF no implementó eso. –