我有一种情况,我有一个通过动作从表单加载回MVC控制器的对象。我们不使用FormCollection,而是使用直接使用类的那个。
[HttpPost]
public ActionResult AjaxUpdate(Customer customer) { ...
Customer对象包含一个名为customer的对象,它似乎已更新但在上下文中使用SaveDatabase()时根本不起作用。
为了使它有效,我必须在动作中使用:
myDbContext.Customers.Attach(customer)
//...Code here that set to the customer.SubObject a real object from the database so I am sure that the SubObject contain an id which is valid and the datacontext is aware of it...
myDbContext.Entry(customer).State = EntityState.Modified;
尽管如此,我有一个例外,关于“存储更新,插入或删除语句影响了意外的行数(0)”,我可以使用以下方法删除:
Database.ObjectContext().Refresh(RefreshMode.ClientWins,customer);
所以,为了解决我的问题,为什么我必须附加+更改状态+调用刷新。是不是有更好的方法来更新包含在其他表中引用的对象的对象。我正在使用Code第一个实体框架(Poco对象)。另外,我不喜欢使用Refresh,因为它隐藏在我的Databasecontext中。
答案 0 :(得分:2)
我使用EF 4.3.1制作了一个控制台测试项目。代码是我的猜测你的意思是注释行和你的评论下面的问题(但我的猜测可能是错误的,因为该程序不会重现你的错误):
您可以将代码复制到program.cs中并添加对EF 4.3.1的引用:
using System.Data;
using System.Data.Entity;
namespace EFUpdateTest
{
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public int SubObjectId { get; set; }
public SubObject SubObject { get; set; }
}
public class SubObject
{
public int Id { get; set; }
public string Something { get; set; }
}
public class CustomerContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public DbSet<SubObject> SubObjects { get; set; }
}
class Program
{
static void Main(string[] args)
{
int customerId = 0;
int subObject1Id = 0;
int subObject2Id = 0;
using (var ctx = new CustomerContext())
{
// Create customer with subobject
var customer = new Customer { Name = "John" };
var subObject = new SubObject { Something = "SubObject 1" };
customer.SubObject = subObject;
ctx.Customers.Add(customer);
// Create a second subobject, not related to any customer
var subObject2 = new SubObject { Something = "SubObject 2" };
ctx.SubObjects.Add(subObject2);
ctx.SaveChanges();
customerId = customer.Id;
subObject1Id = subObject.Id;
subObject2Id = subObject2.Id;
}
// New context, simulate detached scenario -> MVC action
using (var ctx = new CustomerContext())
{
// Changed customer name
var customer = new Customer { Id = customerId, Name = "Jim" };
ctx.Customers.Attach(customer);
// Changed reference to another subobject
var subObject2 = ctx.SubObjects.Find(subObject2Id);
customer.SubObject = subObject2;
ctx.Entry(customer).State = EntityState.Modified;
ctx.SaveChanges();
// No exception here.
}
}
}
}
这无一例外地起作用。问题是:代码中哪些内容可能会导致错误?
修改强>
对于您在客户类中没有外键属性SubObjectId
的注释:如果我在上面的示例程序中删除了该属性,则可以重现该错误。
解决方案是在更改关系之前从数据库加载原始子对象:
// Changed customer name
var customer = new Customer { Id = customerId, Name = "Jim" };
ctx.Customers.Attach(customer);
// Load original SubObject from database
ctx.Entry(customer).Reference(c => c.SubObject).Load();
// Changed reference to another subobject
var subObject2 = ctx.SubObjects.Find(subObject2Id);
customer.SubObject = subObject2;
ctx.Entry(customer).State = EntityState.Modified;
ctx.SaveChanges();
// No exception here.
如果没有外键属性,您将拥有独立关联,要求在更改之前,包含所有引用的对象必须代表数据库中的状态。如果未在SubObject
中设置customer
的引用,则EF假定数据库中的原始状态是该客户未引用任何子对象。生成的UPDATE语句的SQL包含一个WHERE子句,如下所示:
WHERE [Customers].[Id] = 1 AND [Customers].[SubObject_Id] IS NULL
如果客户在数据库[SubObject_Id]
中有子对象不 NULL,则不满足条件且不会发生UPDATE(或者发生“意外的行数0” “)。
如果您有外键属性(外键关联),则不会出现此问题:在这种情况下,WHERE子句仅为:
WHERE [Customers].[Id] = 1
因此,SubObject
和SubObjectId
的原始值无关紧要。您可以保留值null
,然后更新UPDATE。
因此,加载原始子对象的替代解决方案是在Customer
中引入外键属性:
public int SubObjectId { get; set; }
或者,如果不需要这种关系:
public int? SubObjectId { get; set; }