2010-04-06 4 views

Répondre

9

Pour Entity Framework 1.0, j'ai créé des méthodes d'extensions pour ce faire.

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

Utilisation:

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

Je n'ai pas testé sur EF4.0, mais j'attendre que cela fonctionne.

+1

Il devrait également fonctionner pour EF 4.0, mais seulement si vous utilisez les classes générées concepteur. Cela ne fonctionnera pas avec les entités POCO, puisqu'elles n'héritent pas de 'StructuralObject' –

+0

Cela ne compile malheureusement pas (.Net 4), car' FixedWrappedMemberAcces' est inconnu. –

+0

ah, c'est une victime d'une copie et coller de notre source .. la vérité est, vous n'avez pas besoin de cette ligne .. c'est simplement pour résoudre un problème avec les champs enveloppés (voir http: //landman-code.blogspot .com/2010/08/protection-your-entitycollections-from.html pour plus d'informations à ce sujet) mais vous n'avez pas besoin de cela pour que les includes fonctionnent. –

8

C'est assez facile à faire, en utilisant des expressions:

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

Vous pouvez l'utiliser exactement comme dans l'exemple dans votre question


MISE À JOUR

version améliorée, ce qui prend en charge plusieurs propriétés enchaînées:

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

Exemple:

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

oui, c'est comme ça que j'ai commencé, mais le vôtre a l'inconvénient que pendant la compilation, il peut générer des exceptions d'exécution. parce que vous ne limitez pas ce que TProperty peut être. EF n'aime ses propres propriétés que dans Include, car il ne peut pas mapper les propriétés auto déclarées sur son modèle de données (au moins dans EF1.0). C'est pourquoi j'ai inclus toutes les surcharges, qui limitent les expressions pour fournir la sécurité du temps de compilation pour les inclus. Bien que toutes les autres expressions dans LINQ peuvent toujours générer des erreurs d'exécution. –

+0

D'accord ... malheureusement, vous ne pouvez pas vérifier * tout * au moment de la compilation. Il est toujours de la responsabilité du développeur de s'assurer que l'expression renvoie vraiment une propriété mappée. –

+0

D'accord, c'est un compromis. –

1

Bonnes nouvelles que EF4 CTP4 supporte actuellement cette fonctionnalité.

1

Une autre option consiste à inclure une classe partielle interne dans votre classe en utilisant le modèle TT.

Code 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(); 
#> 

qui produira quelque chose comme:

#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

Dans EF 4.1, il y a une built-in extension method pour cela.

Vous devez avoir une référence à l'assembly "EntityFramework" (où EF 4.1 réside) dans votre projet et utiliser System.Data.Entity.

using System.Data.Entity; 

Si vous souhaitez inclure les entités imbriquées, vous le faites comme ceci:

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

Je ne sais pas si cela fonctionne pour EF4.0 entités (sur la base ObjectContext).

+0

Juste testé it & ça marche! J'ai des entités de suivi automatique (car je ne peux pas utiliser les entités POCO de DbContext avec mon scénario WCF). Ils sont basés sur ObjectContext et j'ai juste dû ajouter la référence EntityFramework (à travers NuGet), mettre la ligne using comme vous le dites ici et j'ai été capable d'utiliser l'extension Include methods! –

+0

La question est, je n'ai absolument aucune raison d'ajouter la référence EF 4.1 autre que cette Compiler la vérification de temps de mon chargement impatient ... Je suppose qu'il est acceptable de l'ajouter car il est seulement utilisé sur le côté serveur? Peut-être trouverai-je d'autres méthodes d'extension que j'utiliserai. –

+0

La méthode d'extension EF4.1 analyse simplement l'expression que vous fournissez, puis la convertit en un appel Include basé sur une chaîne. Donc, il convertit effectivement l'exemple ci-dessus en '.Include (" Orders.LineItems ")'. Vous pouvez probablement trouver d'autres personnes qui ont écrit des méthodes d'extension qui font la même chose si vous ne voulez vraiment pas installer EF4.1. Avant 4.1, j'ai écrit le mien (empruntant à d'autres exemples), et je peux vous dire que ce n'est pas trop amusant. (L'expression API est ... étrange.) J'ai été heureux d'avoir accès à une méthode intégrée pour cela. –

2

Une autre solution consiste à récupérer le nom de l'entité en utilisant EntitySet.Name.
Code Vous sera:

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