Think First Development

Erst denken, dann programmieren

Durchsuche Beiträge, die in der Kategorie Juli, 2010 erstellt wurden

Vor kurzem ist die Preview1 des ASP.NET MVC 3. Die offensichtlichste Neuerung ist die Unterstützung für verschiedene View-Engines. In der Preview 1 gibt es dafür verschiedene Projektvorlagen, das wird aber später in den Konfigurationdialog, in dem unter anderem das Testprojekt ausgewählt wird, integriert.

image

Die View-Engine verwendet einen einfacheren Syntax um die Lesbarkeit zu verbessern. Details zu Razor sind hier auf scottgu’s blog zu finden. Ein Vergleich der beiden View-Engines ist hier zu finden.

Interessant ist, dass ein Projekt die Views aus beiden View Engines gleichzeitig verwenden kann. So ist es möglich, Razor-Views und ASPX-View beliebig zu mischen.

image

Ist eine View doppelt enthalten, also sowohl für Razor, als auch für ASPX, wird die ASPX View bevorzugt. Interessant hierbei ist, dass in meinem Fall die ContactView.cshtml die MasterPage der Index.aspx überschreibt.

Das Ermitteln einer View folgt dem Mechanismus “Von Spezialisierten zum Generalisierten”. Das folgende Bild zeigt die Reihenfolge in Kombination mit Areas.

Diese Reihenfolge gilt für den Lookup von sowohl Views als auch Partial Views. Das gilt für jedes (!) Lookup, auch innerhalb von Views (z.B. bei Html.RenderPartial()). Diesen Umstand kann man sich zu nutze machen um Polymorphie abzubilden. Gegeben ist folgendes Modell.

image

Damit bietet es sich an, eine Shared View für “Contact” zu erstellen. Aber was passiert mit den Eigenschaften, die nur in den spezialisierten Klassen “Person” und “Company” existieren. Die ContactView ist für die “Contact” Basisklasse typisiert.

An dieser Stelle hilft die Lookup-Reihenfolge. Benutzt man in der Shared View ein RenderPartial(), kann man spezialisierten Views in den Controllern definieren.

image

Das Ergebnis ist, dass im CompanyController zusätzlich die CompanyView “SpecialView” gerendert wird und im PersonController die PersonView “SpezialView”. Es wird also automatisch zuerst in den Views für den Controller nachgeschaut. Einziges Problem mit RenderPartial() ist eine Exception, wenn in einem dritten Controller die “SpezialView” nicht implementiert ist, die ContactView aber gerendert wird.

image  image

Zu bemerken ist abschließend noch, dass die SpezialViews auf die spezialisierten Klassen typisiert ist, also Person bzw. Company. Das MVC2 Framework übernimmt (vereinfacht dargestellt) die Umwandlung von Contact in Person bzw. Company.

Das Html-Helper Object des MVC2 Frameworks unterstützt kein Generieren von File-Input Steuerelementen. Hier ist ein Workaround für dieses Problem: Man überschreibt das Type-Attribut mit “file”.

<%: Html.TextBoxFor(model => model.File, new { size = "30", type = "file" }) %>

weiter lesen

Serverseitige Validierung

Das ASP.NET MVC2 Framework biete eine einfache Möglichkeit zur Validierung eines Modells: Data Annotations. Mit Attributen wird die Validierung für das Modell definiert. Im MVC2 Framework sind vier Validatoren enthalten: Required, StringLength, Range und RegularExpression. weiter lesen

Vor nicht allzu langer Zeit … habe ich im SQL Server 2008 ein Aggregat-Funktion vermisst, die es bereits in MYSQL gibt: GROUP_CONCAT. Damit ist es bei einer Gruppierung möglich, Werte zu verketten.

Es begab ich folgendes vereinfachtes Problem: Es war Tabelle mit Salaten, die Personen zum Grillen mitbringen. Das Ziel sollte ein Liste gruppiert nach dem Salat sein. Zusätzlich zur Zählung (COUNT) sollten noch alle Personen aufgelistet werden, die den entsprechenden Salat mitbringen (CONCAT).

imageimage

Google hatte für dieses Problem verschiedene Lösungen. Die einen waren zu kompliziertes SQL (un-Wartbarkeit!) und funktionierten nur für endlich Anzahl von Zeilen bzw. nicht wiederverwendbare benutzerdefinierte Funktion. Die eleganteste aber aufwändigste Lösung im Bezug auf das Deployment schien das Entwickeln einer eigenen Aggregat-Funktion “Concat”. An Ende war die eigene Aggregatfunktion das einfachste und das Deployment überschaubar (Adminsicher ;) ).

Vorgehen

  1. Erstellen der Assembly
  2. SQL Server anpassen
  3. Aggregatfunktion verwenden

.NET Assembly erstellen

Visual Studio 2010 unterstützt bei der Erstellung von SQL Server Assemblys über den “Neues Projekt” Wizard.

image

Anschließen kann über “Hinzufügen” eine neues Aggregat angelegt werden.

image

Das Klasse der Aggregatfunktion in der .NET Assembly ist selbsterklärend. Die Methoden werden mittels Reflection aufgerufen, daher ist keine separate Schnittstelle zu implementieren. Die Schnittstelle “IBinarySerialize” dient dazu, das in dem Klassenattribut deklarierte “Format.UserDefined” (MSDN) zu implementieren.

using System;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;
using System.IO;
using System.Text;

namespace SwkAggregates
{
    [Serializable]
    [SqlUserDefinedAggregate(
        Format.UserDefined, //use clr serialization to serialize the intermediate result
        IsInvariantToNulls = true, //optimizer property
        IsInvariantToDuplicates = false, //optimizer property
        IsInvariantToOrder = false, //optimizer property
        MaxByteSize = 8000) //maximum size in bytes of persisted value
    ]
    public class Concat : IBinarySerialize
    {
        /// <summary>
        /// The variable that holds the intermediate result of the concatenation
        /// </summary>
        private StringBuilder _intermediateResult;

        /// <summary>
        /// Initialize the internal data structures
        /// </summary>
        public void Init()
        {
            this._intermediateResult = new StringBuilder();
        }

        /// <summary>
        /// Accumulate the next value, not if the value is null
        /// </summary>
        /// <param name="value"></param>
        public void Accumulate(SqlString value)
        {
            if (value.IsNull)
            {
                return;
            }

            this._intermediateResult.Append(value.Value).Append(", ");
        }

        /// <summary>
        /// Merge the partially computed aggregate with this aggregate.
        /// </summary>
        /// <param name="other"></param>
        public void Merge(Concat other)
        {
            this._intermediateResult.Append(other._intermediateResult);
        }

        /// <summary>
        /// Called at the end of aggregation, to return the results of the aggregation.
        /// </summary>
        /// <returns></returns>
        public SqlString Terminate()
        {
            string output = string.Empty;
            //delete the trailing comma, if any
            if (this._intermediateResult != null
                && this._intermediateResult.Length > 1)
            {
                output = this._intermediateResult.ToString(0, this._intermediateResult.Length – 2);
            }

            return new SqlString(output);
        }

        public void Read(BinaryReader r)
        {
            _intermediateResult = new StringBuilder(r.ReadString());
        }

        public void Write(BinaryWriter w)
        {
            w.Write(this._intermediateResult.ToString());
        }
    }
}

Hinweis: Die Assembly muss keinen Strong Name besitzen. Es lassen sich auch eine nicht-signierte Assemblys in den SQL Server hochladen. Ob das Bestandteil der neuen Sicherheitspolitik von Microsoft ist wage ich zu bezweifeln.

Also bleibt nur noch das Kompilieren der DLL und anschließend das Integrieren in den SQL Server.

SQL Server Integration

Die SQL Server Integration besteht aus zwei Schritten.

  1. Hochladen der Assembly in den SQL Server
  2. Erstellen einer Aggregatfunktion mit einem “API-Call” auf die Klasse in der DLL

Das Script enthält auch die optionalen Schritte, wenn das Ausführen von Assemblys in der Datenbank noch nicht aktiviert wurde. Das Script ist durch die Kommentierung (hoffentlich) selbsterklärend.

– Add assembly to database
CREATE ASSEMBLY SwkUtilities FROM ‘d:\SwkAggregates.dll’
WITH PERMISSION_SET=SAFE;
GO

– Create aggregate function which calls the concrete "Concat" class
CREATE AGGREGATE Concat(@input nvarchar(4000))
RETURNS nvarchar(4000)
EXTERNAL NAME [SwkUtilities].[SwkAggregates.Concat];
GO

– Enable cls code execution. It is disabled by default
sp_configure ‘clr enabled’, 1;

GO

– Deploy the "clr enabled" setting in sql server
reconfigure
GO

Zu bemerken ist, dass die Assembly im SQL Server einen symbolischen Namen “SwkUtilities” erhält. Der API-Call erfolgt über diesen symbolischen Namen. Außerdem wird die DLL wird in den SQL Server hochgeladen und ist damit Bestandteil der Datenbank. Das hat natürlich Auswirkungen auf Backup und Restore Szenarien. Bei einem Backup werden die Assemblies mitgesichert d.h. es entfällt ein FileSystem Backup. Im Gegenzug werden mit einem Restore der Datenbank auch die Assemblies wiederhergestellt.

SQL Verwendung

Ab jetzt heißt es viel Spaß mit der neuen Aggregat Funktion und der geplanten Grillfete ;) .

SELECT Salat, Count(*), dbo.Concat(Person)
FORM Grillfest
GROUP BY Salat

Powered by WordPress Web Design by SRS Solutions © 2010 Think First Development Design by SRS Solutions