entity-framework - 首先使用实体​​框架代码保存单个对象

标签 entity-framework entity-framework-4.1 entity-framework-4.3

我在项目中使用 Entity Framework 4.3.1,首先使用代码和 DbContext API。我的应用程序是一个 n 层应用程序,其中断开连接的对象可能来自客户端。我正在使用 SQL Server 2008 R2,但很快将迁移到 SQL Azure。我遇到了一个我似乎无法解决的问题。

想象一下我有几节课:

class A {
    // Random stuff here
}
class B {
    // Random stuff here
    public A MyA { get; set; }
}
class C {
    // Random stuff here
    public A MyA { get; set; }
}

默认情况下,EF 对对象图进行操作。例如,如果我有一个封装了 A 实例的 B 实例,我调用 myDbSet.Add(myB); ,它还会将 A 的实例标记为正在添加(假设它尚未被跟踪)。

我的应用程序中有一个场景,我需要明确哪些对象被持久化到数据库中,而不是让它跟踪整个对象图。操作顺序如下:
A myA = new A(); // Represents something already in DB that doesn't need to be udpated.
C myC = new C() { // Represents something already in DB that DOES need to be updated.
    A = myA;
}
B myB0 = new B() { // Not yet in DB.
    A = myA;
}
B myB1 = new B() { // Not yet in DB.
    A = myA;
}

myDbSetC.Attach(myC);
context.Entry(myC).State = Modified;

myDbSetB.Add(myB0); // Tries to track myA with a state of Added
myDbSetB.Add(myB1);

context.SaveChanges();

此时我收到一条错误消息 AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.我相信会发生这种情况,因为在 myB0 上调用 add 会将 A 的实例标记为已添加,这与已跟踪的 A 实例冲突。

理想情况下,我可以做一些类似电话 myDbSet.AddOnly(myB) 的事情。 ,但显然我们没有这个选项。

我尝试了几种解决方法:

尝试#1:
首先,我尝试创建一个辅助方法来防止 myA 被第二次添加。
private void MarkGraphAsUnchanged<TEntity>(TEntity entity) where TEntity : class {
        DbEntityEntry entryForThis = this.context.Entry<TEntity>(entity);
        IEnumerable<DbEntityEntry> entriesItWantsToChange = this.context.ChangeTracker.Entries().Distinct();

        foreach (DbEntityEntry entry in entriesItWantsToChange) {
            if (!entryForThis.Equals(entry)) {
                entry.State = System.Data.EntityState.Unchanged;
            }
        }
    }

...

myDbSetB.Add(myB0);
MarkGraphAsUnchanged(myB0);

虽然这解决了它尝试添加 myA 的问题,但它仍然会导致 ObjectStateManager 中的键违规。

尝试#2:
我尝试做与上面相同的操作,但将状态设置为 Detached 而不是 Unchanged。这适用于保存,但它坚持设置 myB0.A = null ,这对我的代码有其他不利影响。

尝试#3:
我在整个 DbContext 周围使用了 TransactionScope。但是,即使调用 SaveChanges()每个 Attach() 之间和 Add() ,更改跟踪器不会刷新其跟踪的条目,因此我遇到了与尝试 #1 相同的问题。

尝试#4:
我继续使用 TransactionScope,除了我使用存储库/DAO 模式并在内部创建一个新的 DbContext 并调用 SaveChanges()对于我所做的每一个不同的操作。在这种情况下,我收到错误消息“存储更新、插入或删除语句影响了意外的行数。”使用 SQL Profiler 时,我发现调用 SaveChanges()在我做的第二个操作(第一个 Add() )中,它实际上发送了 UPDATE第二次从第一次操作到数据库的 SQL ——但不更改任何行。对我来说,这感觉就像 Entity Framework 中的一个错误。

尝试#5:
我决定只使用 DbTransaction,而不是使用 TransactionScope。我仍然创建多个上下文,但是在创建每个新上下文时将一个预构建的 EntityConnection 传递给它(通过缓存和手动打开由第一个上下文构建的 EntityConnection)。但是,当我这样做时,第二个上下文会运行我定义的初始化程序,即使它在应用程序首次启动时已经运行。在开发环境中,我有一些测试数据的种子,它实际上超时等待我的第一个表上的数据库锁定 Attach()已修改(但由于事务仍处于打开状态而仍被锁定)。

帮助!!我已经尝试了所有我能想到的东西,但没有完全重构我的应用程序以不使用导航属性或使用手动构建的 DAO 来执行 INSERT、UPDATE 和 DELETE 语句,我不知所措。似乎必须有一种方法可以让 Entity Framework 为 O/R 映射带来好处,但仍然需要手动控制事务中的操作!

最佳答案

一定有其他你没有显示的东西,因为你附加和添加实体的方式没有问题。以下代码将附上myA , myC , myB0myB1将上下文设置为未更改的状态并设置 myC 的状态来修改。

myDbSetC.Attach(myC);
context.Entry(myC).State = Modified;

以下代码将正确检测到所有实体都已附加,而不是抛出异常(如在 ObjectContext API 中所做的那样)或再次插入所有实体(如您所料),它只会更改 myB0myB1添加状态:
myDbSetB.Add(myB0);
myDbSetB.Add(myB1);

如果您的 myAmyC使用现有实体的键正确初始化整个代码将正确执行并保存,除了单个问题:
C myC = new C() { 
    A = myA;
}

这看起来像 independent association并且独立关联有自己的状态,但设置其状态的 API 是 not available in DbContext API .如果这是您要保存的新关系,它将不会被保存,因为它仍被跟踪为未更改。您必须使用外键关联,或者必须将您的上下文转换为 ObjectContext :
ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;

并使用 ObjectStateManagerchange state of the relation .

关于entity-framework - 首先使用实体​​框架代码保存单个对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9523188/

相关文章:

c# - 将 SQL Server 2012 指定为目标平台的项目可能会遇到与 Microsoft Azure SQL 数据库 v12 的兼容性问题

entity-framework - 在 Windows Azure 上使用 EntityFramework 时出现无效的列名称 'CreatedOn'

database - 使用 OR-Mapper 有意义吗?

entity-framework-4.1 - 找不到新的 Code First Entity Framework 数据库

c# - EF Code First 级联删除和更新?

c# - DBContext 不包含条目 : Cannot load related data 的定义

linq - Entity Framework 链接 where 子句

sql-server-2008 - SQL Server 2008 FILESTREAM 进度指示器

metadata - EF 4.3 与 Firebrid Dot Net Provider

c# - Entity Framework (4.3) 寻找单数名称而不是复数名称(当实体名称以 "s"结尾时)