I have an "Item" table defined as such:
item_id
Name
Description
itemseries_id
itemtype_id
itemcondition_id
And then I have "ItemForSale" table:
itemforsale_id
item_id
price
date_added
Entities:
public class Item
{
public int ItemId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int ItemSeriesId { get; set; }
public ItemSeries ItemSeries { get; set; }
public int ItemConditionId { get; set; }
public ItemCondition ItemCondition { get; set; }
public int ItemTypeId { get; set; }
public ItemType ItemType { get; set; }
public List<ItemTag> ItemTags { get; set; }
public List<ItemImage> ItemImages { get; set; }
public List<ItemPurchase> ItemPurchases { get; set; }
public List<ItemSale> ItemSales { get; set; }
}
public class ItemForSale
{
public int ItemForSaleId { get; set; }
public decimal Price { get; set; }
public DateTime AddedDate { get; set; }
public int ItemId { get; set; }
public Item Item { get; set; }
}
How would I use the FluentAPI between these? I know I could add a reference to ItemForSale inside the Item entity class, but it doesn't make sense to me. So far I have mapped all of my One-to-one and many-to-many relationships, but the relationship between Item and ItemForSale is just confusing me.
Note: I am distinguishing between items that have been sold as a "Sale" or "ItemSale" and an item up for sale with no buyer as "ItemForSale"
From the EF Core Docs, you can do something like this:
class MyContext : DbContext
{
public DbSet<Item> Items { get; set; }
public DbSet<ItemSale> ItemSales { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ItemSale>()
.HasOne(p => p.Item)
.WithMany(b => b.ItemSales)
.HasForeignKey(p => p.ItemId)
.IsRequired(false);
}
}
public class Item
{
public int ItemId { get; set; }
public string Url { get; set; }
public List<ItemSale> ItemSales { get; set; }
}
public class ItemSale
{
public int ItemSaleId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Item Item { get; set; }
// note that the reference id is nullable
public int? ItemId { get; set; }
}
And then, mark the property ItemId in your model class as int?. While on EF 6, we had the HasOptional configuration option, but on EF Core, if the reference property can be nullable, it assumes the property starts from 0, like 0..N. I think that even the IsRequired(false) is not needed in this context as well, but here it goes.
Related
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();
I want to configure a one-to-many relationship in Ef core. As you see I have a class for order and the other one for OrderItems.
I do it when I use NHibernate.of course, I consider orderItem class as ValueObject.But I want to do it using EF Core.
public class Order
{
public long Id { get; set; }
public long CustomerId { get; set; }
public DateTime OrderDateTime { get; set; }
public ICollection<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public string BookId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal? Discount { get; set; }
public decimal Total { get; set; }
public Order Order { get; set; }
}
you should define meta data [ForeignKey] with type of primary key in order entity. after that ef core automatically set in db by your chosen name
public class Order
{
public long Id { get; set; }
public long CustomerId { get; set; }
public DateTime OrderDateTime { get; set; }
public virtual ICollection<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public long Id { get; set; }
[ForeignKey(nameof(OrderId)]
public virtual Order Order { get; set; }
public long OrderId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal? Discount { get; set; }
public decimal Total { get; set; }
}
Here is a simple demo like below:
1.Model:
public class Order
{
public long Id { get; set; }
public long CustomerId { get; set; }
public DateTime OrderDateTime { get; set; }
public ICollection<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
public int OrderItemId { get; set; }//you need to define a primary key for OrderItem model
public string BookId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal? Discount { get; set; }
public decimal Total { get; set; }
public Order Order { get; set; }
}
2.DbContext:
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{
}
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.HasMany(c => c.OrderItems)
.WithOne(e => e.Order);
}
}
3.Startup.cs:
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyDbContext")));
4.appsettings.json:
"ConnectionStrings": {
"MyDbContext": "Server=(localdb)\\mssqllocaldb;Database=DatabaseName;Trusted_Connection=True;MultipleActiveResultSets=true"
}
5.Run command line on Package Nuget Manager:
PM>add-migration init
PM>update-database
Model
public class Order {
public long Id { get; set; }
public long CustomerId { get; set; }
public DateTime OrderDateTime { get; set; }
public virtual ICollection<OrderItem> OrderItems { get; set; }
}
public class OrderItem
{
[Key]
public int OrderItemId { get; set; }
public string BookId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal? Discount { get; set; }
public decimal Total { get; set; }
public long OrderId{get;set;} // ForeignKey OrderId
[ForeignKey("OrderId")]
public virtual Order Order { get; set; }
}
// open PackageManager console
PM>add-migration "orderItem changed"
PM>update-database
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.
Im trying to do a one-to-many map by using fluent api.
This is my classes
public class Product : EntityBase
{
public Product()
{
this.ProductArticles = new List<ProductArticle>();
}
[Key]
public int ProductId { get; set; }
public string Description { get; set; }
public string ReportText1 { get; set; }
public string ReportText2 { get; set; }
public bool Standard { get; set; }
public int ProductGroupId { get; set; }
public decimal? Surcharge1 { get; set; }
public decimal? Surcharge2 { get; set; }
public decimal? Surcharge3 { get; set; }
public decimal? Surcharge4 { get; set; }
public decimal PriceIn { get; set; }
public decimal PriceOut { get; set; }
public decimal PriceArtisanIn { get; set; }
public decimal PriceArtisanOut { get; set; }
public decimal PriceTotalIn { get; set; }
public decimal PriceTotalOut { get; set; }
public decimal PriceTotalOutVat { get; set; }
public decimal PriceAdjustment { get; set; }
public bool Calculate { get; set; }
public string Notes { get; set; }
[ForeignKey("ProductGroupId")]
public virtual ProductGroup ProductGroup { get; set; }
public virtual ICollection<ProductArticle> ProductArticles { get; set; }
}
public class ProductArticle : EntityBase
{
[Key]
public int ProductArticleId { get; set; }
public int ProductId { get; set; }
public int ArticleId { get; set; }
public decimal Qty { get; set; }
public decimal PriceIn { get; set; }
public bool Primary { get; set; }
public virtual Product Product { get; set; }
public virtual Article Article { get; set; }
}
Now i want from single Product include all ProductArticles
This is my mapping
public class ProductMap : EntityTypeConfiguration<Product>
{
public ProductMap()
{
// Primary Key
this.HasKey(p => p.ProductId);
// Table & Column Mappings
this.ToTable("Product");
this.HasMany(p => p.ProductArticles)
.WithOptional()
.Map(p => p.MapKey("ProductId").ToTable("ProductArticle"));
}
But it doesnt work.. Please help :)
First - by convention EF treats property with name equal to Id or EntityTypeName + Id is a primary key. So, you don't need to configure that manually.
Second - if you don't want table names to be plural, just remove that convention from your context instead of providing table name for each entity mapping:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
And last - EF smart enough to define foreign keys which have names like RelatedEntityTypeName + Id. So, you don't need any fluent configurations here.
Here is my model
public class Horse
{
public int HorseId { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public LegType LegType { get; set; }
public Character Character { get; set; }
public int Hearts { get; set; }
public bool Retired { get; set; }
// Parents
public Horse Sire { get; set; }
public Horse Dam { get; set; }
// Internals
public int Stamina { get; set; }
public int Speed { get; set; }
public int Sharp { get; set; }
// Special
public int Dirt { get; set; }
// Externals
public int Start { get; set; }
public int Corner { get; set; }
public int OutOfTheBox { get; set; }
public int Competing { get; set; }
public int Tenacious { get; set; }
public int Spurt { get; set; }
//Races
public virtual ICollection<Race> RaceResults { get; set; }
//Training
public virtual ICollection<Training> TrainingResults { get; set; }
}
public class Race
{
public int RaceId { get; set; }
public int Favorite { get; set; }
public LegType LegType { get; set; }
public int Players { get; set; }
public DateTime Split { get; set; }
public DateTime Final { get; set; }
public int Position { get; set; }
public virtual int TrackId { get; set; }
public virtual Track Track { get; set; }
public virtual int LinkedHorseId { get; set; }
public virtual Horse LinkedHorse { get;set; }
}
public class Training
{
public int TrainingId { get; set; }
public string Type { get; set; }
public string Result { get; set; }
public string Food { get; set; }
public int Start { get; set; }
public int Corner { get; set; }
public int Outofthebox { get; set; }
public int Competing { get; set; }
public int Tenacious { get; set; }
public int Spurt { get; set; }
public virtual int LinkedHorseId { get; set; }
public virtual Horse LinkedHorse { get; set; }
}
public class Track
{
public int TrackId { get; set; }
public string Name { get; set; }
public int Distance { get; set; }
public bool G1 { get; set; }
public int Prize { get; set; }
}
And here is my fluent API code.
public class HorseTracker : DbContext
{
public DbSet<Horse> Horses { get; set; }
public DbSet<LegType> LegTypes { get; set; }
public DbSet<Character> Characters { get; set; }
public DbSet<Training> TrainingResults { get; set; }
public DbSet<Track> Tracks { get; set; }
public DbSet<Race> Races { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Race>()
.HasRequired(r => r.LinkedHorse)
.WithMany(h => h.RaceResults)
.HasForeignKey(r => r.LinkedHorseId);
modelBuilder.Entity<Training>()
.HasRequired(t => t.LinkedHorse)
.WithMany(t => t.TrainingResults)
.HasForeignKey(t => t.LinkedHorseId);
modelBuilder.Entity<Race>()
.HasRequired(r => r.Track)
.WithMany()
.HasForeignKey(r => r.TrackId)
.WillCascadeOnDelete(false);
}
}
I keep getting this error:
Unable to determine the principal end of an association between the types 'DOCCL.Models.Horse' and 'DOCCL.Models.Horse'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Any clue what i'm doing wrong.
I've been playing around with no foreign keys. making one of the required lists optional.
they all result in different errors.
mostly saying that the relation needs to be a 1:1 relation.
And once it said that it had a non nullable field.
I made that nullable int? and then i got the first error again.
I think you need to setup self-referencing relationships manually (specifically, the Horse class properties Sire and Dam are causing an issue).
Try this (in the answer):
What is the syntax for self referencing foreign keys in EF Code First?
You could add two more int IDs representing the foreign keys (SireId, DamId).
If you add this to your model configuration it should work:
modelBuilder.Entity<Horse>()
.HasRequired(h => h.Dam) // or HasOptional
.WithMany();
modelBuilder.Entity<Horse>()
.HasRequired(h => h.Sire) // or HasOptional
.WithMany();
The problem is that the mapping conventions try to create a one-to-one relationship between Dam and Sire and therefore cannot determine what's the principal and what's the dependent because both are optional. Anyway I guess you don't want a one-to-one relationships but actually two one-to-many relationships (the many-side (the "children") not being exposed in the model).