【问题标题】:Linking multiple properties to the same table in Entity Framework将多个属性链接到实体框架中的同一个表
【发布时间】:2018-12-12 00:06:57
【问题描述】:

让我先说这是我对实体框架和一般关系数据库的第一次真实体验。如果我完全错了,请告诉我。

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

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”的注释。有什么帮助吗?

【问题讨论】:

  • 你能发布你的实际代码吗?
  • 当然。我避免这样做,因为我发现它更加不透明:gist.github.com/knyzorg/085509cc85dce0cd3f51c4ff6b8ffd34@Hintham
  • 我无法从您的代码中真正看出各种实体之间的关系是什么,但很可能您应用了错误的 ForeignKey 属性。它可以通过以下三种方式之一使用: - [ForeignKey(NavigationPropertyName)] 用于依赖实体中的外键引用属性 - [ForeignKey(ForeignKeyPropertyName)] 用于依赖实体中的导航属性 - [ForeignKey(ForeignKeyPropertyName)] 用于主体实体中的导航属性。
  • public int NameTranslationRefID { get; set; } [ForeignKey("NameTranslationRefID")] public ICollection&lt;Translation&gt; Names { get; set; } 不是第二种方式吗? @Hintham
  • 是的,确实如此。但就像我说的,我无法真正说出实体之间的关系,所以不确定它们是否被正确应用

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


【解决方案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, English, "My text in english"2, Spanish, "My text in spanish" 之类的记录。指标可能有1, My First Indicator2, My second indicator 之类的数据。在后台创建了一个表 (IndicatorTranslation),它的 IndicatorId 和 TranslationId 包含 1,1 和 1,2 以及 2,1 和 2,2。
  • 嗯。我会试一试。这是处理翻译的好方法吗?
  • 这是一个广泛的问题。你可以使用resource strings。如果您要使用数据库方法,您可以独立存储翻译,然后在控制器中检索所选语言所需的内容并将它们保存到视图模型。
  • 在 EF 术语中这是一个优雅的解决方案,但应该注意的是,EF Core 不支持“幕后”多对多表(但希望不记得了)。可能更糟糕的是,除了符合 EF 的功能之外,您最终还会得到 4 个 *-*-tables,这可能不是……最佳的。我不明白这个问题需要任何多对多表,所以我可能误解了这个问题。
【解决方案2】:

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

在您的情况下,您最终会得到翻译所需的 4 个导航属性。 (int IndicatorRef1,int IndicatorRef2,int MeasureRef3,int MeasureRef4)。大多数人不会称其为梦幻场景。

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

因此,这是您想要实现的目标的答案,甚至可能是您的 2 个问题的解决方案:

不要依赖 EF 处理任何场景。实际上,除了 1-1、1-* 或 *-* 之外,几乎不依赖 EF 来处理关系。以及某些形式的继承。

在大多数其他情况下,您最终会为您尝试引用的每种类型提供一个导航属性,其中每个导航属性的数据都填充了空值,但具体目标是一个。

好消息?您不必依赖 EF。 EF的主要优点是它的生产力。在某些情况下,仍然值得利用 EF,但提供您自己的生产力方法。如果您想获得一组指标,其中包含基于 ref 的 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 将优雅地为您处理一个查询。

对于类似的事情,当然有很多更优雅的解决方案。重要的是,有时值得自己做,而不是将自己限制在所选工具的功能上。 (嗯,至少这是我最终学到的重要部分:))

【讨论】:

  • 确实开始看起来在 EF 中不可行。我不想超越 EF 提供的服务,因为这是过去几周让我完成工作的唯一方法,而且我不想踢掉追随我的人。明天我将尝试史蒂夫的解决方案,否则我将尝试一种完全不同的方法。
【解决方案3】:

长话短说,需要注意的是 .Net Core 的“核心”部分。 EF Core 不支持约定优于配置的多对多关系(请参阅here)。

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

在实体框架的早期版本中,此模型定义足以让 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);
}

【讨论】:

    猜你喜欢
    • 2014-09-29
    • 2016-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-17
    • 1970-01-01
    • 2012-12-20
    相关资源
    最近更新 更多