我已经在这个问题上待了几个小时了,所以我决定写信并寻求帮助。
在这个论坛上已经有很多答案,我可以认为它是我问题的解决方案,但我只是无法连接我脑子里的所有点;而且我没有完成它。
我试图在大型MVC应用程序上更改注入器,从StructureMap更改为SimpleInjector。
我需要正确地将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) { }
(...由magic ...)构造函数将使用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; }
}
比了解解决方案更重要的是,我想了解解决方案的工作原理,并克服过去几个小时试图理解和解决这个问题的困难。
答案 0 :(得分: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);
Here您有更多关于在Simple Inject中未实现将原始运行时参数传递给构造函数的原因的信息。
关于生活方式范围:您可能不应该使用Lifestyle.Singleton
作为此组件的范围,因为它只会被实例化一次并重新用作单例。在Web应用程序中,您通常希望应用Per-HttpRequest范围。您可以这样做:创建容器后,将其默认范围定义为WebRequestLifestyle
或WebApiRequestLifestyle
:
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
然后当您注册组件时,请使用值Lifestyle.Scoped
,这将应用默认范围的生活方式:
container.Register<SomeInterface, SomeClass>(Lifestyle.Scoped);
根据{{3}}的评论 修改,在这种情况下,最好将HttpSessionUserContext
注册为Singleton
,因为它是无状态的。通常,Singleton
具有更好的性能,因为它只实例化一次并共享,但要注意非无状态或与其他组件有依赖关系的组件。
此外,请确保您已注册MVC控制器并将容器实例分配给MVC DependencyResolver
。这才能真正实现控制器中构造函数中参数的自动解析和注入。我想你是在Application_Start
事件处理程序中完成的。
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
container.Verify();
DependencyResolver.SetResolver(
new SimpleInjectorDependencyResolver(container));