0

私は、EFCore2.0と2クライアントを同時に使用してアクションメソッドにアクセスしようとしているWebApiを持っています... すべてが1つのクライアントで正常に動作しています。 2以上が同時に一つの特定のアクションメソッドにアクセスしようとする。しかし、私はMicrosoft.EntityFrameworkCoreで、このエラーを得た:第2の操作が完了前の操作 前に、この文脈で開始WebapiのDbContextへの複数同時実行アクセス

。どのインスタンスメンバーもスレッドセーフであることは保証されていません。

私はWebApiにDIとリポジトリを使用しました。私はIUnitOfWorkのためにScopeを定義しました。私はTransientを定義しましたが、何も働いていません。

これは私のスタートアップである:

.... 
services.AddSingleton(provider => Configuration); 
      services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>(); 
      services.AddScoped<IUnitOfWork, ApplicationDbContext>(); 
      services.AddTransient<IRoleService, RoleService>(); 
      services.AddTransient<ISecurityService, SecurityService>(); 
      services.AddTransient<IDbInitializerService, DbInitializerService>(); 
      services.AddTransient<ITokenStoreService, TokenStoreService>(); 
      services.AddTransient<ITokenValidatorService, TokenValidatorService>(); 
      services.AddTransient<ICookieValidatorService, CookieValidatorService>(); 
      services.AddTransient<IRequestRepository, RequestRepository>(); 
      services.AddDbContextPool<ApplicationDbContext>(options => 
      { 
       options.UseSqlServer(
        Configuration["ConnectionStrings:ApplicationDbContextConnection"].ToString(), 
        serverDbContextOptionsBuilder => 
        { 
         var minutes = (int)TimeSpan.FromMinutes(3).TotalSeconds; 
         serverDbContextOptionsBuilder.CommandTimeout(minutes); 
         serverDbContextOptionsBuilder.EnableRetryOnFailure(); 
        }); 
      }); 
.... 

これは私のDbContextです:

namespace Eela.Data 
{ 

    public class 
     ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext> 
    { 


     public ApplicationDbContext CreateDbContext(string[] args) 
     { 
      var services = new ServiceCollection(); 
      services.AddOptions(); 
      services.AddScoped<IHostingEnvironment, CustomHostingEnvironment>(); 
      services.AddSingleton<ILoggerFactory, LoggerFactory>(); 
      var serviceProvider = services.BuildServiceProvider(); 
      var loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>(); 
      var logger = loggerFactory.CreateLogger<ConfigProvider>(); 
      var hostingEnvironment = serviceProvider.GetRequiredService<IHostingEnvironment>(); 
      Console.WriteLine($"Using `{hostingEnvironment.ContentRootPath}` as the ContentRootPath"); 
      var configuration = new ConfigurationBuilder() 
       .SetBasePath(basePath: hostingEnvironment.ContentRootPath) 
       .AddJsonFile(path: "appsettings.json", reloadOnChange: true, optional: false) 
       .AddEncryptedProvider(hostingEnvironment: hostingEnvironment, logger: logger) 
       .AddJsonFile(path: $"appsettings.{hostingEnvironment.EnvironmentName}.json", optional: true) 
       .Build();    
      var builder = new DbContextOptionsBuilder<ApplicationDbContext>(); 
      var connectionString = configuration["ConnectionStrings:ApplicationDbContextConnection"]; 
      var useInMemoryDatabase = configuration[key: "UseInMemoryDatabase"].Equals(value: "true", 
       comparisonType: StringComparison.OrdinalIgnoreCase); 
      if (useInMemoryDatabase) 
       builder.UseInMemoryDatabase("MyDatabase"); 
      else 
      builder.UseSqlServer(connectionString); 
      builder.ConfigureWarnings(warnings => warnings.Log(CoreEventId.IncludeIgnoredWarning)); 
      return new ApplicationDbContext(builder.Options); 
     } 
    } 

    public class ApplicationDbContext : DbContext, IUnitOfWork 
    { 

     public ApplicationDbContext(DbContextOptions options) : base(options) 
     { } 
     protected override void OnModelCreating(ModelBuilder model) 
     { 
      base.OnModelCreating(model); 

      model.Entity<Person>().Property(p => p.PersonId).ValueGeneratedOnAdd(); 
      model.Entity<Person>() 
       .HasDiscriminator<int>(name: "Type") 
       .HasValue<WorkerTaxi>(value: Convert.ToInt32(value: AccountType.TaxiWorker)) 
       .HasValue<User>(value: Convert.ToInt32(value: AccountType.User)) 
       .HasValue<Reseller>(value: Convert.ToInt32(value: AccountType.Reseller)); 

      model.Entity<Log>().Property(p => p.Id).ValueGeneratedOnAdd(); 
      model.Entity<Log>() 
       .HasDiscriminator<int>(name: "Type") 
       .HasValue<LogRequest>(value: Convert.ToInt32(value: LogLevel.Information)) 
       .HasValue<LogError>(value: Convert.ToInt32(value: LogLevel.Error)); 

      model.Entity<Request>().Property(p => p.RequestId).ValueGeneratedOnAdd(); 
      model.Entity<Request>() 
       .HasDiscriminator<int>(name: "Type") 
       .HasValue<RequestTaxi>(value: Convert.ToInt32(value: RequestType.TaxiRequester)); 

      model.Entity<ApplicationUsers>().Property(p => p.Id).ValueGeneratedOnAdd(); 
      model.Entity<Role>().Property(p => p.RoleId).ValueGeneratedOnAdd(); 
      model.Entity<Car>().Property(p => p.CarId).ValueGeneratedOnAdd(); 
      model.Entity<Address>().Property(p => p.AddressId).ValueGeneratedOnAdd(); 
      model.Entity<Organization>().Property(p => p.OrganizationId).ValueGeneratedOnAdd(); 
      model.Entity<Credit>().Property(p => p.CreditId).ValueGeneratedOnAdd(); 
      model.Entity<StablePrice>().Property(p => p.StablePriceId).ValueGeneratedOnAdd(); 
      model.Entity<Package>().Property(p => p.PackageId).ValueGeneratedOnAdd(); 
      model.Entity<Rating>().Property(p => p.RatingId).ValueGeneratedOnAdd(); 
      model.Entity<City>().Property(p => p.CityId).ValueGeneratedOnAdd(); 
      model.Entity<SpecialAddress>().Property(p => p.SpecialAddressId).ValueGeneratedOnAdd(); 
      model.Entity<UserToken>().Property(p => p.Id).ValueGeneratedOnAdd(); 
      model.Entity<PersonRequest>(entity => 
      { 
       entity.HasKey(e => new {e.RequestId, e.PersonId}) 
        .HasName(name: "PK_dbo.PersonRequest"); 

       entity.HasIndex(e => e.RequestId) 
        .HasName(name: "IX_RequestId"); 

       entity.HasIndex(e => e.PersonId) 
        .HasName(name: "IX_PersonId"); 
      }); 
      model.Entity<PackageReseller>(entity => 
      { 
       entity.HasKey(e => new { e.PackageId, e.ResellerId }) 
        .HasName(name: "PK_dbo.PackageReseller"); 

       entity.HasIndex(e => e.PackageId) 
        .HasName(name: "IX_PackageId"); 

       entity.HasIndex(e => e.ResellerId) 
        .HasName(name: "IX_ResellerId"); 
      }); 
      model.Entity<UserRole>(entity => 
      { 
       entity.HasKey(e => new { e.ApplicationUserId, e.RoleId }) 
        .HasName(name: "PK_dbo.UserRole"); 

       entity.HasIndex(e => e.ApplicationUserId) 
        .HasName(name: "IX_ApplicationUserId"); 

       entity.HasIndex(e => e.RoleId) 
        .HasName(name: "IX_RoleId"); 
      }); 
     } 
     public virtual DbSet<ApplicationUsers> ApplicationUsers { get; set; } 
     public virtual DbSet<Role> Role { get; set; } 
     public virtual DbSet<UserRole> UserRole { get; set; } 
     public virtual DbSet<UserToken> UserToken { get; set; } 

     public virtual DbSet<Address> Address { get; set; } 
     public virtual DbSet<Credit> Credit { get; set; } 
     public virtual DbSet<Organization> Organization { get; set; } 
     public virtual DbSet<City> City { get; set; } 
     public virtual DbSet<StablePrice> StablePrice { get; set; } 
     public virtual DbSet<PersonRequest> PersonRequest { get; set; } 
     public virtual DbSet<Discount> Discount { get; set; } 
     public virtual DbSet<Rating> Rating { get; set; } 
     public virtual DbSet<SpecialAddress> SpecialAddress { get; set; } 


     public void AddRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class 
     { 
      Set<TEntity>().AddRange(entities: entities); 
     } 

     public void RemoveRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class 
     { 
      Set<TEntity>().RemoveRange(entities: entities); 
     } 

     public void MarkAsChanged<TEntity>(TEntity entity) where TEntity : class 
     { 
      Entry(entity: entity).State = EntityState.Modified; // Or use ---> this.Update(entity); 
     } 

     public void ExecuteSqlCommand(string query) 
     { 
      Database.ExecuteSqlCommand(sql: query); 
     } 

     public void ExecuteSqlCommand(string query, params object[] parameters) 
     { 
      Database.ExecuteSqlCommand(sql: query, parameters: parameters); 
     } 

     public int SaveAllChanges() 
     { 
      return SaveChanges(); 
     } 

     public Task<int> SaveAllChangesAsync() 
     { 
      return SaveChangesAsync(); 
     } 
    } 
} 

この私のIUnitOfWork:

namespace Eela.Data 
{ 
    public interface IUnitOfWork : IDisposable 
    { 
     DbSet<TEntity> Set<TEntity>() where TEntity : class; 

     void AddRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class; 
     void RemoveRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class; 

     EntityEntry<TEntity> Entry<TEntity>(TEntity entity) where TEntity : class; 
     void MarkAsChanged<TEntity>(TEntity entity) where TEntity : class; 

     void ExecuteSqlCommand(string query); 
     void ExecuteSqlCommand(string query, params object[] parameters); 

     int SaveAllChanges(); 
     Task<int> SaveAllChangesAsync(); 

     int SaveChanges(bool acceptAllChangesOnSuccess); 
     int SaveChanges(); 
     Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken()); 
     Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()); 
    } 
} 

これは私のリポジトリの1つである:

public class RequestRepository : IRequestRepository 
    { 
     private readonly IMapper _mapper; 
     private readonly IUnitOfWork _unitOfWork; 
     private readonly DbSet<Request> _request; 
     private readonly DbSet<Person> _person; 
     private readonly DbSet<PersonRequest> _personRequest; 
     public RequestRepository(IMapper mapper, IUnitOfWork unitOfWork) 
     { 
      _mapper = mapper; 
      _unitOfWork = unitOfWork; 
      _request = _unitOfWork.Set<Request>(); 
      _person = _unitOfWork.Set<Person>(); 
      _personRequest = _unitOfWork.Set<PersonRequest>(); 
     } 
    public async Task<DetailPageViewModel> GetRequestAsync(string requestId) 
     { 
      var request = await (from x in _request 
       where x.RequestId == Guid.Parse(requestId) 
       from y in x.PersonsRequests 
       where y.Person is User 
       select new DetailPageViewModel 
       { 
        RequestId = x.RequestId.ToString(), 
        CustomerName = y.Person.LastName, 
        SourceAddress = ((RequestTaxi) x).SourceAddress, 
        DestinationAddress = ((RequestTaxi) x).DestinationAddress, 
        DestinationLat = x.DestinationLat, 
        DestinationLon = x.DestinationLon, 
        EstimateDistance = ((RequestTaxi) x).Distance.ToString(CultureInfo.InvariantCulture), 
        EstimateDriverPrice = x.Price.ToString(), 
        EstimatePassengerPrice = x.PaymentType == PaymentType.Cash ? x.Price.ToString() : "0", 
        SourceLat = ((RequestTaxi) x).SourceLat, 
        SourceLon = ((RequestTaxi) x).SourceLon 
       }).FirstOrDefaultAsync(); 

      return 
       _mapper.Map<DetailPageViewModel>(
        source: request); 
     } 
     ..... 

そして最後に、これは私のコントローラのいずれかです。

public class DetailPageController:Controller 
    { 
     private readonly IPersonRequestRepository _personRequest; 
     private readonly IRequestRepository _request; 
     private readonly IApplicationUsersRepository _appUser; 
     private readonly IStablePriceRepository _stablePrice; 
     private readonly ILogRepository _log; 
     private readonly ICreditRepository _credit; 
     private readonly INotificationService _notification; 
     private readonly IPasswordGenerator _charecterGenerator; 

     public DetailPageController(IPersonRequestRepository personRequest,ICreditRepository credit, 
      ILogRepository log,IStablePriceRepository stablePrice,IApplicationUsersRepository appUser, 
      IRequestRepository request,INotificationService notification,IPasswordGenerator charecterGenerator) 
     { 
      _personRequest = personRequest; 
      _credit = credit; 
      _log = log; 
      _stablePrice = stablePrice; 
      _appUser = appUser; 
      _request = request; 
      _notification = notification; 
      _charecterGenerator = charecterGenerator; 
     } 

     [HttpPost] 
     [ActionName("GetRequest")] 
     public async Task<ActionResult> GetRequest([FromBody]string model) 
     { 
      var requestId = model; 
      return Json(data: await _request.GetRequestAsync(requestId)); 
     } 

RequestLoggingMiddleware.cs:

public class RequestLoggingMiddleware 
    { 
     private readonly RequestDelegate _next; 
     private readonly ILogger<RequestLoggingMiddleware> _logger; 
     private readonly ILogRepository _logRepository; 
     private readonly IConfigurationRoot _configuration; 

     public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger, 
      ILogRepository logRepository,IConfigurationRoot configuration) 
     { 
      _next = next; 
      _logger = logger; 
      _logRepository = logRepository; 
      _configuration = configuration; 
     } 

     public async Task<OperationResult> Invoke(HttpContext context) 
     { 
      using (MemoryStream requestBodyStream = new MemoryStream()) 
      { 
       using (MemoryStream responseBodyStream = new MemoryStream()) 
       { 
        Stream originalRequestBody = context.Request.Body; 
        context.Request.EnableRewind(); 
        Stream originalResponseBody = context.Response.Body; 

        OperationResult op= new OperationResult(); 
        try 
        { 
         await context.Request.Body.CopyToAsync(requestBodyStream); 
         requestBodyStream.Seek(0, SeekOrigin.Begin); 

         string requestBodyText = new StreamReader(requestBodyStream).ReadToEnd(); 

         requestBodyStream.Seek(0, SeekOrigin.Begin); 
         context.Request.Body = requestBodyStream; 

         string responseBody = ""; 


         context.Response.Body = responseBodyStream; 
         Stopwatch watch = Stopwatch.StartNew(); 
         await _next(context); 
         watch.Stop(); 

         responseBodyStream.Seek(0, SeekOrigin.Begin); 
         responseBody = new StreamReader(responseBodyStream).ReadToEnd(); 
         var log = new LogRequestViewModel 
         { 
          Host= context.Request.Host.Host, 
          Path= context.Request.Path, 
          QueryString= context.Request.QueryString.ToString(), 
          ClientIp= context.Connection.RemoteIpAddress.MapToIPv4(), 
          Date= DateTime.Now.ToString(CultureInfo.InvariantCulture), 
          Duration= watch.ElapsedMilliseconds, 
          Method= context.Request.Method, 
          RequestContentLength= context.Request.ContentLength, 
          RequestContentType= context.Request.ContentType, 
          Application= GetType().Namespace, 
          User= context.User.Claims 
           .FirstOrDefault(x => x.Type == _configuration["UserIdType"])?.Value, 
          Headers= string.Join(",", context.Request.Headers.Select(he => he.Key + ":[" + he.Value + "]").ToList()), 
          RequestBodyText= requestBodyText, 
          ResponseBodyText = responseBody 

         }; 
         var result = await _logRepository.SaveRequestLogAsync(log); 
         if (!result.Success) 
         { 
          op.Success = false; 
          op.AddMessage("Couldn't add request log to database"); 
          _logger.LogError(message: result.MessageList.FirstOrDefault()); 
          var ex = new Exception(message: result.MessageList.FirstOrDefault()); 
          await _logRepository.SaveErrorLogAsync(exception: ex); 
         } 
         responseBodyStream.Seek(0, SeekOrigin.Begin); 

         await responseBodyStream.CopyToAsync(originalResponseBody); 
        } 
        catch (Exception ex) 
        { 
         _logger.LogError(message: ex.Message); 
         await _logRepository.SaveErrorLogAsync(exception: ex); 
         byte[] data = System.Text.Encoding.UTF8.GetBytes("Unhandled Error occured, the error has been logged and the persons concerned are notified!! Please, try again in a while."); 
         originalResponseBody.Write(data, 0, data.Length); 
         op.Success = false; 
         op.AddMessage(ex.Message); 
        } 
        finally 
        { 
         context.Request.Body = originalRequestBody; 
         context.Response.Body = originalResponseBody; 

        } 
        const string logTemplate = @" 
         Client IP: {clientIP} 
         Request path: {requestPath} 
         Request content type: {requestContentType} 
         Request content length: {requestContentLength} 
         Start time: {startTime} 
         Duration: {duration}"; 
        _logger.LogInformation(logTemplate, 
         context.Connection.RemoteIpAddress.ToString(), 
         context.Request.Path, 
         context.Request.ContentType, 
         context.Request.ContentLength, 
         DateTime.UtcNow, 
         Stopwatch.StartNew()); 
        return op; 

       } 

      } 
     } 
    } 

と、これは私のスタックトレースです:

Microsoft.EntityFrameworkCoreで。 Microsoft.EntityFrameworkCore.Chan(内部番号) geTracking.Internal.StateManager.d__61.MoveNext() ---例外がスローされた前の場所からのスタックトレースの終了--- でSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()に System.Runtime.CompilerServices。 Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.d__59.MoveNextでSystem.Runtime.CompilerServices.TaskAwaiter1.GetResult()でTaskAwaiter.HandleNonSuccessAndDebuggerNotification(タスク タスク)() ---例外以前の位置からスタックトレースの終わり--- でSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() にSystem.Runtime.CompilerServiceでSystem.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(タスク タスク)をスローしました。でのs.TaskAwaiter1.GetResult()---例外がスローされた前の場所からのスタックトレースの終了--- でSystem.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() ) Dで System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification Eela.Service.LogRepository.d__7.MoveNext(AT System.Runtime.CompilerServices.TaskAwaiter1.GetResultで(タスク タスク)())において:\ EELA \ EELA .Service \ LogRepository.cs:41行目---例外がスローされた前の場所からの のスタックトレースの末尾--- でSystem.Runtime.ExceptionServices。 でEela.Web.Models.RequestLoggingMiddleware.d__5.MoveNext(AT System.Runtime.CompilerServices.TaskAwaiter1.GetResultで System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(タスク タスク)でExceptionDispatchInfo.Throw()()) D:\ EELA \ Eela.Web \モデル\ RequestLoggingMiddleware.cs:ライン82

更新:

私は私のstartup.csでミドルウェアを持っている:

app.UseMiddleware<RequestLoggingMiddleware>(); 

私はそれをコメントアウトすると、私のコードは問題なく動作します。 私の質問にRequestLoggingMiddleware.csソースが含まれています。

主な問題はどこですか?

+0

[前回の非同期操作が完了する前に、このコンテキストで2番目の操作が開始されました](https://stackoverflow.com/questions/28331244/keep-getting-a-second-operation-started-on-前の非同期の文脈) – JOSEFtw

+0

@JOSEFtw私は2日間インターネット上で多くのものを検索しましたが、成功しませんでした。 –

+0

@JOSEFtwあなたが送ったリンクによれば、私はローカル変数DbContextではなく依存性注入を使いたいです! –

答えて

1

私の推測では、ミドルウェアは一度だけインスタンス化されています。つまり、データベースへの同時アクセスを実行するために、コンテキストの単一のインスタンスが使用されます。

2つの解決策があります。最初のものは、コンテキストファブリックを持ち、Invokeメソッドが呼び出されるたびにコンテキストのインスタンスを作成することです。もう1つは、コレクション内のミドルウェアにログレコードを格納することです。何らかの条件(レコード数が特定の数値に達したときやタイムアウトがゼロになったとき)でデータベースに保存します。

ログレコードの量によっては、パフォーマンスの問題が発生する可能性があります。第2のアプローチでは、コレクションへの正しい同時アクセスの実装が必要です。場合によっては、ログレコードの一部を失うこともあります。

関連する問題