Passive Attributes by Mark Seemann
Passive Attributes are Dependency Injection-friendly.
In my article about Dependency Injection-friendly frameworks, towards the end I touched on the importance of defining attributes without behaviour, but I didn't provide a constructive example of how to do this. In this article, I'll outline how to write a Dependency Injection-friendly attribute for use with ASP.NET Web API, but as far as I recall, you can do something similar with ASP.NET MVC.
Problem statement #
In ASP.NET Web API, you can adorn your Controllers and their methods with various Filter attributes, which is a way to implement cross-cutting concerns, such as authorization or error handling. The problem with this approach is that attribute instances are created by the run-time, so you can't use proper Dependency Injection (DI) patterns such as Constructor Injection. If an attribute defines behaviour (which many of the Web API attributes do), the most common attempt at writing loosely coupled code is to resort to a static Service Locator (an anti-pattern).
This again seems to lead framework designers towards attempting to make their frameworks 'DI-friendly' by introducing a Conforming Container (another anti-pattern).
The solution is simple: define attributes without behaviour.
Metering example #
Common examples of cross-cutting concerns are authentication, authorization, error handling, logging, and caching. In these days of multi-tenant on-line services, another example would be metering, so that you can bill each user based on consumption.
Imagine that you're writing an HTTP API where some actions must be metered, whereas others shouldn't. It might be nice to adorn the metered actions with an attribute to indicate this:
[Meter] public IHttpActionResult Get(int id)
Metering is a good example of a cross-cutting concern with behaviour, because, in order to be useful, you'd need to store the metering records somewhere, so that you can bill your users based on these records.
A passive Meter attribute would simply look like this:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class MeterAttribute : Attribute { }
In order to keep the example simple, this attribute defines no data, and can only be used on methods, but nothing prevents you from adding (primitive) values to it, or extend its usage to classes as well as methods.
As you can tell from the example, the MeterAttribute has no behaviour.
In order to implement a metering cross-cutting concern, you'll need to define an IActionFilter implementation, but that's a 'normal' class that can take dependencies:
public class MeteringFilter : IActionFilter { private readonly IObserver<MeterRecord> observer; public MeteringFilter(IObserver<MeterRecord> observer) { if (observer == null) throw new ArgumentNullException("observer"); this.observer = observer; } public Task<HttpResponseMessage> ExecuteActionFilterAsync( HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation) { var meterAttribute = actionContext .ActionDescriptor .GetCustomAttributes<MeterAttribute>() .SingleOrDefault(); if (meterAttribute == null) return continuation(); var operation = actionContext.ActionDescriptor.ActionName; var user = actionContext.RequestContext.Principal.Identity.Name; var started = DateTimeOffset.Now; return continuation().ContinueWith(t => { var completed = DateTimeOffset.Now; var duration = completed - started; var record = new MeterRecord { Operation = operation, User = user, Started = started, Duration = duration }; this.observer.OnNext(record); return t.Result; }); } public bool AllowMultiple { get { return true; } } }
This MeteringFilter class implements IActionFilter. It looks for the [Meter]
attribute. If it doesn't find the attribute on the method, it immediately returns; otherwise, it starts collecting data about the invoked action:
- From
actionContext.ActionDescriptor
it retrieves the name of the operation. If you try this out for yourself, you may find thatActionName
alone doesn't provide enough information to uniquely identify the method - it basically just contains the value "Get". However, theactionContext
contains enough information about the action that you can easily build up a better string; I just chose to skip doing that in order to keep the example simple. - From
actionContext.RequestContext.Principal
you can get information about the current user. In order to be useful, the user must be authenticated, but if you need to meter the usage of your service, you'll probably not allow anonymous access. - Before invoking the
continuation
, the MeteringFilter records the current time. - After the
continuation
has completed, the MeteringFilter again records the current time and calculates the duration. - Finally, it publishes a MeterRecord to an injected dependency.
Configuring the service #
MeteringFilter is a normal class with behaviour, which you can register as a cross-cutting concern in your Web API service as easily as this:
var filter = new MeteringFilter(observer); config.Filters.Add(filter);
where observer
is your preferred implementation of IObserver<MeterRecord>. This example illustrates the Pure DI approach, but if you rather prefer to resolve MeteringFilter with your DI Container of choice, you can obviously do this as well.
The above code typically goes into your Global.asax file, or at least a class directly or indirectly invoked from Application_Start. This constitutes (part of) the Composition Root of your service.
Summary #
Both ASP.NET Web API and ASP.NET MVC supports cross-cutting concerns in the shape of filters that you can add to the service. Such filters can look for passive attributes in order to decide whether or not to trigger. The advantage of this approach is that you can use normal Constructor Injection with these filters, which completely eliminates the need for a Service Locator or Conforming Container.
The programming model remains the same as with active attributes: if you want a particular cross-cutting concern to apply to a particular method or class, you adorn it with the appropriate attribute. Passive attributes have all the advantages of active attributes, but none of the disadvantages.
Comments
What if the dependency I'm injecting needs to be a transient dependency? Injecting a transient service into a singleton (the filter), would cause issues. My initial idea is to create an abstract factory as a dependency, then when the filter action executes, create the transient dependency, use it, and dispose. Do you have any better ideas?
Jonathan, thank you for writing. Does this article on the Decoraptor pattern (and this Stack Overflow answer) answer your question?
Yes, that's a good solution for this situation. Thanks!
Reading through Asp.Net Core Type and Service Filters, do you think it's sufficient to go that way (I know that TypeFilter is a bit clumsy), but let's assume that I need simple injection in my filter - ServiceFilterAttribute looks promising. Or you still would recomment to implement logic via traditional filter pipeline: `services.AddMvc(o => o.Filters.Add(...));`?
Valdis, thank you for writing. I haven't looked into the details of ASP.NET Core recently, but even so: on a more basic level, I don't understand the impulse to put behaviour into attributes. An attribute is an annotation. It's a declaration that a method, or type, is somehow special. Why not keep the annotation decoupled from the behaviour it 'triggers'? This would enable you to reuse the behaviour in other scenarios than by putting an attribute on a method.
I got actually very similar feelings, just wanted to get your opinion. And by the way - there is a catch, you can mismatch type and will notice that only during runtime. For instance: `[ServiceFilter(typeof(HomeController))]` will generate exception, because given type is not derived from `IFilterMetadata`
Indeed, one of the most problematic aspects of container-based DI (as opposed to Pure DI) is the loss of compile-time safety - to little advantage, I might add.
I'm concerned with using attributes for AOP at all in these cases. Obviously using attributes that define behaviors that rely on external depedencies is a bad idea as you and others have already previously covered. But is using attributes that define metadata for custom behaviors all that much better? For example, if one provides a framework with libraries containing common controllers, and another pulls those controllers into their composition root in order to host them, there is no indication at compile time that these custom attributes may be present. Would it not be better to require that behavior be injected into the constructor and simply consume the behavior at the relevant points within the controller? Or if attributes must be used, would it not be better for the component that implements the behavior to somehow be injected into the controller and given the opportunity to intercept requests to the controller earlier in the execution pipeline so that it can check for the attributes? Due to the nature of the Web API and .Net MVC engines, attributes defined with behavior can enforce their behaviors to be executed by default. And while attributes without behavior do indicate the need for a behavior to be executed for the class they are decorating, it does not appear that they can enforce said behavior to be executed by default. They are too easy to miss or ignore. There has got to be a better way. I have encountered this problem while refactoring some code that I'm working on right now (retro fitting said code with more modern, DI based code). I'm hoping to come up with a solution that informs the consuming developer in the composition root that this behavior is required, and still be able enforce the behavior with something closer to a decoration rather than a function call.
Tyree, thank you for writing. That's a valid concern, but I don't think it's isolated to passive attributes. The problem you outline is also present if you attempt to address cross-cutting concerns with the Decorator design pattern, or with dynamic interception as I describe in chapter 9 of my book. You also have this problem with the Composite pattern, because you can't have any compile-time guarantee that you've remembered to compose all required elements into the Composite, or that they're defined in the appropriate order (if that matters).
In fact, you can extend this argument to any use of polymorphism: how can you guarantee, at compile-time, that a particular polymorphic object contains the behaviour that you desire, instead of, say, being a Null Object? You can't. That's the entire point of polymorphism.
Even with attributes, how can you guarantee that the attributes stay there? What if another developer comes by and removes an attribute? The code is still going to compile.
Ultimately, code exists in order to implement some desired behaviour. There are guarantees you can get from the type system, but static typing can't provide all guarantees. If it could, you be in the situation where, 'if it compiles, it works'. No programming language I've heard of provides that guarantee, although there's a spectrum of languages with stronger or weaker type systems. Instead, we'll have to get feedback from multiple sources. Attributes often define cross-cutting concerns, and I find that these are often best verified with a set of integration tests.
As always in software development, you have to find the balance that's right for a particular scenario. In some cases, it's catastrophic if something is incorrectly configured; in other cases, it's merely unfortunate. More rigorous verification is required in the first case.
Thanks for the post, I tried to do the same for a class attribute (AttributeTargets.Class) and I am getting a null object every time I get the custom attributes. Does this only work for Methods? Or how can I make it work with classes? Thanks.
Cristian, thank you for writing. The example code shown in this article only looks in the action context's
ActionDescriptor
, which is an object that describes the action method. If you want to look for the attribute on the class, you should look in the action context'sControllerDescriptor
instead, like this:Obviously, if you want to support putting the attribute both on the class and the method, you'd need to look in both places, and decide which one to use if you find more than one.
I'm really struggling to get everything hooked up so it's initialised in the correct order, with one DbContext created per web request or scheduled task. The action filter is my biggest sticking point.
I had previously split an IAuthorizationFilter off from a WebAPI AuthorizationFilterAttribute following the advice of this article. I create my filter in WebApiConfig.Register, which is called from MvcApplication.Application_Start(). My problem is that I can't inject my UserService here, because this code is shared across requests, and I want to use one DbContext per request (UserService depends on DbContext). It works if I inject the DependencyResolver, but I realise that's an anti-pattern.
What am I missing? Should I new up a DbContext and UserService just for my AuthorizationFilter, that then gets shared acorss requests? Is there somewhere else I can add my ActionFilter, that gets called once per request? I can inject my service into the created AuthorizationFilterAttribute using the steps described at https://michael-mckenna.com/dependency-injection-for-asp-net-web-api-action-filters-in-3-easy-steps/ but would that just make it a captive dependency of an AuthorizationFilterAttribute that gets shared across multiple requests?
Ian, thank you for writing. It sounds like you need a Decoraptor.