不先查询就更新记录?


103

可以说我查询数据库并加载项目列表。然后,我以详细视图视图的形式打开其中一个项目,而不是从数据库中重新查询该项目,而是从列表中的数据源创建该项目的实例。

有没有一种方法可以更新数据库记录而无需获取单个项目的记录?

这是我现在如何做的一个示例:

dataItem itemToUpdate = (from t in dataEntity.items
                                 where t.id == id
                                 select t).FirstOrDefault();

然后,在拉出记录后,我会更新项目中的一些值并将记录推回去:

itemToUpdate.itemstatus = newStatus;
dataEntity.SaveChanges();

我想会有更好的方法做到这一点,有什么想法吗?


2
做事情不是很糟糕的方法。您可以同时访问该表吗?
Henk Holterman 2010年

我认为这就是像EF这样的ORM完全可以使用的用法。要允许在要创建/修改/删除的对象上执行应用程序上下文中的操作,而无需考虑基础数据库的实现?
Pero P.

40
我认为对于具有TSQL背景的开发人员尝试接受和接受ORM来说,查找记录只是为了更新记录而从不使用获取的数据,效率较低。开发人员无需关心基础数据库实现的这一概念很麻烦。开发人员对整个系统了解得越多,解决方案就越好。选择永远不是坏事。
barrypicker 2011年

1
ORM方法适用于实际对象,但是如果您还将其他内容(例如大型二进制Blob)存储在数据库中,则能够在不先加载原始内容的情况下进行更新就非常有用。
BrainSlugs83

Answers:


68

您应该使用Attach()方法。

附加和分离对象


16
你能提供一个例子吗?
Bart Calixto 2012年

16
context.Products.Attach(product); context.Entry(product).State = EntityState.Modified;
加百利

6
@Gabriel虽然不会更新所有属性吗?如果我只想修改一个怎么办?
David Pfeffer,2012年

22
是的,这将更新所有属性。如果要更新单个属性,则可以执行以下操作:context.Entry(user).Property(x => x.Property).IsModified = true; (看看这里stackoverflow.com/a/5567616/57369
加布里埃尔

6
我只想添加该context.Entry()仅在.net 4.1中可用,如果您仍在使用4.0(像我一样),请查看以下替代方法:stackoverflow.com/questions/7113434/where-is- context-entry 本质上是:context.ObjectStateManager.ChangeObjectState(yourObject,EntityState.Modified);
dyslexicanaboko 2012年

39

您还可以使用数据存储的上下文对数据库使用直接SQL。例:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = 123 ");

出于性能原因,您可能希望传递变量而不是传递单个硬编码的SQL字符串。这将允许SQL Server缓存查询并与参数一起重用。例:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });

更新-EF 6.0

dataEntity.Database.ExecuteSqlCommand
       ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });

9
您为什么要降级此答案而无需发表评论。该建议可以解决原始作者的问题。
barrypicker 2012年

18
ExecuteStoreCommand并不是真正的EF方法,它只是使用DbConnection内含的内容DbContext来执行命令。它与数据库无关,更不用说与持久性无关(例如,如果OP切换到XML,则此示例将崩溃)。
just.another.programmer

9
@ just.another.programmer-功能强大,责任重大。
barrypicker

13
是否必须与持久性无关?并非您每隔一天就要更改存储系统。
大卫,

5
@ BrainSlugs83-尝试在仅支持OpenQuery的链接服务器上使用EF-很有趣。有时您绝对需要原始SQL才能完成工作。并非总是可以将代码隔离进行测试。它不是一个完美的世界。
barrypicker

20

代码:

ExampleEntity exampleEntity = dbcontext.ExampleEntities.Attach(new ExampleEntity { Id = 1 });
exampleEntity.ExampleProperty = "abc";
dbcontext.Entry<ExampleEntity>(exampleEntity).Property(ee => ee.ExampleProperty).IsModified = true;
dbcontext.Configuration.ValidateOnSaveEnabled = false;
dbcontext.SaveChanges();

结果TSQL:

exec sp_executesql N'UPDATE [dbo].[ExampleEntities]
SET [ExampleProperty ] = @0
WHERE ([Id] = @1)
',N'@0 nvarchar(32),@1 bigint',@0='abc',@1=1

注意:

需要“ IsModified = true”行,因为当您创建新的ExampleEntity对象(仅填充了Id属性)时,所有其他属性都具有其默认值(0,null等)。如果要使用“默认值”更新数据库,则实体框架将不会检测到更改,因此不会更新数据库。

例如:

exampleEntity.ExampleProperty = null;

没有行“ IsModified = true”将无法工作,因为在创建空的ExampleEntity对象时,属性ExampleProperty已经为空,您需要对EF说必须更新此列,这是此行的目的。


太棒了。我刚刚测试了它,这正是我想要的。我希望更改能够通过EF基础结构(包括使用EntityFramework.Triggers项目)进行,但是希望能够仅具有主键才能更改1列。
MikeJansen

11

如果DataItem具有EF字段将进行预验证(例如非空字段),则必须在此上下文中禁用该验证:

DataItem itemToUpdate = new DataItem { Id = id, Itemstatus = newStatus };
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.Configuration.ValidateOnSaveEnabled = false;
dataEntity.SaveChanges();
//dataEntity.Configuration.ValidateOnSaveEnabled = true;

否则,我们可以尝试满足预验证,并且仍然只更新单个列:

DataItem itemToUpdate = new DataItem
{
    Id = id,
    Itemstatus = newStatus,
    NonNullableColumn = "this value is disregarded - the db original will remain"
};
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.SaveChanges();

假设dataEntity是一个System.Data.Entity.DbContext

您可以通过将查询添加到来验证生成的查询DbContext

/*dataEntity.*/Database.Log = m => System.Diagnostics.Debug.Write(m);


0

在EF Core中,它的工作方式有些不同:

在EF Core中可能有一种更快的方法来执行此操作,但是以下内容可确保无需执行SELECT即可进行UPDATE(在.NET Framework 4.6.2上经过EF Core 2和JET的测试):

确保您的模型没有IsRequired属性

然后使用以下模板(在VB.NET中):

    Using dbContext = new MyContext()
        Dim bewegung = dbContext.MyTable.Attach(New MyTable())
        bewegung.Entity.myKey = someKey
        bewegung.Entity.myOtherField = "1"

        dbContext.Entry(bewegung.Entity).State = EntityState.Modified
        dbContext.Update(bewegung.Entity)

        Dim BewegungenDescription = (From tp In dbContext.Model.GetEntityTypes() Where tp.ClrType.Name = "MyTable" Select tp).First()
        For Each p In (From prop In BewegungenDescription.GetProperties() Select prop)
            Dim pp = dbContext.Entry(bewegung.Entity).Property(p.Name)
            pp.IsModified = False
        Next
        dbContext.Entry(bewegung.Entity).Property(Function(row) row.myOtherField).IsModified = True
        dbContext.SaveChanges()
    End Using

-1

一般而言,如果您使用实体框架查询所有项目,并且保存了实体对象,则可以更新实体对象中的各个项目,并SaveChanges()在完成时调用。例如:

var items = dataEntity.Include("items").items;
// For each one you want to change:
items.First(item => item.id == theIdYouWant).itemstatus = newStatus;
// After all changes:
dataEntity.SaveChanges();

检索所需的一项不应生成新查询。


有趣的答案,还有其他人确认吗?
伊恩

5
这与OP的问题相同:提取整个记录,然后对其进行更新。.First()反序列化对象。
Jerther '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.