注入不同的 project/assembly

Injecting into a different project/assembly

我已经研究这个主题好几个小时了,所以我决定写信寻求帮助。

这个论坛上已经有很多答案,我可以确定它们是我的问题的解决方案,但我无法将脑海中的所有点联系起来;我还没完成。

我正在尝试更改大型 MVC 应用程序上的注入器,从 StructureMap 更改为 SimpleInjector。

我需要正确地将 HttpContext 的当前用户传递给同一解决方案中的另一个项目,该解决方案包含所有存储库和 DBContext,因此当我执行一些 CRUD 操作时它可以是 used/written。

我显然不明白 StructureMap 是如何连线的;我发现它很复杂。我在 MVC 项目上有这个(可以提供更多信息,因为有很多 classes 使用 StructureMap):

public class GlobalRegistry : StructureMap.Registry
{
    public GlobalRegistry()
    {
        For<INotificationRepository>().Use<NotificationRepository>();
        ...

和存储库 project/class:

public class NotificationRepository : BaseRepository,INotificationRepository
{
    public NotificationRepository(string userContext) : base(userContext) { }

(...魔术...)构造函数将采用 userContext 参数稍后在被调用方法中使用。

换成SimpleInjector后,不明白这个参数是怎么注入的

出于测试目的,这有效:

container.Register<INotificationRepository>(() => new NotificationRepository("username"), 
    Lifestyle.Singleton);

我读到我不应该在构造函数中注入 HttpContext,因为它是一个运行时变量,我明白为什么。

接下来我尝试了 IUserContextFactory,但它也没有用。

在同一个 MVC 项目上,我有他的 class:

public static class ObjectFactory
{
    private static SimpleInjector.Container _container;

    public static void SetContainer(Container container)
    {
        ObjectFactory._container = container;
    }

    public static T GetInstance<T>() where T : class
    {
        return _container.GetInstance<T>();
    }
}

我正在使用这个 class 在 container.Verify();

之后存储容器
ObjectFactory.SetContainer(container);

在任何 MVC 控制器上,我都是这样使用的:

IUserContext ctxUser = ObjectFactory.GetInstance<IUserContext>();

在整个尝试过程中,我还在存储库中尝试了类似以下内容,但我总是以空值结束 UserName

public NotificationRepository(IUserContext userContext) : base(userContext) { }

(MVC 项目和存储库项目之间的通用接口)

public interface IUserContext
{
    string Username { get; set; }
}

比了解解决方案更重要的是,我想了解解决方案的工作原理,并克服过去几个小时试图理解和解决这个问题时遇到的困难。

将 运行 时间原始值作为参数传递给构造函数在 Simple Inject 中不能直接使用。相反,您可以注入一个允许您在 运行 时间获得该值的组件,因此您使用 IUserContext 的方法似乎是个好方法,它应该有效。修改 class 以在构造函数中添加该组件,而不是 userName 字符串。注册新组件,让你容器在调用构造函数时自动注入。

示例实现:

class HttpSessionUserContext : IUserContext 
{
    //Your specific implementation of getting the user name from your context
    public string CurrentUserName => (string)HttpContext.Session["userName"];
}

报名人数:

container.Register<IUserContext, HttpSessionUserContext>(Lifestyle.Scoped);
container.Register<INotificationRepository, NotificationRepository> (Lifestyle.Scoped);

Here 你有更多关于为什么在 Simple Inject 中没有实现将原始 运行-time 参数传递给构造函数的原因的信息。

关于生活方式范围:您可能不应该使用 Lifestyle.Singleton 作为此组件的范围,因为它只会被实例化一次并作为单例重复使用。在 Web 应用程序中,您通常希望应用 Per-HttpRequest 范围。您可以这样做:创建容器后,将其默认范围定义为 WebRequestLifestyle,或 WebApiRequestLifestyle:

var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();

然后当您注册组件时使用值 Lifestyle.Scoped,这将应用默认范围内的生活方式:

container.Register<SomeInterface, SomeClass>(Lifestyle.Scoped);

编辑: 根据 Steven 的评论,在这种情况下最好将 HttpSessionUserContext 注册为 Singleton,因为它是无国籍的。一般来说,Singleton性能更好,因为它只实例化一次并共享,但要小心那些不是无状态或对其他组件有依赖的组件。

此外,请确保您已注册 MVC 控制器并将容器实例分配给 MVC DependencyResolver。这才是真正能够在控制器的构造函数中自动解析和注入参数的原因。我猜你是在 Application_Start 事件处理程序中这样做的。

container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Verify();
DependencyResolver.SetResolver(
        new SimpleInjectorDependencyResolver(container));