C# EF代码优先:对同一集合类型进行一到两次
简化:在我的数据库中,我有一个产品在不同的日期以不同的价格出售。换句话说,它有一个价格历史。我有两个类:一对多关系的产品和价格:C# EF代码优先:对同一集合类型进行一到两次,c#,entity-framework,many-to-many,C#,Entity Framework,Many To Many,简化:在我的数据库中,我有一个产品在不同的日期以不同的价格出售。换句话说,它有一个价格历史。我有两个类:一对多关系的产品和价格: public class Product { public int ProductId {get; set;} public string Name {get; set;} public ICollection<Price> Prices {get; set;} } public class Price { public
public class Product
{
public int ProductId {get; set;}
public string Name {get; set;}
public ICollection<Price> Prices {get; set;}
}
public class Price
{
public int PriceId {get; set;}
// foreign key to Product:
public int ProductId {get; set;}
public Product Product {get; set;}
public DateTime ActivationDate {get; set;}
public decimal value {get; set;}
}
public class MyDbContext : DbContext
{
public DbSet<Price> Prices { get; set; }
public DbSet<Product> Products { get; set; }
}
公共类产品
{
public int ProductId{get;set;}
公共字符串名称{get;set;}
公共ICollection价格{get;set;}
}
公共类价格
{
public int PriceId{get;set;}
//产品外键:
public int ProductId{get;set;}
公共产品产品{get;set;}
公共日期时间激活日期{get;set;}
公共十进制值{get;set;}
}
公共类MyDbContext:DbContext
{
公共数据库集价格{get;set;}
公共数据库集产品{get;set;}
}
到目前为止,实体框架知道如何处理这个问题。通过使用这两个类,我能够在特定日期获得特定产品的价格
但是,如果我的产品有两个价格历史记录:购买价格历史记录和零售价格历史记录呢
public class Product
{
public int ProductId {get; set;}
public string Name {get; set;}
public ICollection<Price> PurchasePrices {get; set;}
public ICollection<Price> RetailPrices {get; set;}
}
公共类产品
{
public int ProductId{get;set;}
公共字符串名称{get;set;}
公共ICollection PurchasePrices{get;set;}
公共ICollection零售价格{get;set;}
}
因为这两个集合属于同一类型,所以我不希望用同一类型的对象填充单独的表(真正的原因是:我有很多带有price集合的类)
因此,我必须使用fluentapi进行一些编码。我的直觉告诉我,我需要加入表格,就像在多对多关系中一样,可以使用ManyToManyNavigationPropertyConfiguration.Map
如何做到这一点?由于EF命名约定,目前您的代码正在工作: 如果类上的属性名为“ID”(不区分大小写),或者类名后跟“ID”,则代码首先推断属性是主键。如果主键属性的类型是数字或GUID,则将其配置为标识列 EF看到您有一对多,因此它会自动将
ProductId
作为外键。如果要定义同一实体的多个集合,则必须手动定义外键
public class Price
{
public int PriceId {get; set;}
public int ProductPurchaseId {get; set;}
public Product ProductPurchase {get; set;}
public int ProductRetailId {get; set;}
public Product ProductRetail {get; set;}
public DateTime ActivationDate {get; set;}
public decimal value {get; set;}
}
在fluent api中:
modelBuilder<Product>().HasMany(p => p.PurchasePrices)
.WithRequired(p => p.ProductPurchase)
.HasForeignKey(p => p.ProductPurchaseId);
modelBuilder<Product>().HasMany(p => p.RetailPrices)
.WithRequired(p => p.ProductRetail)
.HasForeignKey(p => p.ProductRetailId);
modelBuilder().HasMany(p=>p.PurchasePrices)
.WithRequired(p=>p.ProductPurchase)
.HasForeignKey(p=>p.ProductPurchaseId);
modelBuilder().HasMany(p=>p.RetailPrices)
.WithRequired(p=>p.ProductRetail)
.HasForeignKey(p=>p.ProductRetailId);
这当然意味着您需要在
价格表中有两个产品
外键。根据我对您需求的理解,您需要在价格表中增加一个字段,它将告诉您要存储的价格类型。例如:
public class Price
{
public int PriceId {get; set;}
// The PriceType will recognise among different type of price- Sell Price, Purchase Price etc.
public string PriceType{get;set;}
// foreign key to Product:
public int ProductId {get; set;}
public Product Product {get; set;}
public DateTime ActivationDate {get; set;}
public decimal value {get; set;}
}
在阅读了一篇关于一对多关联的文章并将其用于一对多关联之后,我能够按照以下要求实现它:
- 我可以有许多不同的类,它们具有相同类型T的属性
- 类型T的所有实例都可以放在一个表中,即使该类型的“所有者”位于不同的表中。
- 一个类甚至可以有两个类型为T的属性
例如:客户可能有BillingAddress和DeliveryAddress,这两种类型都是Address。两个地址可以放在一个表中:Address
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
}
public class User
{
public int UserId { get; set; }
public string Name { get; set; }
public int BillingAddressId { get; set; }
public Address BillingAddress { get; set; }
public int DeliveryAddressId { get; set; }
public Address DeliveryAddress { get; set; }
}
public class MyDbContext : DbContext
{
public DbSet<Address> Addresses { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasRequired(p => p.DeliveryAddress)
.WithMany()
.HasForeignKey(p => p.DeliveryAddressId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasRequired(p => p.BillingAddress)
.WithMany()
.HasForeignKey(p => p.BillingAddressId)
.WillCascadeOnDelete(false);
}
}
公共类地址
{
公共int Id{get;set;}
公共字符串Street{get;set;}
公共字符串City{get;set;}
公共字符串ZipCode{get;set;}
}
公共类用户
{
public int UserId{get;set;}
公共字符串名称{get;set;}
public int billingaddress{get;set;}
公共广播计费地址{get;set;}
public int DeliveryAddressId{get;set;}
公共地址传递地址{get;set;}
}
公共类MyDbContext:DbContext
{
public DbSet不确定您正在实现什么,但我认为您的价格实体上应该有另一个属性来区分销售价格和购买价格。布尔类型的属性可能会有所帮助。“我不想要单独的表”然后在表中需要鉴别器列,即TPH继承策略。这确实是一个解决方案,但我有6个不同的类,每个类都有一个价格历史记录。这将导致一个表有6个外键,其中5个外键为空,或者6个不同的表有价格历史记录这确实是我的第一个解决方案。但是我发现我有6个不同的类,每个类都有一个价格历史记录。这将导致一个表有6个外键,其中5个为空,或者6个不同的表有价格历史记录。因此,我正在寻找一个类似于耦合表的解决方案:一边是价格的Id,另一边是拥有价格历史记录的类的Id。Ho无论如何,这仍然会导致每个类有一个耦合表,其中包含一个价格历史记录
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
virtual public ICollection<Post> Posts {get; set;}
}
public class Post
{
public int Id { get; set; }
public string Text { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
modelBuilder.Entity<Blog>()
.HasMany(b => b.Posts)
.WithRequired(post => post.Blog)
.HasForeignKey(post => post.BlogId);
public class Price
{
public int Id { get; set; }
public int PriceHistoryId { get; set; }
public virtual PriceHistory PriceHistory { get; set; }
public DateTime ActivationDate { get; set; }
public decimal Value { get; set; }
}
public class PriceHistory
{
public int Id { get; set; }
virtual public ICollection<Price> Prices { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
// Purchase Prices
public virtual PriceHistory PurchasePriceHistory { get; set; }
public int PurchasePriceHistoryId { get; set; }
// Retail prices
public virtual PriceHistory RetailPriceHistory { get; set; }
public int RetailPriceHistoryId { get; set; }
}
public class MyDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<PriceHistory> PriceHistories { get; set; }
public DbSet<Price> Prices { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// one price history has many prices: one to many:
modelBuilder.Entity<PriceHistory>()
.HasMany(p => p.Prices)
.WithRequired(price => price.PriceHistory)
.HasForeignKey(price => price.PriceHistoryId);
// one product has 2 price histories, the used method is comparable
// with the method user with two addresses
modelBuilder.Entity<Product>()
.HasRequired(p => p.PurchasePriceHistory)
.WithMany()
.HasForeignKey(p => p.PurchasePriceHistoryId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Product>()
.HasRequired(p => p.RetailPriceHistory)
.WithMany()
.HasForeignKey(p => p.RetailPriceHistoryId)
.WillCascadeOnDelete(false);
}
}