注入到不同的项目/程序集中

3
我已经花了几个小时来研究这个主题,所以我决定写信寻求一些帮助。
在这个论坛上已经有很多答案可以被认为是我的问题的解决方案,但是我就是不能在脑海中将所有的东西联系起来;而且我做不好。
我正在尝试将注射器从StructureMap更改为SimpleInjector,用于一个大型MVC应用程序。
我需要正确地将HttpContext的当前用户传递给同一解决方案上的另一个项目,该项目存储着所有的存储库和DBContext,以便在进行一些CRUD操作时可以使用/编写。
我显然没有理解StructureMap如何连接;我在MVC项目上找到了它(可以提供更多信息,因为有十几个类使用StructureMap):
public class GlobalRegistry : StructureMap.Registry
{
    public GlobalRegistry()
    {
        For<INotificationRepository>().Use<NotificationRepository>();
        ...

在代码库的项目/类中:

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

(...通过魔法...) 构造函数将接受 userContext 参数以便稍后在调用的方法中使用。

替换为 SimpleInjector 后,我不明白如何注入此参数。

为了测试目的,这样可以工作:

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

我看到过不应该在构造函数中注入HttpContext,因为它是一个运行时变量,我也理解为什么。

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

在同一个MVC项目中,我有这个类:

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>();
    }
}

我正在使用这个类来存储在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; }
}

比知道解决方法更重要的是,我希望能够理解解决方案的工作原理,并克服我过去几个小时试图理解和解决它时遇到的困难。


好的,StructureMap 中也应该有上下文的注册,将其转换为另一种语法。顺便说一下,这个 Lifestyle.Singleton 看起来不太好。 - Gert Arnold
我不明白你实际的问题在哪里,你卡住了什么,以及你试图理解什么。 - Steven
@Gert Arnold,没有找到关于StructureMap被注入的上下文。 当用户登录后,它会一直保持登录状态,直到注销,我错过了什么吗?多个用户!? - chapas
@Steven,我希望从你那里得到一个答案,你是所有#simple-injector标签的常数。我的错,我不知道如何解释/表达自己,这就是问题所在。已经解决了我的问题。不确定是否完全理解了自己的解决方案。我问了很多次……作为程序员的我……哦,好吧,这个线程应该被删除,它很令人困惑。 - chapas
上下文必须已经注册。否则,IoC容器怎么知道如何将其注入到存储库中呢?(这对你来说可能是“魔法”,但实际上只是配置)。 - Gert Arnold
1个回答

1
将运行时原始值作为参数传递给构造函数在Simple Inject中不是直接可用的。相反,您可以注入一个组件,允许您在运行时获取该值,因此您使用IUserContext的方法似乎是正确的方法,它应该可以工作。修改您的类以在构造函数中添加该组件,而不是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);

这里提供了更多关于为什么在Simple Inject中不实现将原始的运行时参数传递给构造函数的原因的信息。

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

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));

我同意你的答案Diana,感谢你的帮助。 我正在LoginControler上注入IUserContext,然后在有效用户登录后获取用户名,这将保持不变直到注销,但在这种情况下可能看不到正确的东西。 - chapas
由于HttpSessionUserContext类是无状态的,因此它可以注册为单例。我的建议是实际上将其注册为单例。这样做没有任何坏处。事实上,我认为尽可能多地进行单例注册是一个好习惯。 - Steven
@Steven 你说得对,那样会更好,性能更佳。我以前习惯于按请求注册事物,这只是一种保守的做法,如果性能不那么关键,但单例注册更有效率。只要注意那些不是无状态或有依赖于其他组件的组件。 - Diana
@Diana,如果一个单例组件有依赖项,Simple Injector将始终为您检查是否存在生命周期不匹配。如果该组件依赖于作用域或瞬态,则Simple Injector将引发异常。 - Steven
@Steven 好的,谢谢。我想的更多是组件具有修改某处数据或与可能是单例的其他组件执行非线程安全操作的方法。我过去曾遇到过这样的问题(可能是因为糟糕的实现和对作用域工作原理的误解)。 - Diana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接