注入不同的项目/程序集

时间:2016-11-15 19:32:10

标签: c# entity-framework structuremap httpcontext simple-injector

我已经在这个问题上待了几个小时了,所以我决定写信并寻求帮助。

在这个论坛上已经有很多答案,我可以认为它是我问题的解决方案,但我只是无法连接我脑子里的所有点;而且我没有完成它。

我试图在大型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; }
}

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

1 个答案:

答案 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范围。您可以这样做:创建容器后,将其默认范围定义为WebRequestLifestyleWebApiRequestLifestyle

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