15

Is there any alternative to this:

Organizations.Include("Assets").Where(o => o.Id == id).Single() 

I would like to see something like:

Organizations.Include(o => o.Assets).Where(o => o.Id == id).Single() 

to avoid the hard-coded string "Assets".

7 Answers 7

13

In EF 4.1, there is a built-in extension method for this.

You must have a reference to "EntityFramework" assembly (where EF 4.1 lives) in your project and use System.Data.Entity.

using System.Data.Entity; 

If you want to include nested entities, you do it like this:

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

Not sure if this works for EF4.0 entities (ObjectContext based).

Sign up to request clarification or add additional context in comments.

3 Comments

Just tested it & it works! I have Self Tracking entities (as I can't use DbContext POCO entities with my WCF scenario). They are ObjectContext based and I just had to Add the EntityFramework Reference (through NuGet), put the using line as you state here and I was able to use the extension Include methods!
The question is, I have absolutely no reason to add the EF 4.1 reference other than this Compile time checking of my eager loading... I guess It's acceptable to add it as it's only used on the Server side? Perhaps I'll find other extension methods I will use.
The EF4.1 extension method just parses the expression you provide and then translates it into a string-based Include call. So it effectively converts the example above into .Include("Orders.LineItems"). You can probably find others who have written extension methods that do the same thing if you really don't want to install EF4.1. Prior to 4.1, I wrote my own (borrowing from other examples), and I can tell you it's not too fun. (The expression API is ... strange.) I was glad to get access to a built-in method for this.
10

For Entity Framework 1.0, I created some extensions methods for doing this.

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

Usage:

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

I haven't tested this on EF4.0, but I expect it to work.

4 Comments

It should also work for EF 4.0, but only if you use the designer-generated classes. It won't work with POCO entities, since they don't inherit from StructuralObject
This unfortunately doesn't compile (.Net 4), because FixedWrappedMemberAcces is unknown.
ah, it is a victim of a copy and pasting from our source.. the truth is, you don't need that line.. it's merely to solve a problem with wrapped fields (see landman-code.blogspot.com/2010/08/… for more about this) but you don't need this for the includes to work.
In EF4 you can include for EntityCollection and then to digg deeper on he type of the collection. Is your solution support it?
8

That's pretty easy to do, using 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"); } } 

You can use it exactly as in the example in your question


UPDATE

Improved version, which supports multiple chained properties :

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

Example :

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

3 Comments

yes, that's how I started out as well, but yours has the disadvantage that while it compiles, it can generate run-time exceptions. because you don't limit what TProperty can be. EF only likes it's own properties in the Include, because it cannot map self declared properties to it's data model (at least in EF1.0). That why I included all the overloads, which restrict the expressions to provide compile time safety for the includes. Although all the other expressions in LINQ can still generate runtime errors.
Agreed... unfortunately you can't check everything at compile time. It's still the developer's responsibility to make sure that the expression really returns a mapped property
I tried your code with EF4, but it doesn´t work. Method Include is defined on ObjectQuery<T> not ObjectSet<T>. So its a extension method on the Query object.
2

See 'Say goodbye to the hardcoded ObjectQuery(T).Include calls'.

Comments

2

Another solution is to retrieve entity name using EntitySet.Name.
You code will be:

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

Comments

1

Good news that EF4 CTP4 currently support this feature.

Comments

1

Another option is to include an internal partial class inside your class using the TT template.

T4 code:

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

Which will produce something like:

 #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 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.