代码之家  ›  专栏  ›  技术社区  ›  Scott Baker

在MVC5控制器中使用StructureMap[4.7.0]Setter注入

  •  2
  • Scott Baker  · 技术社区  · 6 年前

    我想注射 IApplicationConfigurationSection 实现到这个MVC5中 Controller ,以便我可以从 web.config 我所有视图中的自定义部分:

    public class BaseController : Controller
    {
        public IApplicationConfigurationSection AppConfig { get; set; }
    
        public BaseController()
        {
            ViewBag.AppConfig = AppConfig; // AppConfig is always null
        }
    }
    

    我想使用setter注入,这样就不必把派生的 控制器 具有他们并不真正关心的参数的构造函数。

    注意 : 如果有更好的方法注入基类依赖项,请告诉我。我承认我可能走错了路 .

    在我的 Global.asax 我加载结构映射配置:

    private static IContainer _container;
    
    protected void Application_Start()
    {
        _container = new Container();
    
        StructureMapConfig.Configure(_container, () => Container ?? _container);
        // redacted other registrations
    }
    

    我的 StructureMapConfig 类加载我的注册表:

    public class StructureMapConfig
    {
        public static void Configure(IContainer container, Func<IContainer> func)
        {
            DependencyResolver.SetResolver(new StructureMapDependencyResolver(func));
    
            container.Configure(cfg =>
            {
                cfg.AddRegistries(new Registry[]
                {
                    new MvcRegistry(),
                    // other registries redacted
                });
            });
        }
    }
    

    我的MvcRegistry为StructureMap提供映射:

    public class MvcRegistry : Registry
    {
        public MvcRegistry()
        {
            For<BundleCollection>().Use(BundleTable.Bundles);
            For<RouteCollection>().Use(RouteTable.Routes);
            For<IPrincipal>().Use(() => HttpContext.Current.User);
            For<IIdentity>().Use(() => HttpContext.Current.User.Identity);
            For<ICurrentUser>().Use<CurrentUser>();
            For<HttpSessionStateBase>()
                .Use(() => new HttpSessionStateWrapper(HttpContext.Current.Session));
            For<HttpContextBase>()
                .Use(() => new HttpContextWrapper(HttpContext.Current));
            For<HttpServerUtilityBase>()
                .Use(() => new HttpServerUtilityWrapper(HttpContext.Current.Server));
            For<IApplicationConfigurationSection>()
                .Use(GetConfig());
    
            Policies.SetAllProperties(p => p.OfType<IApplicationConfigurationSection>());
        }
    
        private IApplicationConfigurationSection GetConfig()
        {
            var config = ConfigurationManager.GetSection("application") as ApplicationConfigurationSection;
            return config; // this always returns a valid instance
        }
    }
    

    我也“举起手来”试着用 [SetterProperty] BaseController上的属性-该技术也失败了。


    尽管我尽了最大努力寻找解决办法, AppConfig 我的控制器构造函数中的属性总是 null . 我以为

    `Policies.SetAllProperties(p => p.OfType<IApplicationConfigurationSection>());` 
    

    会成功,但没有。

    我发现,如果我放弃setter注入并使用构造器注入,它就会像广告中所说的那样工作。我还是想知道我错在哪里,但我想强调我不是一个结构图大师- 也许有更好的方法可以避免构造函数注入我的基类依赖项 是的。如果你知道我应该怎么做,但不是,请分享。

    1 回复  |  直到 6 年前
        1
  •  0
  •   Nkosi    6 年前

    而此场景中的构造函数注入似乎是解决所述问题的更好方法,如下所示 The Explicit Dependencies Principle

    方法和类应该显式地(通常通过方法参数或构造函数参数)需要它们所需的任何协作对象才能正常工作。

    只需要访问 AppConfig 在你看来,我认为 XY problem 以及一个贯穿各领域的问题。

    控制器本身似乎不需要使用依赖项,因此不必显式地将它们注入控制器,只需使依赖项对 意见 .

    考虑使用一个操作过滤器,它可以解析依赖关系,并通过相同的 ViewBag 当请求通过管道时。

    public class AccessesAppConfigAttribute : ActionFilterAttribute {
        public override void OnActionExecuting(ActionExecutingContext filterContext) {
            var resolver = DependencyResolver.Current;
            var appConfig = (IApplicationConfigurationSection)resolver.GetService(typeof(IApplicationConfigurationSection));
            filterContext.Controller.ViewBag.AppConfig = appConfig;
        }
    }
    

    现在,这使得所需的信息可用于视图,而不需要使用控制器的紧密耦合不需要将依赖项注入派生类。

    通过使用filter属性修饰Controller/Action

    [AccessesAppConfig] //available to all its actions
    public class HomeController : Controller {
    
        //[AccessesAppConfig] //Use directly if want to isolate to single action/view
        public ActionResult Index() {
            //...
            return View();
        }
    }
    

    或全球范围内的所有请求。

    public class FilterConfig {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
            filters.Add(new AccessesAppConfigAttribute());
        }
    }
    

    在这一点上,使用哪个IoC容器并不重要一旦配置了依赖关系解析程序,视图就应该可以访问 可视包

    推荐文章