Thomas Bandt

Über mich | Kontakt | Archiv

ASP.NET - Profile-Provider mit LINQ und Reflection in 10 Minuten

Dass LINQ in meinen Augen die Produktivität steigert, habe ich ja bereits geschrieben - jetzt lasse ich auch mal ein kurzes Beispiel anhand eines ASP.NET-Profile-Providers folgen.

Profile-Provider werden in ASP.NET dazu verwendet, Profilinformationen wie etwa den Namen, die Anschrift oder sonstige Infos bereitzustellen. In der Standardimplementierung von Microsoft wird dabei allerdings in meinen Augen zusammen mit dem Membershipprovider mit einer gewaltigen Kanone auf Spatzen geschossen. Deshalb implementiere ich diese Sachen (ebenso den Role-Provider) standardmäßig selbst - wie das gehen kann, zeige ich hier anhand einer kleinen Demo. Und weil es sich gerade anbietet, tue ich das mal in Verbindung mit LINQ to SQL.

Die Datenbank

Die Benutzerinformationen werden zusammen mit den Logindaten in einer einfachen, flachen Tabelle gespeichert, die in diesem Test schlicht "Users" heißt. Der LINQ-to-SQL-Designer erstellt aus dieser Tabelle automatisch eine "Entität", d.h. ein Geschäftsobjekt, dass das Mapping der Eigenschaften auf die entsprechenden Datenbankfelder vollautomatisch übernimmt. Das einzige was man also tun muss, ist die Datenbanktabelle auf den Designer zu ziehen, et voilà:

Der Provider

Der Provider muss jetzt im Wesentlichen zwei Dinge erfüllen:

  1. Profilinformationen auslesen.
  2. Profilinformationen speichern.

Außerdem sollte er leicht erweiterbar und konfigurierbar sein, d.h. nicht bei jeder Änderung an der Profilstruktur geändert werden müssen.

Das alles erreicht man schnell in Kombination mit dem generierten Geschäftsobjekt, einer entsprechenden Konfiguration (siehe unten) und Reflection.

Warum Reflection? Ganz einfach: weil wir damit nicht jede einzelne Profileigenschaft kennen müssen.

Beispiel:

public override SettingsPropertyValueCollection GetPropertyValues(SettingsContext context, SettingsPropertyCollection collection)
{
    SettingsPropertyValueCollection settings = new SettingsPropertyValueCollection();
    if ((bool)context["IsAuthenticated"])
    {
        ProfileDemoDataContext db = new ProfileDemoDataContext();
        User user = (from u in db.Users where u.Username == (string)context["UserName"] select u).Single();
        if (user != null)
        {
            foreach (SettingsProperty sp in collection)
            {
                SettingsPropertyValue settingsProperty = new SettingsPropertyValue(sp);
                if (user.GetType().GetProperty(sp.Name) != null)
                    settingsProperty.PropertyValue = user.GetType().GetProperty(sp.Name).GetValue(user, null);
                settings.Add(settingsProperty);
            }
        }
    }
    return settings;
}

Das sieht auf den ersten Blick vielleicht kompliziert aus, ist im Detail aber doch relativ einfach:

Die Konfiguration

Damit das alles auch funktioniert, ist es nötig den Provider in der web.config zu registrieren und die entsprechenden Profileigenschaften bekannt zu machen:

<profile enabled="true" automaticSaveEnabled="false" defaultProvider="Demo">
  <properties>
    <add name="Firstname" type="String" />
    <add name="Lastname" type="String" />
    <add name="Telephone" type="String" />
  </properties>
  <providers>
    <add name="Demo" type="ProfileDemo" />
  </providers>
</profile>

Mehr muss nicht getan werden.

Die Praxis: Profildaten auslesen und ändern

Die so bereitgestellten Profilinformationen können nun für angemeldete Benutzer ausgelesen und natürlich auch geändert werden.

Auslesen der Daten:

Vorname: <asp:TextBox ID="BoxFirstname" runat="server" Text="<%# Profile.Firstname %>" /><br />
Nachname: <asp:TextBox ID="BoxLastname" runat="server" Text="<%# Profile.Lastname %>" /><br />
Telefon: <asp:TextBox ID="BoxTelephone" runat="server" Text="<%# Profile.Telephone %>" />

Visual Studio generiert zur Designzeit automatisch eine entsprechende Wrapper-Klasse für das Profil, sodass die Eigenschaften direkt mit IntelliSense abrufbar sind. Sollte das nicht gleich klappen, einfach Visual Studio mal neustarten bzw. das Projekt schließen und neu öffnen.

Ändern der Daten:

Das Ändern bzw. aktualisieren des Profils kann nun genauso einfach geschehen:

protected void Button1_Click(object sender, EventArgs e)
{
Profile.Firstname = BoxFirstname.Text;
Profile.Lastname = BoxLastname.Text;
Profile.Telephone = BoxTelephone.Text;
Profile.Save();
Response.Redirect("Profile.aspx");
}

Erst beim Aufruf von Profile.Save() werden die Daten in die Datenbank geschrieben, was im Übrigen durch die Konfiguration von automaticSaveEnabled in der Profil-Sektion der web.config erreicht wird. Wenn automaticSaveEnabled auf true stehen würde, würde bei jeder Zuweisung die entsprechende Methode im Profile-Provider aufgerufen werden.

Und LINQ?

Zugegeben, ich habe mit diesem Artikel zwei Fliegen mit einer Klappe erschlagen - hauptsächlich ging es darum zu zeigen, wie man schnell, einfach und flexibel einen Profile-Provider implementiert. Die Verwendung von LINQ to SQL ist das praktische Sahnehäubchen obendrauf, zeigt aber auch die Vorteile dieses O/R-Mappers auf: durch die Erzeugung von echten .NET-Objekten aus Datenbankobjekten lässt sich sehr viel Zeit sparen, weil ein zusätzliches Mapping auf eigene Objekte unter Umständen entfallen kann.

Das hat natürlich seine Grenzen - spätestens dann beispielsweise, wenn man diese Daten an etwas im UI binden will, etwa ein GridView - denn meiner Meinung nach haben diese Objekte nichts in dieser Schicht zu suchen. Sei es drum - wenn man schnell etwas erreichen will, kommt das gerade recht.

Davon abgesehen spart man sich aber jede einzelne Zeile manuellen SQL-Codes, d.h. es sind im gesamten Beispiel nur drei (!) LINQ-Abfragen notwendig - eine zum authentifizieren des Benutzers (was ich hier nicht erwähnt habe), und jeweils eine um den Benutzer aus der Datenbank zu holen, beim Aktualisieren und Auslesen des Profils.

Einfacher geht es nicht mehr.

Was sonst noch bleibt

Bleibt zu erwähnen, dass dieses Vorgehen via Reflection natürlich auch mit anderen Datenbankzugriffsarten neben LINQ funktioniert, notwendig ist jeweils nur ein richtiges Objekt, welches den Benutzer und seine Eigenschaften repräsentiert. Wie das erstellt und befüllt wird ist jedem selbst überlassen, dazu benötigt es kein LINQ to SQL.

Das Projekt steht als ausführbare ASP.NET-3.5-Website zum Download bereit.

Downloads



« Zurück  |  Weiter »