L'injection de dépendances dans ASP.NET MVC 3 filtres d'actions. Quel est le problème avec cette approche?
Voici la configuration. Dire que j'ai un peu d'action, filtre a besoin d'une instance d'un service:
public interface IMyService
{
void DoSomething();
}
public class MyService : IMyService
{
public void DoSomething(){}
}
J'ai ensuite une ActionFilter qui a besoin d'une instance de ce service:
public class MyActionFilter : ActionFilterAttribute
{
private IMyService _myService; //<--- How do we get this injected
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_myService.DoSomething();
base.OnActionExecuting(filterContext);
}
}
MVC 1/2 l'injection de dépendances dans l'action des filtres était un peu d'une douleur dans le cul. L'approche la plus commune est d'utiliser un appelant d'action comme on peut le voir ici: http://www.jeremyskinner.co.uk/2008/11/08/dependency-injection-with-aspnet-mvc-action-filters/ La motivation principale derrière cette solution de contournement a été parce que cette approche suivante a été considéré comme bâclée et le couplage avec le conteneur:
public class MyActionFilter : ActionFilterAttribute
{
private IMyService _myService;
public MyActionFilter()
:this(MyStaticKernel.Get<IMyService>()) //using Ninject, but would apply to any container
{
}
public MyActionFilter(IMyService myService)
{
_myService = myService;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_myService.DoSomething();
base.OnActionExecuting(filterContext);
}
}
Ici nous sommes à l'aide du constructeur d'injection et de surcharger le constructeur à utiliser le conteneur et injecter le service. Je suis d'accord que ne couplent le récipient avec le ActionFilter.
Ma question est: est-ce Maintenant dans ASP.NET MVC 3, où nous avons une abstraction du récipient utilisé (par le biais de la DependencyResolver) sont tous ces cerceaux-elle encore nécessaire? Permettez-moi de démontrer:
public class MyActionFilter : ActionFilterAttribute
{
private IMyService _myService;
public MyActionFilter()
:this(DependencyResolver.Current.GetService(typeof(IMyService)) as IMyService)
{
}
public MyActionFilter(IMyService myService)
{
_myService = myService;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_myService.DoSomething();
base.OnActionExecuting(filterContext);
}
}
Maintenant, je sais que certains puristes pourraient se moquer de cela, mais sérieusement, quel serait l'inconvénient? C'est toujours vérifiable que vous pouvez utiliser le constructeur qui prend un IMyService au temps de test et d'injecter une maquette au service de cette façon. Vous n'êtes pas lié à la mise en œuvre des DI conteneur puisque vous êtes à l'aide de la DependencyResolver, alors y at-il des inconvénients à cette approche?
D'ailleurs, voici une autre bonne approche pour ce faire dans MVC3 à l'aide de la nouvelle IFilterProvider interface: http://www.thecodinghumanist.com/blog/archives/2011/1/27/structuremap-action-filters-and-dependency-injection-in-asp-net-mvc-3
- Merci pour les liens vers mon post :). Je pense que ce serait bien. Malgré les articles de mon blog plus tôt cette année, je ne suis pas un grand fan de la DI-elles incluses dans MVC 3 et les ont pas été à l'aide de ces derniers temps. Il semble que le travail, mais se sent un peu maladroit parfois.
- Si vous utilisez Ninject, cela pourrait être une approche possible: stackoverflow.com/questions/6193414/...
- +1, bien que le service locator est considéré comme un anti-modèle par beaucoup de gens, je pense que je préfère votre approche sur les Marques pour sa simplicité et le fait que la dépendance est résolu en un seul endroit, le conteneur IOC, tandis que dans la Marque de l'exemple que vous auriez à résoudre en deux endroits, dans le programme d'amorçage et lors de l'enregistrement de l'filtres globaux, qui se sent mal.
- vous pouvez toujours utiliser le "DependencyResolver.Actuel.La méthode GetService(Type) n'importe quand vous voulez.
Vous devez vous connecter pour publier un commentaire.
je ne suis pas positif, mais je crois que vous pouvez simplement utiliser un constructeur vide (pour les attribut partie) et ensuite avoir un constructeur qui fait injecte la valeur (pour le filtre partie).*Modifier: Après un peu de lecture, il semble que la façon de le faire est par la propriété d'injection:
Concernant la pourquoi ne pas utiliser un Localisateur de Service question: Il la plupart du temps simplement réduit la souplesse de votre dépendance à l'injection. Par exemple, si vous avez été l'injection d'un service d'enregistrement, et vous avez voulu donner automatiquement le service d'enregistrement de nom de la classe c'est d'être injecté dans? Si vous utilisez constructeur d'injection, qui serait de l'excellent travail. Si vous êtes à l'aide d'un Solveur de Dépendances/Localisateur de Service, vous serez hors de la chance.
Mise à jour
Depuis cet été admis que la réponse, j'aimerais bien aller sur le dossier de dire que je préfère Marque Seeman l'approche de parce qu'il sépare l'Action de Filtre responsabilité à l'écart de l'Attribut. En outre, Ninject de MVC3 extension a de très puissants moyens pour configurer des filtres d'actions via des liaisons. Consultez les références suivantes pour plus de détails:
Mise à jour 2
Comme @usr souligné dans les commentaires ci-dessous,
ActionFilterAttribute
s sont instanciés lorsque la classe est chargée, et ils durent toute la durée de vie de l'application. Si leIMyService
interface n'est pas censé être un Singleton, alors il finit par être une En Captivité Dépendance. Si sa mise en œuvre n'est pas thread-safe, vous pourriez être dans un beaucoup de douleur.Chaque fois que vous avez une dépendance avec une durée de vie plus courte que celle de votre classe de durée de vie prévue, il est sage d'injecter une usine à produire que la dépendance à la demande, plutôt que de l'injecter directement.
DependencyResolver.GetService
, la méthode de reliure a aucune idée de ce qui classe cette dépendance est d'être injecté dans. Que faire si vous voulez créer un autreIMyService
pour certains types d'action des filtres? Ou, comme je l'ai dit dans ma réponse, et si vous vouliez un argument à laMyService
mise en œuvre de dire dans quelle classe il a été injecté dans (ce qui est utile pour les bûcherons)?Injected
attribut, vous pouvez spécifier votre propre attribut via leNinjectSettings.InjectAttribute
de la propriété. Cela rend plus facile de garder votre Ninject références contenues dans votre DI code d'installation.ServiceLocator.Get<LoggerFactory>().Create(typeof(Foo))
, mais cela vous ouvre à une forte probabilité de copier-coller des erreurs. Si vous ne modifiez pas le type de param, Le journal pourrait-on dire, un message d'erreur est produit par unFoo
quand elle est produite par unBar
.MyService.DoSomething()
n'est pas thread-safe. Il n'a pas d'introduire une nouvelle thread-des questions de sécurité qui n'étaient pas déjà dans le cas des OP code. Il n'est ni moins thread-safe de la Marque Seeman de réponse. Cependant, j'ai fait la mise à jour de la réponse à indiquer que l'action des filtres sont partagés entre les demandes, donc vous avez besoin pour regarder dehors pour les Captifs Dépendances.[Injected]
attribut, vous prenez une dépendance sur votre conteneur dans votre action de filtre. Je ne suis pas familier avec beaucoup de DI conteneurs, mais LightInject, lorsqu'il est configuré pour le Web API, va injecter un service en action des filtres sans avoir à utiliser n'importe quel type de[Injected]
attribut.Oui, il y a des inconvénients, comme il y a beaucoup de problèmes avec IDependencyResolver lui-même, et à ceux que vous pouvez ajouter l'utilisation d'un Singleton Localisateur de Service, ainsi que Bâtard D'Injection.
Une meilleure option est de mettre en place le filtre de classe normale dans laquelle vous pouvez injecter selon les services que vous souhaitez:
Remarquez comment le filtre examine la filterContext pour déterminer si oui ou non le comportement doit être appliquée.
Cela signifie que vous pouvez toujours utiliser les attributs de contrôler si oui ou non le filtre doit être appliqué:
Cependant, maintenant que l'attribut est complètement inerte.
Le filtre peut être composé avec la dépendance et ajouté les filtres globaux mondiaux.asax:
Pour un exemple plus détaillé de cette technique, bien qu'appliquées à ASP.NET l'API Web au lieu de la MVC, voir cet article: http://blog.ploeh.dk/2014/06/13/passive-attributes
((Controller)base.CreateController(requestContext, controllerName)).ActionInvoker = _customActionInvoker
où_customActionInvoker
implémenteControllerActionInvoker
et vous pouvez les écraser et de contrôleGetFilters
. Si vous vous souciez par exemple, le code, je vais poster une suite de réponse à Marc.La solution la Marque Seemann a suggéré semble élégant. Cependant assez complexe pour un simple problème. En utilisant le cadre de la mise en œuvre de AuthorizeAttribute se sent plus naturel.
Ma solution a été de créer un AuthorizeAttribute avec un délégué statique de l'usine à un service social mondial.asax. Il fonctionne pour tous les DI conteneur et se sent un peu mieux qu'un Localisateur de Service.
Mondiale.asax:
Ma classe attribut personnalisé: