ASP.net Session_Start方法中的依赖注入

时间:2015-09-29 19:40:40

标签: c# asp.net-mvc dependency-injection autofac

我正在学习依赖注入并首次使用autofac。我在几个autofac示例中提到了容器(见下文),并从我的application_start

调用
public class ContainerConfig
{
    public static void RegisterContainer() 
    {
        //Create a new ContainerBuilder
        var builder = new ContainerBuilder();

        // Register all the controllers using the assembly object
        builder.RegisterControllers(Assembly.GetExecutingAssembly());

        //Registering default convention -- IExample and Example
        builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
            .Where(t => t.Name.Single(i => i.Name == "I" + t.Name))
            .AsImplementedInterfaces();

        //Build the container
        var container = builder.Build();

        //Set the default resolver to use Autofac
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 
    }
}

我使用IUserService在我的Core项目中创建了UserService。这有使dbcall从表中获取用户信息的方法。在我的UI项目中,我有一个名为UserProvider的类,我想要注入UserService。

public class UserProvider
{
    private readonly IUserService _userService;

    public UserProvider(IUserService userService) 
    {
        _userService = userService;
    }

    public void LoadCurrentUser()
    {
        Users FoundUser = _userService.ImportOrGetUser();

        if (FoundUser != null)
        {
            //add it to session
            CurrentUser = FoundUser;
        }
    }
}

此UserProvider,我在session_start中使用

void Session_OnStart()
{
     UserProvider OUsrPrv = new UserProvider(new UserService());
     OUsrPrv.LoadCurrentUser();
}

在上面的代码中,如果我传递'new UserService()',我的理解是我手动注入UserService。我不知道autofac如何在这里提供帮助。 google中的所有示例都在讨论MVCController或WebApiController中的依赖注入,而不是像我正在做的那样在单个类(UserProvider)中。

有人可以请点亮一下吗?我做错了吗?

2 个答案:

答案 0 :(得分:3)

为了正确使用依赖注入,您不应该自己创建实例,底层框架应该为您提供实例。

但ASP.net在没有任何依赖注入的情况下调用Session_OnStart。在这种情况下,您可以使用DependencyResolver.Current静态属性来解析所请求的服务。

void Session_OnStart()
{
    UserProvider userProvider = DependencyResolver.Current.GetService<UserProvider>();
    userProvider.LoadCurrentUser(); 
}

答案 1 :(得分:2)

System.Web.HttpApplication中的事件模型是ASP.NET的一部分,而不是MVC。它不是为与依赖注入一起使用而设计的。

Cyril建议的答案是使用service locator来获取对服务的引用。这远非理想,因为您在代码中对服务定位器进行了依赖。

实现横切关注点(例如将用户数据加载到会话状态)的以MVC为中心的方法是使用全局注册的过滤器。您可以实施IAuthorizationFilterIActionFilter以获得所需的效果。在这种情况下,使用IActionFilter是有意义的,因为您要等到确定在被调用之前有授权用户。

  

注意:虽然这回答了您的具体问题,但最好不要在MVC中使用会话状态。另一种方法是使用ASP.NET Identity with Claims来存储用户配置文件数据,而不是使用Session。

using System;
using System.Web.Mvc;
using System.Security.Principal;

public class GetUserActionFilter : IActionFilter
{
    private readonly IUserRepository userRepository;

    public GetUserActionFilter(IUserRepository userRepository)
    {
        if (userRepository == null)
            throw new ArgumentNullException("userRepository");

        this.userRepository = userRepository;
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        // Do nothing - this occurs after the action method has run
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        IPrincipal user = filterContext.HttpContext.User;

        if (user == null)
        {
            return;
        }

        IIdentity identity = user.Identity;

        if (identity == null)
        {
            return;
        }

        // Make sure we have a valid identity and it is logged in.
        if (identity.IsAuthenticated)
        {
            string key = "__CurrentUserData";
            var userData = filterContext.HttpContext.Session[key];
            if (userData == null)
            {
                // User data doesn't exist in session, so load it
                userData = userRepository.GetUserData(identity.Name);

                // Add it to session state
                filterContext.HttpContext.Session[key] = userData;
            }
        }
    }
}

现在,要全局添加过滤器,您需要:

  1. 使用Autofac注册过滤器及其依赖项。
  2. 将容器传递给静态RegisterGlobalFilters方法。
  3. 注册过滤器

    使用命名实例将其与其他潜在的IActionFilter实例区分开来。

    builder.RegisterType<GetUserActionFilter>()
           .Named<IActionFilter>("getUserActionFilter");
    

    传递容器

    FilterConfig.cs

    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters, IContainer container)
        {
            filters.Add(container.ResolveNamed<IActionFilter>("getUserActionFilter"));
            filters.Add(new HandleErrorAttribute());
        }
    }
    

    的Global.asax.cs

    public class MvcApplication : System.Web.HttpApplication
    {
        // This method serves as the composition root
        // for the project.
        protected void Application_Start()
        {
            // Register Autofac DI
            IContainer container = ContainerConfig.RegisterContainer();
    
            AreaRegistration.RegisterAllAreas();
    
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters, container);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            AuthConfig.RegisterAuth();
        }
    }
    

    ContainerConfig.cs

    public class ContainerConfig
    {
        public static IContainer RegisterContainer() 
        {
            //Create a new ContainerBuilder
            var builder = new ContainerBuilder();
    
            // Register all the controllers using the assembly object
            builder.RegisterControllers(Assembly.GetExecutingAssembly());
    
            //Registering default convention -- IExample and Example
            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
                  .Where(t => t.Name.Single(i => i.Name == "I" + t.Name))
                  .AsImplementedInterfaces();
    
            // Register our filter
            builder.RegisterType<GetUserActionFilter>()
               .Named<IActionFilter>("getUserActionFilter");
    
            //Build the container
            var container = builder.Build();
    
            //Set the default resolver to use Autofac
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container)); 
    
            // Return the container to our composition root.
            return container;
        }
    }
    

    请注意,我刚刚在这里使用了存储库服务,因为HttpContext已经通过动作过滤器直接可用,并且此处需要其他逻辑,因为我们不确定它是否存在于会话状态或不是或者是否有用户要查找,所以除了加载会话状态之外,我们的过滤器还会执行这些检查。