在我的MVC5控制器中使用StructureMap [4.7.0]设置器注入

时间:2018-07-19 15:33:37

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

我正在尝试将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专家-有一种更好的方法来避免必须构造函数注入我的基类依赖项。如果您知道我应该怎么做,但不是,请分享。

1 个答案:

答案 0 :(得分:0)

在这种情况下,虽然遵循The Explicit Dependencies Principle

似乎是解决上述问题的更好的解决方案,
  

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

在您看来,仅需要访问AppConfig的提法使我认为这更多是XY problem和跨领域关注点。

似乎控制器本身不需要使用依赖项,因此可以断定没有必要将它们明确地注入控制器中,从而使依赖项可用于 View

请考虑使用动作过滤器,该过滤器可以解决依赖关系,并通过与请求通过管道相同的ViewBag将其提供给View。

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容器都没有关系。配置依赖关系解析程序后,视图应该有权访问ViewBag

中的必需信息。