【问题标题】:Using StructureMap[4.7.0] Setter Injection in my MVC5 Controller在我的 MVC5 控制器中使用 StructureMap[4.7.0] Setter 注入
【发布时间】:2018-07-19 15:33:36
【问题描述】:

我正在尝试将 IApplicationConfigurationSection 实现注入此 MVC5 Controller,以便我可以在我的所有视图中访问我的 web.config 自定义部分的一些信息(各种字符串):

public class BaseController : Controller
{
    public IApplicationConfigurationSection AppConfig { get; set; }

    public BaseController()
    {
        ViewBag.AppConfig = AppConfig; // AppConfig is always null
    }
}

我想使用 setter 注入,这样我就不必将派生的 Controller 构造函数与他们并不真正关心的参数混在一起。

注意如果有更好的方法注入基类依赖,请告诉我。我承认我可能没有走在正确的轨道上

在我的 Global.asax 中,我加载了我的 StructureMap 配置:

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
    }
}

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


尽管我尽最大努力寻找解决方案,但我的控制器构造函数中的AppConfig 属性始终为null。我以为

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

可以解决问题,但没有。

我发现如果我放弃 setter 注入并使用构造函数注入,它会像宣传的那样工作。我仍然想知道我哪里出错了,但我想强调我不是 StructureMap 大师 - 可能有更好的方法来避免构造函数注入我的基类依赖项。如果你知道我应该怎么做,但不是,请分享。

【问题讨论】:

  • 构造函数注入是更好的方法。我打算回答这个问题,但刚刚看到你的更新说明你已经这样做了。其次,我相信ViewBag 仅填充了一个动作,这与您在构造函数中提供的示例不同。
  • 在一个完全独立的注释上,我刚刚读到StructureMap has been sunsetted.
  • ViewBag 可以从构造函数中轻松访问。我没有听说过关于 StructureMap 的消息——我得看看能不能找到链接。
  • 好吧,我对构造函数对 ViewBag 的访问进行了更正。至于日落,它在框架的主站点上structuremap.github.io

标签: asp.net-mvc-5 inversion-of-control structuremap structuremap4 setter-injection


【解决方案1】:

虽然这种情况下的构造函数注入似乎是上述问题的更好解决方案,因为它遵循The Explicit Dependencies Principle

方法和类应该明确要求(通常通过方法参数或构造函数参数)它们需要的任何协作对象才能正常运行。

在您的观点中提到只需要访问 AppConfig 让我认为这更像是一个 XY problem 和一个跨领域问题。

控制器本身似乎不需要使用依赖项,因此可以理解,不需要显式地将它们注入控制器,以便 View 可以使用依赖项.

考虑使用一个动作过滤器,它可以解决依赖关系,并在请求通过管道时通过相同的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;
    }
}

这使得视图可以使用所需的信息,而无需紧密耦合可能对它有用的控制器。无需将依赖项注入派生类。

通过使用过滤器属性装饰控制器/动作

[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 容器实际上并不重要。一旦配置了依赖解析器,视图应该可以访问ViewBag中的所需信息

【讨论】:

  • 优秀的解决方案。与控制器的解耦非常巧妙。还不能奖励赏金,但它是你的。
  • @ScottBaker 很高兴为您提供帮助。
  • @ScottBaker 在 Asp.Net Core 中值得期待的很酷的事情之一是视图允许强类型依赖注入。即@inject IApplicationConfigurationSection AppConfig参考Dependency injection into views in ASP.NET Core
猜你喜欢
  • 2023-03-17
  • 1970-01-01
  • 1970-01-01
  • 2015-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-11
  • 1970-01-01
相关资源
最近更新 更多