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<TModel>().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# 实体框架向来自外部库的实体添加新列的主要内容,如果未能解决你的问题,请参考以下文章