0

私が指定してもカントー、私のデータベースコンテキストが安全、過渡スレッドされていないことが表示されます:データベースのスタートアップコンフィギュレーションのEntity Frameworkのコア - スレッドセーフではありません - ServiceLifetime.Transient

ServiceLifetime.Transient 

を。

Startup.cs

services.AddEntityFrameworkSqlServer().AddDbContext<DatabaseContext>((serviceProvider, options) => options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking).UseSqlServer(connectionString).UseInternalServiceProvider(serviceProvider), ServiceLifetime.Transient); 

アピ

[HttpPost("GetOrganisations")] //Yes post, dont harass me :) 
public async Task<IActionResult> GetOrganisations([FromBody] GetOrganisationsModel model) 
{ 
    Task<IEnumerable<OrganisationModel>> organisations = _organisationService.GetOrganisations(model?.Id, model?.StatusIds); 

    Task<int> organisationTotalCount = _organisationService.GetOrganisationCount(); 

    await Task.WhenAll(organisations, organisationTotalCount); 

    return Ok(new OrganisationViewModel 
    { 
     Organisations = await organisations, 
     OrganisationTotalCount = await organisationTotalCount 
    }); 
} 

レポ

public class OrganisationRepository : IOrganisationRepository 
{ 
    private readonly DatabaseContext _database; 

    public OrganisationRepository(DatabaseContext database) 
    { 
     _database = database; 
    } 

    public async Task<List<OrganisationEntity>> GetOrganisations(int? organisationId, List<int> statusIds) 
    { 
     IQueryable<OrganisationEntity> organisations = Database.Organisation.Include(o => o.Status).OrderByDescending(d => d.Created).AsQueryable(); 

     if (organisationId != null) 
     { 
      organisations = organisations.Where(o => o.Id == organisationId); 
     } 

     if (statusIds != null && statusIds.Count > 0) 
     { 
      organisations = organisations.Where(o => statusIds.Contains(o.StatusId)); 
     } 

     return await organisations.ToListAsync(); 
    } 

    public async Task<int> GetOrganisationCount() 
    { 
     return await Database.Organisation.CountAsync(); 
    } 
} 

例外

System.InvalidOperationException:接続が閉じられていません。 接続の現在の状態が接続しています。

ここでの問題は、私はGetOrganisations()とGetOrganisationCount()非同期呼び出し、何らかの理由で、(と思う)のために、同じデータベース・コンテックスが..私はと思っ両方の呼び出し、(異なるスレッド)のために使用されていることですオプションを追加することによって:

ServiceLifetime.Transient 

を(デフォルトはスコープされているので)それは、私の問題を解決するだろうが、無...

しかし、私は文を使用して追加した場合..

public async Task<List<OrganisationEntity>> GetOrganisations(int? organisationId, List<int> statusIds) 
{ 
    var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>(); 
    optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking) 
     .UseSqlServer(
      "Data Source=.;Initial Catalog=MYDATABASE;Integrated Security=True;Connect Timeout=30;"); 

    using (var db = new DatabaseContext(optionsBuilder.Options, null)) 
    { 
     IQueryable<OrganisationEntity> organisations = 
      db.Organisation.Include(o => o.Status).OrderByDescending(d => d.Created).AsQueryable(); 

     if (organisationId != null) 
     { 
      organisations = organisations.Where(o => o.Id == organisationId); 
     } 

     if (statusIds != null && statusIds.Count > 0) 
     { 
      organisations = organisations.Where(o => statusIds.Contains(o.StatusId)); 
     } 

     return await organisations.ToListAsync(); 
    } 
} 

public async Task<int> GetOrganisationCount() 
{ 
    var optionsBuilder = new DbContextOptionsBuilder<DatabaseContext>(); 
    optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking) 
     .UseSqlServer(
      "Data Source=.;Initial Catalog=MYDATABASE;Integrated Security=True;Connect Timeout=30;"); 

    using (var db = new DatabaseContext(optionsBuilder.Options, null)) 
    { 
     return await db.Organisation.CountAsync(); 
    } 
} 

すべて機能します!

なぜ依存性注入が正常に機能しないのですか?

私は完全に間違っていますか?私は一時的なコンテキストを必要とするなら、私はすべての私の仕事のためにステートメントを使うことを余儀なくされましたか?

ServiceLifetime.Transient bugged ?!

編集: System.Data.ProviderBase.DbConnectionClosedConnecting.TryOpenConnection(たDbConnection outerConnection、DbConnectionFactoryはconnectionFactory、 TaskCompletionSource 1 retry, DbConnectionOptions userOptions)\r\n
at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource
1で

Stacktrade、(いくつかの情報がマスクされた)

」 再試行)\ r \ nで System.Data.SqlClient.SqlConnection.OpenAsync(CancellationToken cancellationToken)\ r \ n ---前の場所からのスタックトレースの末尾 例外がスローされました--- \ r \ n: System.Runtime.ExceptionServices.ExceptionDispatchInfo。スロー()\ R \ n System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotificationで
(タスク タスク)\ R \ nは Microsoft.EntityFrameworkCore.Storage.RelationalConnection.d__31.MoveNext()で\ R \ N-- - 前位置からスタックトレースの終了例外がスローされた場合 --- \ Rの\ N System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throwで()\ R \ n System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotificationで
(タスク タスク)\ r \ n Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable.AsyncEnumerator.d__9.MoveNext()\ r \ n --- スタックトレースの終わりからprevio私たちの場所に例外がスローされた場所 --- \ rをする\ nはSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throwで()\ rを\ nは System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(タスク タスク)で
\ rを\ n Microsoft.EntityFrameworkCore.Storage.Internal.SqlServerExecutionStrategy.d__6 2.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable.AsyncEnumerator.<MoveNext>d__8.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.EntityFrameworkCore.Query.AsyncQueryMethodProvider.<GetResult>d__16
1.MoveNext()\ r \ n --- 例外がスローされた前の場所からのスタックトレースの末尾 ---システムで\ r \ n .Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \ n
( )System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(タスク タスク)\ r \ n Microsoft.EntityFrameworkCore.Query.Internal.TaskResultAsyncEnumerable 1.Enumerator.<MoveNext>d__3.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor
1.EnumeratorExceptionInterceptor.d__5.MoveNext()\ r \ n 例外がスローされた前の場所からのスタックトレースの末尾 ---システムで\ r \ n。 Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ R \ n System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotificationで
(タスク タスク)\ R \ nは 1.GetResult()\r\n at XXXXXXX.Data.Repositories.Organisation.OrganisationRepository.<GetOrganisationCount>d__8.MoveNext() in C:\\Users\\XXXXXXX\\Documents\\Visual Studio 2017\\Projects\\XXXXXXX\\XXXXXXX.Data\\Repositories\\Organisation\\OrganisationRepository.cs:line 65\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter
System.Runtime.CompilerServices.TaskAwaiter 1.GetResultで() \\ \ nで XXXXXX.Api.Services.Organisation.OrganisationService.d__4.MoveNext() C:\ Users \ XXXXXX \ Documents \ Visual Studio 2017 \ Projects \ XXXXXXX \ XXXXXXX.Api \ Services \ Organisa \ 012 \ 169 \ r \ n ---例外 がスローされた前の場所からのスタックトレースの終了--- \ r \ n にSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\ r \ N
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotificationで(タスク タスク)\ R \ n System.Runtime.CompilerServices.TaskAwaiter.GetResultで()\ R \ n XXXXXXX.Api.Controllers.Administrationで.OrganisationController.d__5.MoveNext() C:¥Users¥XXXXXX¥Documents¥Visual Studio 2017 \ Projects \ XXXXXX \ XXXXXXX.Api \ Controllers \ Administration \ OrganisationController.cs:行 51 "

答えて

1

ServiceLifetime.Transient bugged ?!

バグではありません。 DbContextはスレッドセーフではないことが分かっているので、両方のメソッド呼び出しは同じOrganisationServiceインスタンスを使用します。このインスタンスは、同じインスタンスDatabaseContextを使用して作成されました。

Task<IEnumerable<OrganisationModel>> organisations = 
    _organisationService.GetOrganisations(model?.Id, model?.StatusIds); 

Task<int> organisationTotalCount = _organisationService.GetOrganisationCount(); 

これは、スレッドスイッチング環境でのEntity FrameworkのDbContextの制限です。そのため、我々は以下の伝統的なアプローチを使用する - リポジトリ内のアップ

[HttpPost("GetOrganisations")] 
public async Task<IActionResult> GetOrganisations([FromBody] GetOrganisationsModel model) 
{ 
    var organisations = 
     await _organisationService.GetOrganisations(model?.Id, model?.StatusIds); 

    var organisationTotalCount = await _organisationService.GetOrganisationCount(); 

    return Ok(new OrganisationViewModel 
    { 
     Organisations = organisations, 
     OrganisationTotalCount = organisationTotalCount 
    }); 
} 

NEWINGの下振れすることは、彼らがthisのようなリポジトリしっかりとカップルになり、あなたがすることはできませんユニットテストです。 Task.WhenAll対ユニットテストの中からという特定のシナリオを選択する必要がある場合は、Unit Testを選択します。

あなた自身がTask.WhenAllをたくさん使っていると思うなら、EFと一緒に使うことができるDapper ORMを見てください。

+0

Aha .. Okey ..実際に私はWhenAllを使わずに待っていましたが、間違っていないと、WhenAll、GetOrganisationCount()はgetOrganisations()が完了した後にのみ実行されます。 (明白にするために) – Reft

+0

'await'を使うと、' GetOrganisationCount'は 'GetOrganisations'が終了した後でのみ呼び出されます。 – Win

関連する問題