C# 如何在实体框架中更新大型对象
在我的项目中,我的实体模型如下所示:C# 如何在实体框架中更新大型对象,c#,entity-framework-6,C#,Entity Framework 6,在我的项目中,我的实体模型如下所示: public class OrdersDbContext : DbContext { public DbSet<Customer> Customers { get; set; } public DbSet<Order> Orders { get; set; } public DbSet<OrderPosition> OrderPositions { get; set; } } public cla
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; }
}
公共类OrdersDbContext:DbContext
{
公共数据库集客户{get;set;}
公共数据库集命令{get;set;}
公共DbSet OrderPositions{get;set;}
}
公共类客户
{
公共Guid Id{get;set;}
公共虚拟订单LiveOrder{get;set;}
}
公共阶级秩序
{
公共Guid Id{get;set;}
公共虚拟IList位置{get;set;}
公共字符串名称{get;set;}
}
公共类OrderPosition
{
公共Guid Id{get;set;}
公共整数价格{get;set;}
}
我需要提供一个方法来更新一些客户的属性以及他的LiveOrder和所有OrderPositions(插入新的、更新现有的和删除旧的)。我尝试了几种方法,但都失败了:
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();
}
}
使用系统;
使用System.Collections.Generic;
使用System.Data.Entity;
使用System.Linq;
公共类OrdersDbContext:DbContext
{
公共数据库集客户{get;set;}
公共数据库集命令{get;set;}
公共DbSet OrderPositions{get;set;}
}
公共类客户
{
公共Guid Id{get;set;}
公共虚拟订单LiveOrder{get;set;}
}
公共阶级秩序
{
公共Guid Id{get;set;}
公共虚拟IList位置{get;set;}
公共字符串名称{get;set;}
}
公共类OrderPosition
{
公共Guid Id{get;set;}
公共整数价格{get;set;}
}
班级计划
{
静态void Main(字符串[]参数)
{
var parentId=Guid.NewGuid();
var childId=Guid.NewGuid();
使用(var ctx=new OrdersDbContext())
{
var agg=新客户{
Id=Guid.NewGuid(),
LiveOrder=新订单
{
Id=父Id,
Name=“第一订单。”,
Positions=new[]{neworderposition{Id=childId,Price=3}
}};
ctx.Customers.Add(agg);
ctx.SaveChanges();
}
var newParent=新订单
{
Id=父Id,
Name=“已更新订单。”,
Positions=new[]{neworderposition{Id=childId,Price=5}
};
尝试
{
使用(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();
}
}
捕获(例外情况除外)
{
控制台写入线(ex);
}
控制台。WriteLine(“按任意键退出”);
Console.ReadLine();
}
}
这完全不是EF问题-使用纯SQL会遇到同样的问题
SQL Server在每次更新时检查唯一性,而不是在提交时检查唯一性。由于重新排序会临时创建非唯一的。。。。嗯
你需要两张通行证。首先把东西移开,然后更新到最终位置
怎么做?SQL Server数据类型支持负数;)将它们放在负数位置(即-4而不是关4),然后您可以创建一个SP(或直接SQL),为客户反转负数。完成了
但是你需要在更新过程中打破唯一性。唉,实体框架在这种类型的操作中非常愚蠢,它提供了框架,而不是一个完整的解决方案 正如你所做的;这可以通过多次调用SaveChanges来实现 一次调用SaveChanges无法实现的原因是无法告诉实体框架您希望操作的顺序,即它可能会在更改外键之前删除一条表记录等 现在,在一个事务中执行多个保存更改的更好方法如下:
TransactionScope scope = new TransactionScope(TransactionScopeOption.RequiresNew, new TransactionOptions() { IsolationLevel = IsolationLevel.Serializable });
try
{
using (scope)
{
using (OrdersDbContext ctx = new OrdersDbContext())
{
ctx.Connection.Open();
//Do something with DBcontext - Update existing
//Save Changes
ctx.SaveChanges(false);
//Do something else with DBcontext - Delete old
//Save Changes
ctx.SaveChanges(false);
//Do something else with DBcontext - Insert new
//Save Changes
ctx.SaveChanges(false);
//if we get here things are looking good.
scope.Complete();
ctx.AcceptAllChanges();
}
scope.Complete();
}
}
catch (TransactionAbortedException ex)
{
ErrorLogger.LogException();
}
catch (ApplicationException ex)
{
ErrorLogger.LogException();
}
这里的主要好处是,如果一个SaveChanges失败,那么其他SaveChanges将回滚/不执行操作
当调用SaveChanges(false)将必要的命令发送到数据库时,上下文本身不会更改,因此您可以在必要时再次执行该操作,或者在需要时询问ObjectStateManager
这意味着,如果事务实际中止,您可以通过重新尝试或在某处记录context ObjectStateManager的状态来进行补偿。要扩展我上面的评论 数据库和EF将无法处理该问题。您正在从数据库中分离实体存储,并在实体存储中创建一个新对象,该对象不会链接到数据库中的对象 这里的问题是,该行仍然存在于数据库中,在客户端,您要删除它,然后告诉实体存储忘记它,然后向实体存储添加一个新的行,然后提交它。这将使EF执行插入而不是更新,使用相同的Guid会导致重复密钥错误 你在这里应该做的是
try
{
using (var ctx = new OrdersDbContext())
{
var agg = ctx.Customers.First(x => x.LiveOrder.Id == parentId);
var pos = agg.LiveOrder.Single(o => o.Id == childId);
pos.Price = 5;
ctx.SaveChanges();
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
实体将对象存储在客户端,并将检测对它们的更改。您只需像普通对象一样修改它们,然后调用
SaveChanges()
据我所知,Enities不会批量更新/插入,因此如果您更改订单位置y