Archiv für den Monat September 2011

Dynamische Controls in ASP.NET MVC erstellen


Als ich mich mit der Thematik erstellen von Dynamischen Controls in ASP.NET auseinander setzten musste, war es gar nicht so einfach Informationen zu finden wie das ganze im Detail in MVC abläuft und funktioniert dabei hat mir prinzipiell erst einmal der folgende stackoverflow Beitrag geholfen.

Es ist eigentlich egal wo die Daten herbekommt welche Controls von euch dynamisch erstellt werden sollen, die Daten können aus einer XML-Datei oder auch der Datenbank kommen. Dabei gibt es quasi 3 Schritte zu beachten.

1. Das Erstellen der passenden Eingabecontrols auf der Oberfläche:

Ich habe mir dazu eine eigene HTML-Helper Extension geschrieben, welcher ich eine Liste mit den Controls und den Daten übergebe die angezeigt / bearbeitet werden sollen, diese Daten befinden sich bereits im Model was angezeigt werden soll und muss nur noch an die Helper Methode übergeben werden, diese übernimmt dann den Rest. Es ist auch möglich das ganze über “EditorTemplates” im “Shared” Folder zu machen, was ich als schwieriger und unübersichtlicher empfand wie das ganze in eine Extension auszulagern die einen MvcHtmlString zurück gibt.

Dann wird je nach Datentyp, der erstellt werden soll, das passende Control erstellt, z.B. eine Textbox, Dropdownliste oder Checkbox. Beim Erstellen der Controls kann z.B. auf die Client Validation von MVC zurück gegriffen werden, hier müsst ihr nur die passenden “data-“ Attribute im Control setzten, was ihr erstellt.

Außerdem solltet Ihr darauf achten das Ihr den Controls entsprechende Namen (Ids) gebt die Ihr bei einem Postback auch wieder dementsprechend auswerten könnt.

Das war der einfache Teil :-), jetzt folgt das Auslesen der Daten.

2. Auslesen der eingegebenen Daten bei einem Postback:

Wenn ihr einmal soweit seid das Ihr die passenden Controls auf der Oberfläche darstellen könnt, dann wollt Ihr die Daten bei einem Postback ja auch wieder auslesen und wissen was der Benutzer eingegeben hat. Hier müssen zwei Schritte durchgeführt werden, damit dies reibungslos funktioniert.

2.1 Schreiben eines eigenen Modelbinders:

Ich habe es aber nur geschafft in dem ich die Methode “BindProperty” überschrieben habe. (Es gibt auch noch Möglichkeiten mit dem “IModelBinder” Interface zu arbeiten, hier habe ich es aber nicht geschafft, das meine Funktionen aus dem Interface aufgerufen wurden, wenn jemand einen Tipp hat bin ich dafür sehr dankbar.)

image

In unserer überschriebenen Methode prüfen wir ob das aktuelle Property was ausgewertet werden soll, dem entspricht welches wir selbst auswerten wollen, denn die Funktion wird “BindProperty” wird für jedes Property aus unserem Model aufgerufen. Da für unseren Typ bzw. in meinem Fall übergebe ich dem Model eine Liste mit Dynamischen Datentypen, kein Verfahren existiert wie diese Daten aus dem Postback dem Model hinzugefügt werden können, müssen wir die Daten entsprechend selbst auslesen aus dem Postback. Das wird wie schon gesagt in der “BindProperty” Methode gemacht. Hier warte ich bis das Property vom Typ “IENumerable<EFDynamicData>” mit Daten gefüllt werden soll.

image

Wenn unser Property mit Daten gefüllt werden soll auf das wir dann im Controller zugreifen können, müssen wir dementsprechend alle Postdaten unserer Customcontrols auslesen. Dafür suche ich meist erst einmal alle Postback values aus dem aktuellen HttpContext heraus und werte diese dann aus.

image

Dann muss auch eine Variable von unserem Typen angelegt werden “List<EFDynamicData>” damit wir diese mit den Daten aus dem Postback füllen können und erst wenn unsere Variable “dynamicDataList” mit “Setproperty” auch unserem “Model” zugewiesen wurde:

image

können wir im Controller einfach auf unsere Modelvariable zugreifen, die unsere Daten aus dem Postback enthält und können die Daten dann dementsprechend unserem Datenmodell zuweisen.

Ob Daten im gültigen Wertebereich eingegeben wurden, prüfe ich bereits im “BindProperty”, denn hier hat man die Möglichkeit direkt Fehler dem Modelstate hinzuzufügen, was es später im Controller ermöglicht einfach nur zu prüfen “ModelState.IsValid”. Sobald ein Error dem ModelState hinzugefügt wurde ist dieser nicht mehr Valide.

image

Wenn das aktuelle Property nicht unserem Typ entspricht den wir im “BindProperty” suchen, dann muss dieses Property mit den Standardmitteln dem Model hinzugefügt werden, damit wir auch auf alle anderen Properties im Controller zugreifen können:

image

2.2 Datentyp zur Modelbinder Liste hinzufügen

Damit unser Selbst geschriebener Modelbinder aber auch aufgerufen wird, muss jedes Model in dem unser Typ vorkommt der Liste mit ModelBindern in der Global.asax im Application_Start hinzugefügt werden.

image

d.h. für obiges Beispiel, das immer wenn das “EditUserAdminModel” verwendet wird unser Modelbinder zum Einsatz kommt und wenn in diesem Model dann noch der Typ “IENumerable<EFDynamicData>” gefunden wird, dann werden unsere Funktionen genommen um die Liste mit Daten zu füllen. Wenn unser Typ noch in anderen Models benutzt wird, dann muss jedes dieser Models auch in der Global.asax der Liste mit den “ModelBinders” hinzugefügt werden.

(Wie bereits erwähnt muss das ganze auch funktionieren in dem man nur den Typ registriert “IENumerable<EFDynamicData>” und damit nicht jedes einzelne Model der Liste hinzufügen muss, nur leider war es mit nicht möglich diese Variante zum Laufen zu bekommen. Wenn jemand Tipps hat, würde ich mich sehr über einen Kommentar freuen)

Ich kann NICHT garantieren, das es sich hierbei um die einzige und “beste” Methode handelt, so etwas umzusetzen aber es funktioniert einwandfrei.

Advertisements

Upgrade Entity Framework (EF) 4.0 auf 4.1


Ein Upgrade von Entity Framework 4.0 auf 4.1 ist recht einfach und mit wenigen Schritten zu bewerkstelligen und erfordert hier auch erst einmal keine weiteren Codeanpassungen (zumindest bei Meinem Projekt musste ich hier nichts anpassen). Code muss erst angepasst werden, wenn man statt dem bisherigen “ObjectContext” jetzt den “DbContext” nutzen möchte, welcher einige neuerungen zur Verfügung stellt, wie z.B. eine “Find” Methode oder das direkte Einstellen von No-Tracking in der Abfrage, dazu aber später mehr und erst einmal gehen ich auf das Upgrade ein.

Ich habe bei mir zuerst einmal über den NuGet Library Package Manager das EF 4.1 installiert. (Hier bin ich mir nicht ganz sicher ob dies wirklich notwendig ist.)

image

image

Danach habe ich dann das EF 4.1 von Microsoft heruntergeladen, damit mir auch die neue Codegenerierung für den “DbContext” zur Verfügung steht. Die Codegenerierung für den “DbContext” muss dann noch eingestellt werden, in dem man auf der Oberfläche unseres Klassendiagrammes mit der Rechten Maustaste “Neues Codegenerierungselement hinzufügen” klickt.

image

Und wenn das EF4.1 richtig installiert wurde, steht uns hier “ADO.NET DbContext Generator” zur Verfügung den wir auswählen und einen passenden Namen vergeben.

image

Bisher wurde das Klassenmodell für unsere edmx Datei direkt unterhalb der edmx Datei dargestellt. Wenn wir aber den “DbContext” nutzen, wird die Standardmäßige Codegenerierung ausgestellt und erfolgt dann unter den neuen “tt” Dateien und ist nicht mehr nur eine Datei wie im Beispiel die “EFModel.Designer.cs” sondern jede Klasse bekommt eine eigene Datei.

image

Wichtig zu wissen ist, das beim “DbContext” kein Connectionstring mehr an den Konstruktor übergeben wird, sondern der “DbContext”, sucht selbst nach dem passenden Connectionstring, anhand des Modelnamens. In meinem Beispiel wird nach einem Connectionstring mit dem Namen “EFModelContainer” entweder in der “App.config” oder bei Webanwendungen in der “Web.config” gesucht. Ich habe es leider nicht hinbekommen, das ich einen Connectionstring übergeben konnte, wenn jemand weiß wie das geht würde ich mich über einen Kommentar freuen :-). (Problem konnte ich hier lösen)

image

image

Am EF4.1 mit “DbContext” gefällt mir sehr gut, dass ich die Möglichkeit habe direkt nach dem Primärschlüssel einer Klasse zu suchen mit der “Find(id)” Methode, diese Möglichkeit bestand im EF4.0 nicht nativ und musste mittels Extension selbst geschrieben werden. Wenn man von einer Basisklasse im Modell ableitet, dann sieht ein Find z.B. Folgendermaßen aus:

image

EFPerson leitet von EFBase ab und EFBase enthält den Primärschlüssel die “id”. Um jetzt eine Person, von der man nur die Id kennt zu laden reicht folgendes Statement aus, das man ein Find über die EFBaseMenge ausführt und dann das ganze auf die EFPerson Castet. Funktioniert einwandfrei.

image

Dann gab es noch einen Guten Artikel in der .Net Pro 10.2011 – “Laden ohne Ballast” (auf Seite 92) von Dr. Holger Schwichtenberg über das No-Tracking im EF und das lässt sich mittelt EF4.1 auch sehr einfach abschalten.

image

Wenn das Tracking für eine Abfrage ausgeschalten wurde, dann bedeutet dies, wenn Änderungen in den Daten gemacht werden, dann werden diese beim nächsten Speichern nicht erkannt und somit auch nicht mit abgespeichert. Aber wenn man z.B. nur eine Liste mit Daten anzeigen möchte, dann kommt es bei ausgeschaltetem Tracking zu erheblichen Performance gewinnen. (Sehr schön nachzulesen in der .Net Pro)

Weitere Quellen und Interessante Links:

EF4.1 Download bei MS

Erstellen eines DbContextes mit EF4.1

EF4.1 Beispiele