Question

I'd like to create a container where it will allow ISomeService to be resolved, but not ISomeOtherService. Even though my registration for ISomeService depends on ISomeOtherService.

Does that make sense?

public interface ISomeService {}

public interface ISomeOtherService {}

public class SomeService : ISomeService
{
    public SomeService(ISomeOtherService someOtherService) {}
}
public class SomeOtherService : ISomeOtherService {}

this container I'd like would resolve SomeService for ISomeService but if I tried to resolve ISomeOtherService or SomeOtherService it would fail.

Is that bad design?

So, a little context... I've got ASP.Net MVC Controllers that will be developed by various developers. Those controllers SHOULD have access to the application services like ISomeService but not to their dependencies. I'd like to avoid having to code review all of these services to make sure developers are not violating architecture design. They should be able to get references to ISomeService, but ISomeOtherService is a DB repository and they should never deal with it directly, but ISomeService does need this reference.

I don't mind hoping in the middle of the resolving (it's an ASP.NET MVC application and I have an extension point already for Controller creation) so I COULD take a look at the Controller being resolved, look at it's dependencies and ensure they're on a whitelist, but I don't know how to easily evaluate dependencies with Windsor. Or, would I just have to do it myself by looking at the constructor parameters?

Was it helpful?

Solution

You could check with a SubDependencyResolver. See the code below:

public class SubDependencyResolver : ISubDependencyResolver
{
    public bool CanResolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model,
                           DependencyModel dependency)
    {
        // If not an ISomeotherComponent or SomeOtherComponent is resolved ignore.
        if (dependency.TargetType != typeof (ISomeOtherComponent) && dependency.TargetType != typeof (SomeOtherComponent)) return false;

        // check if we are resolving for SomeComponent
        if (model.Implementation == typeof (SomeComponent)) return false;

        // We are resolving for a different component then SomeComponent.
        Debug.Assert(false);
        return false;
    }

    public object Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model,
                          DependencyModel dependency)
    {
        // We will never actually resolve the component, but always use the standard SubDependencyResolver, as Can resolve always returns false;
        return null;
    }
}


class Program
{
    static void Main(string[] args)
    {
        var container = new WindsorContainer();
        container.Kernel.Resolver.AddSubResolver(new SubDependencyResolver());
        container.Register(
            Component.For<ISomeOtherComponent>().ImplementedBy<SomeOtherComponent>(),
            Component.For<ISomeComponent>().ImplementedBy<SomeComponent>(),
            Component.For<Legal>()
//          Component.For<Illegal>() uncommenting this line will assert.
            );
    }
} 

public interface ISomeComponent
{
}

public interface ISomeOtherComponent
{
}

public class SomeComponent : ISomeComponent
{
    public SomeComponent(ISomeOtherComponent someOtherComponent)
    {
    }
}

public class SomeOtherComponent : ISomeOtherComponent
{
}

public class Legal
{
    public Legal(ISomeComponent component)
    {
    }
}

public class Illegal
{
    public Illegal(ISomeOtherComponent component)
    {
    }
}

OTHER TIPS

I'd like to avoid having to code review all of these services to make sure developers are not violating architecture design. They should be able to get references to ISomeService, but ISomeOtherService is a DB repository and they should never deal with it directly, but ISomeService does need this reference.

In order to reach this goal I would avoid reference from 'WebUI' to DataAccess at all and develop it the following way:

Let's say we have tre projects WebUI, ApplicationServices, DataAcccess.

WebUI knows about (has reference to) ApplicationServices, ApplicationServices knows about DataAcccess. So, WebUI has no dirrect reference to types in DataAcccess types and it is imposible to create instatnce of any repository in the WebUI.

WebUI:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var service = new ApplicationServices.SomeService();
        service.DoSmth();

        return View();
    }
}

ApplicationServices:

public class SomeService : ISomeService
{
    private readonly ISomeOtherService someOtherService;

    public SomeService() : this(new SomeOtherService())
    {

    }

    public SomeService(ISomeOtherService someOtherService)
    {
        this.someOtherService = someOtherService;
    }

    public void DoSmth()
    {
        someOtherService.DoDbCall();
    }
}

public interface ISomeService
{
    void DoSmth();
}

DataAcccess:

public class SomeOtherService : ISomeOtherService
{
    public void DoDbCall()
    {
        /* Db Calls */
    }
}

public interface ISomeOtherService
{
    void DoDbCall();
}

In order to install components from DataAcccess ther installers should be put into ApplicationServices.

Looking at the Windsor documentation, you might be able to leverage the UsingFactoryMethod configuration option to determine if the component being instantiated is allowed to use the component.

Below is a basic implementation where the Disallowed class is not able to resolve because it has a dependency on Restricted, and only the Allowed class is able to use that dependency. Its essentially a whitelist, except configured within Windsor.

class Program
{
    static void Main(string[] args)
    {

        try
        {
            var container = new Castle.Windsor.WindsorContainer();

            container.Register
            (
                Component
                .For<Restricted>()
                .UsingFactoryMethod
                (
                    (k, c) =>
                    {
                        var requestingType = c.Handler.ComponentModel.Implementation;
                        if (requestingType == typeof(Allowed))
                        {
                            return new RestrictedImp();
                        }
                        else
                        {
                            var errorMessage = string.Format
                            (
                                "The type [{0}] is not permitted to resolve [{1}].", 
                                requestingType.Name, 
                                c.RequestedType.Name
                            );
                            throw new InvalidOperationException(errorMessage);
                        }
                    }
                )
                .LifeStyle
                .Transient
            );
            container.Register(Component.For<Allowed>());
            container.Register(Component.For<Disallowed>());

            var a = container.Resolve<Allowed>();
            var b = container.Resolve<Disallowed>();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
        }

        Console.ReadLine();
    }
}

interface Restricted { }

class RestrictedImp : Restricted
{

}

class Allowed
{
    public Allowed(Restricted restricted)
    {

    }
}

class Disallowed
{
    public Disallowed(Restricted restricted)
    {

    }
}

Please note I am not actually all that familiar with Castle Windsor, I just assumed it would have something similar to the Ninject Bind<T>.ToMethod(blah), which allows you to call a method each time the component is being resolved. There appears to be enough context attached to the resolution that you can perform some basic permission checking. There might be better ways.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top