【问题标题】:one-to-many relation using two columns in Entity Framework Core在 Entity Framework Core 中使用两列的一对多关系
【发布时间】:2018-10-08 02:50:47
【问题描述】:

在我的项目中,我有一个表格Translation,可以翻译任何模型。为此,该表有两个字段:ModelModelIdModel 属性包含一个指示模型类型的整数,ModelId 具有此模型的 id。
因此,例如:Product 表的模型类型 ID 为 1。要获取 ID 为 317 的产品的所有翻译,我会搜索 Model=1 AND ModelId=317 的翻译。

现在我想在 Entity Framework Core 中创建这个关系。我所有的模型都继承自 BaseModel 类,该类有一个属性 ModelType 保存模型类型的 id。该字段未映射,因此在数据库中不可用。

我尝试使用 fluent api 创建关系,但它不允许我指定更多要过滤的列。

modelBuilder.Entity<BaseModel>()
    .HasMany<Translation>(bm => bm.Translations)
    // Extra filters

有没有什么方法可以创建这种关系,而不必为每个需要翻译的查询手动创建连接?

【问题讨论】:

  • 我不知道任何允许您开箱即用的 EF 功能。请注意,要使其正常工作,您的数据库必须先天地放弃对 ID 列的 FK 约束;这使得夜间无法即时查询(或更新)正确的数据。这将成为一个两步过程,因为表名(第一步)将影响 ID 查找逻辑(第二步)。我建议编写自定义DbContext,或自定义Repository,为您填充必要的导航属性。
  • modelBuilder.Entity&lt;BaseModel&gt;() 将使您的BaseModel 实体 EF 将使用TPH inheritance strategy 映射它。换句话说,到一个单个数据库表。我猜这不是你想要的?

标签: c# sql-server .net-core entity-framework-core


【解决方案1】:

由于modelBuilder.Entity&lt;BaseModel&gt;() 将使用TPH 继承方法,我假设您没有使用EF 代码优先方法来创建数据库,而是使用它将模型映射到现有数据库。然后你可以尝试这样的事情:

型号:

public class Translation
{
    public int Id { get; set; }
    public int Model { get; set; }
    public int ModelId { get; set; }
}

public class BaseModel
{
    public BaseModel(int modelType)
    {
        ModelType = modelType;
    }
    public int Id { get; set; }
    public int ModelType { get; set; }

    public ICollection<Translation> Translations { get; set; }// only for internal use
    public IEnumerable<Translation> ModelTypeTranslations
    {
        get
        {
            return this.Translations.Where(t => t.Model == this.ModelType);
        }
    }

}

public class SomeModel : BaseModel
{
    public SomeModel() : base(1) { }
    public int SomeProperty { get; set; }
}

public class AnotherModel : BaseModel
{
    public AnotherModel() : base(2) { }
    public int AnotherProperty { get; set; }
}

DbContext:

public class MyDbContext: DbContext
{
    ...

    public DbSet<Translation> Translations { get; set; }
    public DbSet<SomeModel> SomeModels { get; set; }
    public DbSet<AnotherModel> AnotherModels { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        ...
        modelBuilder.Entity<Translation>().HasKey(e => e.Id);

        var baseModelTypes = typeof(BaseModel).Assembly.GetExportedTypes()
            .Where(t => typeof(BaseModel).IsAssignableFrom(t) && t != typeof(BaseModel)).ToList();

        foreach (var type in baseModelTypes)
        {
            modelBuilder.Entity<Translation>().HasOne(type).WithMany(nameof(BaseModel.Translations)).HasForeignKey(nameof(Translation.ModelId));

            modelBuilder.Entity(type).Ignore(nameof(BaseModel.ModelType));
            modelBuilder.Entity(type).Ignore(nameof(BaseModel.ModelTypeTranslations));
            modelBuilder.Entity(type).HasKey(nameof(BaseModel.Id));
        }
    }
}

如您所见,您可以使用ModelTypeTranslations 仅获取当前模型类型的翻译。

我应该注意这种方法可能存在性能问题,因为它仅在内存中通过ModelType 过滤Translations。我还尝试通过使用lazy loading 来避免在内存中进行过滤,但我得到了some exception,即使我只是安装了该软件包而没有调用optionsBuilder.UseLazyLoadingProxies()。我希望它会在下一个版本中得到修复。

【讨论】:

    【解决方案2】:

    是的,有。使用这个:

    modelBuilder.Entity<BaseModel>()
    .HasMany(bm => bm.Translations)
    .WithForeignKey(x => new { x.Key1, x.Key2 });
    

    Key1 和 Key2 显然需要成为关系的键,您可能还需要为 Translation 实体定义它们(以相同的顺序)。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-12
    • 2017-02-17
    • 2019-08-07
    • 2016-12-18
    • 1970-01-01
    • 1970-01-01
    • 2017-02-27
    • 1970-01-01
    相关资源
    最近更新 更多