Ok, grazie mille
si utilizza il pattern repository per disaccoppiare la web api dal motore database
Entity Framework, dato che è un ORM basato su provider, già ti consente di disaccoppiare la web api dal motore database.
Ecco l'elenco delle tecnologie database supportate:
https://entityframework.net/supported-database-providers
l'utilizzo del repository pattern mi sarebbe utile nel caso in cui l'azienda decidesse di cambiare di punto in bianco il motore db
Giusta osservazione ma ricordati che non devi prendere per oro colato quello che trovi online (compreso quello che ti sto scrivendo io). Sii sempre critico e realista. Nel tuo caso, che probabilità ha questa situazione di verificarsi?
E perché considerare solo questo, e non altri temi forse più probabili, tipo:
- E se 100.000 utenti volessero consumare la mia webapi, terrebbe il carico?
- E se mi chiedessero di creare anche un sito web, oltre alla Web API?
- E se si verifica un errore imprevisto, informo correttamente l'utente su cosa fare?
- E se la mia azienda volesse passare a Java perché non si trovano abbastanza sviluppatori .NET?
- E se l'Italia tornasse alla lira?
Scrivere codice in maniera modulare va bene, ma scriverlo solo perché altri hanno fatto così può portarti a perdere le tempo. Infatti tu non sai a che tipo di progetto stavano lavorando gli altri quando hanno scritto quell'articolo, o quali erano i loro requisiti.
Non è che non devi seguire gli articoli, ma sii sempre critico e chiediti se quello che viene scritto ha senso nel tuo specifico caso.
Ora ti do un'opinione sul codice che hai postato ma, ancora una volta, è solo una fra tante.
public IHttpActionResult Delete(long id)
{
using (TransactionScope scope = new TransactionScope(
TransactionScopeOption.Required, TRANSACTION_OPTIONS))
{
//...
}
}
Il codice che hai messo nella delete, secondo me, è codice applicativo e che perciò non dovrebbe trovarsi lì. Se ti dovessero chiedere di sviluppare anche un sito MVC, ti troveresti a dover duplicare il codice.
Oltretutto, perché un'action dovrebbe sapere che è necessario creare un TransactionScope?
La responsabilità di un'action è semplicemente quella di ricevere l'input dell'utente e passarlo all'opportuno servizio applicativo. Quindi il codice situato nell'action, io direi che dovrebbe essere semplicemente questo:
public IHttpActionResult Delete(long id)
{
return UoW.Person.DeleteById(id);
}
Personalmente non mi piace fare riferimento ad UoW così, perché il suo riferimento è cablato nell'action e ne riduce la testabilità. *Potresti* riceverlo come parametro del costruttore grazie a un motore di dependency injection.
public PersonController : Controller {
private readonly IUnitOfWork unitOfWork;
public PersonController(IUnitOfWork unitOfWork) {
this.unitOfWork = unitOfWork;
}
public IHttpActionResult Delete(long id)
{
return unitOfWork.Person.DeleteById(id);
}
}
Ovviamente questo ha senso se scrivi unit testing (dovresti) e testi i tuoi controller.
Altra cosa più importante: UoW lo riassegni ad ogni richiesta, sì? Come fai? Il DbContext non è thread safe e perciò la stessa istanza non dovrebbe essere usata da più richieste contemporanee.
Secondo me, UoW è un concetto che andrebbe relegato in un servizio applicativo. L'action non dovrebbe avere alcuna cognizione di come vengono persistiti gli oggetti né che sia necessario usare il pattern unit of work. Semplicemente, se devi persistere più entità di tipo diverso con una sola azione, ti crei un servizio applicativo che espone un metodo idoneo che accetta un DTO comprensivo di tutte le informazioni, e poi pensa lui a usare i repository (o EF nudo e crudo) per persistere tutto.
Poi, hai notato come improvvisamente tu sia costretto a usare un TransactionScope? Chiediti: perché è stato necessario farlo? Ne è valsa la pena?
A proposito delle eccezioni, non mettere try..catch solo per rilanciare l'eccezione originale con throw. Il metodo Dispose viene automaticamente invocato se hai usato un blocco using, anche quando si verifica un'eccezione. Caso mai usa try...catch per sollevare nuove eccezioni di altro tipo, di livello più alto.
Cioè, cerca di catturare le eccezioni che arrivano dallo strato infrastrutturale (tipo DbUpdateException) e risollevale di altro tipo, più significativo, che possano aiutare la Web API a dare un messaggio utile all'utente. Ad esempio: se il Person che si vuole eliminare non esiste, allora fai sollevare dal repository una PersonNotFoundException in modo che chi consuma la tua API possa vedere scritto "The person with the id {id} does not exist" e gli dai status code 404.
A proposito di questo:
public interface IPersonRepository : IRepositoryBase<Person>
{
PersonDTO CreateWithHobbies(PersonUpdateDto person);
IEnumerable<PersonDTO> GetAllWithHobbies();
PersonDTO GetUserWithHobbiesById(long id);
IEnumerable<PersonDTO> GetUsersWithHobbiesByName(string name);
void UpdateWithHobbies(PersonUpdateDto person);
}
Ok, hai fatto bene a definire metodi specifici per l'IPersonRepository però ricordati di aggiungere anche DeleteById e ogni altro metodo abbia a che fare con il recupero o la persistenza delle entità. Ad esempio, "GetVeryImportantPeople" o qualsiasi altro metodo richieda la composizione di una query LINQ.
Abituati a usare async/await. Questo è importante per razionalizzare l'uso dei thread e fare in modo che l'applicazione riesca a gestire più richieste contemporanee senza dover aumentare le caratteristiche hardware del server.
ciao,
Moreno