Entity FrameworkからのSqlException-セッションで他のスレッドが実行されているため、新しいトランザクションは許可されません


600

私は現在このエラーを受けています:

System.Data.SqlClient.SqlException:セッションで他のスレッドが実行されているため、新しいトランザクションは許可されません。

このコードを実行している間:

public class ProductManager : IProductManager
{
    #region Declare Models
    private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
    private RivWorks.Model.NegotiationAutos.RivFeedsEntities _dbFeed = RivWorks.Model.Stores.FeedEntities(AppSettings.FeedAutosEntities_connString);
    #endregion

    public IProduct GetProductById(Guid productId)
    {
        // Do a quick sync of the feeds...
        SyncFeeds();
        ...
        // get a product...
        ...
        return product;
    }

    private void SyncFeeds()
    {
        bool found = false;
        string feedSource = "AUTO";
        switch (feedSource) // companyFeedDetail.FeedSourceTable.ToUpper())
        {
            case "AUTO":
                var clientList = from a in _dbFeed.Client.Include("Auto") select a;
                foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
                {
                    var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
                    foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
                    {
                        if (companyFeedDetail.FeedSourceTable.ToUpper() == "AUTO")
                        {
                            var company = (from a in _dbRiv.Company.Include("Product") where a.CompanyId == companyFeedDetail.CompanyId select a).First();
                            foreach (RivWorks.Model.NegotiationAutos.Auto sourceProduct in client.Auto)
                            {
                                foreach (RivWorks.Model.Negotiation.Product targetProduct in company.Product)
                                {
                                    if (targetProduct.alternateProductID == sourceProduct.AutoID)
                                    {
                                        found = true;
                                        break;
                                    }
                                }
                                if (!found)
                                {
                                    var newProduct = new RivWorks.Model.Negotiation.Product();
                                    newProduct.alternateProductID = sourceProduct.AutoID;
                                    newProduct.isFromFeed = true;
                                    newProduct.isDeleted = false;
                                    newProduct.SKU = sourceProduct.StockNumber;
                                    company.Product.Add(newProduct);
                                }
                            }
                            _dbRiv.SaveChanges();  // ### THIS BREAKS ### //
                        }
                    }
                }
                break;
        }
    }
}

モデル#1-このモデルは、開発サーバーのデータベースにあります。 モデル#1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png

モデル#2-このモデルは、製品サーバーのデータベースにあり、自動フィードによって毎日更新されます。 代替テキストhttp://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png

注-モデル#1の赤い丸で囲まれた項目は、モデル#2への「マッピング」に使用するフィールドです。モデル#2の赤い丸は無視してください。これは、今答えられた別の質問からのものです。

注:isDeletedチェックを追加する必要があるので、クライアントの在庫がなくなった場合にDB1からソフト削除できます。

私がやりたいのは、この特定のコードで、DB1の会社をDB2のクライアントに接続し、DB2から製品リストを取得して、まだない場合はDB1に挿入することです。初めての場合は、在庫を完全に引き出す必要があります。毎晩新しい在庫がフィードに到着しない限り、何も起こらなくてもそこで実行されます。

だから大きな質問-私が得ているトランザクションエラーをどうやって解決するのですか?ループを実行するたびにコンテキストを削除して再作成する必要がありますか(意味がありません)?


6
これは私が今まで見た中で最も詳細な質問です。

9
まだストアドプロシージャを見逃している人はいますか?
デビッド

回答:


690

髪の毛を何度も抜いた後、foreachループが原因であることがわかりました。必要なのは、EFを呼び出すが、それをIList<T>そのターゲットタイプのに戻し、次にでループすることIList<T>です。

例:

IList<Client> clientList = from a in _dbFeed.Client.Include("Auto") select a;
foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
{
   var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
    // ...
}

14
うん、これも頭痛の種だった。問題を発見したとき、私はほとんど椅子から落ちました!私は問題の背後にある技術的な理由を理解していますが、これは直感的ではなく、開発者がblogs.msdn.com/brada/archive/2003/10/02/50420の
ジョーンズ博士、

9
大規模なデータセットのパフォーマンスはそれほど悪くありませんか?テーブルに数百万のレコードがある場合。ToList()はそれらすべてをメモリに吸い込みます。私はこの非常に問題に遭遇しており、次のことが可能かどうか疑問に思っていました。a)エンティティをデタッチします。c)新しいObjectContextでSaveChanges()を呼び出しますd)新しいObjectContextからエンティティを切り離しますe)古いObjectContextに接続し直します
Abhijeet Patel

150
問題はSaveChanges、DBから結果を取得している間は呼び出すことができないことです。したがって、別の解決策は、ループが完了したら変更を保存することです。
Drew Noakes、

4
かまれたので、これをMicrosoft Connectに追加しました:connect.microsoft.com/VisualStudio/feedback/details/612369/…お 気軽に投票してください。
Ian Mercer

36
開発者は、結果を考えずに.ToList()をLINQクエリに追加する傾向があります。これは、.ToList()を追加するのが初めての場合に便利です。
2011

267

すでに特定foreachしたように、アクティブなリーダーを介してデータベースからまだ描画している内からは保存できません。

呼び出すToList()ToArray()小さなデータセットの場合は、問題ありませんが、数千の行がある場合、大量のメモリを消費します。

行をチャンクでロードすることをお勧めします。

public static class EntityFrameworkUtil
{
    public static IEnumerable<T> QueryInChunksOf<T>(this IQueryable<T> queryable, int chunkSize)
    {
        return queryable.QueryChunksOfSize(chunkSize).SelectMany(chunk => chunk);
    }

    public static IEnumerable<T[]> QueryChunksOfSize<T>(this IQueryable<T> queryable, int chunkSize)
    {
        int chunkNumber = 0;
        while (true)
        {
            var query = (chunkNumber == 0)
                ? queryable 
                : queryable.Skip(chunkNumber * chunkSize);
            var chunk = query.Take(chunkSize).ToArray();
            if (chunk.Length == 0)
                yield break;
            yield return chunk;
            chunkNumber++;
        }
    }
}

上記の拡張メソッドを指定すると、次のようにクエリを記述できます。

foreach (var client in clientList.OrderBy(c => c.Id).QueryInChunksOf(100))
{
    // do stuff
    context.SaveChanges();
}

このメソッドを呼び出すクエリ可能なオブジェクトは、順序付けする必要があります。 これは、Entity Framework IQueryable<T>.Skip(int)が順序付けされたクエリでのみサポートするためです。これは、異なる範囲の複数のクエリが順序付けを安定させる必要があると考える場合に理にかなっています。順序が重要でない場合は、主キーで並べ替えてください。これは、クラスター化インデックスを持つ可能性が高いためです。

このバージョンは、100のバッチでデータベースにクエリを実行しますSaveChanges()。エンティティごとに呼び出されることに注意してください。

スループットを劇的に向上させたい場合は、呼び出しSaveChanges()頻度を少なくする必要があります。代わりに次のようなコードを使用してください。

foreach (var chunk in clientList.OrderBy(c => c.Id).QueryChunksOfSize(100))
{
    foreach (var client in chunk)
    {
        // do stuff
    }
    context.SaveChanges();
}

これにより、データベース更新呼び出しが100分の1になります。もちろん、これらの呼び出しはそれぞれ完了するまでに時間がかかりますが、最終的にはまだ先に進んでいます。あなたの走行距離は変わるかもしれませんが、これは私にとっては世界的に速いものでした。

そして、それはあなたが見ていた例外を回避します。

編集 SQLプロファイラーを実行した後でこの質問を再検討し、パフォーマンスを改善するためにいくつかの点を更新しました。興味のある方のために、DBによって何が作成されるかを示すサンプルSQLを以下に示します。

最初のループは何もスキップする必要がないため、より簡単です。

SELECT TOP (100)                     -- the chunk size 
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
FROM [dbo].[Clients] AS [Extent1]
ORDER BY [Extent1].[Id] ASC

後続の呼び出しでは、結果の前のチャンクをスキップする必要があるため、次のように使用しますrow_number

SELECT TOP (100)                     -- the chunk size
[Extent1].[Id] AS [Id], 
[Extent1].[Name] AS [Name], 
FROM (
    SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], row_number()
    OVER (ORDER BY [Extent1].[Id] ASC) AS [row_number]
    FROM [dbo].[Clients] AS [Extent1]
) AS [Extent1]
WHERE [Extent1].[row_number] > 100   -- the number of rows to skip
ORDER BY [Extent1].[Id] ASC

17
ありがとう。あなたの説明は、「回答済み」とマークされたものよりもはるかに役に立ちました。
Wagner da Silva

1
これは素晴らしい。たった1つ:列をクエリしてその列の値を更新する場合は、chunkNumber ++に注意する必要があります。。「ModifiedDate」列があり、.Where(x => x.ModifiedDate!= null)をクエリしていて、foreachの最後にModifiedDateの値を設定するとします。この方法では、レコードの半分がスキップされるため、レコードの半分を反復していません。
Arvand

残念ながら、巨大なデータセットではOutofMemoryExceptionが発生します。メモリ不足例外については、エンティティフレームワークの大きなデータセットの説明をご覧ください 。Entity FrameworkからSqlExceptionの
Michael Freidgeim

これでうまくいくと思います。var skip = 0; const int take = 100; List <Employee> emps; while((emps = db.Employees.Skip(skip).Take(take).ToList())。Count> 0){skip + = take; foreach(var emp in emps){//ここで何かを行う}}私はこれを答えに定式化しますが、それは以下の山積みの下に埋もれていて、この質問に関連しています。
jwize 2017年

123

Connectで開かれたバグに対する正式な返信を投稿しました。推奨される回避策は次のとおりです。

このエラーは、Entity FrameworkがSaveChanges()呼び出し中に暗黙のトランザクションを作成することが原因です。エラーを回避する最良の方法は、別のパターンを使用する(つまり、読み取りの最中に保存しない)か、トランザクションを明示的に宣言することです。考えられる3つの解決策を次に示します。

// 1: Save after iteration (recommended approach in most cases)
using (var context = new MyContext())
{
    foreach (var person in context.People)
    {
        // Change to person
    }
    context.SaveChanges();
}

// 2: Declare an explicit transaction
using (var transaction = new TransactionScope())
{
    using (var context = new MyContext())
    {
        foreach (var person in context.People)
        {
            // Change to person
            context.SaveChanges();
        }
    }
    transaction.Complete();
}

// 3: Read rows ahead (Dangerous!)
using (var context = new MyContext())
{
    var people = context.People.ToList(); // Note that this forces the database
                                          // to evaluate the query immediately
                                          // and could be very bad for large tables.

    foreach (var person in people)
    {
        // Change to person
        context.SaveChanges();
    }
} 

6
Transactionルートを使用する場合、TransactionScopeをスローするだけでは修正されない可能性があります。たとえば、実行している処理に長い時間がかかる可能性がある場合は、タイムアウトを延長することを忘れないでください。たとえば、コードをインタラクティブにデバッグする場合DB呼び出し。トランザクションタイムアウトを1時間に延長するコードを次に示します。使用(var transaction = new TransactionScope(TransactionScopeOption.Required、new TimeSpan(
1、0、0

「チュートリアルの道」から自分で実際の例に脱出したのは、初めてこのエラーに遭遇したときです。ただし、私にとっては、より単純なソリューションであるSAVE AFTER ITERATIONの方が優れています。(これは99%の場合にあてはまり、実際には1%だけがループ内でデータベースの保存を実行する必要があります)
スパイダーマン

キモい。このエラーに遭遇しました。非常に厄介です。2番目の提案は、SaveChangesをループに移動するとともに、私にとって魅力のように機能しました。ループの外で変更を保存する方が、変更をバッチ処理するのに適していると思いました。でも大丈夫。違いますか?!:(
ヤング氏

.NET 4.5では機能しませんでした。TransactionScopeを使用すると、「基になるプロバイダーがEnlistTransactionで失敗しました。{」というエラーが発生しました。パートナートランザクションマネージャーは、リモート/ネットワークトランザクションのサポートを無効にしました。(HRESULTからの例外:0x8004D025) "}"。私はイテレーションの外で仕事をしてしまう。
Diganta Kumar 2013

TransactionScopeを使用すると、テーブルがトランザクション全体の時間中ロックされるため、危険です。
Michael Freidgeim、2016年

19

実際、foreachEntity Frameworkを使用してC#のループ内で変更を保存することはできません。

context.SaveChanges() メソッドは、通常のデータベースシステム(RDMS)でのコミットのように機能します。

すべての変更(Entity Frameworkがキャッシュする)を行ってからSaveChanges()、データベースコミットコマンドのように、ループの後に(ループの外で)呼び出しを一度にすべて保存します。

すべての変更を一度に保存できる場合、これは機能します。


2
ここで「通常のデータベースシステム(RDMS)」を見るのは興味深いと思いました
Dinerdo

1
EFのコンテキストの90%でSaveChangesを繰り返し呼び出すのは問題ないので、これは間違っているようです。
Pxtl 2019

foreachループがdbエンティティを反復処理しない限り、SaveChangesを繰り返し呼び出すのは問題ないようです。
ケルバサウルス

1
ああ!for-eachループ内にコンテキストを入れてください!(pffft ...私は何を考えていましたか?..)ありがとう!
Adam Cox

18

context.SaveChanges()あなたのforeach(ループ)の終わりの後に置くだけです。


これは、私の内部でforeachを保存したため、私が見つけたより良いオプションです
Almeida

2
これは常にオプションとは限りません。
Pxtl

9

常に選択をリストとして使用する

例えば:

var tempGroupOfFiles = Entities.Submited_Files.Where(r => r.FileStatusID == 10 && r.EventID == EventId).ToList();

次に、変更を保存しながらコレクションをループします

 foreach (var item in tempGroupOfFiles)
             {
                 var itemToUpdate = item;
                 if (itemToUpdate != null)
                 {
                     itemToUpdate.FileStatusID = 8;
                     itemToUpdate.LastModifiedDate = DateTime.Now;
                 }
                 Entities.SaveChanges();

             }

1
これはまったく良い習慣ではありません。頻繁にSaveChangesを実行する必要がない場合は実行しないでください。また、「常に選択内容をリストとして使用する」ことは絶対に避けてください
Dinerdo

@Dinerdoそれは本当にシナリオに依存します。私の場合、2つのforeachループがあります。外部の1つは、リストとしてdbクエリを持っていました。たとえば、これはハードウェアデバイスをトラバースします。内部のforeachは、各デバイスからいくつかのデータを取得します。要件ごとに、各デバイスから1つずつ取得したデータをデータベースに保存する必要があります。プロセスの最後にすべてのデータを保存するオプションではありません。同じエラーが発生しましたが、mzonerzのソリューションは機能しました。
jstuardo

@jstuardoバッチ処理でも?
Dinerdo

@Dinerdo私はそれが哲学的なレベルで良い習慣ではないことに同意します。ただし、forループ内でコードが別のメソッド(たとえばAddToLog()メソッド)を呼び出す状況がいくつかあります。このメソッドには、ローカルでdb.SaveChanges()への呼び出しが含まれています。この状況では、db.Save Changesの呼び出しを実際に制御することはできません。この場合、ToList()または類似の構造体を使用すると、mzonerzの提案どおりに機能します。ありがとう!
A. Varma

実際には、これは役立つよりもあなたを傷つけるでしょう。私は言ったことを支持します-ToList()は常に使用するべきではありません。高性能のアプリでは、すべてのアイテムの後に変更を保存することは可能な限り避けなければなりません。これは一時修正IMOです。ロギング方法が何であっても、理想的にはバッファリングを利用する必要があります。
Dinerdo

8

参考までに:本と、そのスタイルが有効であるために調整された一部の行から:

SaveChanges()メソッドを呼び出すと、トランザクションが開始され、反復が完了する前に例外が発生すると、データベースに保持されているすべての変更が自動的にロールバックされます。それ以外の場合、トランザクションはコミットします。特に大量のエンティティを更新または削除する場合は、反復が完了した後ではなく、各エンティティの更新または削除後にメソッドを適用したくなるかもしれません。

すべてのデータが処理される前にSaveChanges()を呼び出そうとすると、「セッションで実行中の他のスレッドがあるため、新しいトランザクションは許可されません」という例外が発生します。例外は、SQL Serverが、SqlDataReaderが開いている接続で新しいトランザクションを開始することを許可していないために発生します。

なぜ物事が起こっているのかを理解する方が良い場合があります;-)


1
これを回避する良い方法は、リーダーを開いて2つ目のリーダーを開き、それらの操作を2つ目のリーダーに置くことです。これは、エンティティフレームワークでマスター/詳細を更新するときに必要になる可能性があります。マスターレコードの最初の接続を開き、詳細レコードの2番目の接続を開きます。読んでいるだけなら問題はないはずです。問題は更新中に発生します。
Herman Van Der Blom 14

役立つ説明。そうです、なぜ物事が起こっているのかを理解するのは良いことです。
Dov Miller


5

これと同じ問題が発生していましたが、状況が異なりました。リストボックスにアイテムのリストがありました。ユーザーはアイテムをクリックして[削除]を選択できますが、アイテムの削除には多くのロジックが含まれているため、ストアドプロシージャを使用してアイテムを削除しています。ストアドプロシージャを呼び出すと、削除は正常に機能しますが、今後SaveChangesを呼び出すとエラーが発生します。私の解決策は、EFの外部でストアドプロシージャを呼び出すことでしたが、これはうまくいきました。何らかの理由で、EFの方法でストアドプロシージャを呼び出すと、何かが開いたままになります。


3
最近同様の問題がありました。私の場合の理由は、SELECT空の結果セットを生成するストアドプロシージャのステートメントであり、その結果セットが読み取られなかった場合、SaveChangesその例外がスローされました。
2014

SPからの未読の結果と同じこと、ヒントをありがとう)
Pavel K

4

foreachループでSaveChanges()を呼び出すことができる別の2つのオプションを次に示します。

最初のオプションは、1つのDBContextを使用して繰り返し処理するリストオブジェクトを生成し、次に2番目のDBContextを作成してSaveChanges()を呼び出すことです。次に例を示します。

//Get your IQueryable list of objects from your main DBContext(db)    
IQueryable<Object> objects = db.Object.Where(whatever where clause you desire);

//Create a new DBContext outside of the foreach loop    
using (DBContext dbMod = new DBContext())
{   
    //Loop through the IQueryable       
    foreach (Object object in objects)
    {
        //Get the same object you are operating on in the foreach loop from the new DBContext(dbMod) using the objects id           
        Object objectMod = dbMod.Object.Find(object.id);

        //Make whatever changes you need on objectMod
        objectMod.RightNow = DateTime.Now;

        //Invoke SaveChanges() on the dbMod context         
        dbMod.SaveChanges()
    }
}

2番目のオプションは、DBContextからデータベースオブジェクトのリストを取得することですが、IDのみを選択します。次に、ID(おそらくint)のリストを反復処理して、各intに対応するオブジェクトを取得し、そのようにSaveChanges()を呼び出します。このメソッドの背後にあるアイデアは、整数の大きなリストを取得することであり、dbオブジェクトの大きなリストを取得してオブジェクト全体で.ToList()を呼び出すよりもはるかに効率的です。このメソッドの例を次に示します。

//Get the list of objects you want from your DBContext, and select just the Id's and create a list
List<int> Ids = db.Object.Where(enter where clause here)Select(m => m.Id).ToList();

var objects = Ids.Select(id => db.Objects.Find(id));

foreach (var object in objects)
{
    object.RightNow = DateTime.Now;
    db.SaveChanges()
}

これは私が考え、実行した素晴らしい代替案ですが、これは賛成する必要があります。注:i)非常に大きなセットに適している列挙可能なものとして反復できます。ii)NoTrackingコマンドを使用して、非常に多くのレコードのロードに関する問題を回避できます(それがシナリオの場合)。iii)主キーのみのオプションも本当に気に入っています。メモリにロードするデータがはるかに少ないため、非常にスマートですが、潜在的に動的な基礎となるデータセットでTake / Skipを処理していません。
トッド

4

foreachが原因でこのエラーが発生し、最初に1つのエンティティをループ内に保存し、生成されたIDをループ内でさらに使用する必要がある場合、私の場合と同様に、最も簡単な解決策は、IDを返し、使用するエンティティを挿入する別のDBContextを使用することです。外部IDのこのID

例えば

    using (var context = new DatabaseContext())
    {
        ...
        using (var context1 = new DatabaseContext())
        {
            ...
               context1.SaveChanges();
        }                         
        //get id of inserted object from context1 and use is.   
      context.SaveChanges();
   }

2

だから、このプロジェクトでは、私は問題はでなかったこの正確に同じ問題が持っていたし、foreachまたは.toList()それは我々が使用AutoFac構成で実際にしました。これにより、上記のエラーがスローされただけでなく、他の同等のエラーが多数スローされたという、奇妙な状況が発生しました。

これは私たちの修正でした:これを変更しました:

container.RegisterType<DataContext>().As<DbContext>().InstancePerLifetimeScope();
container.RegisterType<DbFactory>().As<IDbFactory>().SingleInstance();
container.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();

に:

container.RegisterType<DataContext>().As<DbContext>().As<DbContext>();
container.RegisterType<DbFactory>().As<IDbFactory>().As<IDbFactory>().InstancePerLifetimeScope();
container.RegisterType<UnitOfWork>().As<IUnitOfWork>().As<IUnitOfWork>();//.InstancePerRequest();

問題が何だったと思いますか、詳しく説明していただけますか?あなたは毎回新しいDbcontextを作成することでこれを解決しましたか?
eran otzap 2016年

2

私はそれが古い質問であることを知っていますが、私は今日このエラーに直面しました。

データベーステーブルトリガーがエラーを受け取ったときに、このエラーがスローされる可能性があることを発見しました。

参考までに、このエラーが発生したときにテーブルトリガーも確認できます。


2

巨大なResultSetを読み取り、テーブル内のいくつかのレコードを更新する必要がありました。Drew Noakes回答で提案されているように、チャンクを使用しようとしました 。

残念ながら、50000レコードの後、OutofMemoryExceptionが発生しました。エンティティフレームワークの大規模なデータセット、メモリ不足の例外の答えは、

EFは、変更の検出に使用するデータの2番目のコピーを作成します(データベースへの変更を永続化できるようにするため)。EFは、この2番目のセットをコンテキストの存続期間保持し、このセットがメモリ不足に陥ります。

バッチごとにコンテキストを再作成することをお勧めします。

したがって、主キーの最小値と最大値を取得しました。テーブルには自動インクリメンタル整数として主キーがあります。次に、各チャンクのコンテキストを開いて、データベースのレコードのチャンクから取得しました。チャンクコンテキストの処理後、メモリを閉じて解放します。メモリ使用量が増加していないことを保証します。

以下は私のコードからの抜粋です:

  public void ProcessContextByChunks ()
  {
        var tableName = "MyTable";
         var startTime = DateTime.Now;
        int i = 0;
         var minMaxIds = GetMinMaxIds();
        for (int fromKeyID= minMaxIds.From; fromKeyID <= minMaxIds.To; fromKeyID = fromKeyID+_chunkSize)
        {
            try
            {
                using (var context = InitContext())
                {   
                    var chunk = GetMyTableQuery(context).Where(r => (r.KeyID >= fromKeyID) && (r.KeyID < fromKeyID+ _chunkSize));
                    try
                    {
                        foreach (var row in chunk)
                        {
                            foundCount = UpdateRowIfNeeded(++i, row);
                        }
                        context.SaveChanges();
                    }
                    catch (Exception exc)
                    {
                        LogChunkException(i, exc);
                    }
                }
            }
            catch (Exception exc)
            {
                LogChunkException(i, exc);
            }
        }
        LogSummaryLine(tableName, i, foundCount, startTime);
    }

    private FromToRange<int> GetminMaxIds()
    {
        var minMaxIds = new FromToRange<int>();
        using (var context = InitContext())
        {
            var allRows = GetMyTableQuery(context);
            minMaxIds.From = allRows.Min(n => (int?)n.KeyID ?? 0);  
            minMaxIds.To = allRows.Max(n => (int?)n.KeyID ?? 0);
        }
        return minMaxIds;
    }

    private IQueryable<MyTable> GetMyTableQuery(MyEFContext context)
    {
        return context.MyTable;
    }

    private  MyEFContext InitContext()
    {
        var context = new MyEFContext();
        context.Database.Connection.ConnectionString = _connectionString;
        //context.Database.Log = SqlLog;
        return context;
    }

FromToRangeは、FromプロパティとToプロパティを持つ単純な構造です。


私はあなたがあなたの文脈をどのように「更新」しているのかを見ることができませんでした。チャンクごとに新しいコンテキストを作成しているようです。
Suncat2000

Suncat2000 @、あなたは正しい、コンテキストが短い生活のオブジェクトでなければなりませんstackoverflow.com/questions/43474112/...
マイケルFreidgeim

2

「セッションで他のスレッドが実行されているため、新しいトランザクションは許可されません」というエラーが表示され始めましたEF5からEF6に移行した後ました。

Googleが私たちをここに連れてきましたがSaveChanges()、ループの内部では呼び出していません。DBから読み取るforeachループ内でObjectContext.ExecuteFunctionを使用してストアドプロシージャを実行すると、エラーが発生しました。

ObjectContext.ExecuteFunctionを呼び出すと、関数がトランザクションにラップされます。すでに開いているリーダーがあるときにトランザクションを開始すると、エラーが発生します。

以下のオプションを設定することで、トランザクションでのSPのラップを無効にすることができます。

_context.Configuration.EnsureTransactionsForFunctionsAndCommands = false;

このEnsureTransactionsForFunctionsAndCommandsオプションを使用すると、独自のトランザクションを作成せずにSPを実行でき、エラーは発生しなくなります。

DbContextConfiguration.EnsureTransactionsForFunctionsAndCommandsプロパティ


1

私も同じ問題に直面していました。

これが原因と解決策です。

http://blogs.msdn.com/b/cbiyikoglu/archive/2006/11/21/mars-transactions-and-sql-error-3997-3988-or-3983.aspx

挿入、更新などのデータ操作コマンドを実行する前に、以前のアクティブなSQLリーダーをすべて閉じていることを確認してください。

最も一般的なエラーは、dbからデータを読み取り、値を返す関数です。たとえば、isRecordExistのような関数です。

この場合、レコードを見つけてリーダーを閉じるのを忘れると、関数からすぐに戻ります。


7
Entity Frameworkで「リーダーを閉じる」とはどういう意味ですか?var result = from customer in myDb.Customersのようなクエリには目に見えるリーダーはありません。ここで、customer.Id == customerId select customer; return result.FirstOrDefault();
Anthony

@Anthony他の答えが言うように、EFを使用してLINQクエリ(IQueryable)を列挙する場合、基になるDataReaderは最後の行が反復されるまで開いたままになります。ただし、MARSは接続文字列で有効にする重要な機能ですが、OPの問題はまだMARSだけでは解決されません。問題は、基になるDataReaderがまだ開いている間にSaveChangesを試行することです。
トッド

1

以下のコードは私のために働きます:

private pricecheckEntities _context = new pricecheckEntities();

...

private void resetpcheckedtoFalse()
{
    try
    {
        foreach (var product in _context.products)
        {
            product.pchecked = false;
            _context.products.Attach(product);
            _context.Entry(product).State = EntityState.Modified;
        }
        _context.SaveChanges();
    }
    catch (Exception extofException)
    {
        MessageBox.Show(extofException.ToString());

    }
    productsDataGrid.Items.Refresh();
}

2
SOへようこそ!これが機能する理由を説明する説明やリンクを追加することを検討してください。コードのみの回答は、通常、SOには適さないと見なされます。
codeMagic 2014年

1

私の場合、EF経由でストアドプロシージャを呼び出したときに問題が発生し、後でSaveChangesがこの例外をスローしました。問題はプロシージャの呼び出しにあり、列挙子は破棄されませんでした。私は次の方法でコードを修正しました:

public bool IsUserInRole(string username, string roleName, DataContext context)
{          
   var result = context.aspnet_UsersInRoles_IsUserInRoleEF("/", username, roleName);

   //using here solved the issue
   using (var en = result.GetEnumerator()) 
   {
     if (!en.MoveNext())
       throw new Exception("emty result of aspnet_UsersInRoles_IsUserInRoleEF");
     int? resultData = en.Current;

     return resultData == 1;//1 = success, see T-SQL for return codes
   }
}

0

パーティーにはかなり遅れましたが、今日は同じエラーに直面し、解決方法は簡単でした。私のシナリオは、ネストされたfor-eachループ内でDBトランザクションを作成していたこの特定のコードに似ていました。

問題は、シングルDBトランザクションはfor-eachループよりも少し時間がかかるため、前のトランザクションが完了しないと、新しいトラクションが例外をスローするため、解決策はfor-eachループで新しいオブジェクトを作成することですここで、dbトランザクションを作成しています。

上記のシナリオでは、ソリューションは次のようになります。

foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
                {
private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
                    if (companyFeedDetail.FeedSourceTable.ToUpper() == "AUTO")
                    {
                        var company = (from a in _dbRiv.Company.Include("Product") where a.CompanyId == companyFeedDetail.CompanyId select a).First();
                        foreach (RivWorks.Model.NegotiationAutos.Auto sourceProduct in client.Auto)
                        {
                            foreach (RivWorks.Model.Negotiation.Product targetProduct in company.Product)
                            {
                                if (targetProduct.alternateProductID == sourceProduct.AutoID)
                                {
                                    found = true;
                                    break;
                                }
                            }
                            if (!found)
                            {
                                var newProduct = new RivWorks.Model.Negotiation.Product();
                                newProduct.alternateProductID = sourceProduct.AutoID;
                                newProduct.isFromFeed = true;
                                newProduct.isDeleted = false;
                                newProduct.SKU = sourceProduct.StockNumber;
                                company.Product.Add(newProduct);
                            }
                        }
                        _dbRiv.SaveChanges();  // ### THIS BREAKS ### //
                    }
                }

0

私は少し遅れましたが、私にもこのエラーがありました。値がどこで更新されるかを確認することで問題を解決しました。

私のクエリは間違っていて、250以上の編集が保留されていることがわかりました。だから私は私のクエリを修正し、今それは正しく動作します。

だから私の状況では:クエリが返す結果をデバッグして、エラーがないかクエリを確認します。その後、クエリを修正します。

これが将来の問題の解決に役立つことを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.