Query through multiple navigation properties - c#

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.

Related

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 to create a relation between existing entities in EF core?

I have a case scenario with two tables References and Products alreading containing many entries which can be dynamically related on demand.
public class Reference
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ReferenceId { get; set; }
public string Name { get; set; }
public string Status { get; set; }
public virtual ICollection<Product> ManyProducts { get; set; }
public Reference() {}
}
public class Product
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ProductId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
[ForeignKey("Reference")]
public Guid ReferenceId { get; set; }
public virtual Reference OneReference { get; set; }
public Product() {}
}
When a user ask to link a reference to a product I simply do :
product.ReferenceId = reference.ReferenceId ;
await context.SaveChangesAsync() ;
The entry in Products table is updated correctly, but when I try to access a reference's related data, it does not retrieve any ?? After eager loading :
var reference = await context.References
.Include(r => r.ManyProducts)
.SingleAsync(r => r.ReferenceId == referenceId) ;
or explicit loading :
var reference = await context.References.FindAsync(referenceId) ;
await context.Entry(reference).Collection(s => s.ManyProducts).LoadAsync() ;
reference.ManyProducts is empty. So I have to do something like this :
var reference = await context.References.FindAsync(referenceId) ;
var products = await context.Products.Where(l => l.ReferenceId == referenceId).ToListAsync() ;
result.ManyProducts = products ;
which works fine, but I would like to understand why ?
I´m using DataAnnotation
Sample
public class spread
{
[Key]
public int spreadid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange origem")]
public virtual exchange exchange { get; set; } // One to one
[ForeignKey("spreadid")]
public virtual ICollection<spreadhelper> spreadhelper { get; set; } // One to many
}
public class spreadhelper
{
[Key]
public int spreadhelperid { get; set; }
[Required]
public int spreadid { get; set; }
[Required]
public int exchangeid { get; set; }
[ForeignKey("exchangeid"), Display(Name = "Exchange")] // One to one
public virtual exchange exchange { get; set; }
[Required, Range(0, 200)]
public decimal spreadvalue { get; set; }
}
one to one - sample
public class exchange
{
[Key]
public int exchangeid { get; set; }
[Required]
public DateTime insertdate { get; set; }
[Required, MaxLength(50)]
public string name { get; set; }
[MaxLength(128)]
public string token { get; set; }
}
One to many sample

Which one is the correct one-to-many relation in EF

i am designing a system and one of my entity has one to many relation as shown below.
public class Product
{
public int Id { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public Product Product { get; set; }
}
competitorProduct indicates that product has a equivalent which is sold by different store. should i define one-to-many relation as shown above or below? which one is correct?
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts{ get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
}
Assuming it is a one to many relationship (what would happen if a competitor product was competing with more than one of your products for example) you can do both and add in a foreign key as well.
public class Product
{
public int Id { get; set; }
public virtual ICollection<CompetitorProduct> CompetitorProducts { get; set; }
}
public class CompetitorProduct
{
public int Id { get; set; }
public int ProductId { get; set; }
public virtual Product Product { get; set; }
}
You can then set up your relationship using fluent API as so:
modelBuilder.Entity<CompetitorProduct>(entity =>
{
entity.HasOne(e => e.Product)
.WithMany(e => e.CompetitorProducts)
.HasForeignKey(e => e.ProductId)
.HasConstraintName("FK_ComptetitorProduct_Product");
});
This way you can access the competitor products from the product and the product from the competitor products.
Here is a quick example of a ecommerce site I have worked on and how we did table relations.
I removed a bunch of the fields so you can see what you really need. Once to make relations and run Add-Migration EF will handle the FK constraints for you as long as you identified them in models like how I have below.
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
Active = true;
CreateDateTimeUtc = DateTime.UtcNow;
ModifiedDateTimeUtc = DateTime.UtcNow;
}
[StringLength(500)]
public string FirstName { get; set; }
[StringLength(500)]
public string LastName { get; set; }
[StringLength(1000)]
public string Address { get; set; }
[StringLength(100)]
public string Unit { get; set; }
[StringLength(250)]
public string City { get; set; }
[StringLength(25)]
public string State { get; set; }
[StringLength(20)]
public string ZipCode { get; set; }
//This will give access to a list of child carts a user could have
[Index]
public bool Active { get; set; }
public virtual ICollection<Cart> Carts { get; set; }
// Account Profile Image
public byte[] ProfileImage { get; set; }
[StringLength(500)]
public string ProfileFilename { get; set; }
[StringLength(100)]
public string ProfileMimeType { get; set; }
}
[Table("Cart", Schema = "dbo")]
public class Cart : AbstractTable
{
public Cart()
{
IsComplete = false;
}
//This create relation to user table where I can get one unique user.
[StringLength(128)]
[ForeignKey("ApplicationUser")]
public string UserId { get; set; }
public virtual ApplicationUser ApplicationUser { get; set; }
//These link us to child tables of Cart where we can get a LIST of the items below
public virtual ICollection<CartCategory> CartCategories { get; set; }
public virtual ICollection<CartItem> CartItems { get; set; }
// Marked when a payment/receipt is generated based off of this cart
public bool IsComplete { get; set; }
}
[Table("CartItem", Schema = "dbo")]
public class CartItem : AbstractTable
{
//This will return one unique cart id and let us access it as the parent record
[ForeignKey("Cart")]
public Guid CartId { get; set; }
public virtual Cart Cart { get; set; }
// Signifies if this was paid for in a receipt
public bool IsComplete { get; set; }
public virtual ICollection<CartItemCustomField> CustomFields { get; set; }
}

how I can reference multiple Entity into one , in Code First c#

I have this two class
public class Category
{
public int Id{ get; set; }
public string Name { get; set; }
}
public class Filter
{
public int Id{ get; set; }
public string Name { get; set; }
}
And I have another Entity like this
public class Menu
{
public int Id{ get; set; }
public string Name { get; set; }
public MenuType MenuType { get; set; }
}
That the MenuType is enum Like this
public enum MenuType
{
Category = 0,
Filter = 1
}
i want to know in class menu how can store Category OR Filter
i mean Menu have relate with one Category or one Filter , now how can I make this relationship?
and one other thing is maybe MenuType will extende and added some other menutype and thats class.
Is not possible to have a column Foreign Key (FK) that can reference two tables.
A option would be:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public List<Menu> Menus { get; set; }
}
public class Filter
{
public int Id { get; set; }
public string Name { get; set; }
public List<Menu> Menus { get; set; }
}
public class Menu
{
public int Id { get; set; }
public string Name { get; set; }
public MenuType MenuType { get; set; }
public int? CategoryId { get; set; }
public int? FilterId { get; set; }
public Category Category { get; set; }
public Filter Filter { get; set; }
}
Inside your context:
modelBuilder.Entity<Menu>().HasOne(x => x.Category).WithMany(x => x.Menus).HasForeignKey(x => x.CategoryId);
modelBuilder.Entity<Menu>().HasOne(x => x.Filter).WithMany(x => x.Menus).HasForeignKey(x => x.FilterId);
And based on MenuType, you either use Category or Filter object.

EF7 nested Include not showing in Razor .net

I have my models like this:
Goup.cs
GroupUser (pivot table)
ApplicationUser (User) -> 4. Profile
And now I want to show the data in Profile on a details page when the User belongs to the group. I'm doing this like this:
private IEnumerable<GroupUser> GetUsers(int groupId)
{
IEnumerable<GroupUser> model = null;
if(groupId == 0)
{
model = _kletsContext.GroupUser.OrderByDescending(o => o.GroupId).AsEnumerable();
}
else
{
model = _kletsContext.GroupUser.Where(g => g.GroupId == groupId).Include(p => p.User.Profile).OrderByDescending(o => o.GroupId).AsEnumerable();
}
return model;
}
This works, if I just want to display the UserId, ... (so the data in the Pivot table) with this code:
#model IEnumerable<App.Models.GroupUser>
#if(Model != null && Model.Count() > 0)
{
#foreach(var user in Model)
{
#user.UserId</h2>
}
}
But for some reason I can't display the data in the Included tables?
Normally you would do something like this: #user.User.Profile.XXXX but then I get the error: System.NullReferenceException: Object reference not set to an instance of an object
So this would mean the return is null, but there are users in the pivot table with a profile.
The models:
Group.cs:
namespace App.Models
{
public class Group : Item
{
public Group() : base()
{
}
[Key]
public Int16 Id { get; set; }
public string Name { get; set; }
public string Images { get; set; }
/* Foreign Keys */
public Nullable<Int16> RegionId { get; set; }
public virtual Region Region { get; set; }
public virtual ICollection<Lets> Lets { get; set; }
public virtual ICollection<GroupUser> Users { get; set; }
}
}
ApplicationUser:
namespace App.Models.Identity
{
public class ApplicationUser : IdentityUser
{
public string Description { get; set; }
public DateTime CreatedAt { get; set; }
public Nullable<DateTime> UpdatedAt { get; set; }
public Nullable<DateTime> DeletedAt { get; set; }
/* Virtual or Navigation Properties */
public virtual Profile Profile { get; set; }
public virtual ICollection<GroupUser> Groups { get; set; }
public virtual ICollection<Lets> Lets { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Region> Regions { get; set; }
public virtual ICollection<Status> Status { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
}
GroupUser:
namespace App.Models
{
public class GroupUser
{
public GroupUser()
{
}
public Nullable<Int16> GroupId { get; set; }
public string UserId { get; set; }
public virtual Group Group { get; set; }
public virtual ApplicationUser User { get; set; }
}
}
Profile.cs:
namespace App.Models
{
public class Profile : Item
{
public Profile() : base()
{
}
[Key]
public string UserId { get; set; }
public string FirstName { get; set; }
public string SurName { get; set; }
public string Email { get; set; }
public string Gender { get; set; }
public Int16 Age { get; set; }
public string City { get; set; }
public string Image { get; set; }
public Int16 Credits { get; set; }
public Int16 Postalcode { get; set; }
}
}
How can i display the nested data with razor?
model = _kletsContext.GroupUser.Where(g => g.GroupId == groupId)
.Include(gu => gu.User)
.ThenInclude(u => u.Profile)
.OrderByDescending(o => o.GroupId)
.AsEnumerable();
Don't get freaked out when intellisense doesn't work for the ThenInclude, just type it, it will compile.
try to include the user-reference
model = _kletsContext.GroupUser.Where(g => g.GroupId == groupId).Include(p => p.User).Include(p => p.User.Profile).OrderByDescending(o => o.GroupId).AsEnumerable();

Categories