1. 程式人生 > >linq 批量更新刪除

linq 批量更新刪除

方法一、官方例子

地球人都知道的,也是不少 Linq To SQL 反對者認為效率低下的一種方法。

NorthwindDataContext db = new NorthwindDataContext();
var customers = db.Customers.Where(c => c.CustomerID.StartsWith("BL"));
foreach (var customer in customers)
{
    customer.Address = "Guangzhou";
    customer.ContactName = "CoolCode";
    customer.CompanyName = "Microsoft"
; } db.SubmitChanges();

這種方法必須要查詢出要更新的資料,確實有點不雅,也是Linq To SQL 略顯尷尬的一面。

方法二、使用ExpressionVisitor獲取Lambda表示式生成的SQL條件語句

db.Customers.Update(c => c.CustomerID == "Bruce",
                     c => new Customer
                     {
                         Address = "Guangzhou",
                         ContactName = "CoolCode"
, CompanyName = "Microsoft" });

方法原型

/// 
/// 批量更新
/// 
/// 
/// 
/// 查詢條件表示式
/// 更新表示式
/// 影響的行數public static int Update(this Table table, Expression<Funcbool>> predicate, Expression<Func> 
updater) where T : class

實現原理:擴充套件Table,解釋表示式樹成SQL語句。其中解釋表示式樹包括和更新表示式,後者相對容易處理,例如表示式:

c => new Customer { Address = "Guangzhou", ContactName = "CoolCode", CompanyName = "Microsoft" }

解釋成

Address = @Address, ContactName = @ContactName, CompanyName = @CompanyName

而相應的值("Guangzhou", "CoolCode",  "Microsoft" )作為SQL引數傳遞。

實現這一步,其實就是從表示式 Expression<Func> 中取到初始化的屬性名字和值就可以,具體做法可以使用Expression Tree Viewer來輔助,從下圖可以瞭解到 Expression<Func> 的樹形結構。

image

然後我按上面的結構圖“照葫蘆畫瓢”就得到要更新的屬性名字和值:

//獲取Update的賦值語句
var updateMemberExpr = (MemberInitExpression)updater.Body;
var updateMemberCollection = updateMemberExpr.Bindings.Cast<MemberAssignment>().Select
(c => new { Name = c.Member.Name, Value = ((ConstantExpression)c.Expression).Value });

而解釋where條件就相對沒這麼輕鬆了。

這裡同 Jeffrey Zhao 的批量刪除一樣,同樣是藉助 ExpressionVisitor 來解釋。ExpressionVisitor 是Expression Tree 的遍歷器,它自身不會幫你生成任何東西,通過繼承 ExpressionVisitor 就可以取表示式的任何資訊,本文就是通過讓 ConditionBuilder 繼承ExpressionVisitor 而生成 Where 條件的 SQL。

:Jeffrey Zhao 的批量刪除一文提供的原始碼中,ConditionBuilder 並不支援生成Like操作,如 字串的 StartsWith,Contains,EndsWith 並不能生成這樣的SQL: Like ‘xxx%’, Like ‘%xxx%’ , Like ‘%xxx’ 。我通過分析 ExpressionVisitor ,也不難發現只要override VisitMethodCall 這個方法即可實現上述功能。

protected override Expression VisitMethodCall(MethodCallExpression m)
{
    if (m == null) return m;
    string format;
    switch (m.Method.Name)
    {
        case "StartsWith":
            format = "({0} LIKE {1}+'%')";
            break;
        case "Contains":
            format = "({0} LIKE '%'+{1}+'%')";
            break;
        case "EndsWith":
            format = "({0} LIKE '%'+{1})";
            break;
        default:
            throw new NotSupportedException(m.NodeType + " is not supported!");
    }
    this.Visit(m.Object);
    this.Visit(m.Arguments[0]);
    string right = this.m_conditionParts.Pop();
    string left = this.m_conditionParts.Pop();
    this.m_conditionParts.Push(String.Format(format, left, right));
    return m;
}

到此刻,已經解決了解釋表示式樹的難題,那麼實現通過表示式樹生成完整的 Update SQL語句這個設想也不是什麼難事了。

/// 
/// 批量更新
/// 
/// 
/// 
/// 查詢條件表示式
/// 更新表示式
/// 影響的行數public static int Update(this Table table, Expression<Funcbool>> predicate, Expression<Func>
 updater) where T : class { //獲取表名 string tableName = table.Context.Mapping.GetTable(typeof(T)).TableName; //查詢條件表示式轉換成SQL的條件語句 ConditionBuilder builder = new ConditionBuilder(); builder.Build(predicate.Body); string sqlCondition = builder.Condition; //獲取Update的賦值語句 var updateMemberExpr = (MemberInitExpression)updater.Body; var updateMemberCollection = updateMemberExpr.Bindings.Cast<MemberAssignment>().
Select(c => new { Name = c.Member.Name, Value = ((ConstantExpression)c.Expression).Value }); int i = builder.Arguments.Length; string sqlUpdateBlock = string.Join(", ", updateMemberCollection.Select(c => string.Format(
"[{0}]={1}"
, c.Name, "{" + (i++) + "}")).ToArray()); //SQL命令 string commandText = string.Format("UPDATE {0} SET {1} WHERE {2}", tableName, sqlUp
dateBlock, sqlCondition); //獲取SQL引數陣列 (包括查詢引數和賦值引數) var args = builder.Arguments.Union(updateMemberCollection.Select(c => c.Value)).ToArray(); //執行 return table.Context.ExecuteCommand(commandText, args); }

例如上面提到的示例所生成的 Updae SQL語句是:

UPDATE dbo.Customers SET [Address]={1}, [ContactName]={2}, [CompanyName]={3} WHERE ([CustomerID] = {0})

相應引數:"Bruce", "Guangzhou", "CoolCode",  "Microsoft"

據不完全統計,實際開發中用的 Update SQL 90%是很簡單的,以上擴充套件基本上符合要求。

方法三、使用 LinqToSQL 自身的解析器來獲取Lambda表示式生成的SQL條件語句

該方法與方法二基本上是同一思路,只是在獲取Lambda表示式生成的SQL條件上有點不一樣。

通過 DataContext 的 GetCommand 可以獲取到 DbCommand,所以通過生成的SQL查詢語句中擷取Where後面的條件,再用方法二生成Update 的賦值語句,兩者拼湊起來即可。

image

該方法比方法二支援更多Lambda表示式(實際上就是所有LinqToSQL支援的)生成SQL條件。

/// 
    /// 批量更新
    /// 
    /// 
    /// 
    /// 查詢條件表示式
    /// 更新表示式
    /// 影響的行數public static int Update(this Table table, Expression<Funcbool>> predicate, Expression<Func
> updater) where T : class { //獲取表名 string tableName = table.Context.Mapping.GetTable(typeof(T)).TableName; DbCommand command = table.Context.GetCommand(table.Where(predicate)); string sqlCondition = command.CommandText; sqlCondition = sqlCondition.Substring(sqlCondition.LastIndexOf("WHERE ", StringCompari
son
.InvariantCultureIgnoreCase) + 6); //獲取Update的賦值語句 var updateMemberExpr = (MemberInitExpression)updater.Body; var updateMemberCollection = updateMemberExpr.Bindings.Cast<MemberAssignment>().
Select(c => { var p = command.CreateParameter(); p.ParameterName = c.Member.Name; p.Value = ((ConstantExpression)c.Expression).Value; return p; }) .ToArray(); string sqlUpdateBlock = string.Join(", ", updateMemberCollection.Select(c => string.Forma
t("[{0}][email protected]{0}", c.ParameterName)).ToArray()); //SQL命令 string commandText = string.Format("UPDATE {0} SET {1} FROM {0} AS t0 WHERE {2}",
tableName, sqlUpdateBlock, sqlCondition); //獲取SQL引數陣列 (包括查詢引數和賦值引數) command.Parameters.AddRange(updateMemberCollection); command.CommandText = commandText; //執行 try { if (command.Connection.State != ConnectionState.Open) { command.Connection.Open(); } return command.ExecuteNonQuery(); } finally { command.Connection.Close(); command.Dispose(); } }

同樣使用文章開頭的示例,生成的 Update SQL 跟方法二略有不同:

UPDATE dbo.Customers SET [Address][email protected], [ContactName][email protected], [CompanyName][email protected] FROM dbo.Customers AS t0 WHERE [t0].[CustomerID] = @p0

方法四、支援多表關聯的複雜條件

要知道,前面提到的方法二和三都不支援多表關聯的複雜條件。可以用一個示例讓大家更清楚為什麼——

例如,更新CustomerID=“Bruce”的使用者的所有訂單的送貨日前是一個月後。

db.Orders.Update(c => c.Customer.CustomerID == "Bruce",
                    c => new Order
                    {
                         ShippedDate =  DateTime.Now.AddMonths(1)
                    });

應該生成的 Update SQL 語句是:

UPDATE [dbo].[Orders] SET [ShippedDate] = @p1
FROM [dbo].[Orders] AS [t0]
    LEFT OUTER JOIN [dbo].[Customers] AS [t1] ON [t1].[CustomerID] = [t0].[CustomerID]
WHERE [t1].[CustomerID] = @p0
[email protected] = 'Bruce', @p1 = '2010-08-11'

但遺憾的是無論用方法二或三都會拋異常,因為兩者皆沒法解釋多表關聯生成的語句: “LEFT OUTER JOIN [dbo].[Customers] AS [t1] ON [t1].[CustomerID] [t0].[CustomerID] ”

UPDATE [dbo].[Orders]
    SET [ShippedDate] = @p1
FROM [dbo].[Orders] AS j0 INNER JOIN (
    SELECT [t0].[OrderID]
    FROM [dbo].[Orders] AS [t0]
        LEFT OUTER JOIN [dbo].[Customers] AS [t1] ON [t1].[CustomerID] = [t0].[CustomerID]
    WHERE [t1].[CustomerID] = @p0
) AS j1 ON (j0.[OrderID] = j1.[OrderID])
-- @p0: Input NVarChar (Size = 5; Prec = 0; Scale = 0) [Bruce]
-- @p1: Input DateTime (Size = 0; Prec = 0; Scale = 0) [2010/8/11 19:51:59]

雖然跟我剛才手寫的SQL略有不同,但 Update 的邏輯是對的。有興趣的朋友不妨試試,Terry Aney在他的文章裡有很詳盡的介紹,這裡不再詳述。

相關博文:

總結

Linq To SQL 有很多地方值得探索的,Expression Tree 是探索的基礎, 嘿嘿!

完整程式碼(內含Terry Aney 的程式碼)

相關推薦

linq 批量更新刪除

方法一、官方例子 地球人都知道的,也是不少 Linq To SQL 反對者認為效率低下的一種方法。 NorthwindDataContext db = new NorthwindDataContext(); var customers = db.Customers.Wh

EF 批量更新/刪除資料

EF6增改刪等常用基類 11-21 4241 using System;using System.Linq;using System.Threading.Tasks;using System.Linq...

EF6 批量更新刪除資料

首先看改進前的版本以批量更新為例: [Obsolete] public void DeleteRoleUser2(string roleId) { IRepository<UserEntity> userRepo = Repos

[轉]EF 批量更新/刪除資料

其實之前遇到過EF批量更新的問題,先前已經寫過一篇(http://blog.csdn.net/afandaafandaafanda/article/details/44936075)來說明如何使用EF擴充套件庫來做批量更新操作,今天看到lee576寫的一篇EF如何做批量更新

MongoDB學習筆記~大叔分享批量新增—批量更新批量刪除

回到目錄 說它是批量操作,就是說將集合物件一次提交到伺服器,並對資料進行持久化,如果您的程式碼是一次一次的提交,那不算是批量操作!在之前的mongodb倉儲中並沒有對批量更新和批量刪除進行實現,而今天在專案中,還是實現了這種批量的操作,並且已經通過測試,下面公開一下原始碼 public

hibernate處理批量更新批量刪除

9.4 批量處理資料 通常,在一個Session物件的快取中只存放數量有限的持久化物件,等到Session物件處理事務完畢,還要關閉Session物件,從而及時釋放Session的快取佔用的記憶體。 批量處理資料是指在一個事務中處理大量資料。以下程式在一個事務中批量更新

JDBC批量插入、批量刪除批量更新

try {                Class.forName("com.mysql.jdbc.Driver");                conn = DriverManager.getConnection(o_url, userName, password);                c

Mybatis(Oracle)批量插入、批量更新批量刪除

mybatis對映中可以通過<foreach></foreach>標籤來實現Oracle的批量插入、更新和刪除     <foreach>標籤中主要有以下屬性:     collection、item、index、open、separat

EF結合SqlBulkCopy實現高效的批量資料插入 |EF外掛EntityFramework.Extended實現批量更新刪除

批量插入 (17597條資料批量插入耗時1.7秒) using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; na

在Hibernate應用中批量更新批量刪除

批量更新是指在一個事務中更新大批量資料,批量刪除是指在一個事務中刪除大批量資料。以下程式直接通過Hibernate API批量更新CUSTOMERS表中年齡大於零的所有記錄的AGE欄位: tx = s

MYSQL-JDBC批量新增-更新-刪除

目錄 1 概述 2 開啟MYSQL服務端日誌 3 深入MYSQL/JDBC批量插入 3.1 從一個例子出發 3.2 JDBC的批量插入操作 3.3 兩個常被忽略的問題

批量更新

data- art length 數據庫 con text blog 技術 net 項目中進行產品升級。數據庫有所變化,為了升級曾經的產品,在一個一個寫update腳本,最後發現是有規律的。!。。。還是應該多思考。差點就的多些好幾行啊 update ep

mysql 批量更新語句

sql enum line new when title class 批量更新 語句 UPDATE `表名` SET `字段` = CASE id WHEN 1 THEN 3 WHEN 2 THEN 4 WHE

JDBC高級特性(一)結果集,批量更新

事務 必須 -h eth mark cep tro cancel 相對定位 一、ResultSet的高級特性 1 可滾動ResultSet 1)向前和向後滾動 滾動特性 在JDBC初期版本號中, ResultSet僅能向前滾動 在JDBC興許版本號中,

C#批量更新mongodb符合條件的數據

div pda 第一條 匹配 upd lag god 批量更新 mongo 默認情況下只會更新匹配的第一條 jingjiaanalyurl.Update(Query.EQ("auid", jingjiaitem.id), Update.Set("aurlname",

SqlBulkCopy批量更新數據庫表用例

conn batch map nsa 更新數據 efault copy mapping using using (SqlBulkCopy sqlbulkcopy = new SqlBulkCopy(da.Connection as SqlConnection, SqlBu

MySql 批量更新語法

舉例 rom info round ber ner from sql數據庫 yam mysql數據庫在批量更新某表的字段時,語法如下: UPDATE A AS aINNER JOIN ( SELECT * FROMA WHERE....) AS b ON a.order

批量更新數據問題

file field 技術分享 query 循環數組 sql 簡寫 div 更新 同事最近遇到一個需要根據索引字段更新狀態數據的需求,而這個處理邏輯是循環查詢單條更新解決 主要代碼如下(簡寫): <?php foreach($array as $v) { q

mybatis批量更新兩種方式:1.修改值全部一樣 2.修改每條記錄值不一樣

nic mis str link eba encoding type 配置 tails Mybatis批量更新數據 mybatis批量更新兩種方式:1.修改值全部一樣 2.修改每條記錄值不一樣 mybatis批量更新兩種方式:

批量更新python庫

ins distrib spa 批量更新 () blog all pre style 1 import pip 2 from subprocess import call 3 4 for dist in pip.get_installed_distributions()