【问题标题】:In-memory database doesn't save data内存数据库不保存数据
【发布时间】:2018-05-21 21:47:14
【问题描述】:

我有一个简单的 web 应用程序,在客户端有 angular,在服务器端有 asp.net core 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();
    }
}

【问题讨论】:

  • 两件事:一,你没有发布你的IUnitOfWork 实现。其次,这种模式(UoW + Repository)在 Entity Framework Core 中是一种浪费,它已经实现了,重新实现它你什么也得不到。更不用说您在该存储库中使用的服务定位器反模式
  • 我刚刚注意到您正在实现 async Task 方法而没有 awaiting 任何东西.. 即在您的 Add(Item item) 函数中
  • @jmesolomon 如果Database.Save() 调用context.SaveChanges() 这不是问题
  • 更正-有Database.SaveAsync();
  • ...发布 IUnitOfWork 实现

标签: c# asp.net-core entity-framework-core


【解决方案1】:

您的代码有多个严重的问题,让我们检查一下。

  1. services.AddDbContext 添加了一个 Scoped 服务,这意味着将在每个请求上创建和处置实例。 services.AddSingleton 添加了一个单例服务,因此只会创建一个实例。您不能将作用域服务添加到单例服务,因为单例服务使用的引用将被释放,您最终将获得释放的上下文。

  2. 这段代码:

     return provider.GetService<IUnitOfWork>();
    

表示服务定位器反模式。您可以猜到,反模式是您想要避免的。我也不知道为什么你会想要一个服务来构建整个 DI 容器,也不知道为什么你会想要一个服务负责获取它自己需要的依赖项.

  1. 这部分是您的问题的真正来源:

     Database.SaveAsync();
    

您正在调用一个异步函数,而不是 awaiting 来完成它。任务可能完成与否,可能会抛出错误,你永远不会知道发生了什么。

最好的事情是,如果人们停止尝试创建工作单元 + 存储库模式而不是另一个工作单元和存储库,那么所有这些都可以避免。 Entity Framework Core 已经实现了这些:

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)
    {
        var item = await context.Items.FindAsync(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();
    }
}

【讨论】:

  • 很好的答案卡米洛。这是 EF + 工作单元 + 存储库带来的疯狂抽象的完美示例。
  • @Brad 感谢并感谢您的编辑,这是一个误会
  • 非常感谢!!这非常有帮助。我不知道添加 services.AddDbContext 会添加范围服务。这就是问题所在。关于 Database.SaveAsync() 我在代码中等待结果,它只是这里的一个类型。
  • 好吧,我创建了 UofW 是为了在存储库之间共享 DbContext... 有一些问题吗?我想知道。
猜你喜欢
  • 2017-06-16
  • 2016-11-11
  • 1970-01-01
  • 2012-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-29
  • 2016-05-15
相关资源
最近更新 更多