【问题标题】:EF: Create/Remove relation from Many-to-Many relations when `AutoDetectChangesEnabled` and `ProxyCreationEnabled` are disabled on DbContextEF:在 DbContext 上禁用“AutoDetectChangesEnabled”和“ProxyCreationEnabled”时从多对多关系创建/删除关系
【发布时间】:2016-09-26 17:01:44
【问题描述】:
  1. 知道Foo.IdBar.Id 如何在不从数据库加载实体的情况下创建它们的关系。

    class Foo {
        public int Id { get; set; }
        public Lst<Bar> Bars { get; set; }
    }
    
    class Bar {
        public int Id { get; set; }
        public Lst<Foo> Foos { get; set; }
    }
    

    DbContext 构造函数中也禁用了此配置:

    Configuration.AutoDetectChangesEnabled = false;
    Configuration.ProxyCreationEnabled = false;
    Configuration.LazyLoadingEnabled = false;
    
  2. 以及如何消除这种关系?


例子:

using (var ctx = new DbCtx())
{
    ctx.Configuration.LazyLoadingEnabled = false;
    ctx.Configuration.ProxyCreationEnabled = false;
    ctx.Configuration.AutoDetectChangesEnabled = false;
    ctx.Database.Log += Console.WriteLine;

    var foo = new Foo {Id = 1, Bars = new List<Bar>() };
    var bar = new Bar { Id = 3, Foos = new List<Foo>() };

    // This approach wont work, as AutoDetectChanges are disabled
    ctx.Foos.Attach(foo);
    ctx.Bars.Attach(bar);

    foo.Bars.Add(bar);
    ctx.SaveChanges();
}

如何在不更改配置的情况下在这里定义关系。

提前谢谢你。

【问题讨论】:

  • 您的问题不清楚?你能提供更多信息吗?
  • @Sampath,我已经用我想避免的示例更新了这个问题,因为它对数据库提出了额外的请求。

标签: c# entity-framework entity-framework-6


【解决方案1】:

好的,找到了解决办法,下面是辅助方法:

static void ChangeRelationship<T1, T2>(
    IObjectContextAdapter ctx, 
    T1 a, 
    T2 b, 
    Expression<Func<T1, object>> getNavigationProperty,
    EntityState state) where T1: class
{
    ctx
        .ObjectContext
        .ObjectStateManager
        .ChangeRelationshipState(
            a,
            b,
            getNavigationProperty,
            state
        );
}

并在我的问题示例中使用它:

using (var ctx = new DbCtx())
{
    ctx.Configuration.LazyLoadingEnabled = false;
    ctx.Configuration.ProxyCreationEnabled = false;
    ctx.Configuration.AutoDetectChangesEnabled = false;
    ctx.Database.Log += Console.WriteLine;

    var foo = new Foo {Id = 1, Bars = new List<Bar>()};
    var bar = new Bar { Id = 3, Foos = new List<Foo>() };

    ctx.Entry(foo).State = EntityState.Unchanged;
    ctx.Entry(bar).State = EntityState.Unchanged;

    // create
    ChangeRelationship(ctx, foo, bar, x => x.Bars, EntityState.Added);
    ctx.SaveChanges();

    // remove
    ChangeRelationship(ctx, foo, bar, x => x.Bars, EntityState.Deleted);
    ctx.SaveChanges();
}

【讨论】:

  • 在 StackOverflow 上有更多关于这些方面的答案。它完成了这项工作,但它是将持久性实现大量泄漏到应用程序逻辑中。不再可能简单地将对象添加到集合中。 IMO,当带有隐藏连接类的多对多模式导致问题时,您应该转向可见连接类。有了它,您可以简单地添加/删除FooBar 联结对象。或者,在保存之前只有一个 DetectChanges 调用有什么问题?
  • 其实是的,就是持久层,很多人在业务领域层使用DBContextSaveChanges,但是我发现这种方式不对。而且我个人发现显式状态更改更好更清晰,然后启用AutodetectChanges。最后,它的性能要好得多。
  • 这就是为什么我说应用程序逻辑,而不是业务逻辑。总有一个地方可以从DbSets 添加/删除对象。现在您必须始终牢记,在这种特定情况下您不能这样做。我的建议是使用可见的连接类来消除这种差异。就像我前几天读到的那样,假设该项目的下一个人是个白痴。
【解决方案2】:

如果我的理解正确,您想将 Bar 对象添加到现有的 Foo 实体而不查找 Foo 实体。

假设你有 Foo (id = 1) 已经存在。想向它添加新的 Bar (id = 100) 实体。

using (var context = new Context())
{
    var bar = new Bar() { Id = 100 };
    var foo = new Foo() { Id = 1 }; // Only ID is required

    context.Foos.Attach(foo);
    bar.Foos.Add(foo);

    context.Bars.Add(bar);
    context.SaveChanges();
}

【讨论】:

  • 正确,谢谢,但是当上下文禁用“AutoDetectChanges”和“ProxyCreation”时,“.Add (bar)”会检测到新关系吗?还有一部分,怎么删除关系?
  • 是的。即使使用禁用选项,dbcontext 也会完成这项工作。如需删除,请按照 Evan 的指示进行。应该管用。告诉我进展如何。
  • 不幸的是,这不起作用,因为 AutoDetectChanges 在上下文中被禁用。
【解决方案3】:

你问的是可能的。步骤如下:

(1) 首先创建两个仅指定 PK 的实体实例,并将其中一个(例如 foo)附加到上下文:

var foo = new Foo { Id = fooId };
var bar = new Bar { Id = barId };
ctx.Foos.Attach(foo);

(2) 将第二个实体集合设置为包含第一个实体的新列表(即“创建”关系):

bar.Foos = new List<Foo> { foo };

(3) 将第二个实体标记如下:

(A) 添加关系:

ctx.Entry(bar).State = EntityState.Added;

(B) 删除关系:

ctx.Entry(bar).State = EntityState.Deleted;

(4) 将第二个实体标记为未更改:

ctx.Entry(bar).State = EntityState.Unchanged;

就是这样!

一旦您调用ctx.SaveChanges();,该关系就会从联结表中添加或删除。

更新:虽然上述方法有效(实际上,如果我们在末尾显式调用DbContext.ChangeTracker.DetectChanges(),我将第二个实体与“原始”集合附加然后模拟修改的原始解决方案也有效),我应该承认,您找到的ObjectContext 解决方案看起来更自然(奇怪的是,此类功能没有通过DbContext 公开),所以我个人的投票就在那里。

【讨论】:

  • 谢谢伊万,我做了一个例子,这不起作用,因为前提条件是,AutoDetectChanges 已关闭。这就是为什么只调用,Add(foo) 不会检测到新关系。有没有其他方法可以做到这一点?
  • 你确定你在第一个Attach之前设置了ctx.Configuration.AutoDetectChangesEnabled = false;吗?当我评论这条线时,你的例子很完美。我正在使用 EF 6.0,但我认为版本并不重要,因为关于 AutoDetectChangesEnabled 的整个想法是消除对实体和集合的任何副作用。所以当我们执行bar.Foos.Add(foo) 时,它应该只执行通常的 IList 操作,没有 EF 混合。
  • @tenbits 请原谅我,忽略我之前的评论。我很确定我确实按要求设置了它,但在某些时候看起来我不小心删除了它。我很确定有一个解决方案,恢复时会更新答案。
  • 是的,它发生了)但无论如何,感谢您的帮助,也感谢您的时间。这里的解决方案是直接使用ObjectStateManager,看我的回答。干杯,亚历克斯
  • @tenbits 是的,很有趣。老好 ObjectContext 东西 :) 好吧,我已经发布了我的 DbContext 小技巧,以防你感兴趣。干杯。
猜你喜欢
  • 1970-01-01
  • 2016-04-11
  • 2021-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-17
  • 1970-01-01
  • 2014-01-10
相关资源
最近更新 更多