You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core中InMemoryDatabase数据不稳定问题求助

Let's dig into what's causing this flaky data state issue in your app—there are a few key problems with your dependency injection setup and service lifetimes that are leading to inconsistent behavior:

Root Causes

  1. Multiple isolated DbContext instances

    • In your Startup.cs, when registering IUnitOfWork as a singleton, you're calling services.BuildServiceProvider() to fetch the ItemsContext. This creates a brand new service provider instead of using the existing container, so the DbContext here is completely separate from the one used elsewhere in your app.
    • Your ApiService is also building its own service provider from the IServiceCollection, which means it's using another independent DbContext instance. Since the InMemory database is isolated per DbContext instance, adding data in one instance won't be visible in another—this explains why delete requests sometimes can't find the data you just added.
  2. Singleton UnitOfWork conflicting with Scoped DbContext

    • By default, DbContext is registered with a Scoped lifetime (one instance per HTTP request). But you've made IUnitOfWork a Singleton, which means it holds onto a single DbContext instance indefinitely. DbContext isn't designed for long-term use; reusing it across multiple requests leads to stale state caching and concurrency issues, making your data seem "unstable".
  3. Unnecessary service provider creation in ApiService

    • Your ApiService is taking an IServiceCollection and building a provider manually, which is an anti-pattern. Dependency injection works best when you let the container inject the dependencies you need directly, rather than constructing them yourself.

Fix Steps

Let's fix these issues one by one:

1. Correct Startup.cs DI Configuration

Update your service registrations to match proper lifetime scoping and avoid manual service provider creation:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddDbContext<ItemsContext>(options => options.UseInMemoryDatabase("ItemsDB"));
    
    // Register UnitOfWork as Scoped (matches DbContext's lifetime)
    services.AddScoped<IUnitOfWork, UnitOfWork>();
    
    // Let the container inject IUnitOfWork into ApiService directly
    services.AddScoped<IApiService, ApiService>();
}

2. Refactor ApiService to Use Constructor Injection

Remove the manual service provider logic and inject IUnitOfWork directly:

public class ApiService: IApiService 
{
    private readonly IUnitOfWork _unitOfWork;

    public ApiService(IUnitOfWork unitOfWork) 
    {
        _unitOfWork = unitOfWork;
    }

    public async Task<Response> Add(Item item) 
    {
        _unitOfWork.Items.Add(item);
        await _unitOfWork.SaveAsync();
        // Use item.Id directly (EF Core auto-populates this after SaveAsync)
        return new Response() { Result = true, ItemId = item.Id };
    }

    public async Task<Response> Delete(int id) 
    {
        var item = await _unitOfWork.Items.Find(id);
        if (item == null)
        {
            // Explicitly handle missing items instead of letting exceptions bubble up
            return new Response() { Result = false, Message = "Item not found" };
        }
        _unitOfWork.Items.Remove(item);
        await _unitOfWork.SaveAsync();
        return new Response() { Result = true };
    }

    public async Task<Response> Put(Item item) 
    {
        _unitOfWork.Items.Update(item);
        await _unitOfWork.SaveAsync();
        return new Response() { Result = true };
    }
}

3. Clean Up UnitOfWork Implementation

Make the UnitOfWork explicitly depend on ItemsContext (instead of generic DbContext) for clarity:

public class UnitOfWork: IUnitOfWork 
{
    private readonly ItemsContext _context;
    private IRepository<Item> _itemsRepository;

    public UnitOfWork(ItemsContext 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();
    }
}

Extra Recommendations

  • Never hold Scoped services in Singletons: This is a common DI mistake that leads to all sorts of state issues. Always match the lifetime of your services to their dependencies.
  • Handle missing data explicitly: Adding a null check in your delete method will make your API more predictable and help with debugging.
  • Remember InMemory DB limitations: It's designed for testing, not production. It doesn't mimic real database behavior like transaction isolation, so don't rely on it for production use cases.

内容的提问来源于stack exchange,提问作者Artleks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:08:13