简体   繁体   English

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

[英]Linking multiple properties to the same table in Entity Framework

Let me foreword this by saying this is my first real experience with both Entity Framework and relational databases in general. 首先,我说这是我对实体框架和一般关系数据库的第一次实际经验。 If I am doing it completely wrong, please tell me. 如果我做的完全错误,请告诉我。

I want my data structured as something like this (Cut down on the "extra" code): 我希望我的数据的结构如下(减少“额外”代码):

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
}

So in essence, the indicators table would have a list of SomeText1 Translations as well as SomeText2, all joined using the TranslationID through the "Ref" properties. 因此,从本质上讲,指标表将具有SomeText1 Translations和SomeText2的列表,所有这些列表均使用TranslationID通过“ Ref”属性进行连接。

I have the translation properties annotated with [ForeignKey("....Ref")] . [ForeignKey("....Ref")]注释的翻译属性。

I expected this to work magically as the rest of the framework seems to, but instead the translation table gets columns named "SomeText1TranslationRef" and "SomeText2TranslationRef". 我希望它可以像框架的其余部分一样神奇地工作,但是转换表将获得名为“ SomeText1TranslationRef”和“ SomeText2TranslationRef”的列。

Am I doing this wrong? 我做错了吗?

I am looking at other features of Entity Framework and see an annotation for "InverseProperty". 我正在查看Entity Framework的其他功能,并看到“ InverseProperty”的注释。 Is it something which may help? 这可能有帮助吗?

I'm not 100% clear on your goal, but if an Indicator can have many Text1 translations and many Text2 translations, then that is 2 many-to-many relationships. 我不确定您的目标是否100%明确,但是如果一个指标可以具有许多Text1翻译和许多Text2翻译,那么这就是2个多对多关系。 Same for Measures. 措施也一样。 EF will need a join/bridge/junction table for this (IndicatorTranslation and MeasureTranslation). EF为此需要一个连接/桥/连接表(IndicatorTranslation和MeasureTranslation)。 You can explicitly create this table, or let EF do it behind the scenes: 您可以显式创建此表,或者让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; }
}

I'm happy to be corrected if I'm wrong since it's nothing I've tried for quite a while, but as far as I'm aware, EF is still not able to create relationships from one property on a type to two different other types, or vice versa, even with constraints that would make it valid. 如果我弄错了,我很高兴得到纠正,因为我已经尝试了很长时间,但我仍然知道,EF仍然无法创建从类型上的一个属性到两个不同的属性的关系。其他类型,反之亦然,即使有约束使其有效。

In your case, you would end up with the 4 navigation properties being required on your translation. 在您的情况下,最终需要翻译上具有4个导航属性。 (int IndicatorRef1, int IndicatorRef2, int MeasureRef3, int MeasureRef4). (int IndicatorRef1,int IndicatorRef2,int MeasureRef3,int MeasureRef4)。 Most wouldn't call it a dream scenario. 大多数人不会将其称为梦想中的场景。

I asked a similar question a couple of years ago, and have since then sort of concluded that i was foolish trying to get EF to solve all my problems. 几年前,我提出了类似的问题,从那以后的结论是,我试图让EF解决我所有的问题是愚蠢的。

So here's an answer to what you're trying to achieve, and perhaps even a solution to 2 of your questions: 因此,这是您要达到的目标的答案,甚至可以解决您的两个问题:

Don't rely on EF handle any scenario. 不要依靠EF处理任何情况。 Actually, pretty much don't rely on EF to handle relationships at all other than 1-1, 1-* or *-*. 实际上,除了1-1、1- *或*-*以外,几乎完全不依赖EF来处理关系。 And some forms of inheritance. 以及某些形式的继承。

In most other cases, you will end up with one navigation property for each type you're trying to reference, with data being populated with nulls for each navigation property but the one specifically targeted. 在大多数其他情况下,最终将为您要引用的每种类型提供一个导航属性,并为每个导航属性填充空值,但专门针对该数据。

The good news? 好消息? You don't have to rely on EF for it. 您不必依靠EF。 The main advantage of EF is it's productivity. EF的主要优点是生产率。 For certain cases, it's still worth leveraging EF, but providing your own methods of productivity. 在某些情况下,仍然值得利用EF,但要提供自己的生产率方法。 If you want to get a set of indicators with 2 collections of translations based on a ref, simply create a method that provides it. 如果您想获得一个基于带有引用的2个翻译集合的指标集,只需创建一个提供该指标的方法即可。

Something like 就像是

        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),  
                                });
        }

Now that's a query EF will handle for you gracefully. 现在,这是EF会为您优雅地处理的查询。

There are of course a lot more elegant solutions to something like it. 对于类似的东西,当然有很多更优雅的解决方案。 The important part is really that it's sometimes worth doing it yourself rather than restricting yourself to the capabilities of your tool of choice. 重要的是,有时候值得自己动手做,而不是将自己局限于所选工具的功能。 (Well, at least that's the important part that I eventually learned :) ) (嗯,至少那是我最终学到的重要部分:)

Long story short, the caveat is the "Core" part of .Net Core. 长话短说,警告是.Net Core的“核心”部分。 EF Core does not support convention-over-configuration many-to-many relationships yet (See here ). EF核心不支持约定优于配置许多一对多的关系,但 (见这里 )。

The only way to achieve this is to manually create the junction tables as Steve suggested. 实现此目的的唯一方法是按照Steve的建议手动创建联结表。 Here is all the information needed: https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration 这是所有需要的信息: https : //www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration

In previous versions of Entity Framework, this model definition was sufficient for EF to imply the correct type of relationship and to generate the join table for it. 在Entity Framework的早期版本中,该模型定义足以使EF暗示正确的关系类型并为其生成连接表。 In EF Core 1.1.0, it is necessary to include an entity in the model to represent the join table, and then add navigation properties to either side of the many-to-many relations that point to the join entity instead: 在EF Core 1.1.0中,必须在模型中包括一个实体来表示联接表,然后将导航属性添加到指向联接实体的多对多关系的任一侧:

The above link will most likely be updated with time so for context purposes, here is the code which goes along with it: 上面的链接很可能会随时间更新,因此出于上下文的考虑,以下是与之配套的代码:

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; }
}

Alternatively, using Fluent: 或者,使用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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM