2

我正在尝试将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
    }
}

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


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

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

会做的伎俩,但它没有。

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

4

1 回答 1

0

虽然这种情况下的构造函数注入似乎是对所述问题的更好解决方案,因为它遵循显式依赖原则

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

在您的视图中提到只需要访问AppConfig让我认为这更像是一个XY 问题和一个横切关注点。

控制器本身似乎不需要使用依赖项,因此可以理解,不需要将它们显式地注入控制器,以便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

于 2018-07-23T17:26:24.577 回答