【发布时间】:2014-11-11 00:40:15
【问题描述】:
在我的项目中,我的实体模型如下所示:
public class OrdersDbContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<OrderPosition> OrderPositions { get; set; }
}
public class Customer
{
public Guid Id { get; set; }
public virtual Order LiveOrder { get; set; }
}
public class Order
{
public Guid Id { get; set; }
public virtual IList<OrderPosition> Positions { get; set; }
public string Name { get; set; }
}
public class OrderPosition
{
public Guid Id { get; set; }
public int Price { get; set; }
}
我需要提供一种方法来更新一些客户的属性以及他的 LiveOrder 和所有 OrderPositions(插入新的、更新现有的和删除旧的)。我尝试了几种方法,但都失败了:
- 删除订单和订单仓位,然后插入新的失败,原因是
- 重复键异常。分离订单,然后附加更新 - 失败:
附加类型为“OrderPosition”的实体失败,因为另一个 相同类型的实体已经具有相同的主键值。
正确的做法是什么?
演示问题的完整控制台程序:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
public class OrdersDbContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<Order> Orders { get; set; }
public DbSet<OrderPosition> OrderPositions { get; set; }
}
public class Customer
{
public Guid Id { get; set; }
public virtual Order LiveOrder { get; set; }
}
public class Order
{
public Guid Id { get; set; }
public virtual IList<OrderPosition> Positions { get; set; }
public string Name { get; set; }
}
public class OrderPosition
{
public Guid Id { get; set; }
public int Price { get; set; }
}
class Program
{
static void Main(string[] args)
{
var parentId = Guid.NewGuid();
var childId = Guid.NewGuid();
using (var ctx = new OrdersDbContext())
{
var agg = new Customer{
Id = Guid.NewGuid(),
LiveOrder = new Order
{
Id = parentId,
Name = "First order.",
Positions = new[] { new OrderPosition { Id = childId, Price = 3 } }
}};
ctx.Customers.Add(agg);
ctx.SaveChanges();
}
var newParent = new Order
{
Id = parentId,
Name = "Updated order.",
Positions = new[] { new OrderPosition { Id = childId, Price = 5 } }
};
try
{
using (var ctx = new OrdersDbContext())
{
var agg = ctx.Customers.First(x => x.LiveOrder.Id == parentId);
ctx.OrderPositions.RemoveRange(agg.LiveOrder.Positions);
var parent = agg.LiveOrder;
agg.LiveOrder = null;
ctx.Entry(parent).State = EntityState.Detached;
Console.WriteLine(ctx.Entry(parent).State);
ctx.Entry(newParent).State = EntityState.Modified;
agg.LiveOrder = newParent;
ctx.SaveChanges();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
}
【问题讨论】:
-
据我所知,实体不会批量更新/插入,因此如果您更改订单位置,您将始终得到重复值。我见过的一种方法是,您在订单中使用浮点数,随机生成分数,同时仍保持整数部分递增
-
在删除 Order 和 OrderPositions 而不是插入它之后,我设法用额外的 SaveChanges 对其进行了更新。但我认为应该有更好的方法来使用单个 SaveChanges。我想我也可以分离所有 OrderPosition,而不是附加新的/更新的。但在这种情况下,我填写就像我在做 ERM 的工作。
-
我将我的评论复制到一个答案中,因为我觉得它提供了一种新的方式。
-
第一点...在事务中执行!...如果您的某个更改失败怎么办?但其他一些人成功了吗?数据损坏?
-
为什么在
agg.LiveOrder = null;之后不调用保存更改?数据库将无法处理。您正在从数据库中分离实体存储并在实体存储中创建一个新对象,该对象不会链接到数据库中的对象。您在这里遇到的问题是该行仍然存在于数据库中,在客户端您正在删除它,然后告诉实体存储忘记它,然后向实体存储添加一个新的,然后提交它。这将使 EF 执行插入而不是更新,相同的 Guid 会导致重复键错误。
标签: c# entity-framework-6