我有以下类,我真的想在EF中正确映射:
internal class Wallet : EntityFrameworkEntity
{
public Wallet()
{
this.Requests = new List<FinancialRequest>();
}
public string Name { get; set; }
public string Description { get; set; }
public decimal CurrentBalance { get; set; }
public decimal BlockedBalance { get; set; }
public virtual ICollection<Paper> Papers { get; set; }
public virtual ICollection<FinancialRequest> Requests { get; set; }
public virtual User Manager { get; set; }
}
internal class Request : EntityFrameworkEntity
{
public Int64 UserId { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime UpdatedAt { get; set; }
public RequestStatus Status { get; set; }
public virtual User User { get; set; }
}
internal class FinancialRequest : Request
{
public DateTime ValidUntil { get; set; }
public FinancialRequestType RequestType { get; set; }
public int Quantity { get; set; }
public bool UseMarketValue { get; set; }
public decimal? Value { get; set; }
public virtual Wallet Source { get; set; }
public virtual Wallet Destination { get; set; }
public virtual Team Team { get; set; }
}
我正在使用Code First,所以这是我映射这些类的方法:
modelBuilder.Entity<Wallet>()
.HasMany(x => x.Requests)
.WithOptional();
modelBuilder.Entity<Wallet>()
.HasMany(x => x.Papers)
.WithOptional(x => x.Owner)
.Map(configuration => configuration.MapKey("OwnerId"));
modelBuilder.Entity<Wallet>()
.HasMany(x => x.Requests)
.WithOptional();
modelBuilder.Entity<Request>().ToTable("Requests");
modelBuilder.Entity<FinancialRequest>().ToTable("FinancialRequests");
modelBuilder.Entity<FinancialRequest>()
.HasRequired(x => x.Team)
.WithOptional()
.Map(configuration => configuration.MapKey("TeamId"));
modelBuilder.Entity<FinancialRequest>()
.HasOptional(x => x.Destination)
.WithOptionalDependent()
.Map(configuration => configuration.MapKey("DestinationWalletId"));
modelBuilder.Entity<FinancialRequest>()
.HasRequired(x => x.Source)
.WithRequiredDependent()
.Map(configuration => configuration.MapKey("SourceWalletId"));
如果我按照现在的方式保留此映射,我的数据库模式如下所示:
如果你仔细观察,你会发现有一个名为“Wallet_Id”的专栏并不适合那里。此列仅存在,因为Wallet类具有“请求”集合。 如果我删除列中的集合就会消失,但我需要这个集合!它表示类之间的重要关系。我不需要的是错误生成的数据库中的第3列。
有谁知道我怎么能避免这种情况?我在这里做错了什么?
答案 0 :(得分:1)
导致冗余外键列Wallet_Id
的问题是EF不知道Wallet.Requests
集合是FinancialRequest.Source
还是FinancialRequest.Destination
的反向导航属性。因为它无法在两个EF之间做出决定,所以假定Wallet.Requests
根本没有反向导航属性。结果是与第三个FK的第三个冗余的一对多关系。
基本上你有三个选择:
删除Wallet.Requests
集合,第三个关系将消失(正如您已经注意到的那样)。但你不希望这样。
如果Wallet.Requests
有Source
或 Destination
作为反向导航属性,请明确告诉EF:
// Remove the modelBuilder.Entity<Wallet>().HasMany(x => x.Requests) mapping
modelBuilder.Entity<FinancialRequest>()
.HasOptional(x => x.Destination)
.WithMany(x => x.Requests)
.Map(config => config.MapKey("DestinationWalletId"));
modelBuilder.Entity<FinancialRequest>()
.HasRequired(x => x.Source)
.WithMany()
.Map(config => config.MapKey("SourceWalletId"));
在其中一个中使用WithMany(x => x.Requests)
(示例中为Destination
,也可能是Source
),但不在两者中。
在Wallet
中引入第二个集合,并将这两个集合分别映射到Source
和Destination
:
internal class Wallet : EntityFrameworkEntity
{
public Wallet()
{
this.SourceRequests = new List<FinancialRequest>();
this.DestinationRequests = new List<FinancialRequest>();
}
// ...
public virtual ICollection<FinancialRequest> SourceRequests { get; set; }
public virtual ICollection<FinancialRequest> DestinationRequests { get; set; }
}
映射:
// Remove the modelBuilder.Entity<Wallet>().HasMany(x => x.Requests) mapping
modelBuilder.Entity<FinancialRequest>()
.HasOptional(x => x.Destination)
.WithMany(x => x.DestinationRequests)
.Map(config => config.MapKey("DestinationWalletId"));
modelBuilder.Entity<FinancialRequest>()
.HasRequired(x => x.Source)
.WithMany(x => x.SourceRequests)
.Map(config => config.MapKey("SourceWalletId"));
顺便说一句:不应该 Source
和Destination
吗?如果是,您可以将HasOptional
替换为HasRequired
,但必须将WillCascadeOnDelete(false)
附加到两个映射中的至少一个以避免多个级联删除路径异常。