Da ich mein Blog nur vorkompiliert veröffentliche, ging mir folgendes Verhalten gehörig auf den Zeiger: Bei jeder kleinen Änderung am statischen Inhalt des Blog musste das gesamte Projekt neu kompiliert und via Ftp auf den Server geladen werden. Manch einer wird jetzt bestimmt sagen: “dann veröffentliche doch nicht vorkompiliert.” Über das für und wieder lässt sich bestimmt streiten, aber nicht hier und heute.
Wie bereits Eingangs erwähnt, störte mich dieses Verhalten immens. Ich wollte aber auch nicht die Datenbankstruktur von BlogEngine.NET ändern, nur um mir ein wenig Arbeit zu ersparen. Bei einem Update auf eine neue Version, könnte die Änderung an der Datenbank nach hinten losgehen und die Erleichterung währe dahin. Also habe ich mich für die einfachste Form der Speicherung strukturierter Daten entschieden; einer XML-Datei.
Da die enthaltenen Daten meistens aus Links und Verknüpfungen zu irgendwelchen Images  sowie Verknüpfungen zu JavaScript-Dateien bestehen, erschien mir das CDATA-Element nahezu perfekt für mein Vorhaben. Jetzt noch ein UserControl welches die Daten aus dem XML darstellt und das Ärgernis ist Geschichte. Einzig die Auflösung von Pfaden konnte ich nicht direkt in XML realisieren. Aber dafür gibt es ja das Codebehind des UserControl. Da die darzustellenden Images in der MasterPage dargestellt werden sollen, helfen relative Pfade nicht wirklich weiter. Also musste eine Auflösung der Pfade, die ursprünglich in der MasterPage etwa so dargestellt wurden:

<a href="<%=VirtualPathUtility.ToAbsolute("~/") %>" title="Zur Startseite des Blog wechseln.">Blog</a>

etwas anders gelöst werden.
Ich entschied mich für die Verwendung eines Schlüsselwort im XML, welches zur Laufzeit durch einen entsprechenden Pfad ersetzt wird. Also wird zum Beispiel aus der Zeichenfolge:

[root]themes/klaus_b/images/stopie6.png

die Pfadangabe:

/BlogEngine.Web/themes/klaus_b/images/stopie6.png

Wobei [root] das Schlüsselwort für die Angabe des Wurzelverzeichnis der Webseite darstellt. Ein kompletter Knoten im XML-File sieht dann also wie folgt aus:

<entry name="stopie6.org">
  <![CDATA[
  <a href="http://www.stopie6.org/" title="Stop IE6 Kampagne">
    <img src="[root]themes/klaus_b/images/stopie6.png" alt="Stop IE6 Kampagne" title="Stop IE6 Kampagne" width="88px" height="31px" />
  </a>
  ]]>
</entry>


Es braucht also nur die XML-Datei geladen, durch die Knoten iteriert und das ganze in einem Literal dargestellt zu werden.
Damit nicht bei jedem einzelnen Aufruf der Seite die XML-Datei geladen werden muss, wird das verwendete XmlDocument in das Cache geschrieben. Bei Änderungen an der XML-Datei soll natürlich darauf reagiert werden, deshalb wird beim Einfügen des XmlDocument in das Cache eine CacheDependency-Instanz mit dem Verweis auf die XML-Datei verwendet. Somit wird bei einer Änderung an der XML-Datei der Eintrag im Cache ungültig, die geänderte Datei wird verwendet und ihrerseits in das Cache eingefügt.

Abschließend für interessierte hier der Code des UserControl. Das Listing des Markup dürfte selbsterklärend sein.

<%@ Control Language="C#" AutoEventWireup="true" EnableViewState="false"
    CodeFile="MetaContent.ascx.cs" Inherits="MetaContent" %>
<h2>
    Meta</h2>
<div class="meta">
    <asp:Literal ID="literalContent" runat="server" />
</div>

Hier wird lediglich ein Literal verwendet um das erzeugte HTML darzustellen. Das umhüllende Div dient lediglich der Verwendung von CSS zur Darstellung des Inhalt.

Die Arbeitsweise des Codebehind wurde im Artikel bereits erläutert. Hier der Vollständigkeit halber das Listing.

using System;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using System.Xml;

using BlogEngine.Core;

public partial class MetaContent : UserControl
{
    #region Fields

    /// <summary>
    /// Hält den Pfad zum Wurzelverzeichnis der Webseite.
    /// </summary>
    private readonly string rootPath = Utils.RelativeWebRoot;

    /// <summary>
    /// Hält den Pfad zur XML-Datei.
    /// </summary>
    private readonly string storagePath =
        HttpContext.Current.Server.MapPath(BlogSettings.Instance.StorageLocation)
        + @"ControlsContent\MetaContent.xml";

    /// <summary>
    /// Hält den Namen des Cache-Schlüssel.
    /// </summary>
    private const string CacheKey = "MetaContent";

    #endregion Fields

    #region Methods

    protected void Page_Load(object sender, EventArgs e)
    {
        XmlDocument doc = null;

        if (HttpRuntime.Cache[CacheKey] != null)
        {
            // XmlDocument aus dem Cache lesen
            doc = (XmlDocument)HttpRuntime.Cache[CacheKey];
        }
        else
        {
            doc = new XmlDocument();
            doc.Load(this.storagePath);

            // in das Cache schreiben, mit Abhängigkeit zum XML-File
            HttpRuntime.Cache.Insert(
				CacheKey,
				doc,
				new CacheDependency(storagePath));
        }

        // HTML im Control erzeugen
        this.CreateControlContent(doc);
    }

    /// <summary>
    /// Erzeugt das HTML das im Literal dargestellt wird.
    /// </summary>
    /// <param name="doc">
    /// Das XmlDocument mit dem darzustellenden Inhalt.
    /// </param>
    private void CreateControlContent(XmlDocument doc)
    {
        XmlNodeList nodes = doc.SelectNodes(
								"//"
								+ CacheKey
								+ "//entry");
        StringBuilder sb = new StringBuilder();
        string content = null;
        char[] lineBreaks = new char[] { '\r', '\n', ' ' };

        foreach (XmlNode node in nodes)
        {
            if (node.InnerText.Contains("[root]"))
            {// root-Schlüssel aus dem XML durch den
				// tatsächlichen Pfad ersetzencontent = node.InnerText
					.Replace("[root]", this.rootPath)    .TrimStart(lineBreaks)    .TrimEnd(lineBreaks);
            }
            else
            {content = node.InnerText    .TrimStart(lineBreaks)    .TrimEnd(lineBreaks);
            }

            sb.Append(content);
        }

        this.literalContent.Text = sb.ToString();
    }

    #endregion Methods
}

Ich hoffe mir in Zukunft einiges an neukompilieren und das kopieren des ganzen Projekts zu sparen.

Technorati-Tags:  |  |  | 
Wenn ihnen der Artikel gefallen hat oder er für sie hilfreich war, bitte "kicken" sie ihn.
kick it on dotnet-kicks.de