views:

236

answers:

4

I am castle Windsor and it works great for controller constructors in passing in the repository that is being used.

private IStoryRepository Repository;
public StoryController(IStoryRepository Repository)
{
    this.Repository = Repository;                   
}

Now I have an Action that is in the admin area to display the main admin menu. I have used a custom authorisation attribute which will just check that the logged in user is an admin (just an isAdmin flag in the users table)

 [AdminAuthorize]
 public ActionResult Menu()

 private IStoryRepository Repository;
 /// <summary>
 /// Initializes a new instance of the <see cref="AdminAuthorizeAttribute"/> class.
 /// </summary>
 public AdminAuthorizeAttribute(IStoryRepository Repository)
 {
     this.Repository = Repository;
 }

 /// <summary>
 /// Checks if the user is authorised
 /// </summary>
 /// <param name="httpContext">The HTTP context.</param>
 /// <returns>
 ///    <c>true</c> if authorized; otherwise, <c>false</c>.
 /// </returns>
 protected override bool AuthorizeCore(HttpContextBase httpContext)
 {
    return this.Repository.UserIsAdmin(httpContext.User.Identity.Name);
 }

How can I get Castle to pass the repository into attribute constructor like it does for a controller constructor?

+1  A: 

You basically have two options. Wrap the filter in a proxy, a good example of this can be found here.

Or, within your custom filter you can do an explicit container call. For example using StructureMap (I have no used castle extensively)

ObjectFactory.GetInstance(IStoryRepository)

There may be a third way which is to extend the ActionInvoker to do the injection but I am not sure how this would be done.

madcapnmckay
A: 

I might be worth looking at FluentMVC project. It allows you to configure attributes at startup and because it uses windsor under the hood should allow for this to be injected pritty easily. For example

FluentMvcConfiguration.Configure = x => {
                                 x.UsingControllerFactory(new WindsorControllerFactory());                                                                x.WithFilter<HandleErrorAttribute>();
                                 x.WithFilter<AuthorizeAttribute>(
                                     Except
                                         .For<AccountController>(ac => ac.LogOn())
                                         .AndFor<AccountController>(ac => ac.LogOn(null, null, false, null))
                                         .AndFor<HomeController>());
                             };

The code above will add the AuthorizeAttribute to all actions except Login and the home controller

Not sure what the current state of the project is but have used it a few time and works pritty well for me

Colin G
A: 

The problem is that attributes are constructed by reflection rather than through calls that can be intercepted and replaced with calls that delegate to the container.

There are numerous approaches that can be used to create filters that can support DI, the simplest IMHO is to extend the action invoker and override GetFilters, providing an implementation that uses the attribute to determine the filter type and then resolving that type from the container. An implementation of this approach can be seen in MvcTurbine ( http://mvcturbine.codeplex.com/sourcecontrol/changeset/view/37298?projectName=mvcturbine#758440 ).

Neal
A: 

Why don't you get the IRepository object from a static factory method inside the Filter constructor? You just use the factory method in a way to allow DI to do its work.

DI will work on your "gateway" method instead of the standard "constructor parameter" approach.

Davide Vosti