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
Multiple isolated DbContext instances
- In your
Startup.cs, when registeringIUnitOfWorkas a singleton, you're callingservices.BuildServiceProvider()to fetch theItemsContext. 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
ApiServiceis also building its own service provider from theIServiceCollection, 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.
- In your
Singleton UnitOfWork conflicting with Scoped DbContext
- By default,
DbContextis registered with a Scoped lifetime (one instance per HTTP request). But you've madeIUnitOfWorka 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".
- By default,
Unnecessary service provider creation in ApiService
- Your
ApiServiceis taking anIServiceCollectionand 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.
- Your
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

