是否可以通过IContext解析Ninject工厂方法中的调用者实例?

时间:2018-01-16 23:37:27

标签: c# .net ninject

我想这是一个我想要实现的非常简单的场景。

我只是想知道是否可以在Ninject工厂方法中获取调用实例。

public static class Program
{
    public static void Main(params string[] args)
    {
        var standardKernelCaller = new StandardKernelCaller();

        standardKernelCaller.Call();

        Console.ReadKey();
    }
}

public interface IA
{
}

public class A : IA
{
    public int Parameter { get; }

    public A(int parameter)
    {
        Parameter = parameter;
    }
}

public class Module : NinjectModule
{
    public override void Load()
    {
        Bind<IA>().ToMethod(Create);
    }

    private static A Create(IContext context)
    {
        var number = // resolve the caller (StandardKernelCaller) Magic Number using context...
        return new A(number);
    }
}

public class StandardKernelCaller
{
    public const int MagicNumber = 42;

    public void Call()
    {
        var standardKernel = new StandardKernel(new Module());

        var stuff = standardKernel.Get<IA>();
    }
}

我不太确定这是不是一个好习惯。目前在相关的生产代码中,我使用的是:

public abstract class BusinessApiController<TBusinessLogic> : ApiController
    where TBusinessLogic : class, IBusinessLogic
{
    protected TBusinessLogic BusinessLogic { get; private set; }

    protected override void Initialize(HttpControllerContext controllerContext)
    {
        base.Initialize(controllerContext);

        BusinessLogic = CreateBusinessLogic();
    }

    protected virtual TBusinessLogic CreateBusinessLogic()
    {
        var businessLogic = BusinessLogicFactory.Create<TBusinessLogic>(this.GetOwinContext());

        return businessLogic;
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (BusinessLogic != null)
            {
                BusinessLogic.Dispose();
                BusinessLogic = null;
            }
        }

        base.Dispose(disposing);
    }
}


public abstract class BusinessController<TBusinessLogic> : Controller
    where TBusinessLogic : class, IBusinessLogic
{
    protected TBusinessLogic BusinessLogic { get; private set; }

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);

        BusinessLogic = CreateBusinessLogic();
    }

    protected virtual TBusinessLogic CreateBusinessLogic()
    {
        var businessLogic = BusinessLogicFactory.Create<TBusinessLogic>(this.GetOwinContext());

        return businessLogic;
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (BusinessLogic != null)
            {
                BusinessLogic.Dispose();
                BusinessLogic = null;
            }
        }

        base.Dispose(disposing);
    }
}

但我不是下面工厂中硬编码“owinContext”参数名称的忠实粉丝:

public static class BusinessLogicFactory
{
    // Potentially obsolete readonly / configuration kernel in upcoming Ninject versions
    private static readonly StandardKernel StandardKernel = new StandardKernel(new BusinessLogicsNinjectModule());

    public static TBusinessLogic Create<TBusinessLogic>(IOwinContext owinContext)
    {
        // Potential refactoring: get the argument name via expression binding or use Ninject providers
        var businessLogic = StandardKernel.Get<TBusinessLogic>(new ConstructorArgument("owinContext", owinContext));

        return businessLogic;
    }
}

以下是Ninject模块简化版的示例:

public class BusinessLogicsNinjectModule : NinjectModule
{
    public override void Load()
    {
        Bind<IUserManagementBusinessLogic>().To<UserManagementBusinessLogic>();
        Bind<IAppointmentManagementBusinessLogic>().To<AppointmentManagementBusinessLogic>();
        Bind<ITeamAppointmentManagementBusinessLogic>().To<TeamAppointmentManagementBusinessLogic>();
    }
}

顺便说一下,如果有更好的方法来进行BusinessLogic注入或更好的整体设计,我很想知道更多。

2 个答案:

答案 0 :(得分:1)

首先,使用Ninject.Web.Common + Ninject.Web.WebApi扩展,以便您的控制器不依赖于服务定位器:https://github.com/ninject/Ninject.Web.Common/wiki

其次,您的业务逻辑依赖于owinContext似乎是一种设计气味。应该遵循关注点分离,这里似乎是混合基础架构/业务层。

第三,如果你真的想要它,你可以通过方法调用作为参数传递owinContext

第四,如果你真的希望它通过一个构造函数,你可以使用Ninject.Extensions.Factory: https://github.com/ninject/Ninject.Extensions.Factory/wiki

public interface IBusinessLogicFactory<TBusinessLogic>
{
    TBusinessLogic Create(IOwinContext owinContext);
}

var factory = kernel.Bind<IBusinessLogicFactory<TBusinessLogic>>().ToFactory(() => new TypeMatchingArgumentInheritanceInstanceProvider());
...
var businessLogic = kernel.Get<IBusinessLogicFactory<TBusinessLogic>>().Create(owinContext);

答案 1 :(得分:1)

要知道,让我回答你的第一个问题: 在您的示例中,无法从StandardKernelCaller确定IContext的实例或类型。

如果你将注入(ctor-injection,property injection)的值改为StandardKernelCaller而不是 resolve Get),那么您将从StandardKernelCaller收集IContext类型。 如果应用了属性注入,也许你甚至可以获得StandardKernelCaller的实例(我怀疑它虽然不可用)。

但是,您可以将参数传递给Get调用:命名绑定的名称(字符串)(解析为使用相同名称注册的绑定,如果没有匹配的绑定则抛出)和IParameter& #39; S。 IParameter上提供了IContext