Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically determining dependency based on user parameters

Problem

I currently have a factory that depends on a few parameters to properly decide which object to return. This factory is not yet bound to DI. As I understand it NInject uses providers as a factory.

Here is what I currently have. I'll warn you it's not pretty.

public interface IRole
{
    string Name { get; }
}

public class FooRole : IRole
{
    public string Name { get { return "Foo Role"; } }
}

public class BarRole : IRole
{
    public string Name { get { return "Bar Role"; } }
}

public class FooBarRoleModule : NinjectModule
{
    public override void Load()
    {
        Bind<IRole>().ToProvider<RoleProvider>();
    }
}

public class RoleProvider : Provider<IRole>
{
    protected override IRole CreateInstance(IContext context)
    {
        var isNewParameter = context.Parameters
            .Where(x => x.Name == "isNew")
            .Select(x => x.GetValue(context))
            .Cast<bool>()
            .FirstOrDefault();

        if (isNewParameter) return new FooRole();
        return new BarRole();
    }
}

var role = kernel.Get<IRole>(new ConstructorArgument("isNew", true));
Console.WriteLine(role.Name);

I'm almost certain this is not the proper way of dynamically injecting dependencies, however it does work. And frankly, looking at what I've hacked together doesn't seem to make much sense.

The reason I need to dynamically resolve the dependency is that during the life cycle of the application the end-user can assume several roles. The sample posted above is pretty simply but the parameters (currently only isNew) would determine would object to inject.

Question

What is the proper way to use a provider with parameters not known at run time? These parameters would be passed in whenever the user triggers the code that could give them a different role.

Thank you.

Edit

public class RoleFactory : IRoleFactory
{
    public IRole Create(bool isNew)
    {
        if (isNew) return new BarRole();
        return new FooRole();
    }
}

Bind it with Bind<IRoleFactory>().To<RoleFactory>(); and use it like so.

var role = kernel.Get<IRoleFactory>();
Console.WriteLine(role.Create(true).Name);

This revised version does look better and makes more sense, however I believe I missed some key details in your post. Wanted to add that the isNew parameter would be true if the factory was called from for example a "Create New Event" button.

like image 606
gcso Avatar asked Feb 06 '11 19:02

gcso


1 Answers

Ninject provides several machanism to handle such problems. Which one that suits best for you depends on the exact problem. Here are the most likely ones:

Use named bindings:

kernel.Bind<IRole>().To<FooRole>().Named("old");
kernel.Bind<IRole>().To<BarRole>().Named("new");
kernel.Get<IRole>("new");

Use conditional Bindings

kernel.Bind<IRole>().To<AdminRole>().When(ctx => UserHasAdminPermission())
kernel.Bind<IRole>().To<UserRole>();
kernel.Get<IRole>();
like image 111
Remo Gloor Avatar answered Oct 14 '22 08:10

Remo Gloor