繁体   English   中英

使用Apache / Mono的ASP.NET Page.ParseControl非常慢

[英]ASP.NET Page.ParseControl very slow with Apache/Mono

我目前正在将ASP.NET 2.0应用程序移植到Apache / Mono(Ubuntu 13.10)。

该应用程序使用XSLT从动态内容创建ASP.NET控件。 它通过使用(臭名昭著的)Page.ParseControl(string)方法来实现。

某些页面可能具有> 500个动态控件。 在IIS / Windows上对它们调用ParseControl仍然只花费几毫秒的时间,并且仅在初始页面加载时完成。 对于异步。 在回发之后,仅重新创建了其中的少数控件(例如被“单击”的控件)以优化该内容。

现在我的Mono问题:ParseControl每次调用花费300-1500毫秒。 循环传递那些“ 500个控件”可能要花很长时间,所以我已经对其进行了优化:

  1. 将所有控件放入包装器DIV中
  2. 调用ParseControl
  3. 通过C#代码提取所有单个元素

这与仅解析1完全相同的时间。我想,ParseControl的代价似乎是完全调用它。 如果我将“新”内容传递给它,它也只是那么慢(所以似乎已经有一些缓存了)。

ParseControl在/ tmp中创建文件,然后据我所知对其启动ASP.NET编译器。 我怎样才能加快速度? Mono-sources充满了涉及该功能的TODO,并且在Ubuntu 14.04上完全不再起作用(抛出“ System.Web.Compilation.CompilationException”,请参阅http://mono.1490590.n4.nabble.com/CS1576 -after-upgrade-to-Ubuntu-14-04-1-td4663599.html

这是我自己的ParseControl实现。 它距离完成还很遥远,因此不要期望太大。

预期结果:

非服务器控件将放入“ LiteralControl”中。

支持的服务器控件(runat =“ server”):

  • “ HtmlControl”的
  • 来自“ System.Web”程序集的控件
  • 在“ Web.config”中定义的自定义控件

其他(如用户控件)应该易于添加(我现在不在我的项目中使用它们)。

结果(如果可行)将类似于“ Page.ParseControl”所做的事情,但不完全相同。

性能:我使用了一些缓存来加快处理速度,但是在Windows上它仍然比“ Page.ParseControl”慢50%。 对于Mono(经过3.12.1测试),现在实际上可以使用。

先决条件:

包含来自http://htmlagilitypack.codeplex.com的 “ HtmlAgilityPack”

用法:

String html = "<div runat=\"server\" id=\"test123\"></div>";
Control c = null;
try {
    // Performance tip: The "doc" object can be cached, if the same HTML needs to be parsed again
    var doc = new HtmlDocument();
    doc.LoadHtml(html);
    c = OwnParseControlEngine.Parse(doc);
}
catch {
    c = Page.ParseControl(html);  // Note: Will crash with Mono 3.x
}

码:

/// <summary>
/// Own implementation of "ParseControl". Returns XHTML (default) or HTML.
/// Custom controls from "Web.config" are supported (TODO: User controls and imports on Page are NOT).
/// </summary>
private class OwnParseControlEngine {

    public class ParseException : System.Exception {
        public ParseException(HtmlNode e)
            : base("Unknown ASP.NET server-tag \"" + e.OriginalName + "\".") {
        }
    }

    private static readonly String _systemWebNamespace;
    private static readonly String _systemWebAssembly;

    private static readonly Dictionary<String, LinkedList<TagPrefixInfo>> _controlsTagPrefixInfos = new Dictionary<String, LinkedList<TagPrefixInfo>>();  // Key is tag-prefix in lowercase

    private class Factory {
        public delegate Control CreateDel(HtmlNode e);
        private readonly CreateDel _del;
        public Boolean DropWhiteSpaceLiterals { get; private set; }
        public Factory(CreateDel del, Boolean dropWhiteSpaceLiterals = false) {
            this._del = del;
            this.DropWhiteSpaceLiterals = dropWhiteSpaceLiterals;
        }
        public Control Create(HtmlNode e) {
            return this._del.Invoke(e);
        }
    }
    private static readonly Dictionary<String, Factory> _factories = new Dictionary<String, Factory>();  // Must be locked. Key is tag-name in lowercase.

    static OwnParseControlEngine() {
        // We cache the results to speed things up. "Panel" is only used to get assembly info.
        _systemWebNamespace = typeof(Panel).Namespace;
        _systemWebAssembly = typeof(Panel).Assembly.FullName;
        var section = (PagesSection)WebConfigurationManager.OpenWebConfiguration("/").GetSection("system.web/pages");
        foreach (TagPrefixInfo info in section.Controls) {
            LinkedList<TagPrefixInfo> l;
            if (!_controlsTagPrefixInfos.TryGetValue(info.TagPrefix, out l)) {
                l = new LinkedList<TagPrefixInfo>();
                _controlsTagPrefixInfos.Add(info.TagPrefix.ToLower(), l);
            }
            l.AddLast(info);
        }
        // Add HTML control types
        _factories.Add("span", new Factory((e) => { return new HtmlGenericControl(e.OriginalName); }));
        _factories.Add("div", new Factory((e) => { return new HtmlGenericControl(e.OriginalName); }));
        _factories.Add("body", new Factory((e) => { return new HtmlGenericControl(e.OriginalName); }));
        _factories.Add("font", new Factory((e) => { return new HtmlGenericControl(e.OriginalName); }));
        _factories.Add("a", new Factory((e) => { return new HtmlAnchor(); }));
        _factories.Add("button", new Factory((e) => { return new HtmlButton(); }));
        _factories.Add("form", new Factory((e) => { return new HtmlForm(); }));
        _factories.Add("input", new Factory((e) => {
            switch (e.Attributes["type"].Value) {
                case "button": return new HtmlInputButton();
                case "checkbox": return new HtmlInputCheckBox();
                case "file": return new HtmlInputFile();
                case "hidden": return new HtmlInputHidden();
                case "image": return new HtmlInputImage();
                case "radio": return new HtmlInputRadioButton();
                case "text": return new HtmlInputText();
                case "password": return new HtmlInputPassword();
                case "reset": return new HtmlInputReset();
                case "submit": return new HtmlInputSubmit();
            }
            throw new ParseException(e);
        }));
        _factories.Add("select", new Factory((e) => { return new HtmlSelect(); }));
        _factories.Add("table", new Factory((e) => { return new HtmlTable(); }, true));  // Adding literals not allowed
        _factories.Add("tr", new Factory((e) => { return new HtmlTableRow(); }, true));  // Adding literals not allowed
        _factories.Add("td", new Factory((e) => { return new HtmlTableCell(); }));
        _factories.Add("textarea", new Factory((e) => { return new HtmlTextArea(); }));
        _factories.Add("link", new Factory((e) => { return new HtmlLink(); }));
        _factories.Add("meta", new Factory((e) => { return new HtmlMeta(); }));
        _factories.Add("title", new Factory((e) => { return new HtmlTitle(); }));
        _factories.Add("img", new Factory((e) => { return new HtmlImage(); }));
    }

    private static void ApplyHtmlControlAttributes(HtmlControl c, HtmlNode e) {
        foreach (HtmlAttribute a in e.Attributes) {
            if (a.Name == "id")
                c.ID = a.Value;
            else if (a.Name != "runat")
                c.Attributes[a.OriginalName] = HttpUtility.HtmlDecode(a.Value);
        }
    }

    private static void ApplyControlAttributes(Control c, HtmlNode e) {
        if (c is WebControl && e.Attributes["style"] != null) {
            String style = HttpUtility.HtmlDecode(e.Attributes["style"].Value);
            foreach (String s in style.Split(new Char[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
                ((WebControl)c).Style[s.Substring(0, s.IndexOf(':'))] = s.Substring(s.IndexOf(':') + 1);
        }
        foreach (PropertyInfo p in c.GetType().GetProperties()) {
            if (p.CanRead && p.CanWrite && e.Attributes[p.Name] != null) {
                try {
                    Object v = null;
                    if (p.PropertyType.IsEnum)
                        v = Enum.Parse(p.PropertyType, e.Attributes[p.Name].Value);
                    else if (p.PropertyType == typeof(String))
                        v = e.Attributes[p.Name].Value;
                    else if (p.PropertyType == typeof(Boolean))
                        v = Boolean.Parse(e.Attributes[p.Name].Value);
                    else if (p.PropertyType == typeof(Int32))
                        v = Int32.Parse(e.Attributes[p.Name].Value);
                    else if (p.PropertyType == typeof(Unit))
                        v = Unit.Parse(e.Attributes[p.Name].Value);
                    // TODO: More types?
                    if (v != null)
                        p.SetValue(c, v, null);
                }
                catch {
                }
            }
        }
    }

    private static Control CreateServerControl(HtmlNode e, out Boolean dropWhiteSpaceLiterals) {                    
        Factory cf;
        lock (_factories) {
            _factories.TryGetValue(e.Name, out cf);
        }
        if (cf == null) {
            Int32 pos = e.Name.IndexOf(':');
            if (pos != -1) {
                String tagPrefix = e.Name.Substring(0, pos).ToLower();
                String name = e.Name.Substring(pos + 1);
                Type t = null;
                // Try "System.Web" (default assembly)
                if (tagPrefix == "asp")
                    t = Type.GetType(String.Format("{0}.{1}, {2}", _systemWebNamespace, name, _systemWebAssembly), false, true);  // "Namespace.ClassName, Assembly"
                if (t == null) {
                    // Try controls specified in "web.config"
                    LinkedList<TagPrefixInfo> l;
                    if (_controlsTagPrefixInfos.TryGetValue(tagPrefix, out l)) {
                        foreach (var info in l) {
                            // Custom controls
                            t = Type.GetType(String.Format("{0}.{1}, {2}", info.Namespace, name, info.Assembly), false, true);  // "Namespace.ClassName, Assembly"
                            if (t != null)
                                break;
                            // TODO: User controls with tag.TagName, tag.Source
                        }
                    }
                }
                if (t != null) {
                    cf = new Factory((e2) => { return (Control)Activator.CreateInstance(t); });
                    lock (_factories) {
                        _factories[e.Name] = cf;  // "Replace" instead of "Add", because another thread might have already added it since the lock above
                    }
                }
            }
        }
        if (cf == null)
            throw new ParseException(e);
        var c = cf.Create(e);
        if (c is HtmlControl)
            ApplyHtmlControlAttributes((HtmlControl)c, e);
        else
            ApplyControlAttributes(c, e);
        dropWhiteSpaceLiterals = cf.DropWhiteSpaceLiterals;
        return c;
    }

    private static void ParseChildren(Control parentC, HtmlNode currE, Boolean xhtml = true, Boolean dropWhiteSpaceLiterals = false) {
        foreach (HtmlNode childE in currE.ChildNodes) {
            Control newC = null, closeTag = null;
            Boolean newDropWhiteSpaceLiterals = false;
            if (childE.Attributes["runat"] != null && childE.Attributes["runat"].Value.ToLower() == "server")  // Server control
                newC = CreateServerControl(childE, out newDropWhiteSpaceLiterals);
            else {  // Literal control
                switch (childE.Name) {
                    case "#text":
                        if (!dropWhiteSpaceLiterals || childE.InnerText.Trim().Length != 0)
                            newC = new LiteralControl(childE.InnerText);
                        break;
                    default:
                        String s = String.Format("<{0}", childE.OriginalName);
                        foreach (HtmlAttribute a in childE.Attributes)
                            s += String.Format(" {0}=\"{1}\"", a.OriginalName, a.Value);
                        s += ">";
                        switch (childE.Name) {
                            // List of void elements taken from http://www.programmerinterview.com/index.php/html5/void-elements-html5/
                            case "area": case "base": case "br": case "col": case "command": case "embed": case "hr": case "img": case "input":
                            case "keygen": case "link": case "meta": case "param": case "source": case "track": case "wbr":
                                if (xhtml)
                                    s = s.Substring(0, s.Length - 1) + "/>";
                                newC = new LiteralControl(s);
                                break;
                            default:
                                newC = new PlaceHolder();  // Used as a wrapper to allow child-controls
                                newC.Controls.Add(new LiteralControl(s));
                                closeTag = new LiteralControl(String.Format("</{0}>", childE.OriginalName));
                                break;
                        }
                        break;
                }
            }
            if (newC != null) {
                parentC.Controls.Add(newC);
                ParseChildren(newC, childE, xhtml, newDropWhiteSpaceLiterals);
                if (closeTag != null)
                    newC.Controls.Add(closeTag);
            }
        }
    }

    private OwnParseControlEngine() {
    }

    /// <summary>
    /// Parses the given HTML document and returns a Control.
    /// Throws "ParseException" on error (TODO: Maybe others too).
    /// </summary>
    public static Control Parse(HtmlDocument doc) {
        var c = new Control();
        ParseChildren(c, doc.DocumentNode, false);
        return c;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM