2014-11-03 53 views
0

我目前正在將Apache 2.0應用程序移植到Apache/Mono(Ubuntu 13.10)。ASP.NET Page.ParseControl使用Apache/Mono很慢

該應用程序使用XSLT從動態內容創建ASP.NET控件。它通過使用(臭名昭着的)Page.ParseControl(string)方法來實現。

某些頁面可能具有> 500個動態控件。對它們調用ParseControl仍然只在IIS/Windows上花費幾毫秒,並且只在初始頁面加載時完成。對於異步。後備,只有少數這些控件被重新創建(如被「點擊」的那個)來優化這些東西。

現在我的問題與單聲道: ParseControl每個呼叫需要300-1500毫秒。在一個循環中傳遞的「500和控制」可能會採取永遠,所以我已經優化過它:

  1. 通過C#把所有控制在一個包裝-DIV
  2. 呼叫ParseControl上
  3. 提取所有單個元素代碼

這和解析只需要1次完全相同。ParseControl的代價似乎是我所想的。另外它只是很慢,如果我傳遞「新」內容(所以似乎已經有一些緩存)。

ParseControl在/ tmp中創建文件,然後啓動它們上的ASP.NET編譯器,就我所見。我怎麼能加快速度? 單來源是全TODOS指的是functionallity的,它不再在所有工作與Ubuntu 14.04(拋出「System.Web.Compilation.CompilationException」,見http://mono.1490590.n4.nabble.com/CS1576-after-upgrade-to-Ubuntu-14-04-1-td4663599.html

+1

*徹底打破*是不足以形容的東西,請告訴我們得到的錯誤 – knocte 2014-11-03 21:43:55

+0

現在我想出了幾個解決方法:要修復Mono 3.x(Ubuntu 14.04)上的「ParseControl」,我現在使用帶有臨時「ascx」文件的「LoadControl」(注意:仍然很慢)。爲了加快速度,我現在使用基於http://htmlagilitypack.codeplex.com/的自行編寫的「ParseControl」-Engine,它實際上比預期的更好,所以我可能會在這裏發佈它,一旦我認爲它「穩定」。 – Tobias81 2014-11-18 14:35:59

回答

0

這是我自己實現ParseControl的。它遠沒有完成,所以不要指望太多。

結果的期望:

非服務器控件將被放入「LiteralControl」 S。

服務器支持的控件(RUNAT = 「服務器」):在的 「web.config」 定義

  • 「HTMLLoader對象」 S從 「System.Web程序」 組件
  • 控制
  • 定製控件

其他(如用戶控件)應該很容易添加(我現在沒有在我的項目中使用它們)。

結果(如果有效)將與「Page.ParseControl」所做的相似,但不是相同的。

性能: 我使用一些緩存來加快速度,但在Windows上比「Page.ParseControl」慢了將近50%。 對於單聲道(用3.12.1測試),它現在實際上可用。

Prerequesites:

包括 「HtmlAgilityPack」 從http://htmlagilitypack.codeplex.com

用法:

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; 
    } 
}