views:

149

answers:

3

I'm trying to implement my custom authorize attribute like:

 public class MyCustomAuth : AuthorizeAttribute
{
    private readonly IUserService _userService;

    public MyCustomAuth(IUserService userService)
    {
        _userService= userService;
    }
... continued
 }

I am using Castle Windsor for automatically resolve the dependency.

When I try to use it as an attribute of an action method obviously I am asked to pass the parameter or I need to have a parameter-less constructor that should resolve in some way it's dependency.

I tried to inject the dependency with a property but Windsor is not injecting it.

The only option I see now would be to instantiate manually the concrete object dependency loosing the benefit of Windsor.

How would you solve this problem?

+1  A: 

You did not supply any castle code or configuration examples. Therefore, I am assuming you are new to castle and unfimilar with the registration process.

First of all, any object you want to act upon must come from Castle Windsor via a registered type. With that said, you are going down the wrong path by trying to use castle windsor with attributes - because attributes are not referenced in code, but instead are reflected upon. Therefore, there is not a place for IoC (Castle Windsor in this case) to create the object.

I say this, because what you are trying to accomplish has already been done via different means. First of all, put your dependency injection on your controllers.

public class PostController
{
  private IUserService _userService;
  public PostController (IUserService userService)
  {
    _userService = userService;
  }

  // other logic

}

For Castle to work, you have register all types. So, you have to register IUserService, and what services implement that interface, within Castle Windsor. You can do this via a manual process in your global.asax with container.AddComponentWithLifeStyle; or the more preferred method is to use a configuration file.

Using a configuration file, your IUserService would look something like this:

<?xml version="1.0" encoding="utf-8" ?>
<castle>
    <components>
     <!--lifestyle="singleton|thread|transient|pooled|webrequest|custom"-->

     <component
      id="UserService"
      service="MyMvcProject.IUserService, MyMvcProject"
      type="MyMvcProject.UserService, MyMvcProject"
      lifestyle="transient">
     </component>
    </components>
</castle>

MyMvcProject is the name of your project and make sure to use the exact namespace and fully qualified name of the interface, and type. Please give code samples of your actual namespaces, else we cannot help you any further.

Before that will work, you will need to modify your app.config or web.config to register the searchGroup for Castle Windsor:

<configSections>

 <section name="castle" 
  type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor"/>

</configSections>

<castle configSource="castle.config"/>

Now that you are all registered and setup, it is time to tell your MVC application to "Use Castle Windsor to create each instance of my Controllers." Doing this means that Castle Windsor, being an Inversion-of-Control (aka IoC) container, will inspect the dependencies of your controller and see that it depends on IUserService and implement an instance of UserService. Castle will look into its configuration of registered types and see that you have registered a service for IUserService. And, it will see in your configuration file that you want to implement the concrete class of UserService that implements IUserService. So, the IoC container will return UserService when a dependency on IUserService is requested.

But before that can happen, you have to tell your MVC application to use your configuired IoC container. You do this by registering a new ControllerFactory. To do that, see my answer over at this question:

http://stackoverflow.com/questions/1598421/why-is-castle-windsor-trying-to-resolve-my-content-and-scripts-folder-as-a-co/1598479#1598479

And notice the global.asax part of how to register that code. It works quite well!

Now, when all of that is said and done, you want to authorize a user. Use the normal [Authorize] that implements Forms Authentication, and use your _userService to grab any user details of the signed in user.

eduncan911
+1  A: 

You cannot use DI with attributes - they're metadata;

public class MyCustomAuth : AuthorizeAttribute
{
    public void OnAuthorization(...)
    {
         IUserService userService = ServiceLocator.Current.GetInstance<IUserService>();
    }
}

Learn about Windsor/ServiceLocator here.

See similar question here.

queen3
+3  A: 

You can use a custom ControllerActionInvoker and inject property dependencies (you can't do constructor injection because the framework handles instantiation of attributes). You can see a blog post I just did on this technique.

Patrick Steele