Asp.net mvc 3 使用Autofac将属性注入自定义WebViewPage

Asp.net mvc 3 使用Autofac将属性注入自定义WebViewPage,asp.net-mvc-3,autofac,Asp.net Mvc 3,Autofac,我正在创建一个内部应用程序框架,我们组织中的其他开发团队将使用该框架构建MVC应用程序。作为其中的一部分,我正在为所有视图创建菜单结构,该结构从配置中读取,并根据当前用户的权限进行修改。为了将菜单作为框架的一部分创建,我创建了一个自定义的WebViewPage实现,其中包含一个自定义的HTML助手类,该类需要依赖于ApplicationDataReader来构建菜单 我读过很多文章,解释说MVC需要WebViewPage来拥有一个无参数构造函数,所以我需要使用属性注入。我已经进行了配置,包括注册

我正在创建一个内部应用程序框架,我们组织中的其他开发团队将使用该框架构建MVC应用程序。作为其中的一部分,我正在为所有视图创建菜单结构,该结构从配置中读取,并根据当前用户的权限进行修改。为了将菜单作为框架的一部分创建,我创建了一个自定义的
WebViewPage
实现,其中包含一个自定义的HTML助手类,该类需要依赖于
ApplicationDataReader
来构建菜单

我读过很多文章,解释说MVC需要
WebViewPage
来拥有一个无参数构造函数,所以我需要使用属性注入。我已经进行了配置,包括注册
ViewRegistrationSource
。问题是,当访问依赖属性时,它总是空的

以下是我尝试拨打的自定义视图页面和帮助程序:

public abstract class OuBaseViewPage<TModel> : WebViewPage<TModel> where TModel : class
{
    public OuHelper<TModel> Ou { get; set; }

    public override void InitHelpers()
    {
        base.InitHelpers();
        Ou = new OuHelper<TModel>(ViewContext, this);
    }
}

public class OuHelper<TModel> where TModel : class
{
    public OuHelper(ViewContext viewContext, IViewDataContainer viewDataContainer)
        : this(viewContext, viewDataContainer, RouteTable.Routes)
    {
    }

    public OuHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection)
    {
        ViewContext = viewContext;
        ViewData = new ViewDataDictionary<TModel>(viewDataContainer.ViewData);
    }

    public ViewDataDictionary<TModel> ViewData { get; private set; }

    public ViewContext ViewContext { get; private set; }

    public IList<BusinessFocusArea> ReadFocusAreas()
    {
        // this is null - yes, service location, but an isolated instance of...
        return DependencyResolver.Current.GetService<IDataReader>().Read();
    }
}
公共抽象类OuBaseViewPage:WebViewPage其中TModel:class
{
公共OuHelper Ou{get;set;}
公共重写void InitHelpers()
{
base.InitHelpers();
Ou=新的OuHelper(ViewContext,this);
}
}
公共类,其中TModel:class
{
公共OuHelper(ViewContext ViewContext、IViewDataContainer viewDataContainer)
:这(viewContext、viewDataContainer、RouteTable.Routes)
{
}
公共OuHelper(ViewContext ViewContext、IViewDataContainer viewDataContainer、RouteCollection RouteCollection)
{
ViewContext=ViewContext;
ViewData=新的ViewDataDictionary(viewDataContainer.ViewData);
}
公共ViewDataDictionary ViewData{get;private set;}
公共视图上下文{get;private set;}
公共IList ReadFocusAreas()
{
//这是空的-是的,服务位置,但是。。。
返回DependencyResolver.Current.GetService().Read();
}
}
问题源于这样一个事实,即在调用
应用程序启动
之前调用
InitHelpers
(通过
Layout.Execute()
),因此没有注册任何依赖项。我知道向视图中注入逻辑不是一个好的做法,视图应该是哑的,但这是一个应用程序框架,它需要执行某些设置步骤,而使用该框架的开发人员不能看到这些步骤

我可以采用更好的方法吗?

这里有一个类似的问题:

问题源于调用InitHelpers(通过 Layout.Execute()

我认为在应用程序启动之前不会调用任何东西。我无法重现你的问题

以下是我所做的步骤,效果非常好:

  • 使用Internet模板创建新的ASP.NET MVC 3应用程序
  • 安装
    Autofac.Mvc3
    NuGet
  • 定义虚拟接口:

    public interface IDataReader
    {
    
    }
    
  • 和虚拟实现:

    public class DataReader : IDataReader
    {
    
    }
    
  • 定义自定义辅助对象:

    public class OuHelper<TModel> where TModel : class
    {
        private readonly IDataReader dataReader;
        public OuHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, IDataReader dataReader)
            : this(viewContext, viewDataContainer, RouteTable.Routes, dataReader)
        {
        }
    
        public OuHelper(ViewContext viewContext, IViewDataContainer viewDataContainer, RouteCollection routeCollection, IDataReader dataReader)
        {
            ViewContext = viewContext;
            ViewData = new ViewDataDictionary<TModel>(viewDataContainer.ViewData);
            this.dataReader = dataReader;
        }
    
        public ViewDataDictionary<TModel> ViewData { get; private set; }
    
        public ViewContext ViewContext { get; private set; }
    
        public IDataReader DataReader
        {
            get { return this.dataReader; }
        }
    
    }
    
  • 应用程序\u Start
    中配置容器:

    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);
    
        var builder = new ContainerBuilder();
        builder.RegisterControllers(typeof(MvcApplication).Assembly);
        builder.RegisterType<DataReader>().As<IDataReader>();
        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }
    

  • 当然,在本例中,我刚刚将
    IDataReader
    依赖项作为公共属性公开,以说明它将始终被注入,并且永远不会为null。在您的特定代码中,您当然可以仅使用帮助程序中的私有只读字段来完成任务。

    谢谢Darin-这与我所做的差不多,但是
    应用程序\u Start
    并不是为我启动的。一旦我弄清楚了原因,剩下的应该可以很好的解决了。问题是我没有考虑到这样一个事实,即我的框架中的
    Application\u Start
    方法不会启动,因为使用框架的应用程序中的
    Application\u Start
    方法会启动。我们使用Web Activator正是出于这个原因,因此我可以通过Web Activator通过
    PostApplicationStartMethod
    hook和bin框架中的Global.asax文件连接所有依赖项,以避免将来出现这种混乱!感谢您强调我的逻辑应该有效。此方法强制我们使用服务位置。以前,我曾成功地用Dependency属性(这是Unity-不是AutoFac)装饰WebViewPage子类中的一个属性,但为了在布局中使用它,我不得不使用您的方法。@DarinDimitrov,我现在不打算更改我的实现,但您认为在这种情况下使用环境上下文模式而不是服务位置可能有用吗?从名称上看,依赖项看起来只读取内容。如果这是一个跨领域的问题,我不知道,它可能是一个更好的方式,用户服务的位置。不是吗?
    <pages pageBaseType="MvcApplication1.OuBaseViewPage">
    
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);
    
        var builder = new ContainerBuilder();
        builder.RegisterControllers(typeof(MvcApplication).Assembly);
        builder.RegisterType<DataReader>().As<IDataReader>();
        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }
    
    @Ou.DataReader.GetType()