3

我想以分离的方式使用 EF DbContext/POCO 实体,即从我的业务层检索实体的层次结构,进行一些更改,然后将整个层次结构发送回业务层以持久化回数据库。每个 BLL 调用使用不同的 DbContext 实例。为了测试这一点,我编写了一些代码来模拟这样的环境。

首先我检索一个Customer加号相关的OrdersOrderLines:-

Customer customer;
using (var context = new TestContext())
{
    customer = context.Customers.Include("Orders.OrderLines").SingleOrDefault(o => o.Id == 1);
}

接下来我添加一个新Order的两个OrderLines:-

var newOrder = new Order { OrderDate = DateTime.Now, OrderDescription = "Test" };
newOrder.OrderLines.Add(new OrderLine { ProductName = "foo", Order = newOrder, OrderId = newOrder.Id });
newOrder.OrderLines.Add(new OrderLine { ProductName = "bar", Order = newOrder, OrderId = newOrder.Id });
customer.Orders.Add(newOrder);
newOrder.Customer = customer;
newOrder.CustomerId = customer.Id;

最后,我坚持更改(使用新上下文):-

using (var context = new TestContext())
{
    context.Customers.Attach(customer);

    context.SaveChanges();
}

我意识到最后一部分是不完整的,因为毫无疑问我需要在调用 SaveChanges() 之前更改新实体的状态。我要添加还是附加客户?我必须更改哪些实体状态?

在我进入这个阶段之前,运行上面的代码会抛出一个异常:

ObjectStateManager 中已存在具有相同键的对象。

这似乎源于没有明确设置两个OrderLine实体的 ID,因此两者都默认为 0。我认为这样做很好,因为 EF 会自动处理事情。难道我做错了什么?

此外,以这种“分离”的方式工作,设置关系似乎需要做很多工作——我必须将新订单实体添加到customer.Orders集合中,设置新订单的Customer属性及其CustomerId属性。这是正确的方法还是有更简单的方法?

看看自我跟踪实体会更好吗?我在某处读到过它们已被弃用,或者至少不鼓励使用 POCO。

4

1 回答 1

3

你基本上有2个选择:

A) 乐观。

您可以非常接近您现在的方式进行,只需将所有内容附加为已修改和希望。您正在寻找的代码而不是 .Attach() 是:

context.Entry(customer).State = EntityState.Modified;

绝对不直观。这个看起来很奇怪的调用附加了已分离的(或您新构建的)对象,如已修改。资料来源:http: //blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states。 aspx

如果您不确定对象是否已添加或修改,您可以使用最后一段的示例:

context.Entry(customer).State = customer.Id == 0 ?
                               EntityState.Added :
                               EntityState.Modified;

您需要对所有正在添加/修改的对象执行这些操作,因此如果此对象很复杂并且有其他需要通过 FK 关系在 DB 中更新的对象,您还需要设置它们的 EntityState。

根据您的情况,您可以通过使用不同的 Context 变体使这些类型的无关写入更便宜:

public class MyDb : DbContext
{
    . . .
    public static MyDb CheapWrites()
    {
        var db = new MyDb();
        db.Configuration.AutoDetectChangesEnabled = false;
        db.Configuration.ValidateOnSaveEnabled = false;
        return db;
    }
}

using(var db = MyDb.CheapWrites())
{
    db.Entry(customer).State = customer.Id == 0 ?
        EntityState.Added :
        EntityState.Modified;
    db.SaveChanges();
}

您基本上只是禁用了 EF 代表您进行的一些额外调用,而您无论如何都忽略了结果。

B) 悲观的。您实际上可以查询数据库以验证自上次获取数据以来数据没有更改/添加,然后在安全的情况下对其进行更新。

var existing = db.Customers.Find(customer.Id);
// Some logic here to decide whether updating is a good idea, like
// verifying selected values haven't changed, then

db.Entry(existing).CurrentValues.SetValues(customer);
于 2013-03-29T00:11:36.947 回答