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

Is it possible to resolve the caller instance in a Ninject factory method via IContext?

我想这是我正在努力实现的一个非常简单的场景。

我只是想知道是否可以在 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 注入方法或更好的整体设计,我很想知道更多。

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

其次,您的业务逻辑依赖于 owinContext 似乎是一种设计味道。应该遵循关注点分离,这里你似乎在混合 infrastructure/business 层。

第三,如果你真的想要它,你可以通过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);

为了知道,让我回答你的第一个问题: 在您的示例中,无法从 IContext.

确定 StandardKernelCaller 的实例或类型

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

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