实体框架-一对多关系-仅映射到一个元素

时间:2019-02-08 14:16:38

标签: c# sql .net entity-framework entity-framework-6

我的示例如下: 我有一个[Table1]与[Table2]有一对多的关系。

让它看起来像:

[Table1]:
    [Id] int

[Table2]:
    [Table1Id] int (foreign key to [Table1])
    [UniqueColumnAtTable2ForGivenTable1Id] int

[Table2]确实具有[Table1]的外键,并且[Table2]中可能有多个与[Table1]相关的元素。

我知道如何通过代码定义[Table1]和[Table2]之间的映射,意思是:

public class Table1
{
    public int Id { get; set; }
    public virtual ICollection<Table2> Table2Objects { get; set; }
}
public class Table1_Mapping : EntityTypeConfiguration<Table1>
{
    public class Table1()
    {
        this.HasKey(x => x.Id);
        this.HasMany(x => x.Table2Objects).WithOptional().HasForeignKey(x => x.Table1);
    }
}

但是有一种方法可以定义实体框架映射,在该映射中,我将指定一对多映射,但是只能映射到[Table2]中的一个特定行]?我确实在[Table2]上有 [UniqueColumnAtTable2ForGivenTable1Id] 列,该列对于给定的[Table1]始终是唯一的-因此,我知道运行类似这样的内容:

SELECT * FROM [Table2] WHERE [Table1Id] = 123 AND [UniqueColumnAtTable2ForGivenTable1Id] = 456

将始终只查询一行。

从C#代码中我需要的是这样的

public class Table1
{
    public int Id { get; set; }
    public virtual ICollection<Table2> Table2Objects { get; set; }
    public virtual Table2 Table2Object456 { get; set; }
}
public class Table1_Mapping : EntityTypeConfiguration<Table1>
{
    public class Table1()
    {
        this.HasKey(x => x.Id);
        this.HasMany(x => x.Table2Objects).WithOptional().HasForeignKey(x => x.Table1);
        this.MagicMethod(x => x.Table2Object456).WithOptional().HasForeignKey(x => x.Table1).OtherMagicMethodWhichAllowsToPutFilter(x => x.UniqueColumnAtTable2ForGivenTable1Id == 456);
    }
}

当然我需要这个片段:

this.MagicMethod(x => x.Table2Object456).WithOptional().HasForeignKey(x => x.Table1).OtherMagicMethodWhichAllowsToPutFilter(x => x.UniqueColumnAtTable2ForGivenTable1Id == 456);

2 个答案:

答案 0 :(得分:0)

只是了解您的实际需求。

我认为您想要这样的东西:

public class User {
    public int Id { get; set; }
    public int PrimaryAddressId { get; set;}

    public virtual Address PrimaryAddress { get; set; }
    public virtual ICollection<Address> Addresses { get; set; }
}

EF应该自动建立PrimaryAddressId和导航属性之间的关系。

您真的不想要那种神奇的配置东西...除非您始终要与PrimaryAddress拥有相同的地址,否则您可以将其作为默认值来使用...但这似乎是一种代码味道可能不想这样做?

答案 1 :(得分:0)

我认为,选项不是这样做的,而是在存储库中创建一个处理这种特殊情况的方法:

public class YourRepository
{
    public Table2 Table2WithUniqueColumn456(Table1 table1)
    {
        using var context = new Table1Context();
        return context.Entry(table1)
                      .Collection(t1 => t1.Table2Objects)
                      .Query()
                      .FirstOrDefault(t2 => t2.UniqueColumnAtTable2ForGivenTable1Id == 456);
    }
}

现在,您每次需要获取一个等于456的Table2时,都可以显式调用此方法。

如果您要好奇,那么的一种选择是利用继承:

public class Table1
{
    public int Id { get; set; }
    public virtual ICollection<Table2> Table2Objects { get; set; }
    public virtual Table2WithUniqueColumn456 Table2Object456 { get; set; }
}

[Table("Table2")]
public class Table2
{
    public int Id { get; set; }
    public int Table1Id { get; set; }
    public int UniqueColumnAtTable2ForGivenTable1Id { get; set; }
}

[Table("Table2")] // this will tell EF to use table-per-hierarchy
public class Table2WithUniqueColumn456 : Table2
{
}

public class Table1_Mapping : EntityTypeConfiguration<Table1>
{
    public class Table1()
    {
        this.HasKey(x => x.Id);
        this.HasMany(x => x.Table2Objects).WithOptional().HasForeignKey(x => x.Table1);
        this.HasOne(x => x.Table2Object456).WithOptional().HasForeignKey(x => x.Table1);
    }
}

如您所见:

  • 您需要处理创建逻辑,并确保所有带有UniqueColumnAtTable2ForGivenTable1Id 456的对象都被创建为Table2WithUniqueColumn456
  • UniqueColumnAtTable2ForGivenTable1Id应该是不变的,否则可能带来更复杂的编辑逻辑
  • 它将在Table2
  • 中创建其他列

也许,如果每个Table2中没有那么多Table1个对象,即使它不是那么有效,它也可能比继承更好。

public class Table1
{
    public int Id { get; set; }

    public virtual ICollection<Table2> Table2Objects { get; set; }

    [NotMapped]
    public virtual Table2WithUniqueColumn456 Table2Object456 
    {
        get { return Table2Objects.FirstOrDefault(x => x.UniqueColumnAtTable2ForGivenTable1Id == 456; }
    }
}