J'ai utilisé un mélange #2 et #3, mais je préfère un strict générique référentiel si possible (plus strictes que même suggéré dans le lien n ° 3). #1 n'est pas bon parce qu'il joue mal avec les tests unitaires.
Si vous avez un petit domaine ou le besoin de resserrer les entités que votre domaine permet d'être interrogé, je suppose, #2 ou #3 qui définit une entité spécifique du référentiel des interfaces qui eux-mêmes de mettre en œuvre un référentiel générique - sens. Cependant, je trouve que c'est épuisant et inutile d'écrire une interface et une implémentation concrète pour chaque entité, je veux interroger. Ce bon est - public interface IFooRepository : IRepository<Foo>
(encore une fois, à moins que j'ai besoin de contraindre les développeurs à un ensemble de permis d'agrégation des racines)?
Je viens de définir mon générique référentiel d'interface, Add
, Remove
, Get
, GetDeferred
, Count
, et Find
méthodes (Trouver retourne un IQueryable
interface permettant de LINQ), créer un béton générique de mise en œuvre, et l'appeler un jour. Je compte fortement sur l' Find
et ainsi de LINQ. Si j'ai besoin d'utiliser une requête spécifique plus d'une fois, j'ai utiliser des méthodes d'extension et d'écrire la requête à l'aide de LINQ.
Ce couvre plus de 95% de ma persistance des besoins. Si j'ai besoin d'effectuer une sorte de persistance d'action qui ne peut pas être fait de façon générique, j'utilise un home-grown ICommand
API. Par exemple, dire que je suis en train de travailler avec NHibernate et j'ai besoin d'effectuer une requête complexe dans le cadre de mon domaine, ou peut-être que je dois faire une commande en vrac. L'API ressemble à peu près comme ceci:
// marker interface, mainly used as a generic constraint
public interface ICommand
{
}
// commands that return no result, or a non-query
public interface ICommandNoResult : ICommand
{
void Execute();
}
// commands that return a result, either a scalar value or record set
public interface ICommandWithResult<TResult> : ICommand
{
TResult Execute();
}
// a query command that executes a record set and returns the resulting entities as an enumeration.
public interface IQuery<TEntity> : ICommandWithResult<IEnumerable<TEntity>>
{
int Count();
}
// used to create commands at runtime, looking up registered commands in an IoC container or service locator
public interface ICommandFactory
{
TCommand Create<TCommand>() where TCommand : ICommand;
}
Maintenant, je peux créer une interface pour représenter une commande spécifique.
public interface IAccountsWithBalanceQuery : IQuery<AccountWithBalance>
{
Decimal MinimumBalance { get; set; }
}
Je peux créer une mise en œuvre concrète et l'utilisation SQL brut, NHibernate HQL, que ce soit, et l'enregistrer avec mon service locator.
Maintenant, dans ma logique d'entreprise, je peux faire quelque chose comme ceci:
var query = factory.Create<IAccountsWithBalanceQuery>();
query.MinimumBalance = 100.0;
var overdueAccounts = query.Execute();
Vous pouvez également utiliser une Spécification de modèle avec IQuery
de construire significative, de l'entrée utilisateur pilotée par les requêtes, plutôt que d'avoir une interface avec des millions de confusion propriétés, mais cela suppose que vous ne trouvez pas la spécification du modèle confus dans son propre droit ;).
Une dernière pièce du puzzle est quand votre dépôt doit faire des pré - et post opération de référentiel. Maintenant, vous pouvez très facilement créer une mise en œuvre de votre générique référentiel pour une entité spécifique, puis remplacer la méthode appropriée(s) et faire ce que vous devez faire, et de mettre à jour votre Cio ou le service localisateur d'inscription et être fait avec elle.
Cependant, parfois, cette logique transversale et difficile à mettre en œuvre par la substitution d'un référentiel de méthode. J'ai donc créé IRepositoryBehavior
, ce qui est essentiellement un récepteur d'événements. (Ci-dessous une ébauche de définition sur le dessus de ma tête)
public interface IRepositoryBehavior
{
void OnAdding(CancellableBehaviorContext context);
void OnAdd(BehaviorContext context);
void OnGetting(CancellableBehaviorContext context);
void OnGet(BehaviorContext context);
void OnRemoving(CancellableBehaviorContext context);
void OnRemove(BehaviorContext context);
void OnFinding(CancellableBehaviorContext context);
void OnFind(BehaviorContext context);
bool AppliesToEntityType(Type entityType);
}
Maintenant, ces comportements peuvent être n'importe quoi. Audit, contrôle de sécurité, soft-supprimer, l'application de contraintes de domaine, validation, etc. J'ai créer un comportement, l'enregistrer avec le Cio ou le service de localisation, et de modifier mon générique référentiel de prendre dans une collection d'inscrits IRepositoryBehavior
s, et vérifier chaque comportement à contre-courant type de référentiel et l'envelopper l'opération dans le pré/post gestionnaires pour chaque comportement.
Voici un exemple de soft-supprimer comportement (soft-supprimer signifie que lorsque quelqu'un vous demande de supprimer une entité, nous venons de le marquer comme étant supprimés de sorte qu'il ne peut pas être retourné à nouveau, mais n'est jamais vraiment physiquement supprimé).
public SoftDeleteBehavior : IRepositoryBehavior
{
// omitted
public bool AppliesToEntityType(Type entityType)
{
// check to see if type supports soft deleting
return true;
}
public void OnRemoving(CancellableBehaviorContext context)
{
var entity = context.Entity as ISoftDeletable;
entity.Deleted = true; // when the NHibernate session is flushed, the Deleted column will be updated
context.Cancel = true; // set this to true to make sure the repository doesn't physically delete the entity.
}
}
Oui, il s'agit essentiellement d'une procédure simplifiée et abstraite de la mise en œuvre de NHibernate est des écouteurs d'événement, mais c'est pourquoi je l'aime. A) je peux unité de test d'un comportement sans apporter de NHibernate dans l'image B) je peux utiliser ces comportements à l'extérieur de NHibernate (dire le référentiel de mise en œuvre du client qui encapsule le RESTE des appels de service) C) NH écouteurs d'événement peuvent être une vraie douleur dans le cul ;)