Ho una semplice web-app con angolare sul lato client e asp.net core web-api sul lato server. Io uso InMemoryDatabase
services.AddDbContext<ItemsContext>(options => options.UseInMemoryDatabase("ItemsDB"));
per memorizzare i dati per la semplicità dello sviluppo. Ma ho riscontrato un problema con questo. Ho un controller su web-api per rispondere alle richieste degli utenti:
[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);
}
}
e le seguenti configurazioni di 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);
});
}
Il problema è che quando aggiungo un nuovo oggetto, tutto va bene ... ma poi pubblico un'altra richiesta per cancellare questo elemento, potrebbe mostrare che non esiste un elemento del genere o, a volte, può cancellarlo ... quindi in altre parole, il database esiste e quindi scompare e non sono sicuro di quando. Ecco un codice aggiuntivo che si riferisce a quanto sopra
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 };
}
}
Aggiornamento: Implementazione 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();
}
}
Il tuo codice ha più problemi seri, passiamo attraverso di loro.
services.AddDbContext
aggiunge un servizio Scoped, il che significa che le istanze verranno create e disposte su ogni richiesta. services.AddSingleton
aggiunge un servizio Singleton, quindi verrà creata una sola istanza. Non è possibile aggiungere un servizio con ambito a un singolo, poiché il riferimento utilizzato dal servizio Singleton verrà eliminato e si otterrà un contesto disposto. Questo codice:
return provider.GetService<IUnitOfWork>();
rappresenta il localizzatore di servizi anti-pattern. Come puoi immaginare, un anti-pattern è qualcosa che vuoi evitare. Inoltre non so perché vorreste un servizio per costruire l'intero contenitore DI, né perché vorreste che un servizio abbia la responsabilità di ottenere le dipendenze di cui ha bisogno.
Questa parte qui è dove la tua domanda proviene effettivamente da:
Database.SaveAsync();
Stai chiamando una funzione asincrona e non stai await
che finisca. L'attività può finire o no, può generare un errore o no, non si saprà mai cosa è successo.
La cosa migliore è che tutti questi potrebbero essere evitati se le persone smettessero di tentare di creare un modello di Unità di Lavoro + Repository su un'altra Unità di Lavoro e Deposito . Entity Framework Core implementa già questi:
DbContext => Unit of Work
DbSet => Repository (generic)
Perché vuoi ancora un'altra astrazione? Eliminerai mai definitivamente EF Core dal progetto per giustificare il costo di manutenzione del tuo codice?
L'intero codice della domanda potrebbe essere appena stato questo:
[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();
}
}