在没有先查询的情况下更新记录?

Sha*_*ant 90 c# entity-framework

让我们说我查询数据库并加载项目列表.然后我打开详细视图表单中的一个项目,而不是从数据库中重新查询该项目,我从列表中的数据源创建项目的实例.

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

以下是我现在正在做的一个示例:

dataItem itemToUpdate = (from t in dataEntity.items
                                 where t.id == id
                                 select t).FirstOrDefault();
Run Code Online (Sandbox Code Playgroud)

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

itemToUpdate.itemstatus = newStatus;
dataEntity.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

我认为有更好的方法可以做到这一点,任何想法?

CD.*_*D.. 66

您应该使用Attach()方法.

附加和分离对象

  • 是的,这将更新所有属性.如果要更新单个属性,可以执行以下操作:context.Entry(user).Property(x => x.Property).IsModified = true; (看看这里http://stackoverflow.com/a/5567616/57369) (18认同)
  • context.Products.Attach(产品); context.Entry(product).State = EntityState.Modified; (16认同)
  • 你能提供一个例子吗? (15认同)
  • 我只想添加context.Entry()仅在.net 4.1中可用,如果你仍在使用4.0(像我一样),那么请检查这个替代方案:http://stackoverflow.com/questions/7113434/ where-is-context-entry本质上是:context.ObjectStateManager.ChangeObjectState(yourObject,EntityState.Modified); (6认同)
  • @Gabriel这不会更新所有属性吗?如果我只想修改一个怎么办? (5认同)

Ami*_*adi 53

现在 EF Core 7 \xe2\x80\x94 对此提供本机支持ExecuteUpdate

\n

最后!经过漫长的等待,EF Core 7.0 现在拥有原生支持的运行UPDATE(以及DELETE)语句的方式,同时还允许您使用任意 LINQ 查询 ( .Where(u => ...)),而无需先从数据库中检索相关实体:新的内置ExecuteUpdate名为\xe2\x80\x94的方法请参阅“EF Core 7.0 中有哪些新增功能?” 官方文档

\n

ExecuteUpdate正是针对此类场景,它可以在任何IQueryable实例上操作,并允许您更新任意数量的行上的特定列,同时始终在幕后发出单个语句 UPDATE,使其尽可能高效。

\n

用法:

\n

想象一下您想要更新Email特定用户的列:

\n
dbContext.Users\n    .Where(u => u.Id == someId)\n    .ExecuteUpdate(b =>\n        b.SetProperty(u => u.Email, "NewEmail@gmail.com")\n    );\n
Run Code Online (Sandbox Code Playgroud)\n

正如您所看到的,调用ExecuteUpdate需要您调用该SetProperty方法,指定要更新哪个属性,以及要为其分配什么新值。

\n

EF Core 会将其转换为以下UPDATE语句:

\n
UPDATE [u]\n    SET [u].[Email] = "NewEmail@gmail.com"\nFROM [Users] AS [u]\nWHERE [u].[Id] = someId\n
Run Code Online (Sandbox Code Playgroud)\n

另外,ExecuteDelete对于删除行:

\n

还有一个与ExecuteUpdatecalledExecuteDelete相对应的功能,顾名思义,它可以用于一次删除单行或多行,而无需先获取它们。

\n

用法:

\n
// Delete all users that haven\'t been active in 2022:\ndbContext.Users\n    .Where(u => u.LastActiveAt.Year < 2022)\n    .ExecuteDelete();\n
Run Code Online (Sandbox Code Playgroud)\n

与 类似ExecuteUpdate,会在后台ExecuteDelete生成SQL 语句 \xe2\x80\x94 在本例中,如下所示:DELETE

\n
DELETE FROM [u]\nFROM [Users] AS [u]\nWHERE DATEPART(year, [u].[LastActiveAt]) < 2022\n
Run Code Online (Sandbox Code Playgroud)\n
\n

其他注意事项:

\n
    \n
  • 请记住, 和ExecuteUpdate都是ExecuteDelete“终止”的,这意味着一旦您调用该方法,更新/删除操作就会发生。你不应该dbContext.SaveChanges()事后打电话。
  • \n
  • 如果您对该方法感到好奇SetProperty,并且对为什么ExectueUpdate不接收成员初始化表达式感到困惑(例如.ExecuteUpdate(new User { Email = "..." }),请参阅此评论此功能的 GitHub 问题上的(以及周围的评论) 。
  • \n
  • Execute此外,如果您对命名背后的基本原理以及为什么选择前缀(还有其他候选者)感到好奇,请参阅此评论以及前面的(相当长的)对话。
  • \n
  • 这两种方法也有async等效的方法,分别名为ExecuteUpdateAsync、 和ExecuteDeleteAsync
  • \n
\n

  • @swinn 你可以做到。将表达式作为第二个参数传递给 `SetProperty`;像这样:`b.SetProperty(u =&gt; u.Counter, u =&gt; u.Counter + 1)`。我链接到的官方文章中演示了这种用法 - 请参阅[本节中的第一个示例](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef- core-7.0/whatsnew#basic-executeupdate-examples)。 (2认同)

bar*_*ker 36

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

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = 123 ");
Run Code Online (Sandbox Code Playgroud)

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

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

更新 - 适用于EF 6.0

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

  • `ExecuteStoreCommand`实际上并不是EF的一种方式,它只是使用`DbContext`中包含的`DbConnection`来执行命令.它不是数据库不可知的,更不用说持久性不可知(例如,如果OP切换到XML,这个例子会崩溃). (18认同)
  • 它是否必须是持久性不可知的?这并不是说你每隔一天都会改变你的存储系统. (13认同)
  • @ just.another.programmer - 强大的力量带来了巨大的责任. (9认同)
  • 你为什么要在没有发表评论的情况下降级这个答案.这个建议解决了原作者的问题. (7认同)
  • @ BrainSlugs83 - 尝试在仅支持OpenQuery的链接服务器上使用EF - 非常有趣.有时您绝对需要原始SQL来完成工作.并不总是可以将代码绘制成隔离以进行测试.它不是一个完美的世界. (4认同)
  • 我需要这个答案.这很简单,正是我需要更新2个表格.有趣的是,整个地方都有大量的MVC HowTos似乎是用埃及的Hyrogliphs(原文如此)写的.除了这个,这家伙没有要求任何东西. (3认同)

tec*_*cla 13

代码:

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();
Run Code Online (Sandbox Code Playgroud)

结果TSQL:

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

注意:

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

例如:

exampleEntity.ExampleProperty = null;
Run Code Online (Sandbox Code Playgroud)

如果没有"IsModified = true"这一行将无法工作,因为属性ExampleProperty在创建空的ExampleEntity对象时已经为空,您需要向EF说这个列必须更新,这就是这一行的目的.


Ask*_* B. 8

如果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;
Run Code Online (Sandbox Code Playgroud)

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

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();
Run Code Online (Sandbox Code Playgroud)

假设dataEntitySystem.Data.Entity.DbContext

您可以通过将此添加到以下内容来验证生成的查询DbContext:

/*dataEntity.*/Database.Log = m => System.Diagnostics.Debug.Write(m);
Run Code Online (Sandbox Code Playgroud)


OMR*_*OMR 6

我建议使用Entity Framework Plus

如果您需要使用相同表达式更新数百或数千个实体,则使用 E​​ntity Framework Core 进行更新可能会非常慢。实体在更新之前首先加载到上下文中,这对性能非常不利,然后它们被一个接一个地更新,这使得更新操作变得更糟。

EF+ 批量更新在单个数据库往返中使用表达式更新多行,并且无需在上下文中加载实体。

// using Z.EntityFramework.Plus; // Don't forget to include this.

// UPDATE all users inactive for 2 years
var date = DateTime.Now.AddYears(-2);
ctx.Users.Where(x => x.LastLoginDate < date)
         .Update(x => new User() { IsSoftDeleted = 1 });
Run Code Online (Sandbox Code Playgroud)