0

I have an interface like this

public interface IPerson { }

And implementations

public class Fireman : IPerson
{
    public string Name { get; set; }
    public bool WithAssignedTruck { get; set; }
    ...
}

public class Pilot : IPerson
{
    public string Name { get; set; }
    public int Age { get; set; }
    ...
}

And pass them to a constructor

public class Registration : IRegistration
{
    private readonly Fireman _fireman;
    private readonly Pilot _pilot;

    public Registration(Pilot pilot, Fireman fireman)
    {
         this._fireman = fireman;
         this._pilot = pilot;
    }    
}

And here's what the initialization method looks like.

public T PopulateProfile<T>() where T : IPerson, new()
{
    var personProfile = Activator.CreateInstance<T>();
    ...

    return personProfile;
}

Please take note that this code is just an example.

I have a method that will set the value of each property of these classes which are from database. What I need to do is that, when I ask Ninject for any class that implements IPerson interface, Ninject should execute the method first, thus, Ninject will return an initialized class. Hope you could give me a hand. Thank you.

Vince Tino
  • 142
  • 13

1 Answers1

0

You can use Ninject.Extensions.Conventions in combination with an IBindingGenerator which generates a ToMethod binding:

BindingGenerator

internal class PersonBindingGenerator : IBindingGenerator
{
    private static readonly MethodInfo PopulateOpenGenericMethodInfo =
        typeof(IProfileService).GetMethod("PopulateProfile");

    public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(
        Type type,
        IBindingRoot bindingRoot)
    {
        yield return bindingRoot
            .Bind(type)
            .ToMethod(x => CreatePerson(x.Kernel.Get<IProfileService>(), type));
    }

    private static object CreatePerson(
        IProfileService profileService,
        Type type)
    {
        var closedGeneric = PopulateOpenGenericMethodInfo.MakeGenericMethod(type);
        return closedGeneric.Invoke(profileService, new object[0]);
    }
}

Bindings

kernel.Bind<IProfileService>().To<ProfileService>();

kernel.Bind(s => s
    .FromThisAssembly()
    .IncludingNonePublicTypes()
    .SelectAllClasses()
    .InheritedFrom<IPerson>()
    .BindWith<PersonBindingGenerator>());

Test

Complete Test code for reference.

using FluentAssertions;
using Ninject;
using Ninject.Extensions.Conventions;
using Ninject.Extensions.Conventions.BindingGenerators;
using Ninject.Syntax;
using System;
using System.Collections.Generic;
using System.Reflection;
using Xunit;

namespace NinjectTest.SO36424126
{
    public interface IPerson
    {
        string SomeValue { get; set; }
    }

    class BarPerson : IPerson
    {
        public string SomeValue { get; set; }
    }

    class FooPerson : IPerson
    {
        public string SomeValue { get; set; }
    }

    public interface IProfileService
    {
        T PopulateProfile<T>() 
            where T : IPerson, new();
    }

    internal class ProfileService : IProfileService
    {
        public T PopulateProfile<T>()
            where T : IPerson, new()
        {
            var personProfile = Activator.CreateInstance<T>();
            personProfile.SomeValue = "initialized";
            return personProfile;
        }
    }

    internal class PersonBindingGenerator : IBindingGenerator
    {
        private static readonly MethodInfo PopulateOpenGenericMethodInfo = typeof(IProfileService).GetMethod("PopulateProfile");

        public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
        {
            yield return bindingRoot
                .Bind(type)
                .ToMethod(x => CreatePerson(x.Kernel.Get<IProfileService>(), type));
        }

        private static object CreatePerson(IProfileService profileService, Type type)
        {
            var closedGeneric = PopulateOpenGenericMethodInfo.MakeGenericMethod(type);
            return closedGeneric.Invoke(profileService, new object[0]);
        }
    }

    public class Test
    {
        [Fact]
        public void Foo()
        {
            var kernel = new StandardKernel();

            kernel.Bind<IProfileService>().To<ProfileService>();

            kernel.Bind(s => s
                .FromThisAssembly()
                .IncludingNonePublicTypes()
                .SelectAllClasses()
                .InheritedFrom<IPerson>()
                .BindWith<PersonBindingGenerator>());

            kernel.Get<BarPerson>().SomeValue.Should().Be("initialized");
        }
    }
}
BatteryBackupUnit
  • 12,934
  • 1
  • 42
  • 68
  • Thank you @BatteryBackupUnit for the response. What if I don't know the actual implementation? What I know is, when someone asks for an instance, Ninject should check if the class that implements IPerson that's the time to call the initialization method before Ninject return the instance. – Vince Tino Apr 06 '16 at 02:07
  • I'm thinking if it's possible with this `kernel.Bind().ToMethod(ctx => ctx.Kernel.Get().PopulateProfile());` – Vince Tino Apr 06 '16 at 02:35
  • @DeathWish i've adjusted the answer accordingly. – BatteryBackupUnit Apr 06 '16 at 09:11
  • I don't know why it's not working, but your code is working. Maybe there's something wrong with my code.. But it's definitely the correct answer. Thank you very much @BatteryBackupUnit :) – Vince Tino Apr 06 '16 at 10:59
  • Hi @BatteryBackupUnit, is there a possible reason why the binding generators is not working? Is it possible because 'ProfileService' is in different namespace or is it about Ninject scope? It's like the binding generator is not executing PopulateProfile(). But everything is working in your code :'( – Vince Tino Apr 08 '16 at 10:42