C# 实体框架向来自外部库的实体添加新列

Posted

技术标签:

【中文标题】C# 实体框架向来自外部库的实体添加新列【英文标题】:C# Entity Framework Adding New Column to Entity that's from external library 【发布时间】:2021-06-22 14:48:45 【问题描述】:

在我正在进行的一个项目中,我在尝试从外部 NuGet 包向实体添加属性时遇到了一些麻烦。

外部团队最初使用 EntityFramework 来创建他们的数据库,不久前我的团队最初使用它来创建我们的数据库,现在有两个独立的数据库,但最初的创建使用了通用的 NuGet 包。

在外部团队方面,他们根本没有更改表,但在我们方面,我们向数据库添加了新的列和属性,现在我们需要在 DBContext 中使用它。如何将这些新字段映射到实体,以便我可以访问和设置属性。我希望它受到保护,但由于它是公开的,我不能只覆盖 DbSet<Profile> Profile 调用。

外部包:

DataContext(扩展 DBContext 并具有public DbSet<Profile> Profile get;set; 的类) 配置文件(映射到数据库中“配置文件”表的实体)

由于无法修改 Profile 类,我该如何添加表中的新列?

我最初的方法是创建:

DataContextExt(扩展DataContext并添加public DbSet<ProfileExt> ProfileExt get;set;的类 ProfileExt(扩展 Profile 并具有不属于原始部分的附加字段的实体

这似乎让我走得最远,但由于 ProfileExt 扩展了 Profile,由于“鉴别器”列,我在使用它时遇到错误,因为它们在技术上都是同一个实体。

然后我尝试通过覆盖 OnModelCreating() 并将我的 ProfileExt 映射到 Profile 来删除 Profile,但同样失败了,它似乎根本没有更改模型构建器。

public class DataContextExt : DataContext


    public DbSet<ProfileExt> ProfileExt  get; set; 

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    
        base.OnModelCreating(modelBuilder);
        modelBuilder.Ignore<Profile>();
        modelBuilder.Entity<ProfileExt>().ToTable("Profile");
        Database.SetInitializer<ApplicationDbContext>(null);
    

有人对我接下来应该尝试什么有任何建议吗?

编辑: 目前,该项目旨在通过存储过程访问信息,然后我将其映射到我的 ProfileExt,但在保存时它被设计为使用

Entity = await DB.Set&lt;TModel&gt;().FindAsync(Key.Compile()(Model)).ConfigureAwait(false);

当模型到达这一点时,它是 ProfileExt 的实例

如果我尝试将 ProfileExt 通过(没有它自己的 DbSet)作为 Profile 传递,它会失败,说 ProfileExt 不在上下文中,如果我注册它(使用它自己的 DbSet)它会抛出鉴别器错误,因为它曾经是一个另一个实例。

【问题讨论】:

您是否尝试过添加阴影属性? @TanveerBadar 所以现在它通过映射到 ProfileExt 模型的 SP 检索表的信息。但是,为了将值保存回数据库,它使用 DB.Set 函数失败,因为 ProfileExt 不是模型的一部分(如果我没有向 DbSet 注册它)或因为 Discriminator 列。 (查看更新后的帖子) 【参考方案1】:

从您正在使用某个第三方提供的库和初始架构的情况来看。您没有与该团队共享代码修改,但您已经在此 Profile 表的副本中更改了他们的部分架构。

为什么不让两个 DbContext 完全分离,而不是尝试继承来覆盖?

一种选择是 将列添加到您没有扩展所有权的表/实体中。将您的自定义列移动到类似于 MasterProfile 表的东西,该表共享一个 ProfileId,因为它是 PK。从那里您可以声明与 Profile 具有一对一关系的 MasterProfile 实体。

public class MasterProfile

    [Key]
    public int ProfileId  get; set; 
    // add custom columns here...

    public virtual Profile Profile  get; set; 

然后配置关系:

modelBuilder.Entity<MasterProfile>()
    .HasRquired(x => x.Profile)
    .WithOptional();

通过这种方式,您可以在 DbContext 中读取自定义对象以及 Profile,而无需对架构进行重大更改。

您可以探索的另一个选项是为您的 DbContext 定义您自己的 Profile 实体定义,同时重用来自第 3 方的其他实体声明。

例如:给定一个定义以下类的第 3 方库:

3rdParty.User 3rdParty.Profile 3rdParty.TableA 3rdParty.TableB 3rdParty.TableC

它们由 3rdParty.DataContext 访问

我可以定义一个不需要扩展 3rdParty.DataContext 的 MyApp.DataContext。该 DbContext 可以引用声明为的 Profiles 集合:

MyApp.Profile

其中包含来自我们的配置文件表的全部属性。如果您不必担心对 3rdParty.Profile 的引用,您不需要为每个表创建自定义实体,您可以在 MyApp.DataContext 中引用 3rdParty.TableA 等。

public class DataContext : DbContext

    public DbSet<Profile> Profiles  get; set; 
    public DbSet<3rdParty.TableA> TableAs  get; set; 
    public DbSet<3rdParty.TableB> TableBs  get; set; 
    public DbSet<3rdParty.TableC> TableCs  get; set; 

要注意的是,这仅在您定义的类未被许多其他实体引用时才有效。我们包含在 DbContext 中的每个 3rdParty 实体定义都不能再引用 3rdParty.Profile,因为我们的 DbContext 不能将两个实体映射到同一个表。

例如,如果 Profile 引用了一个用户,这不是问题,因为 MyApp.Profile 可以引用 3rdParty.User,但是如果 3rdParty.User 有一个引用,例如:

public virtual ICollection<Profiles>  get; set;  

这将指向 3rdParty 程序集中的配置文件,这是一个交易破坏者。我们还需要重新创建一个 MyApp.User。如果需要重新声明像 User 这样的东西并且该类被大多数其他实体引用,这可能会级联。 (即public virtual User CreatedBy get; set;

这可能是一个值得探索的选择。

【讨论】:

以上是关于C# 实体框架向来自外部库的实体添加新列的主要内容,如果未能解决你的问题,请参考以下文章

向实体框架模型添加键以满足需要键?

实体框架不更新位数据类型

如何向现有的 Azure 表存储添加新列

使用通过 React GUI 触发的实体框架向 DB 添加新条目时出现重复条目

核心数据编码模式

使用实体框架添加唯一数据