2010-04-06 17 views

Respuesta

9

Para Entity Framework 1.0, creé algunos métodos de extensiones para hacer esto.

public static class EntityFrameworkIncludeExtension 
{ 
    public static ObjectQuery<T> Include<T>(this ObjectQuery<T> src, Expression<Func<T, StructuralObject>> fetch) 
    { 
     return src.Include(CreateFetchingStrategyDescription(fetch)); 
    } 

    public static ObjectQuery<T> Include<T>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch) 
    { 
     return src.Include(CreateFetchingStrategyDescription(fetch)); 
    } 

    public static ObjectQuery<T> Include<T, TFectchedCollection>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<TFectchedCollection>>> fetch) 
    { 
     return src.Include(CreateFetchingStrategyDescription(fetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, Object>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, RelatedEnd>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, StructuralObject>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, Object>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, RelatedEnd>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, StructuralObject>> secondFetch) 
     where FetchedChild : StructuralObject 
    { 
     return src.Include(CombineFetchingStrategies(fetch, secondFetch)); 
    } 

    private static String CreateFetchingStrategyDescription<TFetchEntity, TFetchResult>(
     Expression<Func<TFetchEntity, TFetchResult>> fetch) 
    { 
     fetch = (Expression<Func<TFetchEntity, TFetchResult>>)FixedWrappedMemberAcces.ForExpression(fetch); 
     if (fetch.Parameters.Count > 1) 
      throw new ArgumentException("CreateFetchingStrategyDescription support only " + 
       "one parameter in a dynamic expression!"); 

     int dot = fetch.Body.ToString().IndexOf(".") + 1; 
     return fetch.Body.ToString().Remove(0, dot); 
    } 

    private static String CreateFetchingStrategyDescription<T>(Expression<Func<T, Object>> fetch) 
    { 
     return CreateFetchingStrategyDescription<T, Object>(fetch); 
    } 

    private static String CombineFetchingStrategies<T, TFetchedEntity>(
       Expression<Func<T, Object>> fetch, Expression<Func<TFetchedEntity, Object>> secondFetch) 
    { 
     return CombineFetchingStrategies<T, Object, TFetchedEntity, Object>(fetch, secondFetch); 
    } 

    private static String CombineFetchingStrategies<TFetchEntity, TFetchResult, TFetchedEntity, TSecondFetchResult>(
     Expression<Func<TFetchEntity, TFetchResult>> fetch, Expression<Func<TFetchedEntity, TSecondFetchResult>> secondFetch) 
    { 
     return CreateFetchingStrategyDescription<TFetchEntity, TFetchResult>(fetch) + "." + 
      CreateFetchingStrategyDescription<TFetchedEntity, TSecondFetchResult>(secondFetch); 
    } 
} 

Uso:

Orders.Include(o => o.Product); // generates .Include("Product") 
Orders.Include(o => o.Product.Category); // generates .Include("Product.Category") 
Orders.Include(o => o.History); // a 1-* reference => .Include("History") 
// fetch all the orders, and in the orders collection. 
// also include the user reference so: .Include("History.User") 
// but because history is an collection you cant write o => o.History.User, 
// there is an overload which accepts a second parameter to describe the fetching 
// inside the collection. 
Orders.Include(o => o.History, h => h.User); 

no he probado esto en EF4.0, pero esperar que funcione.

+1

También debe trabajar para EF 4.0, pero sólo si utiliza las clases de diseño-generado. No funcionará con entidades POCO, ya que no heredan de 'StructuralObject' –

+0

Desafortunadamente no compila (.Net 4), porque' FixedWrappedMemberAcces' se desconoce. –

+0

ah, es una víctima de una copia y pegado de nuestra fuente ... la verdad es que no necesita esa línea ... simplemente es para resolver un problema con campos envueltos (vea http: //landman-code.blogspot .com/2010/08/protection-your-entitycollections-from.html para obtener más información sobre esto) pero no necesita esto para que el include funcione. –

8

Eso es bastante fácil de hacer, utilizando las expresiones:

public static class ObjectQueryExtensions 
{ 
    public static ObjectQuery<T> Include<T, TProperty>(this ObjectQuery<T> objectQuery, Expression<Func<T, TProperty>> selector) 
    { 
     MemberExpression memberExpr = selector.Body as MemberExpression; 
     if (memberExpr != null) 
     { 
      return objectQuery.Include(memberExpr.Member.Name); 
     } 
     throw new ArgumentException("The expression must be a MemberExpression", "selector"); 
    } 
} 

Usted puede usarlo exactamente como en el ejemplo en su pregunta


ACTUALIZACIÓN

mejorada versión, que soporta múltiples propiedades encadenadas:

public static class ObjectQueryExtensions 
{ 
    public static ObjectQuery<T> Include<T, TProperty>(this ObjectQuery<T> objectQuery, Expression<Func<T, TProperty>> selector) 
    { 
     string propertyPath = GetPropertyPath(selector); 
     return objectQuery.Include(propertyPath); 
    } 

    public static string GetPropertyPath<T, TProperty>(Expression<Func<T, TProperty>> selector) 
    { 
     StringBuilder sb = new StringBuilder(); 
     MemberExpression memberExpr = selector.Body as MemberExpression; 
     while (memberExpr != null) 
     { 
      string name = memberExpr.Member.Name; 
      if (sb.Length > 0) 
       name = name + "."; 
      sb.Insert(0, name); 
      if (memberExpr.Expression is ParameterExpression) 
       return sb.ToString(); 
      memberExpr = memberExpr.Expression as MemberExpression; 
     } 
     throw new ArgumentException("The expression must be a MemberExpression", "selector"); 
    } 
} 

Ejemplo:

var query = X.Include(x => x.Foo.Bar.Baz) // equivalent to X.Include("Foo.Bar.Baz") 
+0

sí, así es como empecé también, pero el suyo tiene la desventaja de que, mientras se compila, puede generar excepciones en tiempo de ejecución. porque no limita lo que TProperty puede ser. A EF solo le gustan sus propias propiedades en Incluir, porque no puede mapear propiedades autodeclaradas a su modelo de datos (al menos en EF1.0). Por eso incluí todas las sobrecargas, que restringen las expresiones para proporcionar seguridad en tiempo de compilación para los includes. Aunque todas las otras expresiones en LINQ aún pueden generar errores en tiempo de ejecución. –

+0

De acuerdo ... desafortunadamente no puede verificar * todo * en tiempo de compilación. Sigue siendo responsabilidad del desarrollador asegurarse de que la expresión realmente devuelva una propiedad mapeada. –

+0

De acuerdo, es una solución de compromiso. –

1

Buenas noticias de que EF4 CTP4 actualmente admite esta característica.

1

Otra opción es incluir una clase parcial interna dentro de su clase utilizando la plantilla TT.

código T4:

<# 
    region.Begin("Member Constants"); 
#> 
    public partial class <#=code.Escape(entity)#>Members 
    { 
<# 
    foreach (EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType && p.DeclaringType == entity)) 
    { 
     bool isForeignKey = entity.NavigationProperties.Any(np=>np.GetDependentProperties().Contains(edmProperty)); 
     bool isDefaultValueDefinedInModel = (edmProperty.DefaultValue != null); 
     bool generateAutomaticProperty = false; 
     #> 
     public const string <#=code.Escape(edmProperty)#> = "<#=code.Escape(edmProperty)#>"; 
     <# 
    } 
    #> 
    } 
    <# 
    region.End(); 
#> 

que producirá algo como:

#region Member Constants 
    public partial class ContactMembers 
    { 
     public const string ID = "ID"; 
       public const string OriginalSourceID = "OriginalSourceID"; 
       public const string EnabledInd = "EnabledInd"; 
       public const string EffectiveDTM = "EffectiveDTM"; 
       public const string EndDTM = "EndDTM"; 
       public const string EnterDTM = "EnterDTM"; 
       public const string EnterUserID = "EnterUserID"; 
       public const string LastChgDTM = "LastChgDTM"; 
       public const string LastChgUserID = "LastChgUserID"; 
      } 

    #endregion 
11

En EF 4.1, hay una built-in extension method para esto.

Debe tener una referencia al ensamblado "EntityFramework" (donde EF 4.1 vive) en su proyecto y usar System.Data.Entity.

using System.Data.Entity; 

Si desea incluir las entidades anidadas, lo haces de esta manera:

 db.Customers.Include(c => c.Orders.Select(o => o.LineItems)) 

No estoy seguro si esto funciona para EF4.0 entidades (ObjectContext base).

+0

¡Lo he probado y funciona! Tengo entidades de seguimiento automático (ya que no puedo usar entidades DbContext POCO con mi escenario WCF). Están basados ​​en ObjectContext y yo solo tuve que agregar la Referencia de EntityFramework (a través de NuGet), poner la línea que usa aquí y pude usar la extensión Incluir métodos. –

+0

La pregunta es, no tengo absolutamente ningún motivo para agregar la referencia de EF 4.1 aparte de esta comprobación de tiempo de compilación de mi carga ansiosa ... Supongo que es aceptable agregarlo ya que solo se usa en el lado del servidor. Tal vez encuentre otros métodos de extensión que usaré. –

+0

El método de extensión EF4.1 simplemente analiza la expresión que usted proporciona y luego la traduce a una llamada Incluir basada en cadenas. Por lo tanto, convierte efectivamente el ejemplo anterior en '.Include (" Orders.LineItems ")'. Probablemente pueda encontrar otros que hayan escrito métodos de extensión que hagan lo mismo si realmente no desea instalar EF4.1. Antes de 4.1, escribí el mío (tomando prestado de otros ejemplos), y puedo decirte que no es muy divertido. (La expresión API es ... extraña). Me complació tener acceso a un método integrado para esto. –

2

Otra solución es recuperar el nombre de la entidad utilizando EntitySet.Name.
código será:

var context = new DBContext(); 
context.Organizations.Include(context.Assets.EntitySet.Name).Where(o => o.Id == id).Single()