ninjectninject-extensions

How to remove repetitive Ninject Bindings?


My Current GeneralBindings : Ninject Module

public class GeneralBindings : NinjectModule
    {
        public override void Load()
        {
            // Requires Ninject.Extensions.Conventions
            // This binds all interfaces to concretes of the same name eg IClass -> Class.
            Kernel.Bind(x => x.FromAssembliesMatching("Company.Project.Scm*")
                .SelectAllClasses()
                .Excluding(typeof(AffectedCables),
                    typeof(ApplicationConfigurations),
                    typeof(ApplicationErrors),
                    typeof(ApplicationLogEvents),
                    typeof(AppUsers),
                    typeof(AppUsersContract),
                    typeof(Areas)

                    // etc..

                )
                .BindAllInterfaces());

then

            Bind<IAffectedCables>().To<AffectedCables>()
                .InSingletonScope()
                .WithConstructorArgument("url", @"api/AffectedCables");

            Bind<IApplicationErrors>().To<ApplicationErrors>()
                .InSingletonScope()
                .WithConstructorArgument("url", @"api/AppErrors");

            Bind<IApplicationConfigurations>().To<ApplicationConfigurations>()
                .InSingletonScope()
                .WithConstructorArgument("url", @"api/AppConfigurations");

            Bind<IApplicationLogEvents>().To<ApplicationLogEvents>()
                .InSingletonScope()
                .WithConstructorArgument("url", @"api/AppEventLogs");

            Bind<IAppUsers>().To<AppUsers>()
                .InSingletonScope()
                .WithConstructorArgument("url", @"api/AppUsers");

            Bind<IAppUsersContract>().To<AppUsersContract>()
                .InSingletonScope()
                .WithConstructorArgument("url", @"api/AppUserContract");

            Bind<IAreas>().To<Areas>()
                .InSingletonScope()
                .WithConstructorArgument("url", @"api/Areas");

            // Etc...
...

Can anyone suggest how to do this better? I'm sure that someone can create a single line of code that will save the copy and pasting that I have to perform at the moment.


Solution

  • A blind shot, not tested, but I would try with an attribute

    public class AutoBindWithRouteAttribute : Attribute
    {
        public string Route { get; }
    
        public AutoBindWithRouteAttribute(string route = null)
        {
            Route = route;
        }
    }
    

    then

        // to bind your first block of code for classes not featuring the attribute
        kernel.Bind(
            x => x.FromAssembliesMatching("").SelectAllClasses()
                .Where(t => !t.GetCustomAttributes(typeof(AutoBindWithRouteAttribute), false).Any())
               .BindAllInterfaces());
    
        // to bind your second block of code for classes featuring the attribute
        kernel.Bind(
            x => x.FromAssembliesMatching("").SelectAllClasses()
                .Where(t => t.GetCustomAttributes(typeof(AutoBindWithRouteAttribute), false).Any())
                .BindWith(new BindingGenerator()));
    

    where BindingGenerator is :

    public class BindingGenerator : IBindingGenerator
    {
        public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
        {
            var att =
                type.GetCustomAttributes(typeof(AutoBindWithRouteAttribute), false).FirstOrDefault() as
                    AutoBindWithRouteAttribute;
            if (att == null) yield break;
    
            yield return (IBindingWhenInNamedWithOrOnSyntax<object>)bindingRoot
                .Bind(type.GetInterfaces().First())
                .To(type)
                .InSingletonScope()
                .WithConstructorArgument("url", att.Route ?? $"api/{type.Name}");
        }
    }
    

    usage would then be :

    [AutoBindWithRoute()]
    public class AffectedCables : IAffectedCables
    
    [AutoBindWithRoute(@"api/AppErrors")]
    public class ApplicationErrors : IApplicationErrors