Currently I'm working with WebApi and Entity Framework, So I have 3 entities: Products, Categories and ProductCategory; their relationships are:
My problem is that Category entity has a Category Parent property, so it's recursive, my Category Controller looks like this:
public async Task<IHttpActionResult> GetCategory()
{
var category = await db.Category.Select(x=>new {
x.categoryDesc,
x.CategoryId,
x.categoryImage,
x.categoryName,
x.categoryParent
}).ToListAsync();
return Ok(category);
}
I'm returning an anonymous object, the propierty categoryParent its the same object as category so its recursive; when I fill the database with mock data in the Category table and call the get method, everything runs OK because I dont have any data en ProductCategory, but when I fill it(the ProductCategory table) the program crashes.
MY entity classes are:
public class Category {
public int CategoryId { set; get; }
public string categoryName { set; get; }
public string categoryDesc { set; get; }
public string categoryImage { set; get; }
public int? categoryParentId { set; get; }
public virtual ICollection<ProductCategory> ProductCategories { set; get; }
public virtual Category categoryParent { set; get; }
}
public class Product{
public int ProductId { set; get; }
public string productName { set; get; }
public string productDesc { set; get; }
public double productPrice { set; get; }
public string productUrl { set; get; }
public DateTime productPublishDate { set; get; }
public DateTime productModifyDate { set; get; }
public bool productStatus { set; get; }
public int productStock { set; get; }
public virtual ICollection<ProductCategory> ProductCategories { set; get; }
}
public class ProductCategory : IProductCategory {
[Required]
[Key]
[ForeignKey("Category")]
[Column(Order = 1)]
public int CategoryId { set; get; }
[Required]
[Key]
[ForeignKey("Product")]
[Column(Order = 2)]
public int ProductId { set; get; }
public virtual Product Product { set; get; }
public virtual Category Category { set; get; }
}
Can you help me to fix it?, So when I return categoryParent return it recursively, Thanks
I'm guessing you might have better luck if you explicitly state how you want the information organized, and remove the virtual property
IQueryable<Category> category = db.Category;
var result = category.Where(w => w.categoryParentId != null)
.Join(category,
child => (int)child.categoryParentId,
parent => parent.CategoryId,
(child, parent) => new {
child.categoryDesc,
child.CategoryId,
child.categoryImage,
child.categoryName,
parent
}
);
return Ok(await result.ToListAsync());
That should get you the same result as your query above, then you could remove:
public virtual Category categoryParent { set; get; }
Thank you very much but I found the solution: https://practiceaspnet.wordpress.com/2015/11/09/many-to-many-relationships-with-additional-fields/
I used fluent API to resolve the navigation recursive problem I had:
modelBuilder.Entity<Category>()
.HasMany(x => x.ProductCategories)
.WithRequired(x => x.Category)
.HasForeignKey(x=>x.CategoryId);
modelBuilder.Entity<Product>()
.HasMany(x => x.ProductCategories)
.WithRequired(x => x.Product)
.HasForeignKey(x => x.ProductId);
Basically the WithRequired method prevents a navigation property on the other side of the relationship so it stops the recursion
Related
The database schema created has the following relations
The models used for generating the schema above are
public class Option
{
public int OptionId { get; set; }
public string OptionName { get; set; }
}
public class Value
{
public int ValueId { get; set; }
public string OptionValue { get; set; }
}
public class Sku
{
public int SkuId { get; set; }
public int ProductId { get; set; }
public decimal Price { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
}
public class ProductVariant
{
public int Id { get; set; }
public int ProductId { get; set; }
public int OptionId { get; set; }
public int ValueId { get; set; }
public int SkuId { get; set; }
[ForeignKey("ProductId")]
public Product Product { get; set; }
[ForeignKey("OptionId")]
public Option Option { get; set; }
[ForeignKey("ValueId")]
public Value Value { get; set; }
[ForeignKey("SkuId")]
public Sku Sku { get; set; }
}
while the product class is
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public IEnumerable<ProductVariant> ProductVariants { get; set; }
}
How can i load realated entities with this layout?
I tried the following but Options, Values and Skus are not accesible as navigation properties
var products = context.Products
.Include(x => x.ProductVariants)
.Include(x => x.Options)
.Include(x => x.Values)
.Include(x => x.Skus)
What changes should i make?
You lack navigation property in your product class:
public IEnumerable<Sku> Skus { get; set; }
And you need to use .ThenInclude instead of .Include when you are getting nested entities. It would be:
var products = context.Products
.Include(x => x.Skus)
.Include(x => x.ProductVariants)
.ThenInclude(ProductVariants => ProductVariants.Options)
.Include(x => x.ProductVariants)
.ThenInclude(ProductVariants => ProductVariants.Values)
I am getting confused with my LINQ query and wondering if there is a way I can achieve the following:
A user has a list of liked Categories:
public class ApplicationUser : IdentityUser
{
[PersonalData]
public string Name { get; set; }
[PersonalData]
[DataType(DataType.Date)]
public DateTime DOB { get; set; }
[PersonalData]
public PersonGender Gender { get; set; }
[PersonalData]
[ForeignKey("Suburb")]
public int SuburbId { get; set; }
//Information about user preferences
public ICollection<UserCategory> LikedCategories { get; set; }
public virtual Suburb Suburb { get; set; }
}
Where UserCategory is defined as follows:
public class UserCategory
{
[Key]
public int Id { get; set; }
public ApplicationUser applicationUser { get; set; }
public Category Category { get; set; }
[ForeignKey("ApplicationUser")]
public string applicationUserId { get; set; }
[ForeignKey("Category")]
public int CategoryId { get; set; }
}
And Category is:
public class Category
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
public int CategoryFollowers { get; set; }
public ICollection<EventCategory> EventCategories { get; set; }
public ICollection<UserCategory> UserCategories { get; set; }
}
}
Finally, in my Events class I have the following:
public class Event
{
[Key]
public int ID { get; set; }
[Required]
public string Name { get; set; }
[Display(Name = "Is Featured?")]
public bool isFeatured { get; set; }
public byte[] EventImage1 { get; set; }
public byte[] EventImage2 { get; set; }
public virtual ICollection<EventCategory> EventCategories { get; set; }
public virtual ICollection<UserEvent> UserEvents { get; set; }
My view requires an object of type Event, so I am trying to return a list of Events where the EventCategory is contained in the UserCategory table. In other words I just want to show events that the user has liked the category for.
MORE CLARIFICATION
I am able to filter my events by category from a different function that takes in a hardcoded category Id from the view and this works fine:
//GET:// Browse event by category
public async Task<IActionResult> BrowseByCategory(int? id, int? alt_id)
{
if (id == null)
{
return NotFound();
}
var eventsContext = _context.Events
.Include(m => m.Venue)
.ThenInclude(mf => mf.Suburb)
.ThenInclude(mc => mc.Constituency)
.ThenInclude(md => md.City)
.Where(e => e.EventCategories.Any(c => c.Category.ID == id || c.Category.ID == alt_id))
.Take(15)
.OrderByDescending(o => o.StartDate);
return View("Browse",await eventsContext.ToListAsync());
}
I would like to do the exact same as above, but rather than pass in the hardcoded ID queries from the form, I want the query to check the categoryIDs that are saved in the UserCategory table. There is no set number of how many UserCategory items there are.
I have two entity for save Product and Product's Related Products ... there is One to Many Relationship ... Everything is right before storing the information But in the database two duplicate fields are stored
RelatedProducID
ProductID
public class RelatedCatalogs : EntityBase
{
public Guid ProductID { get; set; }
public Guid RelatedProducID { get; set; }
public Product RelatedProductCatalog { get; set; }
public int Priority { get; set; }
}
Product Class:
public class Product{
public Guid ProductID { get; set; }
public string Name { get; set; }
[ForeignKey("RelatedProductID")]
public virtual List<RelatedCatalogs> RelatedCatalogs { get; set; }
.
.
.
}
What needs to be done now to fix this problem?
Your problem seems similar to this, so try:
public class Product
{
public Guid ProductID { get; set; }
public string Name { get; set; }
...
[InverseProperty("Product")]
public virtual List<RelatedCatalogs> RelatedCatalogs { get; set; }
}
public class RelatedCatalogs : EntityBase
{
public Guid ID { get; set; }
[ForeignKey("Product")]
public Guid ProductID { get; set; }
public Product Product { get; set; }
[ForeignKey("RelatedProductCatalog")]
public Guid RelatedProductID { get; set; }
public Product RelatedProductCatalog { get; set; }
public int Priority { get; set; }
}
Then you can add the mentioned fluent code to avoid cycles:
modelBuilder.Entity<RelatedCatalogs>()
.HasRequired(r => r.RelatedProductCatalog)
.WithMany()
.HasForeignKey(r => r.RelatedProductID)
.WillCascadeOnDelete(false);
modelBuilder.Entity<RelatedCatalogs>()
.HasRequired(r => r.Product)
.WithMany(p => p.RelatedCatalogs)
.HasForeignKey(r => r.ProductID);
I'm tring to create a DbContext with my entites on entityframework5 codefirst way. I've brands, categories and products.
But when I try to get a Product it's Brand and Category fields are null. Category is optional but Brand is not. So at least the Brand field has to be setted. I tried the code below. Is there any thing that I miss?
public DbSet<Brand> Brands { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Brand>()
.HasMany(b => b.Products)
.WithRequired(p => p.Brand)
.HasForeignKey(p => p.BrandId);
modelBuilder.Entity<Category>()
.HasMany(c => c.Products)
.WithOptional(p => p.Category)
.HasForeignKey(p => p.CategoryId);
}
And on MVC controller side:
using (var db = new InonovaContext())
{
var product = db.Products.Single(p => p.Id == id);
model.Description = product.Description;
model.ImageUrl = product.ImageUrl;
model.Name = product.Name;
model.BreadCrumb = product.Brand.Name + " / " + product.Category == null ? "" : (product.Category.Name + " / ") + product.Name; // Here Brand and Category are null
}
Product class is like below
public class Product
{
public int Id { get; set; }
public int BrandId { get; set; }
public virtual Brand Brand { get; set; }
public string Name { get; set; }
public int? CategoryId { get; set; }
public virtual Category Category { get; set; }
public string ImageUrl { get; set; }
public string Description { get; set; }
}
Brand class is like below:
public class Brand
{
public int Id { get; set; }
public string Name { get; set; }
public string ThumbLogoImageUrl { get; set; }
public string Description { get; set; }
public ICollection<Product> Products { get; set; }
}
Thanks.
If you haven't declared the Brand and Category as virtual, lazyloading of the Brand and Category properties will not work.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Brand Brand { get; set; }
public int BrandId { get; set; }
public virtual Category Category { get; set; }
public int? CategoryId { get; set; }
}
See this for more information on lazy and eager loading.
Below is a POCO class i set up with Entity Framework Code first. How can i Query my database so that I can return all brands of a specific category?
Example: You have a list of categories and you click on one. It shows all brands of products available under that category.
I don't know if my classes are set up correctly to do this.
public class Product
{
[Key,ScaffoldColumn(false)]
public int ProductID { get; set; }
public string ProductName { get; set; }
public int? CategoryID { get; set; }
public virtual Category Category { get; set; }
public int? BrandID { get; set; }
public virtual Brand Brand { get; set; }
}
public class Brand
{
[ScaffoldColumn(false)]
public int BrandID { get; set; }
public string BrandName { get; set; }
}
public class Category
{
[ScaffoldColumn(false)]
public int CategoryID { get; set; }
public string CategoryName { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
What about
context.Products.
Where(p => p.Category.CategoryID == categoryToFind).Select(p => p.Brand);
or
var brands = context.Products.
Where(p => p.Category.CategoryID == categoryToFind).
Select(p => p.Brand.BrandName).Distinct().ToList();
if you just need brand names.