Entity Framework (EF) 標準の更新処理では、アトミックインクリメント(デクリメントも)や一括更新(BULK UPDATE/DELETE)ができない。
一括処理については、条件に当てはまるものを select 後に update ないし delete は可能だが、件数が多い場合はかなり効率の悪い処理になる。(select → update/delete × 件数)
そこだけ文字列 SQL 使えば解決・・・なんだけど、せっかく EF 使ってるので文字列 SQL は避けたい、という人は多いと思う。
そういう要望に応えるライブラリがいくつかある。(非 MS 製)
ここでは、一番よく使われているであろう EntityFramework.Extended を触ってみる。
※ EntityFramework.Utilities は EF 6.0 以降がメインだし、ZZZ は有料なので
[環境]
つまり、DbContext.SaveChanges と無関係のトランザクションになる。
EF 標準の更新処理と EF.Extended による更新処理を混ぜる場合は、自分でトランザクションを管理する必要がある。
[トランザクション使用例]
※ /Source/EntityFramework.Extended/Batch 配下に SqlServerBatchRunner.cs しかないため
ただし、EF の接続オブジェクト ObjectContext.Connection を利用するため、他の DB も一応は使用可能。
(その DB が扱えない SQL が構築されると、DB 側でエラーになる。)
一括処理については、条件に当てはまるものを select 後に update ないし delete は可能だが、件数が多い場合はかなり効率の悪い処理になる。(select → update/delete × 件数)
そこだけ文字列 SQL 使えば解決・・・なんだけど、せっかく EF 使ってるので文字列 SQL は避けたい、という人は多いと思う。
そういう要望に応えるライブラリがいくつかある。(非 MS 製)
- loresoft/EntityFramework.Extended · GitHub
- MikaelEliasson/EntityFramework.Utilities · GitHub
- ZZZ Projects
ここでは、一番よく使われているであろう EntityFramework.Extended を触ってみる。
※ EntityFramework.Utilities は EF 6.0 以降がメインだし、ZZZ は有料なので
[環境]
- Visual Studio 2010 SP1
- .NET 4.5.2 (TargetFramework は 4.0)
- Entity Framework 5.0
- EntityFramework.Extended 5.0.0.73
EntityFramework.Extended サンプルコード
[コード]
// テーブル定義
public partial class CountTable
{
public string Name { get; set; }
public int Value { get; set; }
public System.DateTime Updated { get; set; }
}
static class EExfTest
{
// アトミックインクリメント
public static void UpdateIncrement()
{
using( var context = new TestEntities() ){
context.CountTables.Update(
item => item.Name == "aaa",
item => new CountTable {
Value = item.Value + 1,
Updated = EntityFunctions.AddDays(item.Updated, 1).Value
}
);
}
// Update は IQueryable に対する拡張メソッド
// 第 1 引数は where 条件、第 2 引数は set の中身 (オブジェクト初期化子で指定する)
// 両引数は式木として解釈される
// where 条件を指定しないオーバーロードもあり
//
// 数値のインクリメントだけでなく、EntityFunctions を使って日付のインクリメントも可能
}
// 一括変更
public static void BulkUpdate()
{
using( var context = new TestEntities() ){
context.CountTables.Update(
item => new CountTable {
Value = 0
}
);
}
}
// 一括削除
public static void BulkDelete()
{
using( var context = new TestEntities() ){
context.CountTables
.Where(item => item.Value < 1)
.Delete();
}
// Delete は IQueryable に対する拡張メソッド
// 第 1 引数(where 条件)を指定するオーバーロードもあり
}
}
[実行される SQL]
-- UpdateIncrement
UPDATE [dbo].[CountTable] SET
[Value] = [Value] + 1 ,
[Updated] = DATEADD (day, 1, [Updated])
FROM [dbo].[CountTable] AS j0 INNER JOIN (
SELECT
1 AS [C1],
[Extent1].[Name] AS [Name]
FROM [dbo].[CountTable] AS [Extent1]
WHERE 'aaa' = [Extent1].[Name]
) AS j1 ON (j0.[Name] = j1.[Name])
-- BulkUpdate
-- ※実際は sp_executesql で実行
UPDATE [dbo].[CountTable] SET
[Value] = 0
FROM [dbo].[CountTable] AS j0 INNER JOIN (
SELECT
1 AS [C1],
[Extent1].[Name] AS [Name]
FROM [dbo].[CountTable] AS [Extent1]
) AS j1 ON (j0.[Name] = j1.[Name]
-- BulkDelete
DELETE [dbo].[CountTable]
FROM [dbo].[CountTable] AS j0 INNER JOIN (
SELECT
[Extent1].[Value] AS [Value],
[Extent1].[Name] AS [Name]
FROM [dbo].[CountTable] AS [Extent1]
WHERE [Extent1].[Value] < 1
) AS j1 ON (j0.[Name] = j1.[Name])
EntityFramework.Extended のトランザクション
EF.Extended の Update と Delete は、ExecuteSqlCommand と同じく即時 SQL が実行される。つまり、DbContext.SaveChanges と無関係のトランザクションになる。
EF 標準の更新処理と EF.Extended による更新処理を混ぜる場合は、自分でトランザクションを管理する必要がある。
[トランザクション使用例]
using( var context = new TestEntities() )
using( var tx = context.BeginTransaction() ){
// 更新処理
context.SaveChanges();
tx.Commit();
}
// BeginTransaction も EF.Extended にある拡張メソッド
// EF 6.0 からは context.Database.BeginTransaction() が可能になったので、Obsolete に
EntityFramework.Extended の対応 DB
現在(2015/04/13)の EF.Extended のソースを見る限り、SQL Server にしか対応してない。※ /Source/EntityFramework.Extended/Batch 配下に SqlServerBatchRunner.cs しかないため
ただし、EF の接続オブジェクト ObjectContext.Connection を利用するため、他の DB も一応は使用可能。
(その DB が扱えない SQL が構築されると、DB 側でエラーになる。)
0 件のコメント:
コメントを投稿