Projecting self referencing multi level Entities In Entity Framework 6 - c#

Projecting self referencing multi level entities in Entity Framework 6.
Let's say that I have a Category entity as follows:
public class Category
{
public int CategoryId { get; set; }
public int? ParentCategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual ICollection<Category> SubCategories { get; set; }
public virtual ICollection<Product> Products { get; set; }
public Category()
{
SubCategories = new HashSet<Category>();
Products = new HashSet<Product>();
}
}
And I would like to map the whole Category DbSet with all the hierarchy to a following POCO class (while including all possible levels of sub and parent categories):
public class CategoryView
{
public int Id { get; set; }
public int? ParentCategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public CategoryView ParentCategory { get; set; }
public List<CategoryView> SubCategories { get; set; }
public int ProductCount { get; set; }
public Category()
{
SubCategories = new HashSet<CategoryView>();
}
}
Please bear in mind that a single category may have unlimited levels of subcategories as follows:
Category (Level 0)
SubCategory1 (Level 1)
SubCategory2
SubCategory2SubCategory1 (Level 2)
SubCategory2SubCategory2
SubCategory2SubCategory2SubCategory1 (Level 3)
... (Level N)
SubCategory3
When tried to create hierarchy with recursive a method which tries to process every single categories sub and parent categories, got stackoverflow exception, since it get stuck between the first category (Category) and the first subcategory (SubCategory1) due to relation between ParentCategory and SubCategories.
What is the best and elegant way of doing such projection (without eliminating parents)? (Or is there any?)
Any help would be appreciated.
Thank you,

I can't say if it's the best or elegant way, but it's pretty standard and efficient non recursive way of building such structure.
Start with loading all categories without parent / child object links using a simple projection:
var allCategories = db.Categories
.Select(c => new CategoryView
{
Id = c.CategoryId,
ParentCategoryId = c.ParentCategoryId,
Name = c.Name,
Description = c.Description,
ProductCount = c.Products.Count()
})
.ToList();
then create a fast lookup data structure for finding CategoryView by Id:
var categoryById = allCategories.ToDictionary(c => c.Id);
then link the subcategories to their parents using the previously prepared data structures:
foreach (var category in allCategories.Where(c => c.ParentCategoryId != null))
{
category.ParentCategory = categoryById[category.ParentCategoryId.Value];
category.ParentCategory.SubCategories.Add(category);
}
At this point, the tree links are ready. Depending of your needs. either return the allCategories or the root categories if you need a real tree representation:
return allCategories.Where(c => c.ParentCategoryId == null);
P.S. Actually the allCategories list can be avoided, since categoryById.Values could serve the same purpose.

It might not be elegant, but a suitable solution is to have in your code a shared IDictionary<int, CategoryView>. When you are going to map an entity Category into a CategoryView check first if you have already created this object and set the reference stored in the dictionary instead of creating a CategoryView instance. When creating a new instance, store it in the dictionary. This is a way to take advantage of the primary key of your entity to avoid the infinite recursion issue in your code.
Also, notice that in your CategoryView object you shouldn't be referencing Category instances. Update it to reference CategoryView instances like this.
public class CategoryView
{
public int Id { get; set; }
public int? ParentCategoryId { get; set; }
// other properties ...
public CategoryView ParentCategory { get; set; }
public List<CategoryView> SubCategories { get; set; }
public int ProductCount { get; set; }
public CategoryView()
{
SubCategories = new List<CategoryView>();
}
}

Related

Linq join with Many to Many

I'm using MVC, C# and EntityFramework.
I've seen different solutions on Many to Many joins and after a lot of tinkering I got it to work in Linqpad. But when I try it in my solution I get an error because one of the tables isn't in my DBContext.
I have two visible tables and one hidden. Items, Recipes & RecipeItems.
All recipes are based on one item and use two or more items to be made.
So I want a list, IEnumerable or similar with the data from both Items and Recipes that specifies this recipe and then I want all the items needed to make the recipe.
The following query works in LinqPad
var t = from r in Recipes
join i in Items on r.ItemId equals i.Id
select new {FinalProduct = r.FinalProduct, Effect= i.Effect,
Description = r.Description, Ingredients = r.RecipeItems.Select(g => g.Item)};
When I do this in my solution I get the error since my DBContext only contains Recipe and Items but no RecipeItems. Entityframework handles this without me I guess.
I tried to make a DbSet<RecipeItems> without any luck. Any of you who have a suggestion of how I can move forward.
Item Class
public class Item
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Effect { get; set; }
public bool Published { get; set; }
public virtual ICollection<Recipe> Recipe { get; set; }
}
Recipe Class
public class Recipe
{
[Key]
public int Id { get; set; }
public int ItemId { get; set; }
[Display(Name = "Final Product")]
public string FinalProduct { get; set; }
public string Description { get; set; }
public RecipeGroup RecipeGroup { get; set; }
public bool Published { get; set; }
public virtual ICollection<Item> Ingredients { get; set; }
}
The ItemId in Recipe is to set the actual Item the Recipe will make.
Try adding this to your Recipe object:
public Recipe()
{
this.Ingredients = new HashSet<Item>();
}
This overrides the default constructor for the class and kind of gives EF a place that initializes the related objects.

Entity Framework One to Many Recursive Relationship on self

I'm stuck with a problematic, and not sure if this is the best way to solve it.
I've an Entity Category, which can have a list of sub categories ( child categories ) and has a Parent Category ( the only one without a parent category would be the "root" category. Is this the right way to do it ?
Category Class
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual IList<Category> ChildCategories { get; set; }
public virtual IList<Product> Products { get; set; }
}
Fluent API ModelBuilder
//Category Parent - Child
modelBuilder.Entity<Category>()
.HasOptional<Category>(category => category.ParentCategory)
.WithMany(category => category.ChildCategories)
.HasForeignKey(category => category.ParentId);
My seed method looks like this:
protected override void Seed(DAL.EF.MyDbContext context)
{
var catParent = new Category() {Name = "ParentCat"};
context.Categories.Add(catParent);
context.SaveChanges();
var catChild = new Category() { Name = "ChildCat", ParentId = catParent.Id};
context.SaveChanges();
catParent.ChildCategories.Add(catChild);
context.SaveChanges();
}
Now the thing is, In the seed I add 2 categories, 1 parent 1 child. When I debug to check if the structure is good, it's actually correct. The only thing that really bugs me is this structure:
ParentCategory -> ChildCategory -> ParentCategory.
As it is recursive, the child object will have a object "ParentCategory", but I actually only need the ID of it, not the whole object.
Is this normal or is there a fix for it?

Entity Framework: Model for Categories/Products relation

I have following model of data for Entity Framework.
I have abstract Product. Every Product relates with one Category of products. For example:
public abstract class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Category Category { get; set; }
}
And there are concrete products:
public class ConcreteProduct1 : Product
{
// some specific member
}
public class ConcreteProduct2 : Product
{
// some specific member
}
//etc.
I have hierarchical Categories, for example:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public Category Parent { get; set; }
public ICollection<Category> Children { get; set; }
public ICollection<Product> Products { get; set; }
}
Every Category has ICollection<Product> Products.
Problem: Category should be related with only products some concrete product type. I.e. I need be able get Concrete Products into Category, for example:
public Category<ConcreteProduct1> GetCategory<ConcreteProduct1> ()
{
// should return Category that contain ICollection<ConcreteProduct1>
}
How I can describe this restriction in my Entity Framework model? Or may be there are some best practice for building these relations?
It is hard to answer because it depends too much on the requisites of your project.
There are three different approaches to representing an inheritance hierarchy:
Table per Hierarchy (TPH): Enable polymorphism by denormalizing the SQL schema, and utilize a type discriminator column that holds type information.
Table per Type (TPT): Represent "is a" (inheritance) relationships as "has a" (foreign key) relationships.
Table per Concrete class (TPC): Discard polymorphism and inheritance relationships completely from the SQL schema.
You should check the links and find what is the best suitable model for what you need.
Use lists and a categorymanager:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public Category Parent { get; set; }
public ICollection<Category> Children { get; set; }
public List<Product> Products = new List<Product>();
}
public static class CategoryManager
{
public List<Category> Categories = new List<Category>();
}
public Product test = new Product
{
Id = 1
};
public Category add = new Category
{
Id = 1
};
public void Init()
{
add.Products.Add(test);
CategoryManager.Categories.Add(add);
}
public Product GetByID(Category cat, string val)
{
return cat.Where(x => x.Id == val).ToArray()[0];
}
public Category GetCat(Product pro)
{
foreach (var cat in CategoryManager.Categories)
{
if (cat == pro) return cat;
}
return null;
}

NHibernate getting one element per category with single query

I have News Class as follows:
public class News
{
public virtual int Id { set; get; }
public virtual string Title { get; set; }
public virtual Category Category { set; get; }
public virtual DateTime DateCreated { set; get;}
}
and There is category class;
public class Category
{
public virtual int Id { set; get; }
public virtual string Name { set; get;}
}
News table is populated with data.
What I need is to get one News per category ordered by DateCreated. OK So I want latest news from the category, but I want all the news for all categories.
OK I can get all categories and get last News per Category, I m curious if there is an efficient way to do this?
any ideas?
What would be the proper syntax also?
In my opinion, the cleanest way to do it is adding an inverse collection to the Category class (this is technically free, let me know if you need help with the mapping):
public virtual ICollection<News> NewsItems { get; set; }
And then it's a simple LINQ query:
var results = from category in session.Query<Category>()
select new
{
category,
LatestNews = category.NewsItems
OrderByDescending(x => x.DateCreated).FirstOrDefault()
};

How to use EF code first models to use relationships in order to generate data

I have two domain models in my project: Category and Sub-category. This is my one to many relationship and how I created it:
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public List<SubCategory> SubCategories { get; set; }
}
public class SubCategory
{
public int SubCategoryId { get; set; }
public string SubCategoryName { get; set; }
public Category Category { get; set; }
}
Now I am not sure how to add a Sub-Category to a Category, but I want the association to be kept for each Sub-Categories in a Category. So I tried passing a Category to my CreateSubCategory View
public ActionResult CreateSubCategory(int categoryId)
{
return View(_service.GetCategory(categoryId));
}
I came to here in my view and got stumped
#using (Html.BeginForm()){
<h2>SubCategory</h2>
<div>#Html.DisplayFor(model => model.CategoryName)</div>
<div>#*Want to create Sub-category here*#</div>
<p><input type="submit" value="Create" /></p>
}
I guess this is a noob question but I cannot figure out what to do here.
So is there any advice on how to do this, or any way of performing this task of adding my Sub-categories to Categories?
First of all, add the virtual keyword.
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public virtual ICollection<SubCategory> SubCategories { get; set; }
}
// add constructor here for category, and set SubCatogories to new HashSet();
public class SubCategory
{
public int SubCategoryId { get; set; }
public string SubCategoryName { get; set; }
public int CategoryId { get; set; }// add this:)
public virtual Category Category { get; set; }
}
now there are two ways to add a subcategory;
either get an instance of a Category and then add an instance of SubCategory to its SubCategories collection, or create a new SubCategory and set its fields including the CategoryId and then add it to its corresponding DbSet.
If you're creating a SubCategory, you should be passing a SubCategory (or SubCategoryViewModel) object as the model to your View.
You can create a new instance of the SubCategory in your CreateSubCategory() method and assign the CategoryId to the new object (as hazimdikenli pointed out). Be sure to store the Id using #Html.HiddenFor(m => m.CategoryId) so that it's persisted for the returning Post method.
public ActionResult CreateSubCategory(int categoryId)
{
SubCategory model = new SubCategory();
model.CategoryId = categoryId;
return View(model);
}
When the object is posted back, that is the point where you commit to the database, saving the new object and creating the association with the parent Category object (via the Id reference).
If you want to pass the CategoryName, you should probably create a view model with a CategoryName string property (this would of course need to be populated in the CreateSubCategory() method)
It may be simpler to use a single domain model, especially if subcategories can have their own subcategories:
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual ICollection<Category> ChildCategories { get; set; }
}
Then configure the relationship in the OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.Entity<Category>()
.HasMany(x => x.ChildCategories)
.WithOptional(x => x.ParentCategory);
}
To create a new subcategory simply create a new Category and set its ParentCategory before sending the subcategory to your view.

Categories