And*_*uiz 42 c# entity-framework asp.net-web-api
我正在使用Entity Framework Core开发ASP.Net Core 2.0项目
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0"/>
Run Code Online (Sandbox Code Playgroud)
在我的一个列表方法中,我收到此错误:
InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
Run Code Online (Sandbox Code Playgroud)
这是我的方法:
[HttpGet("{currentPage}/{pageSize}/")]
[HttpGet("{currentPage}/{pageSize}/{search}")]
public ListResponseVM<ClientVM> GetClients([FromRoute] int currentPage, int pageSize, string search)
{
var resp = new ListResponseVM<ClientVM>();
var items = _context.Clients
.Include(i => i.Contacts)
.Include(i => i.Addresses)
.Include("ClientObjectives.Objective")
.Include(i => i.Urls)
.Include(i => i.Users)
.Where(p => string.IsNullOrEmpty(search) || p.CompanyName.Contains(search))
.OrderBy(p => p.CompanyName)
.ToPagedList(pageSize, currentPage);
resp.NumberOfPages = items.TotalPage;
foreach (var item in items)
{
var client = _mapper.Map<ClientVM>(item);
client.Addresses = new List<AddressVM>();
foreach (var addr in item.Addresses)
{
var address = _mapper.Map<AddressVM>(addr);
address.CountryCode = addr.CountryId;
client.Addresses.Add(address);
}
client.Contacts = item.Contacts.Select(p => _mapper.Map<ContactVM>(p)).ToList();
client.Urls = item.Urls.Select(p => _mapper.Map<ClientUrlVM>(p)).ToList();
client.Objectives = item.Objectives.Select(p => _mapper.Map<ObjectiveVM>(p)).ToList();
resp.Items.Add(client);
}
return resp;
}
Run Code Online (Sandbox Code Playgroud)
我有点迷失,特别是因为它在我本地运行时有效,但是当我部署到我的登台服务器(IIS 8.5)时,它会让我发现这个错误并且它正常工作.在我增加其中一个模型的最大长度后,错误开始出现.我还更新了相应视图模型的最大长度.还有许多其他列表方法非常相似,它们正在工作.
我运行了一个Hangfire作业,但是这个作业不使用同一个实体.这就是我能想到的所有相关内容.有什么可能导致这个的想法?
als*_*ami 45
我不确定您是否使用IoC和依赖注入来解决您可能使用的DbContext.如果您这样做并且您正在使用来自.NET Core(或任何其他IoC-Container)的本机IoC,并且您收到此错误,请确保将DbContext注册为Transient.做
services.AddTransient<MyContext>();
Run Code Online (Sandbox Code Playgroud)
要么
services.AddDbContext<MyContext>(ServiceLifetime.Transient);
Run Code Online (Sandbox Code Playgroud)
代替
services.AddDbContext<MyContext>();
Run Code Online (Sandbox Code Playgroud)
AddDbContext将上下文添加为作用域,这可能会在处理多个线程时引起麻烦.
此外异步/等待操作可以通过异步lambda表达式时,会导致此行为.
将其添加为瞬态也有其缺点.您将无法在使用上下文的多个类上对某个实体进行更改,因为每个类都将获得自己的DbContext实例.
Gab*_*uci 31
该异常意味着_context
两个线程同时使用该异常; 同一请求中的两个线程,或两个请求.
你_context
声明的静态可能吗?它不应该.
或者您是否GetClients
在代码中的其他位置的同一请求中多次调用?
您可能已经这样做,但理想情况下,你会使用依赖注入你的DbContext
,这意味着你可以使用AddDbContext()
你的Startup.cs,和你的控制器构造将是这个样子:
private readonly MyDbContext _context; //not static
public MyController(MyDbContext context) {
_context = context;
}
Run Code Online (Sandbox Code Playgroud)
如果您的代码不是这样的,请告诉我们,也许我们可以提供更多帮助.
小智 17
使用 Startup.cs 文件中的这行代码解决我的问题。
添加瞬态服务意味着每次请求服务时,在使用依赖注入时都会创建一个新实例
services.AddDbContext<Context>(options =>
options.UseSqlServer(_configuration.GetConnectionString("ContextConn")),
ServiceLifetime.Transient);
Run Code Online (Sandbox Code Playgroud)DiP*_*Pix 15
我遇到了同样的问题,结果发现父服务是一个单身人士。所以上下文也自动变成了单例。即使在 DI 中被声明为 Per Life Time Scoped。
将具有不同生命周期的服务注入另一个
永远不要将 Scoped & Transient 服务注入 Singleton 服务。(这有效地将瞬态或范围服务转换为单例。)
永远不要将瞬态服务注入范围服务(这会将瞬态服务转换为范围服务。)
Ham*_*loo 13
在某些情况下,此错误是由这种情况引起的:您调用异步方法,但在调用该方法之前未使用await。我的问题通过在该方法之前添加等待来解决。但是答案可能与所提到的问题无关,但可以帮助解决类似的错误。
小智 8
我认为这个答案仍然可以帮助某人并节省很多次。我通过更改IQueryable
为List
(或更改为数组、集合...)解决了类似的问题。
例如:
var list=_context.table1.where(...);
Run Code Online (Sandbox Code Playgroud)
到
var list=_context.table1.where(...).ToList(); //or ToArray()...
Run Code Online (Sandbox Code Playgroud)
我有一个后台服务,可以对表中的每个条目执行操作。问题是,如果我迭代并修改 DbContext 同一实例上的某些数据,则会发生此错误。
正如本线程中提到的,一种解决方案是通过如下定义将 DbContext 的生命周期更改为瞬态:
services.AddDbContext<DbContext>(ServiceLifetime.Transient);
Run Code Online (Sandbox Code Playgroud)
但因为我在多个不同的服务中进行了更改,并使用该方法立即提交它们,所以SaveChanges()
该解决方案在我的情况下不起作用。
因为我的代码在服务中运行,所以我正在做类似的事情
using (var scope = Services.CreateScope())
{
var entities = scope.ServiceProvider.GetRequiredService<IReadService>().GetEntities();
var writeService = scope.ServiceProvider.GetRequiredService<IWriteService>();
foreach (Entity entity in entities)
{
writeService.DoSomething(entity);
}
}
Run Code Online (Sandbox Code Playgroud)
能够像一个简单的请求一样使用该服务。因此,为了解决这个问题,我只是将单个作用域分成两个,一个用于查询,另一个用于写入操作,如下所示:
using (var readScope = Services.CreateScope())
using (var writeScope = Services.CreateScope())
{
var entities = readScope.ServiceProvider.GetRequiredService<IReadService>().GetEntities();
var writeService = writeScope.ServiceProvider.GetRequiredService<IWriteService>();
foreach (Entity entity in entities)
{
writeService.DoSomething(entity);
}
}
Run Code Online (Sandbox Code Playgroud)
这样,实际上使用了两个不同的 DbContext 实例。
另一种可能的解决方案是确保读取操作在开始迭代之前已终止。在我的例子中,这不太实用,因为可能有很多结果都需要加载到内存中才能进行操作,而我首先试图通过使用 Queryable 来避免这种情况。
小智 8
Entity Framework Core 不支持在同一DbContext
实例上运行多个并行操作。这包括async
查询的并行执行和来自多个线程的任何显式并发使用。因此,始终await async
立即调用,或者DbContext
为并行执行的操作使用单独的实例。
我遇到了同样的问题,因为我使用的是 Task.WhenAll,并且 DbContext 实例不是线程安全的,因此您可以为每个并行操作创建一个新的 DbContext。
var results = await Task.WhenAll(requests.Select(async request =>
{
using (var dbContext = dbContextFactory.CreateDbContext())
{
var result = await InsertRequestIntoDatabase(request, dbContext);
return result;
}
}));
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
54715 次 |
最近记录: |