I am creating a sort of family tree in entity framework 4.0. I have come across an issue where the Entity Framework is only loading the immediate children. It does not load the children of the children even though i have an include specified.
For example, this is my query :-
public IQueryable<TreeMember> GetTreeMembers(int userId)
{
return this.ObjectContext.TreeMembers.Include("RelatedTreeMembers").Where(u => u.UserId == userId && u.RelatedTreeMemberId == null);
}
This would load the 1st level of children. But it does not load the children of the children. If i have to include children of the children, i have to write :-
public IQueryable<TreeMember> GetTreeMembers(int userId)
{
return this.ObjectContext.TreeMembers.Include("RelatedTreeMembers.RelatedTreeMembers").Where(u => u.UserId == userId && u.RelatedTreeMemberId == null);
}
This is quickly getting to be frustrating because i don't know how many times should i have to write this RelatedTreeMembers as a family tree can extend upto N level. How do i get past this issue? If my question is not clear please let me know.
Thanks in advance :)
That is how EF works. You want to define recursive (hierarchical) query which is not possible with eager loading in EF. You always have specify exactly which navigation properties you want to load - obviously in this scenario you can't because you don't know how deep is your recursion.
I like the idea #Magnus suggested with CTE but I would not use DB View. I would use stored procedure. The reason is that you already have entity TreeMember mapped to a table. If you define the view you will not be able to map it to the same entity type. You will need new entity for the view. If you use stored procedure you can map its result to already existing entity type.
Another way is to use lazy loading.
Write a view with a recursive CTE and than use that with Linq.
http://msdn.microsoft.com/en-us/library/ms186243.aspx
Related
I've been searching a bit on this but thus far haven't been able to find a decent solution. I'm trying to get an entity from my database without the related entities attached to it.
The function goes as following return context.Entity.SingleOrDefault(n => n.Name == name) where context is a DbContext.
As of now the reply contains only one Entity but with an added 50 "child" entities which I do not need.
What would be the best way to go about getting a single entity from the db?
Using EFC2.1 pre release build
Edit:
Also found that if you use DbContext.Entity.AsNoTracking you can get the entity without the child collections.
Not sure if the full entity will be saved after making changes and calling DbContext.saveChanges()
You have to enable Lazy Loading, simply add a property to your class like this.
public virtual ICollection<ChildType> NavigationProperty;
Here is a very useful document for Loading Related Data.
I recently discovered that you can also use DbContext.Entity.AsNoTracking() for this purpose.
Using a linq query when fetching an entity will cause all related entities contained in the linq to also be fetched.
Say you have a Teacher, a Student and a Classroom entity. A Teacher can have multiple Students and Classrooms. You want to find all Teachers with classroom A and all male students so you would do
DbContext.Teachers.Where(x => x.Classroom.Name = "A" && x.Student.Gender = "Male")
This will fetch the Teacher entities with all underlying Classrooms and Students since you called on them in the linq expression.
Since you want just the Teacher entity you should use the following :
DbContext.Teachers.AsNoTracking().Where(x => x.Classroom.Name = "A" && x.Student.Gender = "Male")
Using AsNoTracking() you declare that you do not want the underlying data and just need it to filter through the entity you do want.
I'm building an asp.net mvc app using Entity Framework, and I'm trying to order by a list. By a dynamic changed name, according to the name exists in the database.
bids = bids.OrderBy(s => s.PublisherName);
and the object:
public string PublisherName { get { db.Publishers.Find(pubid).Name; } }
But I'm getting an exception:
The specified type member 'PublisherName' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
What can I do? And how I can make it work?
Thanks.
Only initializers, entity members, and entity navigation properties
are supported.
db.Publishers.Find(pubid).Name; is neither initializer nor entity member, nor navigation property.
One possible way is to bring it on memory with AsEnumerable() :
bids = bids.AsEnumerable().OrderBy(s => s.PublisherName);
This will work perfectly as long as bids is small list of objects.
I believe Bidand Publisher are related, right? Maybe this will help you
var bids = from t in context.Bids
let u = t.Publishers.FirstOrDefault(i => i.Id == pubid)
orderby u.Name
select t;
Untested code, not sure if it will work for you!
When using Entity Framework via LINQ, all of the properties in the linq statement are translated in to SQL. EF only natively understands simple properties. It doesn't understand how to translate properties that contain actual code logic, which is what is causing the error you are seeing. The simplest way to fix this is to do the sort client side, outside of entity framework. The usual way to do this is to call .ToList on the unsorted result, and then sort the resulting list, which will happen client side.
I'm using Entity Framework 5 with ObjectContext on a relatively big and complex data model.
I would like to work around big queries generated when chaining multiple IQueryable.Include(Path) to eager load related objects.
For example, I'm doing something like this :
var queryPe = context.Person.Where(p => p.Id == 110).Include(#"AA");
queryPe = queryPe.Include(#"BB.CC.DD");
queryPe = queryPe.Include(#"EE.FF");
It could be made generic by using a string array and chaining each graph at runtime in a foreach loop.
Instead, I would like to do something like this :
Person pe = context.Person.Where(p => p.Id == 110).First();
context.LoadProperty(pe, "AA");
pe.BB.Attach(pe.BB.CreateSourceQuery().Include(#"CC.DD"));
pe.EE.Attach(pe.EE.CreateSourceQuery().Include(#"FF"));
Instead of having one big query we would have 4 smaller queries hitting the database.
Of course I still want to leverage the power of the graph path as a string.
I may be wrong, but this means that I should use relexion to get the navigation properties by name and executing the CreateSourceQuery() on it because there is no such extension method to do this.
Am I correct ?
EDIT 1 : Well, I have an additionnal constraint, that is, I'm using Self Tracking Entities (STE). This means that Related objects are not materialized as EntityCollection or EntityReference. So Attach() and CreateSourceQuery() are not available !
So I'm stuck with Context.LoadProperty to deal with object graphs.
Is it only possible ?
EDIT 2 : Problem exposed in EDIT 1 solved, thanks to the help of DbContext class. See the code below :
Person pe = context.Person.Where(p => p.Id == 110).First();
context.LoadProperty(pe, "AA");
DbContext dbc = new DbContext(context, false);
dbc.Entry(pe).Collection(#"BB").Query().Include(#"CC.DD").Load();
dbc.Entry(pe).Reference(#"EE").Query().Include(#"FF").Load();
EDIT 3 02/11/2013 : There is an issue with the code presented above (EDIT 2). If the last entity in the path is a reference and not a collection, the code doesn't fail but it is not loaded :-(
EDIT 4 : Instead of using reflection, right now, I'm generating the code by looking at the Entity Data Model with the help of a T4 template.
Sometimes stored procedures are best. Write a stored procedure that returns multiple result sets, one for each type of entity you wish to eagerly load. This is highly performant compared to what you're trying to accomplish and the stored procedure will be far more readable than this jumble of includes and separate load statements per reference/collection. And yes, EF will hook up related entities automatically!
Here's a reference for sprocs with multiple result sets for both EDMX and code first:
http://msdn.microsoft.com/en-us/data/jj691402.aspx
Try to separate your aggregates in multiple contexts. Each Bounded context should have a separate context. This way you create a loosely coupled entity framework solution.
Julie Lerman has a good video on plural sight about this concept.
I would prefer to use stored procedures. Easy maintainance, works faster etc.
I am curious on how to create a property that can be translated by LINQ. Below is a very simple example.
I have a table/class Category, that has a column ParentId linking to itself (So a category can have sub-categories)
EF automatically generates a property Category1, which is the parent category.
For the sake of clarity, I created another property
public partial class Category
{
public Category Parent
{
get { return Category1; }
}
}
The problem is, this works
var categs = ctx.Categories.Where(x => x.Category1 == null);
but this doesn't work
var categs = ctx.Categories.Where(x => x.Parent == null);
The specified type member 'Parent' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Is there any way to create a translatable property (LINQ to SQL) without doing .ToList()?
EDIT: I want to avoid touching Model.edmx because the database often changes during development and the .edmx often needs to be recreated
If you ask if it's possible to create a property with any C# code in the getters/setters and later have it understood by standard LINQ to Entities - then no, it can't be done. C# is much more expressive then SQL and it's unreasonable to expect Entity Framework to act as a general C# to SQL translator.
You can work around this is many cases though, see Using a partial class property inside LINQ statement for an example.
Update
It'd help if you told us what exactly you want to achieve, but here's an example:
public partial class Category
{
public static Expression<Func<Category, bool>> ParentIsNullExpression
{
get
{
return c => c.Category1 == null;
}
}
}
And then
var categs = ctx.Categories.Where(Category.ParentIsNullExpression);
All sorts of manipulations on Expressions are possible, some of them are supported by EF and as such translate to SQL.
in your Entity Data Model Designer (.edmx file), you can rename the Category1 to Parent
You have 4 options:
Use code first
Add it to the EDMX designer
Add your property to the CSDL section of the EDMX, add a column to the SSDL section of the EDMX, then map them to each other in the mapping section of the EDMX.
Bring the query into memory using .ToList() then use internal LINQ instead of LINQ to Entities.
I retrieve a collection with the following query:
var numbers = _betDetailItem.GetBetDetailItems().Where(betDetailItem => betDetailItem.BetDetail.Bet.DateDrawing == resultToCreate.Date && betDetailItem.BetDetail.Bet.Status == 1).Where(condition);
Right there I'm able to access my navigation properties and navigate through binded info. Note how I actually use them to filter the data.
After I group the results, the navigation properties become null.
var grouped = numbers.GroupBy(p => p.BetDetail.Bet);
//Iterate through the collection created by the Grouping
foreach (IGrouping<Bet, BetDetailItem> group in grouped)
{
var details = group.Key.BetDetails; //This is what doesn't work. BetDetails is a navigation property which was accessible in the previous query.
}
Am I doing something wrong?
You are confusing LINQ to Entities and object operations.
This is LINQ to Entities:
var numbers = _betDetailItem.GetBetDetailItems().Where(betDetailItem => betDetailItem.BetDetail.Bet.DateDrawing == resultToCreate.Date && betDetailItem.BetDetail.Bet.Status == 1).Where(condition);
So is this:
var grouped = numbers.GroupBy(p => p.BetDetail.Bet);
These are object operations:
foreach (IGrouping<Bet, BetDetailItem> group in grouped)
{
var details = group.Key.BetDetails; //This is what doesn't work. BetDetails is a navigation property which was accessible in the previous query.
}
In LINQ to Entities, there is never any need to think about loading related instances. You can always refer to any property of any object. However, at some point, you want to move out of the LINQ to Entities world and into object space, because you want to work with instances of type BetDetail instead of type IQueryable<BetDetail>. This means that the Entity Framework is now required to generate SQL to retrieve data from the database. At that point, it doesn't snow which related instances you will be accessing in your code later on. Nothing in your LINQ to Entities query forces the loading of the related Bet. So unless you do something to cause it to be loaded, like use eager loading, explicit loading, or EF 4 lazy loading, it won't be loaded.
Using lazy loading (e.g., in Entity Framework 4, or in another ORM) will make this code appear to function, but it will be unnecessarily slow, due to the large number of database queries generated. A better solution would be to use eager loading or projection. This way there will be only one DB roundtrip.
Once you do a GroupBy(), you're no longer dealing with your entities -- they have been... well, grouped, so the var in var grouped = ... is now of type IEnumerable<IGrouping<.... As such, the methods available on the items in the grouped collection are the methods of the IGrouping<> interface.
You may want to OrderBy() instead of GroupBy(), depending on your need, or you'll need to iterate on two levels: iterate over each group in grouped, and over each member within each of those.
Once you are inside of a particular IGrouping<>, you should have access to the properties for which you are looking.