J'utilise .NET Core Dependency Injection pour instancier un objet SqlConnection
lors du démarrage de l'application, que je prévois ensuite d'injecter dans mon référentiel. Ce SqlConnection
sera utilisé par Dapper pour lire/écrire des données à partir de la base de données dans l'implémentation de mon référentiel. Je vais utiliser les appels async
avec Dapper.
La question est: dois-je injecter le SqlConnection
comme transitoire ou comme singleton? Compte tenu du fait que je veux utiliser async
, ma pensée serait d'utiliser transitoire à moins que Dapper n'implémente des conteneurs d'isolation en interne et que la portée de mon singleton soit toujours enveloppée dans la portée que Dapper utilise en interne.
Existe-t-il des recommandations/meilleures pratiques concernant la durée de vie de l'objet SqlConnection lorsque vous travaillez avec Dapper? Y a-t-il des mises en garde que je pourrais manquer?
Merci d'avance.
Si vous fournissez une connexion SQL en tant que singleton, vous ne pourrez pas servir plusieurs demandes en même temps, sauf si vous activez MARS, qui a également ses limites. La meilleure pratique consiste à utiliser une connexion SQL transitoire et à s'assurer qu'elle est correctement éliminée.
Dans mes applications, je passe des IDbConnectionFactory
personnalisés aux référentiels qui sont utilisés pour créer une connexion à l'intérieur de l'instruction using
. Dans ce cas, le référentiel lui-même peut être singleton pour réduire les allocations sur le tas.
Je suis d'accord avec @Andrii Litvinov, à la fois réponse et commentaire.
Dans ce cas, j'irais avec l'approche d'une fabrique de connexions spécifique à la source de données.
Avec la même approche, je mentionne une manière différente - UnitOfWork.
Référez DalSession
et UnitOfWork
de this réponse. Cela gère la connexion.
Référez BaseDal
de this réponse. Ceci est mon implémentation de Repository
(en fait BaseRepository
).
UnitOfWork
est injecté comme transitoire.DalSession
distinct pour chaque source de données.UnitOfWork
est injecté dans BaseDal
.Existe-t-il des recommandations/meilleures pratiques concernant la durée de vie de l'objet SqlConnection lorsque vous travaillez avec Dapper?
La plupart des développeurs conviennent que la connexion doit être aussi courte que possible. Je vois deux approches ici:
using
pour chaque action. C'est une bonne approche tant que vous ne voulez pas grouper les actions. Même lorsque vous souhaitez regrouper les actions, vous pouvez utiliser la transaction dans la plupart des cas.using
ici. La solution est UnitOfWork comme ci-dessous.Grande question, et déjà deux bonnes réponses. J'ai été perplexe au début et j'ai trouvé la solution suivante pour résoudre le problème, qui encapsule les référentiels dans un gestionnaire. Le gestionnaire lui-même est chargé d'extraire la chaîne de connexion et de l'injecter dans les référentiels.
J'ai trouvé cette approche pour rendre les tests des référentiels individuellement, par exemple dans une application de console fictive, beaucoup plus simples, et j'ai beaucoup de chance de suivre ce modèle sur plusieurs projets à plus grande échelle. Bien que je ne sois certes pas un expert des tests, de l'injection de dépendance, ou bien quoi que ce soit vraiment!
La principale question que je me pose est de savoir si le DbService doit être un singleton ou non. Mon raisonnement était que, il était inutile de créer et de détruire constamment les différents référentiels encapsulés dans DbService
et comme ils sont tous apatrides, je ne voyais pas beaucoup de problème à leur permettre de "vivre". Bien que cela puisse être une logique totalement invalide.
EDIT: Si vous voulez une solution prête à l'emploi, consultez mon implémentation du référentiel Dapper sur GitHub
Le gestionnaire de référentiel est structuré comme suit:
/*
* Db Service
*/
public interface IDbService
{
ISomeRepo SomeRepo { get; }
}
public class DbService : IDbService
{
readonly string connStr;
ISomeRepo someRepo;
public DbService(string connStr)
{
this.connStr = connStr;
}
public ISomeRepo SomeRepo
{
get
{
if (someRepo == null)
{
someRepo = new SomeRepo(this.connStr);
}
return someRepo;
}
}
}
Un exemple de référentiel serait structuré comme suit:
/*
* Mock Repo
*/
public interface ISomeRepo
{
IEnumerable<SomeModel> List();
}
public class SomeRepo : ISomeRepo
{
readonly string connStr;
public SomeRepo(string connStr)
{
this.connStr = connStr;
}
public IEnumerable<SomeModel> List()
{
//work to return list of SomeModel
}
}
Tout câbler:
/*
* Startup.cs
*/
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
//...rest of services
services.AddSingleton<IDbService, DbService>();
//...rest of services
}
Et enfin, en l'utilisant:
public SomeController : Controller
{
IDbService dbService;
public SomeController(IDbService dbService)
{
this.dbService = dbService;
}
public IActionResult Index()
{
return View(dbService.SomeRepo.List());
}
}