将多个属性链接到Entity Framework中的同一表

时间:2018-07-03 15:47:45

标签: c# .net asp.net-mvc entity-framework .net-core

通过说这是我对Entity Framework和关系数据库的第一次实际经验来概括一下。如果我做错了,请告诉我。

我希望我的数据的结构如下(减少“额外”代码):

Indicators {
    int SomeText1TranslationRef
    List<Translation> SomeText1Translations
    int SomeText2TranslationRef
    List<Translation> SomeText2Translations
}

Measures {
    int SomeText3TranslationRef
    List<Translation> SomeText3Translations
    int SomeText3TranslationRef
    List<Translation> SomeText4Translations
}

Translation {
    Int TranslationID
    String LanguageCode
    String Text
}

因此,从本质上讲,指标表将具有SomeText1 Translations和SomeText2的列表,所有这些列表均使用TranslationID通过“ Ref”属性进行连接。

我的翻译属性带有[ForeignKey("....Ref")]注释。

我希望它能够像框架的其余部分一样神奇地工作,但是转换表将获得名为“ SomeText1TranslationRef”和“ SomeText2TranslationRef”的列。

我做错了吗?

我正在查看Entity Framework的其他功能,并看到“ InverseProperty”的注释。这可能有帮助吗?

3 个答案:

答案 0 :(得分:1)

我对您的目标还不是100%清楚,但是如果一个指标可以具有许多Text1翻译和许多Text2翻译,那么这就是2个多对多关系。措施也一样。 EF为此需要一个连接/桥/连接表(IndicatorTranslation和MeasureTranslation)。您可以显式创建此表,或者让EF在后台进行操作:

Indicator {
    // other indicator fields
    public virtual List<Translation> SomeText1Translations
    public virtual List<Translation> SomeText2Translations
}

Measure {
    // other measure fields
    public virtual List<Translation> SomeText3Translations
    public virtual List<Translation> SomeText4Translations
}

Translation {
    Int TranslationID
    String LanguageCode
    String Text

    // Use inverse attributes or fluent code to tell EF how to connect relationships
    [InverseProperty("SomeText1Translations")]
    public virtual ICollection<Indicator> TranslationForIndicatorText1 { get; set; }
    [InverseProperty("SomeText2Translations")]
    public virtual ICollection<Indicator> TranslationForIndicatorText2 { get; set; }
    [InverseProperty("SomeText3Translations")]
    public virtual ICollection<Measure> TranslationForMeasureText3 { get; set; }
    [InverseProperty("SomeText4Translations")]
    public virtual ICollection<Measure> TranslationForMeasureText4 { get; set; }
}

答案 1 :(得分:0)

如果我错了,我很高兴得到纠正,因为我已经尝试了很长时间,但我仍然知道,EF仍然无法从类型的一个属性创建关系转换成两种其他类型,反之亦然,即使有约束使其有效。

在您的情况下,最终需要翻译上具有4个导航属性。 (int IndicatorRef1,int IndicatorRef2,int MeasureRef3,int MeasureRef4)。大多数人不会将其称为梦想中的情景。

几年前,我问了一个类似的问题,从那以后得出的结论是,我试图让EF解决我所有的问题是愚蠢的。

因此,这是您要达到的目标的答案,甚至可以解决您的两个问题:

不要依赖EF处理任何情况。实际上,除了1-1、1- *或*-*以外,几乎完全不依赖EF来处理关系。还有一些继承形式。

在大多数其他情况下,最终会为您要引用的每种类型提供一个导航属性,并且为每个导航属性填充数据为null,但会专门针对该数据。

好消息?您不必依靠EF。 EF的主要优点是生产率。在某些情况下,仍然值得利用EF,但要提供自己的生产率方法。如果您希望获得一套基于引用的包含2种翻译的指标集,只需创建一个提供该指标的方法即可。

类似

        public IQueryable<Indicators> SetOfIndicatorsWithTranslations()
        {
            // Untested query that might need some fixing in an actual implementation 
            return ctx.Set<Indicators>().Select(ind => new Indicators() {
                                    Text1Ref= ind.Text1Ref, // whatever the property is 
                                    Text1RefList = ctx.Set<Translation>().Where(t => t.TranslationId == ind.Text1Ref),  
                                    Text2Ref= ind.Text2Ref,  
                                    Text2RefList = ctx.Set<Translation>().Where(t => t.TranslationId == ind.Text2Ref),  
                                });
        }

现在,这是EF会为您优雅地处理的查询。

对于类似的东西,当然有很多优雅的解决方案。重要的是,有时候值得自己动手做,而不是将自己局限于所选工具的功能。 (嗯,至少那是我最终学到的重要部分:))

答案 2 :(得分:0)

长话短说,警告是.Net Core的“核心”部分。 EF Core不支持<约定>配置(见here)。

实现此目的的唯一方法是按照Steve的建议手动创建联结表。这是所有需要的信息:https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration

  

在先前版本的Entity Framework中,此模型定义足以使EF暗示正确的关系类型并为其生成连接表。在EF Core 1.1.0中,有必要在模型中包括一个实体来表示联接表,然后将导航属性添加到指向联接实体的多对多关系的任一侧:

上面的链接很可能会随时间更新,因此出于上下文的目的,以下是附带的代码:

public class Book
{
    public int BookId { get; set; }
    public string Title { get; set; }
    public Author Author { get; set; }
    public ICollection<BookCategory> BookCategories { get; set; }
} 

public class Category
{
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public ICollection<BookCategory> BookCategories { get; set; }
}

public class BookCategory
{
    public int BookId { get; set; }
    public Book Book { get; set; }
    public int CategoryId { get; set; }
    public Category Category { get; set; }
}

或者,使用Fluent:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<BookCategory>()
        .HasKey(bc => new { bc.BookId, bc.CategoryId });

    modelBuilder.Entity<BookCategory>()
        .HasOne(bc => bc.Book)
        .WithMany(b => b.BookCategories)
        .HasForeignKey(bc => bc.BookId);

    modelBuilder.Entity<BookCategory>()
        .HasOne(bc => bc.Category)
        .WithMany(c => c.BookCategories)
        .HasForeignKey(bc => bc.CategoryId);
}