Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/apache/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 告诉DbContext不要添加整个对象图?_C#_Entity Framework 4.1_Ef Code First - Fatal编程技术网

C# 告诉DbContext不要添加整个对象图?

C# 告诉DbContext不要添加整个对象图?,c#,entity-framework-4.1,ef-code-first,C#,Entity Framework 4.1,Ef Code First,我的模型中有一个类可以引用来自两个不同FK关联/字段的同一个子类。创建父对象时,这两个引用都使用子对象的同一实例填充,然后,可以更新或更改两个子对象中的一个(这并不总是发生),并保留原始引用,因为从未接触过另一个子对象。我希望这是有道理的 当从具有两次引用的相同子对象的数据库中创建或提取父对象时,当您尝试将父对象添加到DbContext时,我们会看到可怕的错误:ObjectStateManager中已存在具有相同键的对象。ObjectStateManager无法跟踪具有相同键的多个对象。引发此问

我的模型中有一个类可以引用来自两个不同FK关联/字段的同一个子类。创建父对象时,这两个引用都使用子对象的同一实例填充,然后,可以更新或更改两个子对象中的一个(这并不总是发生),并保留原始引用,因为从未接触过另一个子对象。我希望这是有道理的

当从具有两次引用的相同子对象的数据库中创建或提取父对象时,当您尝试将父对象添加到DbContext时,我们会看到可怕的错误:
ObjectStateManager中已存在具有相同键的对象。ObjectStateManager无法跟踪具有相同键的多个对象。
引发此问题是因为DbContext正在尝试将整个对象图添加到其更改跟踪器,即指向同一子对象的两个子引用

我们不需要变更跟踪。我们不介意在数据库中抛出一个完全填充的UPDATE语句。有没有办法强迫DbContext不添加整个对象图,只添加我们告诉它要添加的单个实例?如果是这样的话,如果我们在全球范围内禁用它,我们将失去哪些功能

编辑:更新的代码示例

编辑2:更新了代码示例,以包含模拟web服务交互的序列化

[TestClass]
public class EntityFrameworkTests
{
  [TestMethod]
  public void ObjectGraphTest()
  {
    Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0");
    Database.SetInitializer(new DropCreateDatabaseAlways<MyDbContext>());

    string connectionString = String.Format("Data Source={0}\\EntityFrameworkTests.sdf", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
    MyDbContext context = new MyDbContext(connectionString);

    Child child = new Child() { ID = 1, SomeProperty = "test value" };
    //context.Entry<Child>(child).State = EntityState.Added;
    Parent parent = new Parent()
    {
      ID = 1,
      SomeProperty = "some value",
      OriginalChild = child,
      ChangeableChild = child
    };
    context.Entry<Parent>(parent).State = EntityState.Added;
    context.SaveChanges();

    context = new MyDbContext(connectionString);
    //parent = context.Set<Parent>().AsNoTracking().Include(p => p.OriginalChild).Include(p => p.ChangeableChild).FirstOrDefault();
    parent = context.Set<Parent>().Include(p => p.OriginalChild).Include(p => p.ChangeableChild).FirstOrDefault();

    // mimic receiving object via a web service
    SaveToStorage(parent);
    parent = GetSavedItem(1);

    parent.SomeProperty = "some new value";
    context = new MyDbContext(connectionString);
    context.Entry<Parent>(parent).State = EntityState.Modified; // error here
    context.SaveChanges();
  }
}
使用的类(为序列化而更新):

[DataContract]
内部类儿童
{
[数据成员]
公共int ID{get;set;}
[数据成员]
公共字符串SomeProperty{get;set;}
}
[数据合同]
内部类父级
{
[数据成员]
公共int ID{get;set;}
[数据成员]
公共字符串SomeProperty{get;set;}
[数据成员]
public int OriginalChildID{get;set;}
[数据成员]
公共子级原始子级{get;set;}
[数据成员]
public int ChangeableChildID{get;set;}
[数据成员]
公共子级可更改子级{get;set;}
}
内部类MyDbContext:DbContext
{
公共数据库集父项{get;set;}
公共DbSet子项{get;set;}
公共MyDbContext(字符串连接字符串)
:base(connectionString){}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove();
}
}
丑陋的解决方案:

Child originalChild = parent.OriginalChild;
Child changeableChild = parent.ChangeableChild;
parent.OriginalChild = null;
parent.ChangeableChild = null;

context.Entry<Parent>(parent).State = EntityState.Modified;
context.SaveChanges();

parent.OriginalChild = originalChild;
parent.ChangeableChild = changeableChild;

您必须首先从数据库加载父级(使用活动更改跟踪!),但另一方面,此处发出的更新命令将仅包含更改的属性。既然你说你不介意发送一个完整的更新命令(通过将状态设置为
Modified
),我想你没有性能问题。因此,加载原始文件,然后仅发送带有更改属性的小更新,可能不会比发送完整更新命令更糟糕或更糟糕。

是否尝试设置标量属性
OriginalChildID=myChild.ID
?显示的代码不会引发异常。在上下文中有两个指向同一对象的引用是合法的(
myChild
)。如果上下文中有具有相同键的不同对象,则会出现问题。但在示例代码中并非如此。您的示例中一定缺少一些重要的内容。这似乎是最简单的示例。我应该花点时间编写一个可重复的单元测试,只包含简单的类。真正的情况发生在我们的大型系统中,当父对象从数据库中取出时,包含两个子对象。更新父项上的单个字段,然后将父项添加回DbContext以保存到数据库中。将父对象添加回DbContext时,即发生错误时。我将编写一个可重复的单元测试并更新我的帖子。谢谢。更新了显示错误的新代码。我认为您必须在
GetSavedItem
之后创建一个新的上下文,以显示您遇到的实际问题。您的示例现在当然会抛出一个异常,因为旧的
父对象仍然在上下文中(父对象现在是重复的,所有子对象也是重复的)。但这并不是你所说的例外。我可能不得不重新设计我的存储库的核心,以使用你在这里概述的第二种方法。无论如何,我已经在脑子里反复思考了几个星期,如果它解决了这些问题……更改这行代码
context.Entry(parent.State=EntityState.Modified;//此处的错误
指向此
父源Parent=context.Set().FirstOrDefault()
context.Entry(originalParent).CurrentValues.SetValues(parent)修复问题中代码示例的问题。所以看起来我将重新设计我的存储库设计。谢谢
[DataContract]
internal class Child
{
  [DataMember]
  public int ID { get; set; }
  [DataMember]
  public string SomeProperty { get; set; }
}

[DataContract]
internal class Parent
{
  [DataMember]
  public int ID { get; set; }
  [DataMember]
  public string SomeProperty { get; set; }

  [DataMember]
  public int OriginalChildID { get; set; }
  [DataMember]
  public Child OriginalChild { get; set; }
  [DataMember]
  public int ChangeableChildID { get; set; }
  [DataMember]
  public Child ChangeableChild { get; set; }
}

internal class MyDbContext : DbContext
{
  public DbSet<Parent> Parents { get; set; }
  public DbSet<Child> Children { get; set; }

  public MyDbContext(string connectionString)
    : base(connectionString) { }

  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
  }
}
Child originalChild = parent.OriginalChild;
Child changeableChild = parent.ChangeableChild;
parent.OriginalChild = null;
parent.ChangeableChild = null;

context.Entry<Parent>(parent).State = EntityState.Modified;
context.SaveChanges();

parent.OriginalChild = originalChild;
parent.ChangeableChild = changeableChild;
var originalParent = context.Set<Parent>()
    .Where(p => p.ID == parent.ID)
    .FirstOrDefault();

context.Entry(originalParent).CurrentValues.SetValues(parent);
context.SaveChanges();