I have an element that represents a node in a tree structure.
public class Element
{
public int Id { get; set; }
...
public Element Left { get; set; }
public Element Right { get; set; }
}
I am maintaining a table with all those elements by foreign keys to the child elements.
If I try to get this tree back by using eager loading, I get the tree in postorder traversal:
public string GetExpression(int rootId)
{
var root = _context.Set<Element>()
.Include(r => r.Left)
.Include(r => r.Right)
.ToList();
}
Is there a way using queries to get the elements in inorder traversal? Or do I have to do this by myself recursively?
The order that records are returned in a query is not defined unless you have an ORDERBY clause. So it is just luck that the example you gave returns them in a post-order.
I would suggest that you simply define in-order and post-order traversal methods and invoke them after you have loaded the entire set. You can omit the two .Include statements since EF will patch up the navigation relations during the load.
Related
I have 2 classes like this:
Parent.cs
public class Parent
{
public int Id {get;set;}
public virtual ICollection<Child> Children { get; set; }
}
Child.cs
public class Child
{
public int Id {get;set;}
public ItemStatusType ItemStatusTyp { get; set; }
public int ParentId {get;set;}
[ForeignKey("ParentId")]
public virtual Parent Parent { get; set; }
}
ItemStatusType.cs
public enum ItemStatusType
{
Active = 1,
Deactive = 2,
Deleted = 3
}
What I want is to somehow retrieve always the active ones and not the deleted ones. Since I am not deleting the record physically, I'm merely updating the ItemStatusType to Deleted status.
So, when I say ParentObj.Children I only wish to retrieve the active ones without further using Where condition.
Here is so far what I've done but giving an exception on runtime that I stated afterwards:
public class ParentConfiguration : EntityTypeConfiguration<Parent>
{
public ParentConfiguration()
{
HasMany(c => c.Children.Where(p => p.ItemStatusTyp != ItemStatusType.Deleted).ToList())
.WithRequired(c => c.Parent)
.HasForeignKey(c => c.ParentId)
;
}
}
Runtime Exception:
The expression 'c => c.Children.Where(p => (Convert(p.ItemStatusTyp)
!= 3)).ToList()' is not a valid property expression. The expression
should represent a property: C#: 't => t.MyProperty' VB.Net:
'Function(t) t.MyProperty'.
I had to use ToList after the expression, otherwise it does not compile.
What is the proper what to do what I want?
Thanks in advance,
You cannot use Where or any other logic in fluent property mapping - that's just configuration.
Basically you cannot solve what you need in declarative way.
There are some workarounds which you can use for first-level entities, like implement your own extension(s) MySet<T> which will return .Set<T>.Where(x => x.ItemStatusType != ItemStatusType.Deleted) and use it everywhere, but that won't solve filtering issue for child collections.
You can go hard way and prepare a separate set of entities to use for selecting data, which basically should be based on database views or stored procedures; you will have to create separate view for every entity, so you will be able to combine selecting in any relations based on these views-entities.
For inserting though you will need to have entities mapped over "real" tables. No sure if it worth it but it might in some cases.
I used SQLite-Net Extensions
in the following code to retrieve 1000 rows with their children relationships from an Sqlite database:
var list =
SQLiteNetExtensions.Extensions.ReadOperations.GetAllWithChildren<DataModel>(connection);
The problem is that the performance is awkward. Because GetAllWithChildren() returns a List not an Enumerable. Does exist any way to load the records in to an Enumerable using Sqlite.net extensions?
I now use Table() method from Sqlite.net, loads the fetched rows in to the Enumerable but I dont want to use it because it does not understand the relationships and does not load the children entities at all.
GetAllWithChildren suffers from the N+1 problem, and in your specific scenario this performs specially bad. It's not clear in your question what you're trying, but you could try these solutions:
Use the filterparameter in GetAllWithChildren:
Instead of loading all the objects to memory and then filter, you can use the filter property, that internally performs a Table<T>().Where(filter) query, and SQLite-Net will convert to a SELECT-WHERE clause, so it's very efficient:
var list = connection.GetAllWithChildren<DataModel>(d => d.Name == "Jason");
Perform the query and then load the relationships
If you look at the GetAllWithChildren code you'll realize that it just performs the query and then loads the existing relationships. You can do that by yourself to avoid automatically loading unwanted relationships:
// Load elements from database
var list = connection.Table<DataModel>().Where(d => d.Name == "Jason").toList();
// Iterate elements and load relationships
foreach (DataModel element in list) {
connection.GetChildren(element, recursive = false);
}
Load relationships manually
To completely workaround the N+1 problem you can manually fetch relationships using a Contains filter with the foreign keys. This highly depends on you entity model, but would look like this:
// Load elements from database
var list = connection.Table<DataModel>().Where(d => d.Name == "Jason").toList();
// Get list of dependency IDs
var dependencyIds = list.Select(d => d.DependencyId).toList();
// Load all dependencies from database on a single query
var dependencies = connection.Table<Dependency>.Where(d => dependencyIds.Contains(d.Id)).ToList();
// Assign relationships back to the elements
foreach (DataModel element in list) {
element.Dependency = dependencies.FirstOrDefault(d => d.Id == element.DependencyId);
}
This solution solves the N+1 problem, because it performs only two database queries.
Another method to load relationships manually
Imagine we have these classes:
public class Parent
{
[PrimaryKey, AutoIncrement] public int Id { get; set; }
public string Name { get; set; }
public List<Child> children { get; set; }
public override bool Equals(object obj)
{
return obj != null && Id.Equals(((BaseModel) obj).Id);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
and
public class Child
{
[PrimaryKey, AutoIncrement] public int Id { get; set; }
public string Name { get; set; }
public int ParentId { get; set; }
}
Hint these classes have one-to-many relation. Then inner join between them would be:
var parents = databaseSync.Table<Parent>().ToList();
var children = databaseSync.Table<Child>().ToList();
List<Parent> parentsWithChildren = parents.GroupJoin(children, parent => parent.Id, child => child.ParentId,
(parent, children1) =>
{
parent.children = children1.ToList();
return parent;
}).Where(parent => parent.children.Any()).ToList();
So I have a simple table that looks like this to represent a tree of categories and subcategories supporting a dynamic N levels of depth.
CategoryID int NOT NULL (PK)
ParentCategoryID int NULLABLE (FK to self)
CategoryName varchar(100) NOT NULL
My entity (typed this out from memory, sorry if there's a silly mistake in here):
public class Category
{
public int CategoryId { get; set; }
public int ParentCategoryId { get; set; }
public string CategoryName { get; set; }
public virtual Category ParentCategory { get; set; }
public IDbSet<Category> ImmediateChildCategories { get; set; }
}
Using an execution-deferred lambda expression in Entity Framework (6.x) in C# (4.5+), how would I identify all categories that are descendants of a specified category?
My pseudocode SQL query that I'd like would be this:
SELECT * FROM Category WHERE AnyLevelOfAncestorId = 123;
My pseudocode EF query that I'd like to see would be this (paging is there to emphasize my need for execution-deferred support):
_db.Categories.Where(cat => cat.HasAncestor(123)).Skip(1000).Take(25).ToList();
Additional details:
A category with a NULL ParentCategoryID is a root node of a tree (there can be several).
No category will have its own CategoryID nor any descendant's ID as a ParentCategoryID (i.e. no circular relationships and all relationships eventually terminate at a root node although I may query for an ID lower than a root node)
I'm not sure if I want to include the specified ID (123) in the results or not. So I'll accept an answer that goes either way and adjust as necessary once I know if I want that specific one also included.
I assume that your Category entity type has a collection navigation property for retrieving its child categories (as related through the foreign key). This navigation property would cause the child categories to be loaded lazily the first time it is accessed. You can define a method that recursively calls this navigation property (e.g. ChildCategories) on the root and its children.
public static IEnumerable<Category> GetDescendants(Category root)
{
return root.ChildCategories.Concat(root.ChildCategories.SelectMany(GetDescendants));
}
The drawback with the above code is that it will issue a separate database query for retrieving each parent's children. I don't believe that Entity Framework supports the generation of a single recursive query at present (EF 6.1.3). Instead, I would suggest that you define a recursive view in the database that projects all category–descendant pairs; include this view as an entity in your entity data model; and then query or join to it from your LINQ queries. The view can be defined using DBMS-specific technology. SQL Server supports recursive CTEs, as do recent versions of Oracle.
I have a self referencing Category class from which I would like to retrieve parent categories and all corresponding children if it has at least one child category and has at least 1 or more activities (ICollection<Activity>) in the collection.
This would also go for children of children as these should only be returned if there are children categories with at least 1 or more activities.
If there are no child categories with at least 1 or more activities the parent or child Category should not be returned.
The query should return the parent Category as an actual Category object and not just the CategoryId. It this possible?
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public virtual Category Parent { get; set; }
public virtual ICollection<Category> Children { get; set; }
public virtual ICollection<Activity> Activities { get; set; }
}
UPDATE 1
The query which partially works:
var categories = _db.Categories
.Where(x => x.Parent != null && x.Activities.Count > 0)
.GroupBy(x => x.ParentId)
.Select(g => new { Parent = g.Key, Children = g.ToList() }).ToList();
Let's start off a bit smaller, since the query you are looking to create is somewhat complex. We will create your query from the bottom up. First off, you want to eliminate categories that do not have any child categories with at least one or more activities. Let's make a Predicate to return true for those that should be included and false for those that should be excluded, at a single level. We will do this in two stages. First, let's make a predicate that returns true for categories that have activities:
Predicate<Category> hasActivities = cat => cat.Activities.Any();
Second, let's make a Predicate to return true for those categories with child categories that have activities:
Predicate<Category> hasChildWithActivities =
parentCat => parentCat.Children.Any(hasActivities);
Now let's create the filter query that will filter a given Category's descendants. To do this, we will create a Func that takes a parent Category, performs the logic and returns the updated Category:
Func<Category, Category> getFilteredCategory =
parentCat =>
{
parentCat.Children = parentCat.Children
.Where(hasChildWithActivities)
.Select(getFilteredCategory);
return parentCat;
});
Note that this is equivalent to:
Func<Category, Category> getFilteredCategory = delegate(Category parentCat)
{
parentCat.Children = parentCat.Children
.Where(hasChildWithActivities)
.Select(getFilteredCategory);
return parentCat;
};
In your OP, you mentioned that you wanted to filter parents as well. You can use this same logic on the parents by traversing up to the top level and running this query, or by creating a separate query with "joins" or more complex "select" statements. IMHO, the latter would likely be messy and I would advise against it. If you need to apply the logic to parents as well, then first traverse up the tree. Either way, this should give you a good start.
Let me know if you have any questions. Good luck and happy coding! :)
I have a database table which represents accounts with a multi-level hierarchy. Each row has an "AccountKey" which represents the current account and possibly a "ParentKey" which represents the "AccountKey" of the parent.
My model class is "AccountInfo" which contains some information about the account itself, and a List of child accounts.
What's the simplest way to transform this flat database structure into a hierarchy? Can it be done directly in LINQ or do I need to loop through after the fact and build it manually?
Model
public class AccountInfo
{
public int AccountKey { get; set; }
public int? ParentKey { get; set; }
public string AccountName { get; set; }
public List<AccountInfo> Children { get; set; }
}
LINQ
var accounts =
from a in context.Accounts
select new AccountInfo
{
AccountKey = a.AccountKey,
AccountName = a.AccountName,
ParentKey = a.ParentKey
};
The structure you currently have is actually a hierarchy (an adjacency list model). The question is, do you want to keep this hierarchical model? If you do, there's a Nuget package called MVCTreeView. This package works directly with the table structure you describe - in it, you can create a Tree View for your UI, implement CRUD operations at each level, etc. I had to do exactly this and I wrote an article on CodeProject that shows how to cascade delete down an adjacency list model table in SQL via C#. If you need more specifics, leave a comment, and I'll edit this post.
http://www.codeproject.com/Tips/668199/How-to-Cascade-Delete-an-Adjace
You can simply create an association property for the parent key:
public class AccountInfo {
... // stuff you already have
public virtual AccountInfo Parent { get; set; }
}
// in the configuration (this is using Code-first configuration)
conf.HasOptional(a => a.Parent).WithMany(p => p.Children).HasForeignKey(a => a.ParentKey);
With this setup, you can traverse the hierarchy in either direction in queries or outside of queries via lazy-loading if you want lazy loading of the children, make sure to make the property virtual.
To select all children for a given parent, you might run the following query:
var children = context.Accounts
.Where(a => a.AccountKey = someKey)
.SelectMany(a => a.Children)
.ToArray();