我有一个托管服务,它通过 API 端点处理对象 PUT/POST,也就是说,一旦给出新实体或编辑现有实体,(a)托管服务开始处理它(一个长时间运行的过程), (b) 接收/修改的对象(作为 JSON 对象)返回给 API 调用者。
当 PUT/POST 一个实体时,我在这里和那里看到运行时错误(例如,在对象 JSON 序列化程序中)抱怨不同的问题,例如:
ObjectDisposedException:无法访问已处置的对象。此错误的一个常见原因是释放从依赖注入中解析的上下文,然后尝试在应用程序的其他地方使用相同的上下文实例。如果您在上下文上调用 Dispose() 或将上下文包装在 using 语句中,则可能会发生这种情况。如果你使用依赖注入,你应该让依赖注入容器负责处理上下文实例。
或者:
InvalidOperationException:在前一个操作完成之前在此上下文上启动了第二个操作。这通常是由使用相同 DbContext 实例的不同线程引起的。
最初我使用的是数据库上下文池,但据此,似乎池在托管服务方面存在已知问题。因此,我切换到了常规AddDbContext
;但是,这都没有解决问题。
这就是我定义数据库上下文和托管服务的方式:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddCustomDbContext(Configuration);
// This is the hosted service:
services.AddHostedService<MyHostedService>();
}
}
public static class CustomExtensionMethods
{
public static IServiceCollection AddCustomDbContext(
this IServiceCollection services,
IConfiguration configuration)
{
services.AddDbContext<MyContext>(
options =>
{
options
.UseLazyLoadingProxies(true)
.UseSqlServer(
configuration.GetConnectionString("DefaultConnection"),
sqlServerOptionsAction: sqlOptions => { sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name); });
});
return services;
}
}
并且我按以下方式访问托管服务中的数据库上下文(如此处推荐):
using(var scope = Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<MyContext>();
}
编辑 1
如前所述,错误发生在代码周围。但是,由于我提到了序列化程序上发生的错误,因此我将在以下内容中共享序列化程序代码:
public class MyJsonConverter : JsonConverter
{
private readonly Dictionary<string, string> _propertyMappings;
public MyJsonConverter()
{
_propertyMappings = new Dictionary<string, string>
{
{"id", nameof(MyType.ID)},
{"name", nameof(MyType.Name)}
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JObject obj = new JObject();
Type type = value.GetType();
foreach (PropertyInfo prop in type.GetProperties())
{
if (prop.CanRead)
{
// The above linked errors happen here.
object propVal = prop.GetValue(value, null);
if (propVal != null)
obj.Add(prop.Name, JToken.FromObject(propVal, serializer));
}
}
obj.WriteTo(writer);
}
}
更新 2
示例 API 端点如下所示:
[Route("api/v1/[controller]")]
[ApiController]
public class MyTypeController : ControllerBase
{
private readonly MyContext _context;
private MyHostedService _service;
public MyTypeController (
MyContext context,
MyHostedService service)
{
_context = context;
_service = service
}
[HttpGet("{id}")]
public async Task<ActionResult<IEnumerable<MyType>>> GetMyType(int id)
{
return await _context.MyTypes.FindAsync(id);
}
[HttpPost]
public async Task<ActionResult<MyType>> PostMyType(MyType myType)
{
myType.Status = State.Queued;
_context.MyTypes.Add(myType);
_context.MyTypes.SaveChangesAsync().ConfigureAwait(false);
// the object is queued in the hosted service for execution.
_service.Enqueue(myType);
return CreatedAtAction("GetMyType", new { id = myType.ID }, myType);
}
}