How to iterate an Entity Framework tree to find a specific item - entity-framework

I have loaded my product catalog using Entity Framework
I would like to iterate all items to find a specific item
the structure is
Category -> [Subcategory ->] Product -> options
Subcategory, product and options are EntityCollection of their specific type
All types derive from EntityObject
Let's say I'm looking for option 12 but I don't know in which product it is in.
How can I iterate all objects to find the option 12 ? I have this so far. in my EntityObject, I know it's not recursive yet but will eventually be once i know which properties are collections, I might be approaching it the wrong way ...
public T Find<T>(Type type, int id) where T : EntityObject
{
//get all properties
PropertyInfo[] properties = this.GetType().GetProperties();
// foreach property find the one
foreach (PropertyInfo oPropertyInfo in properties)
{
// check for type
if (oPropertyInfo.PropertyType == type)
{
PersistentEntity o = oPropertyInfo.GetValue(this, null) as EntityObject;
if (o != null && o.Id == id)
{
return (T)o;
}
}
// if property has childs, is IEnumerable -> recursive
}
return (T)new EntityObject();
}

How about this?
var query =
from c in categories
from sc in c.SubCategories
from from p in sc.Products
from o in p.Options
where o.Id == 2
select c /* or p?? */;

Related

Linq to select top 1 related entity

How can I include a related entity, but only select the top 1?
public EntityFramework.Member Get(string userName)
{
var query = from member in context.Members
.Include(member => member.Renewals)
where member.UserName == userName
select member;
return query.SingleOrDefault();
}
According to MSDN:
"Note that it is not currently possible to filter which related entities are loaded. Include will always bring in all related entities."
http://msdn.microsoft.com/en-us/data/jj574232
There is also a uservoice item for this functionality:
http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/1015345-allow-filtering-for-include-extension-method
The approach to use an anonymous object works, even though it's not clean as you wish it would be:
public Member GetMember(string username)
{
var result = (from m in db.Members
where m.Username == username
select new
{
Member = m,
FirstRenewal = m.Renewals.FirstOrDefault()
}).AsEnumerable().Select(r => r.Member).FirstOrDefault();
return result;
}
The FirstRenewal property is used just to make EF6 load the first renewal into the Member object. As a result the Member returned from the GetMember() method contains only the first renewal.
This code generates a single Query to the DB, so maybe it's good enough for You.

Querying against DbContext.Set(TypeVariable) in Entity Framework

I'm re-factoring an application to invert some dependencies. Part of the original code uses DbContext.Set<MyEntityType>() to obtain the set to query against.
With the inversion, the MyEntityType is no longer explicitly known to the code using DbContext.Set<MyEntityType>(), so now I'm using DbContext.Set(TypeVariable) instead.
The re-factoring works to the extent that the correct DbSet is being returned.
However, the type DbContext.Set(TypeVariable).Local is IList (contained type unknown) where-as the type of DbContext.Set<MyEntityType>().Local was ObservableCollection<MyEntityType>. This means that it's now impossible to do Linq against the DbSet.
The best workaround I've been able to achieve is to cast to an interface that MyEntityType and other entity types implement (some code omitted for clarity)
var set = Context.Set(targetType);
var entity = set.Local.OfType<IActionTarget>()
.FirstOrDefault(l => l.Id == key.Id && l.EffectiveDate == key.EffectiveDate);
var querables = set as IQueryable<IActionTarget>;
entity = querables.FirstOrDefault(e => e.Id == key.Id && e.EffectiveDate == key.EffectiveDate);
So, two questions:
Why doesn't DbContext.Set(TypeVariable) return a strongly typed set?
Is there a better way to do the dependency inversion?
Some further details as requested
It's all about dependencies. The Model contains POCO classes which are persisted via EF Code First in the typical way (but via a Repository). An ActionEvaluator takes some incoming data, and via Repository methods, determines what actions need to occur - hence the queries against the DbSets.
In the original code, there was only one type of incoming data (CSV of a particular format) and the ActionEvaluator had a tight dependency to this data and knew which POCO classes were applicable to which CSV records.
Now, we want to expand to use different CSV formats and web api messages. To do this, we need to invert the dependencies so that the DataSource tells the ActionEvaluator what POCO classes it's records apply to. This is done by way of a Type variable.
So, the ActionEvaluator can no longer use a generic type parameter, but it can pass the type variable to the Repository which uses it to find the correct DbSet.
The problem is the difference between the two ways of finding the DbSet - DbContext.Set<AnEntity>() and DbContext.Set(TypeVariable).
I guess I'm asking for an enhancement in EF to make these two functionally equivalent in their return values, but that may not be possible since the types of the second version are determined at runtime.
Here's the full method code as requested:
private IActionTarget DbContextGetEntity(Type targetType, IActionTarget key)
{
var set = Context.Set(targetType);
if (set == null)
{
throw new Exception("Unable to find DbSet for type '{0}'".F(targetType.Name));
}
// Look in the local cache first
var entity = set.Local.OfType<IActionTarget>()
.FirstOrDefault(l => l.Id == key.Id && l.EffectiveDate == key.EffectiveDate);
if (entity == null)
{
// If not found locally, hit the database
var querables = set as IQueryable<IActionTarget>;
if (querables != null)
{
entity = querables.FirstOrDefault(e => e.Id == key.Id && e.EffectiveDate == key.EffectiveDate);
}
}
return entity;
}
Ideally, I want to replace
var entity = set.Local.OfType<IActionTarget>()
.FirstOrDefault(l => l.Id == key.Id && l.EffectiveDate == key.EffectiveDate);
with
var entity = set.Local.OfType(targetType)
.FirstOrDefault(l => l.Id == key.Id && l.EffectiveDate == key.EffectiveDate);
I haven't compiled it so please excuse any formatting issues - can you use the dynamic type to achieve the same thing?
private IActionTarget DbContextGetEntity(Type targetType, IActionTarget key)
{
dynamic instance = Activator.CreateInstance(targetType);
return DbContextGetEntity(instance, key);
}
private IActionTarget DbContextGetEntity<T>(T instance, IActionTarget key)
where T : class, IActionTarget
{
var set = Context.Set<T>(targetType);
if (set == null)
{
throw new Exception();
}
// Look in the local cache first
var entity = set.Local
.FirstOrDefault(l => l.Id == key.Id && l.EffectiveDate == key.EffectiveDate);
if (entity == null)
{
// If not found locally, hit the database
entity = set
.FirstOrDefault(e => e.Id == key.Id && e.EffectiveDate == key.EffectiveDate);
}
return entity;
}

How do I filter related Child Record

I am using RIA services. I need to select a parent entity (UnitOccupier) which has a number of related child entities(UnitOccupierDetails). I need to filter the child entities to return a single record. How do I do this?
var q = from uo in _unitOccupierContext.GetUnitOccupierQuery()
where uo.UnitOccupierDetails.????
---> I cant get to the child properties here
Thanks
You cannot include a filtered child collection of the selected parent. You can only include either the full collection or no child collection at all. But as a workaround you could use an intermediate anonymous type, like so:
var q = (from uo in _unitOccupierContext.GetUnitOccupierQuery()
select new {
Parent = uo,
Childs = uo.UnitOccupierDetails
.Where(uod => uod.MyDetailsProp == MyDetailsValue)
}).FirstOrDefault();
if (q != null)
{
UnitOccupier selectedUnitOccupier = q.Parent;
selectedUnitOccupier.UnitOccupierDetails = q.Childs.ToList();
// selectedUnitOccupier now only contains the filtered childs
}
Edit
If you want to query for the childs and include their parents (related to question in comment) you could use:
var q = _unitOccupierContext.GetUnitOccupierQuery()
.SelectMany(uo => uo.UnitOccupierDetails
.Where(uod => uod.MyDetailsProp == MyDetailsValue))
.Include(uod => uod.UnitOccupier)
.FirstOrDefault(); // or .ToList() if you expect more than one record
// q is now null or a single UnitOccupierDetails entity
// with a reference to its parent
I am assuming here that your UnitOccupierDetails class has a navigation property to the parent UnitOccupier.

How to include an inherited property in an "uneven" inheritance with Entity Framework?

I have the following model (simplified):
abstract class CartItem { EntityReference<Cart> Cart; }
class HotelCartItem : CartItem { EntityReference<Hotel> Hotel; }
class TransferCartItem : CartItem { }
class Hotel { }
As expressed "graphically":
CartItem
|<- HotelCartItem
| |-> Hotel
|
|<- TransferCartItem
Now I want to load all CartItems and include data from the Hotel class if the type of CartItem is a HotelCartItem.
This is how I'm trying to do it, but it fails with a "does not declare a navigation property with the name 'Hotel'."
var q = from cartitems in context.CartItems
.Include("Hotel")
where cartitems.CART_ID == CartID
select cartitems;
If I leave out the .Include("Hotel") the Hotel property of CartItems of type Hotel is null.
My question:
Is there a way to get around this?
Eager loading of navigation properties on sub classes is tricky. I haven't found any other way except loading them separately. The easy way to do that is registering custom ObjectMaterialized handler (only in EF 4.0) on ObjectContext:
context.ObjectMaterialized += RegisterEagerLoadingStrategies;
And the handler method looks like:
private static void RegisterEagerLoadingStrategies(object sender, ObjectMaterializedEventArgs e)
{
var context = (ObjectContext)sender;
var cartItem = e.Entity as HotelCartItem;
if (cartItem != null)
{
context.LoadProperty(cartItem, o => o.Hotel);
}
}
This solution has N + 1 problem. N + 1 means that if your main query returns N HotelCartItems you will execute N + 1 queries in database (each LoadProperty calls additional query). Also this method is called for every loaded entity (not only for HotelCartItem). So this solution is really bad for loading large number of entities.
Another approach of loading navigation properties from related entities is dividing your query into two queries. First query will load CartItems and second query will load Hotels for cart items loaded in first query (same conditions). References to hotels in cart items should be automatically set if your entities are still attached to context.
I ended up splitting the query into several parts:
Load the parent item, a "Cart".
For each of the different types I got (HotelCartItem and TransferCartItem) I queried the db for a set of only that type:
private IQueryable<T> GetCartItemQuery<T>(Guid CartID) where T : CartItem
{
if (typeof(T) == typeof(HotelCartItem))
{
var q = from ci in db.CartItems.OfType<T>()
.Include("Hotel")
where ci.CART_ID == CartID
select ci;
return q;
}
else
{
var q = from ci in db.CartItems.OfType<T>()
where ci.CART_ID == CartID
select ci;
return q;
}
}
Call it with:
var hotels = GetCartItemQuery<HotelCartItem>(CartID);
var transfers = GetCartItemQuery<TransferCartItem>(CartID);
3 . Add the CartItems to the collection of the Cart-object.

How to do recursive load with Entity framework?

I have a tree structure in the DB with TreeNodes table. the table has nodeId, parentId and parameterId. in the EF, The structure is like TreeNode.Children where each child is a TreeNode...
I also have a Tree table with contain id,name and rootNodeId.
At the end of the day I would like to load the tree into a TreeView but I can't figure how to load it all at once.
I tried:
var trees = from t in context.TreeSet.Include("Root").Include("Root.Children").Include("Root.Children.Parameter")
.Include("Root.Children.Children")
where t.ID == id
select t;
This will get me the the first 2 generations but not more.
How do I load the entire tree with all generations and the additional data?
I had this problem recently and stumbled across this question after I figured a simple way to achieve results. I provided an edit to Craig's answer providing a 4th method, but the powers-that-be decided it should be another answer. That's fine with me :)
My original question / answer can be found here.
This works so long as your items in the table all know which tree they belong to (which in your case it looks like they do: t.ID). That said, it's not clear what entities you really have in play, but even if you've got more than one, you must have a FK in the entity Children if that's not a TreeSet
Basically, just don't use Include():
var query = from t in context.TreeSet
where t.ID == id
select t;
// if TreeSet.Children is a different entity:
var query = from c in context.TreeSetChildren
// guessing the FK property TreeSetID
where c.TreeSetID == id
select c;
This will bring back ALL the items for the tree and put them all in the root of the collection. At this point, your result set will look like this:
-- Item1
-- Item2
-- Item3
-- Item4
-- Item5
-- Item2
-- Item3
-- Item5
Since you probably want your entities coming out of EF only hierarchically, this isn't what you want, right?
.. then, exclude descendants present at the root level:
Fortunately, because you have navigation properties in your model, the child entity collections will still be populated as you can see by the illustration of the result set above. By manually iterating over the result set with a foreach() loop, and adding those root items to a new List<TreeSet>(), you will now have a list with root elements and all descendants properly nested.
If your trees get large and performance is a concern, you can sort your return set ASCENDING by ParentID (it's Nullable, right?) so that all the root items are first. Iterate and add as before, but break from the loop once you get to one that is not null.
var subset = query
// execute the query against the DB
.ToList()
// filter out non-root-items
.Where(x => !x.ParentId.HasValue);
And now subset will look like this:
-- Item1
-- Item2
-- Item3
-- Item4
-- Item5
About Craig's solutions:
You really don't want to use lazy loading for this!! A design built around the necessity for n+1 querying will be a major performance sucker. ********* (Well, to be fair, if you're going to allow a user to selectively drill down the tree, then it could be appropriate. Just don't use lazy loading for getting them all up-front!!)I've never tried the nested set stuff, and I wouldn't suggest hacking EF configuration to make this work either, given there is a far easier solution. Another reasonable suggestion is creating a database view that provides the self-linking, then map that view to an intermediary join/link/m2m table. Personally, I found this solution to be more complicated than necessary, but it probably has its uses.
When you use Include(), you are asking the Entity Framework to translate your query into SQL. So think: How would you write an SQL statement which returns a tree of an arbitrary depth?
Answer: Unless you are using specific hierarchy features of your database server (which are not SQL standard, but supported by some servers, such as SQL Server 2008, though not by its Entity Framework provider), you wouldn't. The usual way to handle trees of arbitrary depth in SQL is to use the nested sets model rather than the parent ID model.
Therefore, there are three ways which you can use to solve this problem:
Use the nested sets model. This requires changing your metadata.
Use SQL Server's hierarchy features, and hack the Entity Framework into understanding them (tricky, but this technique might work). Again, you'll need to change your metadata.i
Use explicit loading or EF 4's lazy loading instead of eager loading. This will result in many database queries instead of one.
I wanted to post up my answer since the others didn't help me.
My database is a little different, basically my table has an ID and a ParentID. The table is recursive. The following code gets all children and nests them into a final list.
public IEnumerable<Models.MCMessageCenterThread> GetAllMessageCenterThreads(int msgCtrId)
{
var z = Db.MCMessageThreads.Where(t => t.ID == msgCtrId)
.Select(t => new MCMessageCenterThread
{
Id = t.ID,
ParentId = t.ParentID ?? 0,
Title = t.Title,
Body = t.Body
}).ToList();
foreach (var t in z)
{
t.Children = GetChildrenByParentId(t.Id);
}
return z;
}
private IEnumerable<MCMessageCenterThread> GetChildrenByParentId(int parentId)
{
var children = new List<MCMessageCenterThread>();
var threads = Db.MCMessageThreads.Where(x => x.ParentID == parentId);
foreach (var t in threads)
{
var thread = new MCMessageCenterThread
{
Id = t.ID,
ParentId = t.ParentID ?? 0,
Title = t.Title,
Body = t.Body,
Children = GetChildrenByParentId(t.ID)
};
children.Add(thread);
}
return children;
}
For completeness, here's my model:
public class MCMessageCenterThread
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public IEnumerable<MCMessageCenterThread> Children { get; set; }
}
I wrote something recently that does N+1 selects to load the whole tree, where N is the number of levels of your deepest path in the source object.
This is what I did, given the following self-referencing class
public class SomeEntity
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string Name { get; set;
}
I wrote the following DbSet helper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace Microsoft.EntityFrameworkCore
{
public static class DbSetExtensions
{
public static async Task<TEntity[]> FindRecursiveAsync<TEntity, TKey>(
this DbSet<TEntity> source,
Expression<Func<TEntity, bool>> rootSelector,
Func<TEntity, TKey> getEntityKey,
Func<TEntity, TKey> getChildKeyToParent)
where TEntity: class
{
// Keeps a track of already processed, so as not to invoke
// an infinte recursion
var alreadyProcessed = new HashSet<TKey>();
TEntity[] result = await source.Where(rootSelector).ToArrayAsync();
TEntity[] currentRoots = result;
while (currentRoots.Length > 0)
{
TKey[] currentParentKeys = currentRoots.Select(getEntityKey).Except(alreadyProcessed).ToArray();
alreadyProcessed.AddRange(currentParentKeys);
Expression<Func<TEntity, bool>> childPredicate = x => currentParentKeys.Contains(getChildKeyToParent(x));
currentRoots = await source.Where(childPredicate).ToArrayAsync();
}
return result;
}
}
}
Whenever you need to load a whole tree you simply call this method, passing in three things
The selection criteria for your root objects
How to get the property for the primary key of the object (SomeEntity.Id)
How to get the child's property that refers to its parent (SomeEntity.ParentId)
For example
SomeEntity[] myEntities = await DataContext.SomeEntity.FindRecursiveAsync(
rootSelector: x => x.Id = 42,
getEntityKey: x => x.Id,
getChildKeyToParent: x => x.ParentId).ToArrayAsync();
);
Alternatively, if you can add a RootId column to the table then for each non-root entry you can set this column to the ID of the root of the tree. Then you can fetch everything with a single select
DataContext.SomeEntity.Where(x => x.Id == rootId || x.RootId == rootId)
For an example of loading in child objects, I'll give the example of a Comment object that holds a comment. Each comment has a possible child comment.
private static void LoadComments(<yourObject> q, Context yourContext)
{
if(null == q | null == yourContext)
{
return;
}
yourContext.Entry(q).Reference(x=> x.Comment).Load();
Comment curComment = q.Comment;
while(null != curComment)
{
curComment = LoadChildComment(curComment, yourContext);
}
}
private static Comment LoadChildComment(Comment c, Context yourContext)
{
if(null == c | null == yourContext)
{
return null;
}
yourContext.Entry(c).Reference(x=>x.ChildComment).Load();
return c.ChildComment;
}
Now if you were having something that has collections of itself you would need to use Collection instead of Reference and do the same sort of diving down. At least that's the approach I took in this scenario as we were dealing with Entity and SQLite.
This is an old question, but the other answers either had n+1 database hits or their models were conducive to bottom-up (trunk to leaves) approaches. In this scenario, a tag list is loaded as a tree, and a tag can have multiple parents. The approach I use only has two database hits: the first to get the tags for the selected articles, then another that eager loads a join table. Thus, this uses a top-down (leaves to trunk) approach; if your join table is large or if the result cannot really be cached for reuse, then eager loading the whole thing starts to show the tradeoffs with this approach.
To begin, I initialize two HashSets: one to hold the root nodes (the resultset), and another to keep a reference to each node that has been "hit."
var roots = new HashSet<AncestralTagDto>(); //no parents
var allTags = new HashSet<AncestralTagDto>();
Next, I grab all of the leaves that the client requested, placing them into an object that holds a collection of children (but that collection will remain empty after this step).
var startingTags = await _dataContext.ArticlesTags
.Include(p => p.Tag.Parents)
.Where(t => t.Article.CategoryId == categoryId)
.GroupBy(t => t.Tag)
.ToListAsync()
.ContinueWith(resultTask =>
resultTask.Result.Select(
grouping => new AncestralTagDto(
grouping.Key.Id,
grouping.Key.Name)));
Now, let's grab the tag self-join table, and load it all into memory:
var tagRelations = await _dataContext.TagsTags.Include(p => p.ParentTag).ToListAsync();
Now, for each tag in startingTags, add that tag to the allTags collection, then travel down the tree to get the ancestors recursively:
foreach (var tag in startingTags)
{
allTags.Add(tag);
GetParents(tag);
}
return roots;
Lastly, here's the nested recursive method that builds the tree:
void GetParents(AncestralTagDto tag)
{
var parents = tagRelations.Where(c => c.ChildTagId == tag.Id).Select(p => p.ParentTag);
if (parents.Any()) //then it's not a root tag; keep climbing down
{
foreach (var parent in parents)
{
//have we already seen this parent tag before? If not, instantiate the dto.
var parentDto = allTags.SingleOrDefault(i => i.Id == parent.Id);
if (parentDto is null)
{
parentDto = new AncestralTagDto(parent.Id, parent.Name);
allTags.Add(parentDto);
}
parentDto.Children.Add(tag);
GetParents(parentDto);
}
}
else //the tag is a root tag, and should be in the root collection. If it's not in there, add it.
{
//this block could be simplified to just roots.Add(tag), but it's left this way for other logic.
var existingRoot = roots.SingleOrDefault(i => i.Equals(tag));
if (existingRoot is null)
roots.Add(tag);
}
}
Under the covers, I am relying on the properties of a HashSet to prevent duplicates. To that end, it's important that the intermediate object that you use (I used AncestralTagDto here, and its Children collection is also a HashSet), override the Equals and GetHashCode methods as appropriate for your use-case.