当前位置:  开发笔记 > 编程语言 > 正文

通用继承的ViewPage <>和新属性

如何解决《通用继承的ViewPage<>和新属性》经验,为你挑选了1个好方法。



1> Justin Grant..:

更新:看起来Technitium找到了另一种方法,这看起来更容易,至少在较新版本的ASP.NET MVC上.(复制他的评论如下)

我不确定这是否是ASP.NET MVC 3中的新功能,但是当我将Inherits属性从引用C#语法中的泛型转换为CLR语法时,标准ViewPageParserFilter已正确解析泛型 - CustomViewTypeParserFilter不需要.使用Justin的例子,这意味着交换

<%@ Page Language="C#" MyNewProperty="From @Page directive!"
    Inherits="JG.ParserFilter.CustomViewPage

<%@ Page Language="C#" MyNewProperty="From @Page directive!"` 
    Inherits="JG.ParserFilter.CustomViewPage`1[MvcApplication1.Models.FooModel]>

原答案如下:

好的,我解决了这个问题.这是一项非常有趣的练习,解决方案非常重要,但是一旦你第一次使用它就不会太难.

这是底层问题:ASP.NET页面解析器不支持泛型作为页面类型.

ASP.NET MVC解决这个问题的方法是欺骗底层页面解析器,使其认为页面不是通用的.他们通过构建自定义PageParserFilter和自定义FileLevelPageControlBuilder来实现此目的.解析器过滤器查找泛型类型,如果找到一个类型,则将其交换为非泛型ViewPage类型,以便ASP.NET解析器不会阻塞.然后,在页面编译生命周期的后期,他们的自定义页面构建器类将通用类型重新交换.

这是因为通用ViewPage类型派生自非泛型ViewPage,并且在(非泛型)基类上存在@Page指令中设置的所有有趣属性.因此,在@Page指令中设置属性时真正发生的是,这些属性名称是针对非泛型ViewPage基类进行验证的.

无论如何,这在大多数情况下都很好用,但不适用于你的,因为他们在其页面过滤器实现中将ViewPage硬编码为非泛型基类型,并且不提供更改它的简单方法.这就是为什么你在错误消息中看到ViewPage的原因,因为在ASP.NET交换ViewPage占位符和在编译之前交换通用ViewPage之间发生错误.

解决方法是创建您自己的以下版本:

    页面解析器过滤器 - 这几乎是MVC源中ViewTypeParserFilter.cs的精确副本,唯一的区别是它引用了自定义ViewPage和页面构建器类型而不是MVC的

    页面构建器 - 这与MVC源中的ViewPageControlBuilder.cs相同,但它将类放在您自己的命名空间中而不是它们的名称空间中.

    直接从System.Web.Mvc.ViewPage(非泛型版本)派生自定义viewpage类.在这个新的非泛型类上粘贴任何自定义属性.

    从#3派生一个泛型类,从ASP.NET MVC源代码的ViewPage实现中复制代码.

    如果您还需要用户控件指令的自定义属性,请对用户控件(@Control)重复#2,#3和#4.

然后,您需要更改views目录(而不是主应用程序的web.config)中的web.config以使用这些新类型而不是MVC的默认类型.

我附上了一些代码示例,说明了它是如何工作的.非常感谢Phil Haack的文章,以帮助我理解这一点,虽然我不得不在MVC和ASP.NET源代码中进行大量探索,以便真正理解它.

首先,我将从web.config中所需的web.config更改开始:


现在,这是页面解析器过滤器(上面的#1):

namespace JG.ParserFilter {
    using System;
    using System.Collections;
    using System.Web.UI;
    using System.Web.Mvc;

    internal class CustomViewTypeParserFilter : PageParserFilter
    {

        private string _viewBaseType;
        private DirectiveType _directiveType = DirectiveType.Unknown;
        private bool _viewTypeControlAdded;

        public override void PreprocessDirective(string directiveName, IDictionary attributes) {
            base.PreprocessDirective(directiveName, attributes);

            string defaultBaseType = null;

            // If we recognize the directive, keep track of what it was. If we don't recognize
            // the directive then just stop.
            switch (directiveName) {
                case "page":
                    _directiveType = DirectiveType.Page;
                    defaultBaseType = typeof(JG.ParserFilter.CustomViewPage).FullName;  // JG: inject custom types here
                    break;
                case "control":
                    _directiveType = DirectiveType.UserControl;
                    defaultBaseType = typeof(JG.ParserFilter.CustomViewUserControl).FullName; // JG: inject custom types here
                    break;
                case "master":
                    _directiveType = DirectiveType.Master;
                    defaultBaseType = typeof(System.Web.Mvc.ViewMasterPage).FullName;
                    break;
            }

            if (_directiveType == DirectiveType.Unknown) {
                // If we're processing an unknown directive (e.g. a register directive), stop processing
                return;
            }


            // Look for an inherit attribute
            string inherits = (string)attributes["inherits"];
            if (!String.IsNullOrEmpty(inherits)) {
                // If it doesn't look like a generic type, don't do anything special,
                // and let the parser do its normal processing
                if (IsGenericTypeString(inherits)) {
                    // Remove the inherits attribute so the parser doesn't blow up
                    attributes["inherits"] = defaultBaseType;

                    // Remember the full type string so we can later give it to the ControlBuilder
                    _viewBaseType = inherits;
                }
            }
        }

        private static bool IsGenericTypeString(string typeName) {
            // Detect C# and VB generic syntax
            // REVIEW: what about other languages?
            return typeName.IndexOfAny(new char[] { '<', '(' }) >= 0;
        }

        public override void ParseComplete(ControlBuilder rootBuilder) {
            base.ParseComplete(rootBuilder);

            // If it's our page ControlBuilder, give it the base type string
            CustomViewPageControlBuilder pageBuilder = rootBuilder as JG.ParserFilter.CustomViewPageControlBuilder; // JG: inject custom types here
            if (pageBuilder != null) {
                pageBuilder.PageBaseType = _viewBaseType;
            }
            CustomViewUserControlControlBuilder userControlBuilder = rootBuilder as JG.ParserFilter.CustomViewUserControlControlBuilder; // JG: inject custom types here
            if (userControlBuilder != null) {
                userControlBuilder.UserControlBaseType = _viewBaseType;
            }
        }

        public override bool ProcessCodeConstruct(CodeConstructType codeType, string code) {
            if (codeType == CodeConstructType.ExpressionSnippet &&
                !_viewTypeControlAdded &&
                _viewBaseType != null &&
                _directiveType == DirectiveType.Master) {

                // If we're dealing with a master page that needs to have its base type set, do it here.
                // It's done by adding the ViewType control, which has a builder that sets the base type.

                // The code currently assumes that the file in question contains a code snippet, since
                // that's the item we key off of in order to know when to add the ViewType control.

                Hashtable attribs = new Hashtable();
                attribs["typename"] = _viewBaseType;
                AddControl(typeof(System.Web.Mvc.ViewType), attribs);
                _viewTypeControlAdded = true;
            }

            return base.ProcessCodeConstruct(codeType, code);
        }

        // Everything else in this class is unrelated to our 'inherits' handling.
        // Since PageParserFilter blocks everything by default, we need to unblock it

        public override bool AllowCode {
            get {
                return true;
            }
        }

        public override bool AllowBaseType(Type baseType) {
            return true;
        }

        public override bool AllowControl(Type controlType, ControlBuilder builder) {
            return true;
        }

        public override bool AllowVirtualReference(string referenceVirtualPath, VirtualReferenceType referenceType) {
            return true;
        }

        public override bool AllowServerSideInclude(string includeVirtualPath) {
            return true;
        }

        public override int NumberOfControlsAllowed {
            get {
                return -1;
            }
        }

        public override int NumberOfDirectDependenciesAllowed {
            get {
                return -1;
            }
        }

        public override int TotalNumberOfDependenciesAllowed {
            get {
                return -1;
            }
        }

        private enum DirectiveType {
            Unknown,
            Page,
            UserControl,
            Master,
        }
    }
}

这是页面构建器类(上面的#2):

namespace JG.ParserFilter {
    using System.CodeDom;
    using System.Web.UI;

    internal sealed class CustomViewPageControlBuilder : FileLevelPageControlBuilder {
        public string PageBaseType {
            get;
            set;
        }

        public override void ProcessGeneratedCode(
            CodeCompileUnit codeCompileUnit,
            CodeTypeDeclaration baseType,
            CodeTypeDeclaration derivedType,
            CodeMemberMethod buildMethod,
            CodeMemberMethod dataBindingMethod) {

            // If we find got a base class string, use it
            if (PageBaseType != null) {
                derivedType.BaseTypes[0] = new CodeTypeReference(PageBaseType);
            }
        }
    }
}

这里是自定义视图页面类:非泛型基础(上面的#3)和泛型派生类(上面的#4):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Diagnostics.CodeAnalysis;
using System.Web.Mvc;

namespace JG.ParserFilter
{
    [FileLevelControlBuilder(typeof(JG.ParserFilter.CustomViewPageControlBuilder))]
    public class CustomViewPage : System.Web.Mvc.ViewPage //, IAttributeAccessor 
    {
        public string MyNewProperty { get; set; }
    }

    [FileLevelControlBuilder(typeof(JG.ParserFilter.CustomViewPageControlBuilder))]
    public class CustomViewPage : CustomViewPage
        where TModel : class
    {
        // code copied from source of ViewPage

        private ViewDataDictionary _viewData;

        public new AjaxHelper Ajax
        {
            get;
            set;
        }

        public new HtmlHelper Html
        {
            get;
            set;
        }

        public new TModel Model
        {
            get
            {
                return ViewData.Model;
            }
        }

        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public new ViewDataDictionary ViewData
        {
            get
            {
                if (_viewData == null)
                {
                    SetViewData(new ViewDataDictionary());
                }
                return _viewData;
            }
            set
            {
                SetViewData(value);
            }
        }

        public override void InitHelpers()
        {
            base.InitHelpers();

            Ajax = new AjaxHelper(ViewContext, this);
            Html = new HtmlHelper(ViewContext, this);
        }

        protected override void SetViewData(ViewDataDictionary viewData)
        {
            _viewData = new ViewDataDictionary(viewData);

            base.SetViewData(_viewData);
        }

    }
}

以下是用户控件的相应类(上面的#5):

namespace JG.ParserFilter
{
    using System.Diagnostics.CodeAnalysis;
    using System.Web.Mvc;
    using System.Web.UI;

    [FileLevelControlBuilder(typeof(JG.ParserFilter.CustomViewUserControlControlBuilder))]
    public class CustomViewUserControl : System.Web.Mvc.ViewUserControl 
    {
        public string MyNewProperty { get; set; }
    }

    public class CustomViewUserControl : CustomViewUserControl  where TModel : class
    {
        private AjaxHelper _ajaxHelper;
        private HtmlHelper _htmlHelper;
        private ViewDataDictionary _viewData;

        public new AjaxHelper Ajax {
            get {
                if (_ajaxHelper == null) {
                    _ajaxHelper = new AjaxHelper(ViewContext, this);
                }
                return _ajaxHelper;
            }
        }

        public new HtmlHelper Html {
            get {
                if (_htmlHelper == null) {
                    _htmlHelper = new HtmlHelper(ViewContext, this);
                }
                return _htmlHelper;
            }
        }

        public new TModel Model {
            get {
                return ViewData.Model;
            }            
        }

        [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public new ViewDataDictionary ViewData {
            get {
                EnsureViewData();
                return _viewData;
            }
            set {
                SetViewData(value);
            }
        }

        protected override void SetViewData(ViewDataDictionary viewData) {
            _viewData = new ViewDataDictionary(viewData);

            base.SetViewData(_viewData);
        }
    }
}

namespace JG.ParserFilter {
    using System.CodeDom;
    using System.Web.UI;

    internal sealed class CustomViewUserControlControlBuilder : FileLevelUserControlBuilder {
        internal string UserControlBaseType {
            get;
            set;
        }

        public override void ProcessGeneratedCode(
            CodeCompileUnit codeCompileUnit,
            CodeTypeDeclaration baseType,
            CodeTypeDeclaration derivedType,
            CodeMemberMethod buildMethod,
            CodeMemberMethod dataBindingMethod) {

            // If we find got a base class string, use it
            if (UserControlBaseType != null) {
                derivedType.BaseTypes[0] = new CodeTypeReference(UserControlBaseType);
            }
        }
    }
}

最后,这是一个示例视图,显示了这一点:

<%@ Page Language="C#" MyNewProperty="From @Page directive!"  Inherits="JG.ParserFilter.CustomViewPage" %>
    <%=Model.SomeString %>
    

this.MyNewPrroperty = <%=MyNewProperty%>


你我的朋友是你我的英雄.如果我能通过屏幕到达并拥抱你,我会.我永远不会用我的经验解决这个问题.永远!由于这个问题,我停止了一周的编码,最后得到了答案.如果可以的话,我会给你剩下的226点声望,但我无法编辑赏金.再次感谢你,非常彻底的解答,并有一个完美的解释.希望这个答案可以帮助那些试图让这个工作的人.
推荐阅读
黄晓敏3023
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有