Mapping child items of same class with Entity Framework Code First - c#

I'm trying to map a fairly "standard" category model using EF Code First
public class Category
{
public int ID { get; set; }
public int ParentID { get; set; }
public string Name { get; set; }
public Category ParentCategory { get; set; }
public List<Category> ChildCategories { get; set; }
}
I've got something along the lines of:
modelBuilder.Entity<Category>()
.HasOptional(t => t.ParentCategory)
.WithMany()
.HasForeignKey(t => t.ParentCategoryID)
.WillCascadeOnDelete();
But this doesn't seem to take care of ChildCategories??
Am I missing something?
To avoid the duplicate question argument, I followed the following, however didn't quite answer my specific query:
Code First Mapping for Entity Framework Hierarchy
Entity Framework CTP5 Code-First Mapping - Foreign Key in same table

Change your Entity to
public class Category
{
public int ID { get; set; }
public int? ParentID { get; set; }
public string Name { get; set; }
public virtual Category ParentCategory { get; set; }
public virtual IList<Category> ChildCategories { get; set; }
}
Make ParentID nullable and to allow ChildCategories to be lazy loaded, make it virtual.

Related

Load related data without foreign key constraint in ef-core-2.1

I want to load related entities data Parent by using Eager Loading O/RM pattern. But I can't specify a foregin key constraint on ParentId because it creates a cycle which is not allowed. Currently, I'm using an inner join to load Parent data explicitly.
Here is my Domain Model that I'm using.
[Table("Category")]
public class CategoryDM
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CategoryId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[Display(Name="Parent")]
public int ParentId { get; set; }
[NotMapped]
public CategoryDM Parent { get; set; }
}
Is there any way to load related entities like this? or any other recommended way to achieve this.
var result = _context.Category.Include(e => e.Parent);
This should work fine, here is an exemplary working model.
Model
public class Category : ISelfRelated<Category>
{
public int Id { get; set; }
public string Name { get; set; }
public string ThumbnailUrl { get; set; }
public int? ParentId { get; set; }
public Category Parent { get; set; }
public IEnumerable<Category> Children { get; set; }
}
Model configuration
category.HasOne(c => c.Parent)
.WithMany(c => c.Children)
.HasForeignKey(c => c.ParentId)
.HasPrincipalKey(c => c.Id)
.OnDelete(DeleteBehavior.Restrict)
.IsRequired(false);

Sharing a table using Entity Framework Core

I have multiple entities that I would like to share a single "Images" table. For example, products can have a list of images and categories can have a list of images. I would like to use the enum "EntityType" to distinguish what type of entity it is. My solution below doesn't work because there is a foreign key error when I try to insert an image with a EntityId that might exist in Category but not in Product. This makes sense because the solution below isn't taking into account the "EntityType". Are there any recommendations for how I can accomplish this? I know I can use "ProductId", "CategoryId", etc instead of "EntityId" but I will have a lot of entities so I would prefer to not to do it that way.
public class Product
{
public int Id { get; set; }
public List<Image> ProductImages { get; set; }
}
public class Category
{
public int Id { get; set; }
public List<Image> CategoryImages { get; set; }
}
public class Image
{
public int EntityId { get; set; }
public EntityType EntityType { get; set; }
public string ImageUrl { get; set; }
public Product Product { get; set; }
public Category Category { get; set; }
}
modelBuilder.Entity<Product>().ToTable("Product");
modelBuilder.Entity<Category>().ToTable("Category");
modelBuilder.Entity<Image>().ToTable("Image");
modelBuilder.Entity<Image>().HasOne(p => p.Product).WithMany(p => p.ProductImages).HasForeignKey(p => p.EntityId);
modelBuilder.Entity<Image>().HasOne(p => p.Category).WithMany(p => p.CategoryImages).HasForeignKey(p => p.EntityId);
What you're describing is a many-to-many relationship. For that, you'll need an entity to track said relationship:
public class ProductImage
{
[ForeignKey(nameof(Product))]
public int ProductId { get; set; }
public Product Product { get; set; }
[ForeignKey(nameof(Image))]
public int ImageId { get; set; }
public Image Image { get; set; }
}
On your Product/Category classes:
public ICollection<ProductImage> ProductImages { get; set; }
Then, for your fluent config:
modelBuilder.Entity<ProductImage>().HasOne(p => p.Product).WithMany(p => p.ProductImages);
modelBuilder.Entity<ProductImage>().HasOne(p => p.Image).WithMany();
Do the same with your categories.

Entity Framework Core filter related entities and get top 2 for each group

I am using Entity Framework Core 2.0.1 and I have the following models
public class Article
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string Slug { get; set; }
public int Approved { get; set; }
public DateTime ArticleDate { get; set; }
// ... some other fields
public virtual ICollection<ArticleCategoryRelation> ArticleCategoryRelations { get; set; }
}
public class ArticleCategory
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
//... soem other fields
[ForeignKey("ArticleCategoryParent")]
public int? ArticleCategoryParentID { get; set; }
public virtual ArticleCategory ArticleCategoryParent { get; set; }
public virtual ICollection<ArticleCategory> SubCategories { get; set; }
public virtual ICollection<ArticleCategoryRelation> ArticleCategoryRelations { get; set; }
}
public class ArticleCategoryRelation
{
[Column(Order = 0)]
public int ArticleId { get; set; }
public Article Article { get; set; }
[Column(Order = 1)]
public int ArticleCategoryId { get; set; }
public ArticleCategory ArticleCategory {get; set;}
}
Every article belongs to one or more categories. Categories might have parent category.
I want to get from database last two articles (where Approved = 1) with related category details, for each category that belongs to a parent category which id is given as input.
I have tried but with no success. I can't filter results of an .Include() entity. Is it possible... or I don't know how to do it?
All my data are accessed through entity framework with appContext (the context used to get entities from database). Can I achieve what I want through entity framework core (lambda expression is preferred over Linq if possible), or should I use ADO.NET library (which I know how to execute custom queries).
P.S. I want to get data only to show in the view... no edit is needed.
You don't actually need to include here at all, as far as I can tell. Whenever you use data from a nav property, EF will go get the data from that table, as best it can filter it.
var CategoriesUnderParent = AppContext.ArticleCategories
.Where(c => c.ArticleCategoryParent == {parent});
foreach(var category in CategoriesUnderParent)
{
var ArticlesAllowed = category.ArticleCategoryRelations
.Where(acr => acr.Article.Approved == 1).Select(a => a.Article);
var ArticlesPicked = ArticlesAllowed
.OrderByDescending(ar => ar.ArticleDate)
.Take(2);
// Do something with your data
}

asp.net mvc - Inserting multiple records in the third table having foreign keys to parent using entity framework 6

I am trying to learn how to use Entity Framework 6 with an already created database, without creating an .edmx file, i.e, using the DbContext and POCO classes.
These are my model classes:
[Table("Category")]
public class Category
{
[Key]
public long CategoryID { get; set; }
public string CategoryName { get; set; }
}
[Table("RegistrationForm")]
public class RegistrationForm
{
[Key]
public int RegistrationID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Country { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
}
[Table("RegistrationCategory")]
public class RegistrationCategory
{
[Key]
public long RegistrationCategory { get; set; }
public long RegistrationID { get; set; }//Foreign key to RegistrationID in RegistrationForm table in database
public long CategoryID { get; set; }//Foreign key to CategoryID in Category table in database
}
My DbContext class:
public class MyContext : DbContext
{
public virtual DbSet<RegistrationForm> RegistrationForm { get; set; }
public virtual DbSet<Category> Category { get; set; }
public virtual DbSet<RegistrationCategory> RegistrationCategory { get; set; }
}
Here I want to use the default model builder of DbContext.User can select multiple categories in the registration screen so the RegistrationCategory table will have multiple records for each registration. Therefore RegistrationForm and RegistrationCategory are in a one-to-many relationship.
How to write foreign key mappings between the above mentioned models?
How to bind data from Category table data in the mvc view(listbox) so that we can save one record in RegistrationForm table and multiple records in RegistrationCategory table without using loops (using mappings between the c# models) in Entity Framework 6?
The database schema that you have here is a Many to Many relationship between RegistrationForm and Category, with a join table. The RegistrationCategory Table is not necessary to be modeled in Entity Framework at all. You will need to use Entity Framework Fluent API to generate the correct mappings.
First, your RegistrationForm Table:
public class RegistrationForm
{
[Key]
public int RegistrationID { get; set; }
...
// add a navigation property ICollection<Category> to reference the categories
public virtual ICollection<Category> Categories { get; set; }
}
Next, the Category class:
public class Category
{
[Key]
public int CategoryID { get; set; }
public string CategoryName { get; set; }
//Navigation property to reference the RegistrationForms
public virtual ICollection<RegistrationForm> RegistrationForms { get; set; }
}
next, in your DbContext: note the change in pluralization, and the removal of the RegistrationCategory, you do not need a model class for it at all.
public class MyContext : DbContext
{
public virtual DbSet<RegistrationForm> RegistrationForms { get; set; }
public virtual DbSet<Category> Categories { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RegistrationForm>()
.HasMany(r => r.Categories)
.WithMany(c => c.RegistrationForms)
.Map(
m =>
{
m.MapLeftKey("RegistrationID");
m.MapRightKey("CategoryID");
m.ToTable("RegistrationCategory");
}
);
}
With this in place, you can now query all the Categories of a RegistrationForm or all the RegistrationForms of a Category.
foreach (var category in registrationForm.Categories)
{
//do whatever with each category
}

EF Code First Fluent Mapping: 0-1 to Many: HasOptional(), same table

I have a "Category" Entity as follow:
public class Category
{
//<Summary>
//Fields...
//</Summary>
public Guid CategoryId { get; set; }
public string CategoryName { get; set; }
public bool IsDelete { get; set; }
// Fields for relationships
public Guid MainCategoryId { get; set; }
public Category MainCategory { get; set; }
public virtual ICollection<Category> ChildCategories { get; set; }
}
As seen above I want to create 0-one-to-many relationship in same table. I used Fluent API for this as follows:
HasRequired(category => category.MainCategory)
.WithMany(category => category.ChildCategories)
.HasForeignKey(category => category.MainCategoryId);
But it is a one-to-many, isn't 0-1-to-many. I use HasOptional, but it give me an error.
How can I do this with Fluent API?
thanks for reply
Make the MainCategoryId property nullable:
public Guid? MainCategoryId { get; set; }
And then you can use HasOptional method:
HasOptional(category => category.MainCategory)
.WithMany(category => category.ChildCategories)
.HasForeignKey(category => category.MainCategoryId);

Categories

Resources