我在客户端有一个简单的web应用程序,服务器端有asp.net核心web-api。我使用InMemoryDatabase
services.AddDbContext<ItemsContext>(options => options.UseInMemoryDatabase("ItemsDB"));
存储数据以简化开发。但我遇到了一个问题。我在web-api上有一个控制器来响应用户的请求:
[Route("api/[controller]")]
public class ItemsController : Controller
{
private readonly IApiService apiService;
public ItemsController(IApiService apiService)//using DI from Startup.cs
{
this.apiService = apiService;
}
[HttpPost, Route("addItem")]
public async Task<Response> Add([FromBody]Item item)
{
return await apiService.Add(item);
}
[HttpDelete("{id}")]
public async Task<Response> Delete(int id)
{
return await apiService.Delete(id);
}
[HttpPut]
public async Task<Response> Put([FromBody]Item item)
{
return await apiService.Put(item);
}
}
以及以下Startup.cs配置:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<ItemsContext>(options => options.UseInMemoryDatabase("ItemsDB"));
services.AddSingleton<IUnitOfWork, UnitOfWork>(provider => {
var context = services.BuildServiceProvider().GetService<ItemsContext>();
return new UnitOfWork(context);
});
services.AddSingleton<IApiService, ApiService>(provider => {
return new ApiService(services);
});
}
问题是,当我添加新项目时,一切都很好......但随后我发布了另一个请求删除此项目,它可能会显示没有这样的项目或有时可能会删除它。换句话说,数据库存在然后消失,我不知道什么时候。以下是一些参考上述
的附加代码public class ApiService: IApiService
{
private readonly IUnitOfWork database;
private readonly IServiceProvider provider;
public ApiService(IServiceCollection serviceCollection)
{
provider = serviceCollection.BuildServiceProvider();
}
public IUnitOfWork Database
{
get
{
return provider.GetService<IUnitOfWork>();
}
}
public async Task<Response> Add(Item item)
{
Database.Items.Add(item);
await Database.SaveAsync();
var id = Database.Items.LastItem().Id;
return new Response() { Result = true, ItemId = id };
}
public async Task<Response> Delete(int id)
{
var item = await db.Items.Find(id);
Database.Items.Remove(item);
await Database.SaveAsync();
return new Response() { Result = true };
}
public async Task<Response> Put(Item item)
{
Database.Items.Update(item);
await Database.SaveAsync();
return new Response() { Result = true };
}
}
更新: UnitOfWork实施:
public class UnitOfWork: IUnitOfWork
{
private readonly DbContext context;
private IRepository<Item> itemsRepository;
public UnitOfWork(DbContext dbContext)
{
context = dbContext;
}
public IRepository<Item> Items
{
get
{
return itemsRepository ?? (itemsRepository = new Repository<Item>(context));
}
}
public void Dispose()
{
context.Dispose();
}
public void Save()
{
context.SaveChanges();
}
public async Task SaveAsync()
{
await context.SaveChangesAsync();
}
}
答案 0 :(得分:5)
您的代码存在多个严重问题,让我们通过它们。
services.AddDbContext
添加了一个Scoped服务,这意味着将在每个请求上创建和处理实例。 services.AddSingleton
添加了一个Singleton服务,因此只会创建一个实例。您不能将作用域服务添加到单例服务,因为单例服务使用的引用将被处置,您将最终处置已处置的上下文。此代码:
return provider.GetService<IUnitOfWork>();
表示服务定位器反模式。你可以猜到,反模式是你想要避免的。我也不知道为什么你想要一个服务来构建整个DI容器,为什么你希望服务有责任获得它自己需要的依赖项
这部分是您的问题实际来自:
Database.SaveAsync();
您正在调用异步函数而不是await
来完成它。任务可能完成与否,可能会抛出错误,你永远不会知道发生了什么。
最好的事情是,如果人们停止尝试在另一个工作单元和存储库上创建工作单元+存储库模式,则可以避免所有这些。实体框架核心已经实现了这些:
DbContext => Unit of Work
DbSet => Repository (generic)
为什么还想要另一种抽象?您是否真的会从项目中丢弃EF Core来证明代码的维护成本?
整个问题代码可能就是这样:
[Route("api/[controller]")]
public class ItemsController : Controller
{
private readonly YourContext _context;
public ItemsController(YourContext context)
{
_context = context;
}
[HttpPost]
public async Task<IActionResult> Add([FromBody]Item item)
{
context.Items.Add(item);
await context.SaveChangesAsync();
return Ok(item.Id);
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
context.Items.Remove(item);
await context.SaveChangesAsync();
return Ok();
}
[HttpPut]
public async Task<IActionResult> Put([FromBody]Item item)
{
context.Items.Update(item);
await context.SaveChangesAsync();
return Ok();
}
}