5

linqを使用してテーブルからレコードを一括削除したいのですが。それを行う方法を説明する投稿があります: LINQtoEntitiesでの一括削除

var query = from c in ctx.Customers
            where c.SalesPerson.Email == "..."
            select c;

query.Delete();

しかし、関数「削除」は私のvar変数に存在しません。
さらに、関数「SubmitChanges」は私のコンテキストには存在しません。

4

5 に答える 5

4

バッチ削除と更新を実行できる興味深いNuGetパッケージがあります

于 2012-07-21T13:23:43.347 に答える
4

現在、EntityFrameworkに組み込まれている一括削除はサポートされていません。実際、codeplexで議論されている機能の1つは、現在EFがオープンソースです。

EntityFramework.Extendedバッチ削除サポートを提供します(これはnugetで見つけることができます)が、私の経験では、いくつかのパフォーマンスの問題があります。

于 2012-07-21T13:27:34.370 に答える
2

このコードは、提供するエンティティフレームワーククエリ内で参照されるテーブル内のすべてのデータを一括削除する単純な拡張メソッドをDbContextに追加します。これは、クエリに含まれるすべてのテーブル名を抽出し、ほとんどの種類のデータベースに共通する「DELETEFROMtablename」SQLクエリを発行してデータを削除しようとするだけで機能します。

使用するには、次のようにします。

myContext.BulkDelete(x => x.Things);

これにより、Thingsエンティティストアにリンクされているテーブル内のすべてが削除されます。

コード:

using System.Linq;
using System.Text.RegularExpressions;

namespace System.Data.Entity {

    public static class DbContextExtensions {

        /// <summary>
        /// Efficiently deletes all data from any database tables used by the specified entity framework query. 
        /// </summary>
        /// <typeparam name="TContext">The DbContext Type on which to perform the delete.</typeparam>
        /// <typeparam name="TEntity">The Entity Type to which the query resolves.</typeparam>
        /// <param name="ctx">The DbContext on which to perform the delete.</param>
        /// <param name="deleteQuery">The query that references the tables you want to delete.</param>
        public static void BulkDelete<TContext, TEntity>(this TContext ctx, Func<TContext, IQueryable<TEntity>> deleteQuery) where TContext : DbContext {

            var findTables = new Regex(@"(?:FROM|JOIN)\s+(\[\w+\]\.\[\w+\])\s+AS");
            var qry = deleteQuery(ctx).ToString();

            // Get list of all tables mentioned in the query
            var tables = findTables.Matches(qry).Cast<Match>().Select(m => m.Result("$1")).Distinct().ToList();

            // Loop through all the tables, attempting to delete each one in turn
            var max = 30;
            var exception = (Exception)null;
            while (tables.Any() && max-- > 0) {

                // Get the next table
                var table = tables.First();

                try {
                    // Attempt the delete
                    ctx.Database.ExecuteSqlCommand(string.Format("DELETE FROM {0}", table));

                    // Success, so remove table from the list
                    tables.Remove(table);

                } catch (Exception ex) {
                    // Error, probably due to dependent constraint, save exception for later if needed.                    
                    exception = ex;

                    // Push the table to the back of the queue
                    tables.Remove(table);
                    tables.Add(table);
                }
            }

            // Error error has occurred, and cannot be resolved by deleting in a different 
            // order, then rethrow the exception and give up.
            if (max <= 0 && exception != null) throw exception;

        }
    }
}
于 2014-03-26T11:49:39.150 に答える
0

私はこのようにうまくいくようです。これが悪い習慣である理由があるかどうかを知らせてください。

        var customersToDelete = await ctx.Customers.Where(c => c.Email == "...").ToListAsync();

        foreach (var customerToDelete in customersToDelete)
        {
            ctx.Customers.Remove(customerToDelete);
        }

        await ctx.SaveChangesAsync();
于 2016-06-06T09:13:26.160 に答える
0

SaveChangesの呼び出し後にEFが何千ものDELETEクエリを実行するときに同じ問題が発生していました。EntityFramework.Extensions商用ライブラリが役立つかどうかわからなかったので、自分で一括DELETEを実装することにし、BG100のソリューションに似たものを思いつきました。

public async Task<List<TK>> BulkDeleteAsync(List<TK> ids)
{
    if (ids.Count < 1) {
        return new List<TK>();
    }

    // NOTE: DbContext here is a property of Repository Class
    // SOURCE: https://stackoverflow.com/a/9775744
    var tableName = DbContext.GetTableName<T>();
    var sql = $@"
        DELETE FROM {tableName}
        OUTPUT Deleted.Id
        // NOTE: Be aware that 'Id' column naming depends on your project conventions
        WHERE Id IN({string.Join(", ", ids)});
    ";
    return await @this.Database.SqlQuery<TK>(sql).ToListAsync();
}

汎用リポジトリのようなものがあれば、問題なく機能するはずです。少なくとも、EFインフラストラクチャにそれを適合させることを試みることができます。

また、もう少し調整して、エンティティの複数のチャンクに対してクエリを実行できるようにしました。DBにクエリサイズの制限がある場合に役立ちます。

const int ChunkSize = 1024;
public async Task<List<TK>> BulkDeleteAsync(List<TK> ids)
{
    if (ids.Count < 1) {
        return new List<TK>();
    }
    // SOURCE: https://stackoverflow.com/a/20953521/11344051
    var chunks = ids.Chunks(ChunkSize).Select(c => c.ToList()).ToList();
    var tableName = DbContext.GetTableName<T>();

    var deletedIds = new List<TK>();
    foreach (var chunk in chunks) {
        var sql = $@"
            DELETE FROM {tableName}
            OUTPUT Deleted.Id
            WHERE Id IN({string.Join(", ", chunk)});
        ";
        deletedIds.AddRange(DbContext.Database.SqlQuery<TK>(sql).ToListAsync());
    }
    return deletedIds;
}
于 2019-08-29T13:59:13.673 に答える