2015-12-09 2 views
2

J'ai suivi ce article et tout a fonctionné sauf la dépendance injecter (partiellement). Dans mon projet, j'utilise l'unité et j'essaie de créer un attribut Transaction personnalisé dont le but est de démarrer une transaction NHibernate avant l'exécution d'une action et de valider/annuler la transaction après l'exécution de la méthode.Filtre d'action personnalisé unité d'injection de dépendances web api 2

Telle est la définition de mon attribut: -

public class TransactionAttribute : Attribute 
{ 
} 

Voici la définition de mon TransactionFilter

public class TransactionFilter : IActionFilter 
{ 
    private readonly IUnitOfWork _unitOfWork; 

    public TransactionFilter(IUnitOfWork uow) { 
     _unitOfWork = uow; 
    } 

    public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) { 
     var transAttribute = actionContext.ActionDescriptor.GetCustomAttributes<TransactionAttribute>().SingleOrDefault(); 
     if (transAttribute == null) { 
     return continuation(); 
     } 

     var transaction = uow.BeginTransaction(); 
     return continuation().ContinueWith(t => 
     { 
     try{ 
      transaction.Commit(); 
      return t.Result; 
     } 
     catch(Exception e) 
     { 
      transaction.Rollback(); 
      return new ExceptionResult(ex, actionContext.ControllerContext.Controller as ApiController).ExecuteAsync(cancellationToken).Result; 
     } 
     } 
    } 
} 

et moi avons créé un fournisseur de filtre personnalisé qui utilise l'unité pour construire ce filtre.

public class UnityActionFilterProvider 
    : ActionDescriptorFilterProvider, 
     IFilterProvider 
    { 
     private readonly IUnityContainer container; 

     public UnityActionFilterProvider(IUnityContainer container) 
     { 
      this.container = container; 
     } 

     public new IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor) 
     { 
      foreach (IActionFilter actionFilter in container.ResolveAll<IActionFilter>()) 
      { 
       // TODO: Determine correct FilterScope 
       yield return new FilterInfo(actionFilter, FilterScope.Global); 
      } 
     } 
    } 

je me inscrire le UnityActionFilterProvider dans UnityWebApiActivator (j'utilise package Unity.AspNet.WebApi) comme suit

public static void Start() 
     { 
      var container = UnityConfig.GetConfiguredContainer(); 
      var resolver = new UnityDependencyResolver(container); 
      var config = GlobalConfiguration.Configuration; 
      config.DependencyResolver = resolver; 

      var providers = config.Services.GetFilterProviders(); 
      var defaultProvider = providers.Single(i => i is ActionDescriptorFilterProvider); 
      config.Services.Remove(typeof(IFilterProvider), defaultProvider); 
      config.Services.Add(typeof(IFilterProvider), new UnityActionFilterProvider(container)); 
     } 

Le problème est tout fonctionne bien pour la première pour toute action demande, mais les demandes ultérieures la même action ne recrée pas le TransactionFilter ce qui signifie qu'il n'appelle pas le constructeur pour assigner un nouveau UOW. Je ne pense pas que je peux désactiver la mise en cache du filtre d'action. La seule option que j'ai maintenant est d'utiliser le modèle de localisateur de service et d'obtenir l'instance UOW en utilisant un conteneur dans ExecuteActionFilterAsync qui, à mon avis, tue le but de ceci et je ferais mieux d'implémenter ActionFilterAttribute personnalisé.

Des suggestions?

+0

Qu'est-ce que 'repository'? Pourquoi «uow» est-il injecté s'il n'est pas utilisé? –

+0

Désolé, c'était une faute de frappe. Le dépôt aurait dû être uow. J'ai corrigé dans le code original. Merci – tangokhi

Répondre

1

Autant que j'ai été capable de dire au cours des années, ce qui se passe dans le code de démarrage d'application Web a essentiellement la durée de vie de Singleton. Ce code ne s'exécute qu'une fois.

Cela signifie qu'il n'y a qu'une seule instance de chacun de vos filtres. C'est bon pour la performance, mais ne correspond pas à votre scénario.

La solution la plus facile à ce problème, bien qu'un peu d'une abstraction qui fuit, consiste à injecter un résumé usine au lieu de la dépendance elle-même:

public class TransactionFilter : IActionFilter 
{ 
    private readonly IFactory<IUnitOfWork> _unitOfWorkFactory; 

    public TransactionFilter(IFactory<IUnitOfWork> uowFactory) { 
     _unitOfWorkFactory = uowFactory; 
    } 

    // etc... 

Utilisez ensuite l'usine dans la méthode ExecuteActionFilterAsync:

var transaction = _unitOfWorkFactory.Create().BeginTransaction(); 

Une solution plus élégante, à mon avis, serait d'utiliser un Decoraptor qui adapte le TransactionFilter, mais la réponse ci-dessus est probablement plus facile à comprendre.

+0

Merci. C'est une bonne suggestion. – tangokhi