在我的MVC5控制器中使用StructureMap[4.7.0]Setter Injection



我正试图将IApplicationConfigurationSection实现注入这个MVC5Controller,这样我就可以在所有视图中访问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
});
});
}
}

My 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大师-有一种更好的方法可以避免必须构造函数注入基类依赖项。如果你知道我应该怎么做,但不知道,请分享。

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

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

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

控制器本身似乎不需要使用依赖项,因此不需要将它们显式注入控制器,以便视图可以使用依赖项。

考虑使用一个操作过滤器,该过滤器可以解析依赖关系,并在请求通过管道时通过相同的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中所需的信息

最新更新