.ThenInclude().ThenInclude()-ed data is not loaded in LINQ query - c#

I can't seem to load the products from InFrontPages on grandchild levels or deeper in this query. Products are loaded for the first level of sub categories, but for all deeper levels, it just returns null:
var categories = await _context.ProductCategories
.Include(e => e.ProductInCategory)
.ThenInclude(e => e.Product)
.ThenInclude(f => f.InFrontPages)
.AsQueryable() // <-- Force full execution (loading) of the above
.Where(e => e.ParentId == id) // <-- then apply the parent id filter
// (id can be `null` (for root categories), or a category Id)
.OrderBy(o => o.SortOrder)
.ToListAsync();
The models:
public class ProductCategory
{
public int Id { get; set; }
public string Title { get; set; }
public int SortOrder { get; set; }
[ForeignKey(nameof(ParentCategory))]
public int? ParentId { get; set; }
public ProductCategory ParentCategory { get; set; } //nav.prop to parent
public ICollection<ProductCategory> Children { get; set; } = new List<ProductCategory>();
public ICollection<ProductInCategory> ProductInCategory { get; set; }
public ICollection<FrontPageProduct> FrontPageProduct { get; set; }
}
public class ProductInCategory
{
public int Id { get; set; }
public int ProductId { get; set; }
public int ProductCategoryId { get; set; }
public int SortOrder { get; set; }
public Product Product { get; set; }
public ProductCategory ProductCategory { get; set; }
}
public class Product
{
public int Id { get; set; }
public int? ProductTypeId { get; set; }
public string Title { get; set; }
public string Info { get; set; }
public string LongInfo { get; set; }
public decimal Price { get; set; }
public int? Weight { get; set; }
// A product can belong to multiple category front pages
public ICollection<FrontPageProduct> InFrontPages { get; set; }
// A product can belong to multiple categories
public ICollection<ProductInCategory> InCategories { get; set; }
}
public class FrontPageProduct
{
public int Id { get; set; }
public int ProductCategoryId { get; set; }
public int ProductId { get; set; }
public int SortOrder { get; set; }
public Product Product { get; set; }
public ProductCategory ProductCategory { get; set; }
}
What am I missing?

Related

How do I show CategoryName (subcategory) directly instead of Category (parent)?

I have a list of products.
var products = await unitOfWork.ProductRepository.GetAllWithDetailsAsync();
This is the model, so far so good.
public class ProductModel
{
public int Id { get; set; }
public int ProductCategoryId { get; set; }
public string CategoryName { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public ICollection<int> ReceiptDetailIds { get; set; }
}
I also have ProductCategoryModel.
public class ProductCategoryModel
{
public int Id { get; set; }
public string CategoryName { get; set; }
public ICollection<int> ProductIds { get; set; }
}
The problem is with the returned Category name. Instead of returning the correct data, it returns Data.Entities.Product.
In debug mode, after expanding product, I can expand Category and inside it is CategoryName - the expected string.
How do I show CategoryName directly instead of Category?
While requesting Products in your UnitOfWork include Category .Include("Category")
public class ProductModel
{
public int Id { get; set; }
public int ProductCategoryId { get; set; }
public string ProductName { get; set; }
public decimal Price { get; set; }
public ICollection<int> ReceiptDetailIds { get; set; }
public ProductCategoryModel Category { get; set; }
public string CategoryName => Category.CategoryName;
}
https://learn.microsoft.com/en-us/ef/ef6/querying/related-data

Asp.Net Core Linq how to perform a nested groupby with a many to many relationship?

I'm creating a KPI dashboard that displays the total income from orders. The business logic is that each item is linked to a type of event with a many to many relationship , and linked to a supplier type via a one to many relationship. And there are different suppliers which sell these items based on the supplier type. There is also a table that links suppliers to their order by using the orderItemId and supplierId. What I'm trying to achieve is the following:
Get all orders that have been successfully fulfilled and get their order items.
Get the supplier order items from the table I mentioned above using the order items.
Once I have the supplier order items, I want to group them by the supplierId, and iem event type id so that I can display the items that each supplier sold of each event type.
Supplier
Event Type
List of items
Supplier
Event Type
List of items
The above is what I want to happen. I managed to group them by supplier id but I'm struggling to group them by eventTypeId because of the many to many relationship between the item and the event types.
Here are the models:
public partial class Item
{
public Item()
{
Favorites = new HashSet<Favorite>();
ItemDetails = new HashSet<ItemDetail>();
ItemEventTypes = new HashSet<ItemEventType>();
OrderItems = new HashSet<OrderItem>();
SupplierItems = new HashSet<SupplierItem>();
}
[Key]
public int Id { get; set; }
[StringLength(250)]
public string Image { get; set; }
public string Description { get; set; }
[StringLength(200)]
public string Title { get; set; }
public int? CategoryId { get; set; }
[Column("isDeleted")]
public bool? IsDeleted { get; set; }
public double? Price { get; set; }
public int? EventTypeId { get; set; }
public int? NumberOfGuestsId { get; set; }
public double? DaberniPrice { get; set; }
public double? RegularPrice { get; set; }
public int? Tax { get; set; }
[Column("SupplierTypeID")]
public int? SupplierTypeId { get; set; }
[Column("SortID")]
public int? SortId { get; set; }
public bool? IsDisabled { get; set; }
public int? Min { get; set; }
public int? Max { get; set; }
public int? Increment { get; set; }
public bool? IsShisha { get; set; }
public bool? IsSoldByPackage { get; set; }
[Column("ImageAR")]
[StringLength(250)]
public string ImageAr { get; set; }
[Column("DescriptionAR")]
public string DescriptionAr { get; set; }
[Column("TitleAR")]
[StringLength(250)]
public string TitleAr { get; set; }
public int? Capacity { get; set; }
[ForeignKey(nameof(CategoryId))]
[InverseProperty(nameof(Catrgory.Items))]
public virtual Catrgory Category { get; set; }
[ForeignKey(nameof(NumberOfGuestsId))]
[InverseProperty(nameof(NumberOfGuest.Items))]
public virtual NumberOfGuest NumberOfGuests { get; set; }
[ForeignKey(nameof(SupplierTypeId))]
[InverseProperty("Items")]
public virtual SupplierType SupplierType { get; set; }
[InverseProperty(nameof(Favorite.Item))]
public virtual ICollection<Favorite> Favorites { get; set; }
[InverseProperty(nameof(ItemDetail.Item))]
public virtual ICollection<ItemDetail> ItemDetails { get; set; }
[InverseProperty(nameof(ItemEventType.Item))]
public virtual ICollection<ItemEventType> ItemEventTypes { get; set; }
[InverseProperty(nameof(OrderItem.Item))]
public virtual ICollection<OrderItem> OrderItems { get; set; }
[InverseProperty(nameof(SupplierItem.Item))]
public virtual ICollection<SupplierItem> SupplierItems { get; set; }
}
public partial class ItemEventType
{
[Key]
public int Id { get; set; }
public int? EventTypeId { get; set; }
public int? ItemId { get; set; }
public bool? IsDeleted { get; set; }
[ForeignKey(nameof(EventTypeId))]
[InverseProperty("ItemEventTypes")]
public virtual EventType EventType { get; set; }
[ForeignKey(nameof(ItemId))]
[InverseProperty("ItemEventTypes")]
public virtual Item Item { get; set; }
}
public partial class SupplierAssignedOrderItem
{
[Key]
public int Id { get; set; }
[Column("SupplierID")]
public int? SupplierId { get; set; }
[Column("ItemID")]
public int? ItemId { get; set; }
public int? Status { get; set; }
[Column(TypeName = "datetime")]
public DateTime? CreatedDate { get; set; }
[ForeignKey(nameof(ItemId))]
[InverseProperty(nameof(OrderItem.SupplierAssignedOrderItems))]
public virtual OrderItem Item { get; set; }
[ForeignKey(nameof(Status))]
[InverseProperty(nameof(OrderStatus.SupplierAssignedOrderItems))]
public virtual OrderStatus StatusNavigation { get; set; }
[ForeignKey(nameof(SupplierId))]
[InverseProperty("SupplierAssignedOrderItems")]
public virtual Supplier Supplier { get; set; }
}
Any Help is appreciated. Thanks.
I tried with EF Core,you could get orders and Supplier as follow :
DBcontext:
public class EFCoreDbContext : DbContext
{
public EFCoreDbContext(DbContextOptions<EFCoreDbContext> options)
: base(options)
{
}
public DbSet<Item> Item { get; set; }
public DbSet<Order> Order { get; set; }
public DbSet<Supplier> Supplier { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Item>().ToTable("Item");
modelBuilder.Entity<Order>().ToTable("Order");
modelBuilder.Entity<OrderItem>().HasKey(x => new { x.ItemId, x.OrderId });
modelBuilder.Entity<OrderItem>().HasOne(x => x.Item).WithMany(x => x.OrderItems).HasForeignKey(x => x.ItemId);
modelBuilder.Entity<OrderItem>().HasOne(x => x.Order).WithMany(x => x.OrderItems).HasForeignKey(x => x.OrderId);
modelBuilder.Entity<OrderItem>().ToTable("OrderItem");
modelBuilder.Entity<Supplier>().HasMany(x => x.Orders).WithOne(x => x.Supplier).HasForeignKey(x => x.SupplierId);
modelBuilder.Entity<Supplier>().ToTable("Supplier");
modelBuilder.Entity<SupplierItem>().HasKey(x => new { x.ItemId, x.SupplierId });
modelBuilder.Entity<SupplierItem>().HasOne(x => x.Item).WithMany(x => x.SupplierItems).HasForeignKey(x => x.ItemId);
modelBuilder.Entity<SupplierItem>().HasOne(x => x.Supplier).WithMany(x => x.SupplierItems).HasForeignKey(x => x.SupplierId);
modelBuilder.Entity<SupplierItem>().ToTable("SupplierItem");
}
controller:
var orderlist = _context.Order.Include(p => p.OrderItems).ThenInclude(q => q.Item).ToList();
var supplierlist = _context.Supplier.Include(p => p.SupplierItems).ThenInclude(q => q.Item).ToList();
And I think it'll be better if you remove some properties from your itemclass and add them to SupplierItem class,such as the price property and Event Type property.
an item may have different prices if you buy from different supplies,also in different days. If the event type is used to describe the state of the trade ,it should be removed as well.
public class Item
{
public Item()
{
OrderItems = new List<OrderItem>();
SupplierItems = new List<SupplierItem>();
}
public int ItemId { get; set; }
public string ItemName { get; set; }
public List<OrderItem> OrderItems { get; set; }
public List<SupplierItem> SupplierItems { get; set; }
}
public class Supplier
{
public Supplier()
{
Orders = new List<Order>();
SupplierItems = new List<SupplierItem>();
}
public int SupplierId { get; set; }
public string SupplierName { get; set; }
public List<Order> Orders { get; set; }
public List<SupplierItem> SupplierItems { get; set; }
}
public class SupplierItem
{
public Item Item { get; set; }
public int ItemId { get; set; }
public Supplier Supplier { get; set; }
public int SupplierId { get; set; }
public double SupplierItemPrice { get; set; }
}
If you really want to group the supplier list by two properties
you could try:
var somesupplierslist = supplierlist.GroupBy(x => new { x.SupplierId, x.SupplierName }).ToList();

How do I save data in related tables?

I have a question. I created a table with many to many relationships as below. What code should I write so that I can enter multiple categories when adding a product to the database?
I would be glad if you explain with an example.
For example, I can enter product.name information with the name information I received from the user, but I do not know how to save data in the relevant tables.
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public List<ProductCategory> ProductCategories { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public double? Price { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public bool IsApproved { get; set; }
public bool IsHome { get; set; }
public List<ProductCategory> ProductCategories { get; set; }
}
public class ProductCategory
{
public int CategoryId { get; set; }
public Category Category { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
}
You can insert them by the join table like this:
var product = new Product { Name = "AA" };
var categories = new List<Category>
{
new Category{ Name = "a"},
new Category{ Name = "b"},
new Category{ Name = "c"},
};
foreach (var category in categories)
{
_context.ProductCategory.Add(
new ProductCategory
{
Product = product,
Category = category
});
}
_context.SaveChanges();
I am not sure which version you are using. with EF Core 5.o find an example at https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/whatsnew
Models:
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public ICollection<Product> Products { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public double? Price { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public bool IsApproved { get; set; }
public bool IsHome { get; set; }
public ICollection<Category> Categories { get; set; }
}
Or If you are using EE 6 or EF Core 3.1 then your models should be like:
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public ICollection<Product> Products { get; set; }
public ProductCategory ProductCategorie { get; set; }
}
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public double? Price { get; set; }
public string Description { get; set; }
public string ImageUrl { get; set; }
public bool IsApproved { get; set; }
public bool IsHome { get; set; }
public ICollection<Category> Categories { get; set; }
public ProductCategory ProductCategorie { get; set; }
}
public class ProductCategory
{
public int CategoryId { get; set; }
public Category Category { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
}
For inserting data to Many-Many tables: https://learn.microsoft.com/en-us/aspnet/core/data/ef-mvc/complex-data-model?view=aspnetcore-5.0
then please look at the section:Seed database with test data

Query through multiple navigation properties

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.

How to order by another model's value in ASP.NET MVC3

OverstockEntities.cs
namespace Overstock.Models
{
public class OverstockEntities: DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
}
}
Product.cs
namespace Overstock.Models
{
[Bind(Exclude = "ProductId")]
public class Product
{
[ScaffoldColumn(false)]
public int ProductId { get; set; }
[DisplayName("Category")]
public int CategoryId { get; set; }
[DisplayName("Brand")]
public int BrandId { get; set; }
[Required(ErrorMessage="Product title is required")]
[StringLength(160)]
public string Title { get; set; }
[Required(ErrorMessage="Price is required")]
[Range(0.01, 100000.00,
ErrorMessage="Price must be between 0.01 and 100000.00")]
public decimal Price { get; set; }
[DisplayName("Product Art URL")]
[StringLength(1024)]
public string PictureUrl { get; set; }
[Required(ErrorMessage = "Description is required")]
[StringLength(1024)]
public string Description { get; set; }
public virtual Category Category { get; set; }
public virtual Brand Brand { get; set; }
public virtual List<OrderDetail> OrderDetails { get; set; }
}
}
Category.cs
namespace Overstock.Models
{
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }
public List<Product> Products { get; set; }
}
}
Browse action
public ActionResult Browse(string category, string sortOrder)
{
var varCategories = MyDB.Categories.Include("Products").Single(c => c.Name == category);
//How to order by Product's Title value
return View(varCategories);
}
Question: how to get another model's variable(in this case Product.Title) in order to sort by that variable?
foreach (var category in varCategories )
{
category.Products = category.Products.OrderBy(p => p.Title);
}

Categories