一对多具有来自同一类的两个属性

时间:2013-04-01 15:13:31

标签: entity-framework entity-framework-5

我有以下类,我真的想在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"));

如果我按照现在的方式保留此映射,我的数据库模式如下所示:

Wrong database schema

如果你仔细观察,你会发现有一个名为“Wallet_Id”的专栏并不适合那里。此列仅存在,因为Wallet类具有“请求”集合。 如果我删除列中的集合就会消失,但我需要这个集合!它表示类之间的重要关系。我不需要的是错误生成的数据库中的第3列。

有谁知道我怎么能避免这种情况?我在这里做错了什么?

1 个答案:

答案 0 :(得分:1)

导致冗余外键列Wallet_Id的问题是EF不知道Wallet.Requests集合是FinancialRequest.Source还是FinancialRequest.Destination的反向导航属性。因为它无法在两个EF之间做出决定,所以假定Wallet.Requests根本没有反向导航属性。结果是与第三个FK的第三个冗余的一对多关系。

基本上你有三个选择:

  • 删除Wallet.Requests集合,第三个关系将消失(正如您已经注意到的那样)。但你不希望这样。

  • 如果Wallet.RequestsSource 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中引入第二个集合,并将这两个集合分别映射到SourceDestination

    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"));
    

顺便说一句:不应该 SourceDestination吗?如果是,您可以将HasOptional替换为HasRequired,但必须将WillCascadeOnDelete(false)附加到两个映射中的至少一个以避免多个级联删除路径异常。