如何在实体框架模型中使用通用导航属性?
Posted
技术标签:
【中文标题】如何在实体框架模型中使用通用导航属性?【英文标题】:How am I able to use a generic Navigation property in a Entity Framework Model? 【发布时间】:2021-12-08 15:43:51 【问题描述】:我在运行时创建了 2 个 poco 类型(使用反射)。这两个应该互相引用。这些 pocos 的后续实例可以使用实体框架存储在数据库中。
目前我面临两个问题:
-
这是不可能的,或者至少我不知道如何编写这种双向引用(因为在描述 Poco 时,另一个 poco 的类型不存在)。
2.由于我没有找到问题 1 的答案,所以我决定使用 object 作为引用的类型。所以模型现在包含以下行:
$ 公共对象 Poco1 get;放; 并且:
public object Poco2 get; set;
对象的使用现在让我面临另一个问题。因为,在 OnModelCreating 期间会引发异常,该对象需要包含一个 id。
据我所知,这意味着 ef core 认为,“对象”将是模型的类型,应该被引用。
有人知道我可以如何做我想做的事吗?
谢谢:)
【问题讨论】:
如果它们不符合已知的类定义,以后如何存储到数据库中?不同的表不只是随意地相互引用。 (至少在任何不承诺为每个查询运行表扫描的(非)关系数据库中) 【参考方案1】:每当我遇到涉及泛型和反射的问题时。我发现以通用方法解决问题的通用部分会更容易;
public class Parent<TChild> where TChild : class
public ICollection<TChild> Children get; set;
public class Child<TParent> where TParent : class
public TParent Parent get; set;
public void DefineRelationship<TParent, TChild>(ModelBuilder modelBuilder)
where TChild : Child<TParent>
where TParent : Parent<TChild>
modelBuilder.Entity<TParent>()
.HasMany(p => p.Children)
.WithOne(c => c.Parent);
现在您需要使用反射来调用具有正确类型的方法。
【讨论】:
非常感谢,太好了。祝你有美好的一天。【参考方案2】:您的实体应如下所示:
public class Poco1
public int Id get; private set;
...
public Poco2 Poco2 get; private set;
public void SetPoco2(Poco2 poco2)
Poco2 = poco2;
public class Poco2
public int Id get; private set;
...
public int Poco1Id get; private set;
public Poco1 Poco1 get; private set;
然后设置它们:
async Task SomeMethod()
var poco1 = new Poco1();
var poco2 = new Poco2();
poco1.SetPoco2(poco2);
//at first only poco1 has reference to poco2
//and poco2 does not have reference to poco1 yet
Debug.Assert(poco1.Poco2 == poco2);
Debug.Assert(poco2.Poco1 != poco1);
await _someRepository.AddAsync(poco1);
await _someRepository.SaveChnagesAsync();
//After saving changes EF core manages the primary keys and references
Debug.Assert(poco1.Poco2 == poco2);
Debug.Assert(poco2.Poco1 == poco1);
和模型构建器:
protected override void OnModelCreating(ModelBuilder modelBuilder)
var poco1Builder = modelBuilder.Entity<Poco1>();
poco1Builder.HasKey(x => x.Id);
poco1Builder
.HasOne(x => x.Poco2)
.WithOne(x => x.Poco1)
//Poco2 will have Poco1Id in db that will be used for reference
//Also Poco1Id does not have to be set manually, EF core takes care of that
.HasForeignKey<Poco2>(x => x.Poco1Id);
;
var poco12Builder = modelBuilder.Entity<Poco2>();
如果在保存到数据库之前需要参考 SetPoco2 可以修改为:
public void SetPoco2(Poco2 poco2)
Poco2 = poco2;
poco2.SetPoco1(this);
并在 Poco2 中设置方法:
public void SetPoco1(Poco1 poco1)
Poco1 = poco1;
对于可维护性差的大型项目而言,过多的引用是危险的
【讨论】:
以上是关于如何在实体框架模型中使用通用导航属性?的主要内容,如果未能解决你的问题,请参考以下文章
实体框架Linq查询:如何在多个导航属性上从何处选择并从第三个导航属性中选择
如何使用代码优先实体框架在 ASP.Net MVC3 中重新加载多对多导航属性