Execution-Deferred query to identify all descendants? - c#

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.

Related

Why this LINQ expressoin returns the sublist with only one item

Here is my object model.
public class ItemViewModel
{
public Item ItemObject { get; set; }
public IEnumerable<LineItem> Fields { get; set; } = new List<LineItem>();
}
public class Item
{
public int ItemId { get; set; }
public DateTime DatePurchased { get; set; }
}
public class LineItem
{
public int ItemId { get; set; }
public string Name { get; set; }
}
The database contains Items and LineItems tables, corresponding to the object model. I am using the below written LINQ expression to retrieve an ItemViewModel object, provided an Item ID.
var i = from i in _dbContext.Items
where i.ItemId == 123
select new ItemViewModel
{
ItemObject = i,
Fields = from field in _dbContext.LineItems
where field.ItemId == i.ItemId
select field
};
var viewModel = await i.SingleOrDefaultAsync();
The code compiles fine but after its execution the viewModel.Fields property contains only one list item whereas there are three Line Items against ItemID:123, in the database table.
The SQL generated by this snippet (captured through SQL Profiler) returns three line items, through a LEFT JOIN. That means the generated SQL is fine but something is not going well with Entity Framework but I don't know what?
Update:
Here is the generated SQL query which correctly gives me three rows:
SELECT
[t].[ItemId], [t].[DatePurchased],
[s0].[ItemId], [s0].[Name]
FROM
(
SELECT
TOP(2) [s].[ItemId], [s].[DatePurchased]
FROM
[Items] AS [s]
WHERE
[s].[ItemId] = 123
) AS [t]
LEFT JOIN [LineItems] AS [s0] ON [t].[ItemId] = [s0].[ItemId]
ORDER BY [t].[ItemId]
Tech stack is .NET Core 3.1 and Entity Framework Core.
As suggested by Ivan Stoev in the comments under this question, the reason is a bug in Entity Framework Core, when dealing with keyless entities as a child entity. I tested it first-hand by adding a primary key to my earlier keyless table and then verifying the same LINQ statement. After adding a primary key, it starts giving me a List of 3 items, instead of only 1. So after this question and my testing, I have reported it to EF Core issue tracker.
For anyone who stumbles upon this question in search of answer, we have two options here. Either add a primary key to your keyless table or define a composite primary key in your EF entity model. I fixed my situation using the below line:
modelBuilder.Entity<LineItem>().HasKey(i => new { i.ItemId, i.Name });
It works because LineItem.ItemId and LineItem.Name combination is unique for my entities. Credit for this answer goes to an answer (although not officially accepted answer) to an earlier SO question.

Entity Framework inheritance

SQL Layer:
I have a table
Entity Framwork Layer:
I have the following rule: all Offers, which have State is null, are Outstanding offers, State is true are Accepted offers, State is false are Declined offers. Also, part of fields used only for Outstanding, part - only for Accepted etc... I use Database first approach, so, I updated EF model from DB and renamed Offer entity to OfferBase and created 3 child classes:
it works fine for add/select entities/records. Right now I want to "move" offer from outstanding to accept offer, so, I need to set Status=true (from Status is null) for appropriate record. But how to do it by Entity Framework? If I try to select outstanding offer as Accept offer I get an null reference (and clearly why)
// record with ID=1 exists, but State is null, so, EF can not find this record and offer will be null after the following string
var offer = (from i in _db.OfferBases.OfType<EFModels.OfferAccepted>() where i.ID == 1 select i).FirstOrDefault();
if I try to select as OfferBase entity I get the following error:
Unable to cast object of type
'System.Data.Entity.DynamicProxies.OfferOutstanding_9DD3E4A5D716F158C6875FA0EDF5D0E52150A406416D4D641148F9AFE2B5A16A'
to type 'VTS.EFModels.OfferAccepted'.
var offerB = (from i in _db.OfferBases where i.ID == 1 select i).FirstOrDefault();
var offer = (EFModels.OfferAccepted)offerB;
ADDED NOTES ABOUT ARCHITECTURE:
I have 3 types of Offer entity. There are: AcceptOffer, DeclineOffer and OutstandingOffer.
AcceptOffer:
UserID
ContactID
Notes
FirstContactDate
LastContactDate
[... and 5-10 the unique fields...]
DeclineOffer:
UserID
ContactID
Notes
[... and 5-10 the unique fields...]
OutstandingOffer:
UserID
ContactID
FirstContactDate
LastContactDate
[... and 5-10 the unique fields...]
How to do it correctly? Of course, I can select a record, remove from DB and add new with appropriate state value, but how to do it normally?
You can't change the type of an object once it's created. Your object model seems wrong.
Either you delete the outstanding offer and create an accepted offer from it (looks like what you are currently doing) but you may lose relations as you created a new object with a new identity (you can also copy them before removing the old object). Or you want to keep the same object and change its state.
If you want to keep the same identity then preffer composition over inheritance.
Your code could look like this :
public class Offer
{
public int Id { get; set; }
public virtual OfferState State { get; set }
}
public class OfferState
{
public int OfferId { get; set; }
public string Notes { get; set; }
}
public class AcceptedOfferState : OfferState
{
public DateTimeOffset AcceptDate { get; set; }
}
public class DeclinedOfferState : OfferState
{
public DateTimeOffset DeclinedDate { get; set; }
}
If you still want to change the type of the object and keep its identity then you may use stored procedures ; as stated by Noam Ben-Ami (PM owner for EF) : Changing the type of an entity.
Rather than trying to add these custom classes to your entity framework model, just create them as normal c# classes and then use a projection to convert from the entity framework generated class to your own class e.g.
var accepteOffers= from i in _db.Offers
where i.ID == 1 && i.Status == true
select new OfferAccepted { AcceptDate = i.AcceptDate, StartTime = i.StartTime /* map all releaveant fields here */};

Define Many Tables to One Table relationship in Code First approach

I am in the process of building up a data model in Entity Framework using the Code First approach, but one part has me a bit stumped. The title on this question may be a bit confusing, so I will explain my problem in detail. The length of this post may be daunting, but I think it's a fairly straightforward problem.
I have one model defined like this:
public class KeyValuePair
{
[Key]
[MaxLength(128)]
[Column(Order = 0)]
public virtual string OwnerId { get; set; }
[Key]
[MaxLength(128)]
[Column(Order = 1)]
public virtual string Key { get; set; }
public virtual string Value { get; set; }
}
My intent is for this to just define a generic table for storing key-value properties on other entities in the system. I am using GUIDs for all of my Ids, so OwnerId should uniquely refer to one entity in the system, and the pair (OwnerId, Key) should uniquely identify one property on one entity.
In other words, I want to allow multiple tables in my system to have a One->Many relationship to this KeyValuePair table.
So for example, if I wanted to store the height of a Person who has the ID b4fc3e9a-2081-4989-b016-08ddd9f73db0, I would store a row in this table as:
OwnerId = "b4fc3e9a-2081-4989-b016-08ddd9f73db0"
Key = "Height"
Value = "70 in."
So now I want to define navigation properties from the parent entities to this table, like (to take the Person example):
public class Person
{
[Key]
public virtual string Id { get; set; }
public virtual string Name { get; set; }
// I want this to be a navigation property
public ICollection<KeyValuePair> Properties { get; set; }
}
But I'm not sure how do define the relationship between Person and KeyValuePair so that Entity Framework knows that it should look up the Person's properties by matching the Person's Id against the KeyValuePairs' OwnerId. I can't define a foreign key in the KeyValuePair model, because the OwnerId is going to refer to Ids in several different tables.
It looks like I can do the following to define a relationship from Person to KeyValuePair in OnModelCreating:
modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("OwnerId", "Key");
mp.ToTable("PersonDetail");
});
Or I could even give the KeyValuePairs their own unique IDs, get rid of OwnerId, and do this:
modelBuilder.Entity<Person>()
.HasMany(p => p.Properties).WithMany().Map(mp =>
{
mp.MapLeftKey("Id");
mp.MapRightKey("Id");
mp.ToTable("PersonDetail");
});
But both of these approaches involve the creation of an intermediary table to link the Person and KeyValuePair tables, and that seems like excessive overhead in terms of bloating my database schema and requiring more expensive JOINs to query the data.
So is there a way to define the relationship such that I don't need to involve intermediary tables? Am I going about this database design the wrong way?
Side note: For anyone wondering why I am using this approach to define properties on my entities rather than simply adding fixed properties to the data model, I am using fixed properties in the data model where applicable, but the application I am building requires the ability to define custom properties at runtime. I also think this question is applicable to other potential scenarios where multiple tables have a One->Many relationship to a shared table.
The only way I can think of doing it (and I'll admit, this is not the best of ideas, but it will do what you're asking) would be to have any classes that need to have this relationship with KeyValuePair implement an abstract class that contains the fully implemented navigational property, as well as the ID field. By "fully implemented" I don't mean an actual, mapped relationship; I mean that it should use a DbContext to go out to the KeyValuePair table and actually grab the relevant properties given the ID.
Something like this:
public abstract class HasKeyValuePairs
{
[Key]
public virtual string Id { get; set; }
[NotMapped]
public ICollection<KeyValuePair> Properties
{
get
{
using(var db = new DbContext())
{
return db.KeyValuePairs.Where(kvp => kvp.OwnerID == this.ID);
}
}
}
}
Assuming you're using Lazy Loading (given that you're using the virtual keyword), there shouldn't be much extra overhead to doing it like this, since EF would have to go back to the database anyway to pick up the properties if you ever called for them. You might need to have that return a List just to avoid any potential ContextDisposedException later on in your code, but that at least will get you up and running.

Create Hierarchy in LINQ

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

How to Populate the Child Collections / Associations of an Entity from a Stored Procedure using NHibernate

I want to know if there is a way to populate recursive entities ( child collections / associations ) from a stored procedure in NHibernate. Suppose I have the next class:
public class Category
{
public int category_id { set; protected get; }
public string category_description { set; get; }
public IEnumerable<Category> SubCategories { set; get; }
....
}
Is there a way to obtain a list of root categories from a stored procedure that obtains a groups of categories and their whole trees of children, with each category having his respective children inside the SubCategories property? I want to get the same result as the solution proposed in "Eagerly load recursive relation", but getting the result from a stored procedure instead of a table.
This is possible with named queries. Take a look at the official NHibernate documentation on named queries here: http://nhibernate.info/doc/nh/en/index.html#querysql-namedqueries
What you want to do is use the feature of named queries which allows you to join associations: <return-join />
There is also an example on the NHibernate blog here regarding this same topic: http://nhibernate.info/blogs/nhibernate/archive/2008/11/24/populating-entities-with-associations-from-stored-procedures-with-nhibernate.aspx
Convoluted example:
<sql-query name="GetCategory">
<return alias="Category" class="Category"/>
<return-join alias="Subcategory" property="Category.SubCategories">
<return-property column="Subcategory.SubcategoryId" name="Id" />
</return-join>
EXEC someStoredProcedureName :CategoryId
</sql-query>

Categories