【问题标题】:How am I able to use a generic Navigation property in a Entity Framework Model?如何在实体框架模型中使用通用导航属性?
【发布时间】:2021-12-08 15:43:51
【问题描述】:

我在运行时创建了 2 个 poco 类型(使用反射)。这两个应该互相引用。这些 pocos 的后续实例可以使用实体框架存储在数据库中。

目前我面临两个问题:

  1. 这是不可能的,或者至少我不知道如何编写这种双向引用(因为在描述 Poco 时,另一个 poco 的类型不存在)。

2.由于我没有找到问题 1 的答案,所以我决定使用 object 作为引用的类型。所以模型现在包含以下行:

$ 公共对象 Poco1 {get;放;} 并且:

public object Poco2 {get; set;}

对象的使用现在让我面临另一个问题。因为,在 OnModelCreating 期间会引发异常,该对象需要包含一个 id。

据我所知,这意味着 ef core 认为,“对象”将是模型的类型,应该被引用。

有人知道我可以如何做我想做的事吗?

谢谢:)

【问题讨论】:

  • 如果它们不符合已知的类定义,以后如何存储到数据库中?不同的表不只是随意地相互引用。 (至少在任何不承诺为每个查询运行表扫描的(非)关系数据库中)

标签: c# .net entity-framework entity-framework-core ef-code-first


【解决方案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;
}

对于可维护性差的大型项目而言,过多的引用是危险的

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-19
    相关资源
    最近更新 更多