使用 linq 生成直接更新,无需选择

Posted

技术标签:

【中文标题】使用 linq 生成直接更新,无需选择【英文标题】:Use linq to generate direct update without select 【发布时间】:2010-10-01 11:54:36 【问题描述】:

祝大家生日快乐。

我还在学习 LINQ,如果这很幼稚,请原谅我。当您直接处理 SQL 时,您可以生成带有条件的更新命令,而无需运行 select 语句。

当我使用 linq 时,我似乎遵循以下模式:

    选择实体 修改实体 提交更改

我想要做的是使用 linq 和延迟执行的直接更新。是否有可能直接在 SQL 上执行实际执行,而无需将任何数据传输到客户端?

DataContext dc = new DataContext

var q = from product in dc.Products
        where product.Type = 1
        set product.Count = 0

dc.SubmitChanges

所以本质上 LINQ 拥有它需要的所有信息,而无需使用 select 来生成更新命令。它将运行 SQL:

Update Products Set Count = 0 Where Type = 1

LINQ 中是否存在类似“set”的关键字?

【问题讨论】:

当前接受的答案是错误的——你能选择正确的吗? 下面的答案不允许我描述的基于集合的更新,但它们很有趣。 【参考方案1】:

您实际上可以让 LINQ-to-SQL 生成更新语句:

Foo foo=new Foo  FooId=fooId ; // create obj and set keys
context.Foos.Attach(foo);
foo.Name="test";
context.SubmitChanges();

在您的 Dbml 中为所有属性设置 UpdateCheck="Never"。

这将生成一个更新语句,而无需先进行选择。

一个警告:如果您希望能够将 Name 设置为 null,则必须将 foo 对象初始化为不同的值,以便 Linq 可以检测到更改:

Foo foo=new Foo  FooId=fooId, Name="###" ;
...
foo.Name=null;

如果您想在更新时检查时间戳,您也可以这样做:

Foo foo=new Foo  FooId=fooId, Modified=... ; 
// Modified needs to be set to UpdateCheck="Always" in the dbml

【讨论】:

虽然它不是完全基于集合。虽然知道附加语法很酷 另一种方法是:context.Foos.Attach(foo, original: new Foo FooId = fooId );。这将更新原始实体中未设置的所有属性。【参考方案2】:

不,LINQ 和 LINQ to SQL 都没有基于集合的更新功能。

在 LINQ to SQL 中,您必须查询要更新的对象,根据需要更新字段/属性,然后调用 SubmitChanges()。例如:

var qry = from product in dc.Products where Product.Name=='Foobar' select product;
var item = qry.Single();
item.Count = 0;
dc.SubmitChanges();

如果您想进行批处理:

var qry = from product in dc.Products where Product.Type==1 select product;
foreach(var item in qry)

  item.Count = 0;

dc.SubmitChanges();

或者,您可以自己编写查询:

dc.ExecuteCommand("update Product set Count=0 where Type=1", null);

【讨论】:

这是我的代码,我想知道 LINQ 是否有它的语法。不过为答案喝彩,在 C# 中进行更新确实很有意义,因为我想如果你想编写 SQL,你应该像执行命令函数那样编写 sql。 这真是太傻了,linq to sql应该有更新字段 @PeterRuderman 下面的附加答案需要知道 fooId。很少有场景(尤其是我的场景)不想知道此信息,只需将更新转换为 SQL。如果您对要更新的主键有一些其他知识,则以下内容很有用。此外,这只会更新一行,而不是整个表,并且不会获得基于集合的性能提升。【参考方案3】:

PLINQO (http://plinqo.com) 框架正在使用 LINQ 批量更新来执行更新

context.Task.Update(t => t.Id == 1, t2 => new Task StatusId = 2);

这将执行Update Task Set StatusId = 2 Where Id = 1

【讨论】:

【参考方案4】:

Linq 2 SQL 没有直接插入/更新/删除等价的 SQL。在 V1 中,您可以使用 linq 进行的唯一更新被认为是上下文中的 SubmmitChanges,或者如果您回退到 sql。

然而,有些人尝试使用自定义实现来克服 linq 的这一限制。

Linq batch update.

【讨论】:

干杯伙伴,这是一个非常有趣的阅读。让我们希望其中的一些能够进入 C# 4 或 5,因为批量更新和删除的能力是 LINQ 中唯一真正缺少的东西。【参考方案5】:

使用此扩展方法:EntityExtensionMethods.cs

public static void UpdateOnSubmit<TEntity>(this Table<TEntity> table, TEntity entity, TEntity original = null)
    where TEntity : class, new()

    if (original == null)
    
        // Create original object with only primary keys set
        original = new TEntity();
        var entityType = typeof(TEntity);
        var dataMembers = table.Context.Mapping.GetMetaType(entityType).DataMembers;
        foreach (var member in dataMembers.Where(m => m.IsPrimaryKey))
        
            var propValue = entityType.GetProperty(member.Name).GetValue(entity, null);
            entityType.InvokeMember(member.Name, BindingFlags.SetProperty, Type.DefaultBinder,
                original, new[]  propValue );
        
    

    // This will update all columns that are not set in 'original' object. For
    // this to work, entity has to have UpdateCheck=Never for all properties except
    // for primary keys. This will update the record without querying it first.
    table.Attach(entity, original);

要使用它,请确保您传递给UpdateOnSubmit 方法的entity 对象具有为您要更新的记录设置的所有主键属性。然后,此方法将使用 entity 对象中的剩余属性更新记录,而无需先提取记录。

致电UpdateOnSubmit 后,请务必致电SubmitChanges() 以应用更改。

【讨论】:

感谢奥拉德。这与 laktak 的回答一致,但仍然存在同样的问题,即它实际上是在推断单行和表的知识。我的问题是 LINQ 是否最终会向 SQL 发出基于集合的更新查询,而它不会开箱即用。 嗨@Spence,我不认为可以通过LINQ 批量更新set,您可以使用AttachAll 方法,但我认为它仍会为每一行执行单独的更新命令。我的解决方案没有像问题标题中那样选择直接更新,但如果您的具体问题是如何进行批量更新,则接受的答案是正确的。【参考方案6】:

您可以使用Entity Framework Extensions库,它支持批量更新和批量合并,但是该库不是免费的:

PM > 安装包 Z.EntityFramework.Extensions

using Z.EntityFramework.Plus;

...

dc.Products
    .Where(q => q.Type == 1)
    .Update(q => new Product  Count = 0 );

【讨论】:

【参考方案7】:

试试这个:

dbEntities.tblSearchItems
     .Where(t => t.SearchItemId == SearchItemId)
     .ToList()
     .ForEach(t => t.isNew = false);
dbEntities.SaveChanges();

【讨论】:

阿米特,对“ToList”的调用会将数据库中的所有项目加载到内存中。 foreach 在内存中工作,然后 savechanges 将它们传播回来。我正在寻找一些会产生 SQL 语句“更新条件条件”的东西,它不会在本地加载任何数据。

以上是关于使用 linq 生成直接更新,无需选择的主要内容,如果未能解决你的问题,请参考以下文章

Linq to entity 使用“Func”在生成匿名对象的选择语句中创建属性

使用 EF 生成的 MySQL 在 LinQ 中为 FromSqlRaw 在从系统中选择变量时返回“SQL 语法错误”

LINQ学习

如何创建 LINQ 表达式树以选择匿名类型

Linq 按产品排序但显示类别

LINQ Group By 和选择集合