Entity Framework 4 – Benutzerdefinierte Eigenschaften mit dem ADO.NET Entity Data Model Designer Extension Starter Kit erstellen
Im Beitrag habe ich ein Anwendungsszenario für das ADO.NET Entity Data Model Extension Starter Kit beschrieben, wenn es um konkurrenzierende Richtlinien geht. Ein weiteres Anwendungsszenario für dieses Starter Kit ist die Erweiterung der Eigenschaften für die Entitäten und deren Eigenschaften mit eigenen Attributen, die mit Hilfe von MEF in das Eigenschaftsfenster (Propertygrid) eingebunden werden können.
Wenn eine Entität den Fokus hat, so sieht das Eigenschaftsfenster wie in nachfolgender Abbildung aus:
Da das Entity Framework mit der Version 4 die Unterstützung für eigene Codegenerierungsvorlagen bietet, wäre es wünschenswert mit ein paar wenigen Angaben noch ein bisschen mehr zu generieren als den Kontext, die Klassen und Schnittstellen für ein verbessertes Testen.
Der Designer von EF ist zwar nicht so mächtig, aber mit einem pragmatischen Ansatz kommt man hier auch erstaunlich gut vorwärts. In meinen Szenario würden schon ein paar weitere Angaben auf Entitätsebene reichen, um bspw. die Repository-Definitionen zu generieren.
Szenario
Also beginnen wir mit einem kleinen Fallbeispiel. Ich habe eine Legacy-Datenbank, die alle Anforderungen erfüllt und weder migriert bzw. für das O/RM angepasst werden darf. Diese Datenbank verwendet Präfixe, die mit dem Starter Kit für das Model beseitigt werden. Für einzelne Entitäten möchte ich Informationen hinterlegen, die mir als Einstiegspunkte dienen, um die Repositories generieren zu können.
Dazu erstelle ich mithilfe der Vorlage ADO.NET Entity Data Model Designer Extension Starter Kit eine neue Erweiterung und nutze die Beispielklassen MyNewProperty.cs und MyNewPropertyFactory.cs als Vorlage meiner Erweiterung, alles andere wird entfernt. Bezogen auf diese Vorlagen lege ich nun folgenden Code an:
RepositoryProperties.cs basierend auf MyNewProperty.cs (C#)
namespace EdmxExtension.RepositoryProperties
{
using System;
using System.ComponentModel;
using System.Xml.Linq;
using Microsoft.Data.Entity.Design.Extensibility;
class RepositoryProperties
{
private XElement parent;
private PropertyExtensionContext context;
public RepositoryProperties(XElement parent, PropertyExtensionContext context)
{
this.context = context;
this.parent = parent;
}
[DisplayName("Repository Einstiegspunkt")]
[Description("Mit dieser Eigenschaft kann definiert werden, dass ein Repository erstellt werden soll.")]
[Category(RepositorySettings.Category)]
[DefaultValue(false)]
public bool IsRepositoryEntryPoint
{
get
{
string elementValue = EdmxHelper.GetEdmxElementValue(
this.parent,
"IsRepositoryEntryPoint",
RepositorySettings.Namespace);
bool value;
if (bool.TryParse(elementValue, out value))
{
return value;
}
return false;
}
set
{
EdmxHelper.SetEdmxElementValue(
this.parent,
"IsRepositoryEntryPoint",
RepositorySettings.Namespace,
value.ToString(),
this.context);
}
}
[DisplayName("Repository Namensraum")]
[Description("Mit dieser Eigenschaft kann definiert werden, ob ein benutzerdefinierter Namensraum bzw. Zusatz verwendet werden soll.")]
[Category(RepositorySettings.Category)]
[DefaultValue("")]
public string RepositoryNamespace
{
get
{
return EdmxHelper.GetEdmxElementValue(
this.parent,
"RepositoryNamespace",
RepositorySettings.Namespace);
}
set
{
EdmxHelper.SetEdmxElementValue(
this.parent,
"RepositoryNamespace",
RepositorySettings.Namespace,
value,
this.context);
}
}
[DisplayName("Löschbar")]
[Description("Mit dieser Eigenschaft kann definiert werden, ob das löschen möglich sein soll. Wenn die Inaktiv-Eigenschaft abgefüllt wird, kann ein Update stattdessen ausgeführt werden, anderfalls ein Exception")]
[Category(RepositorySettings.Category)]
[DefaultValue(true)]
public bool IsDeleteable
{
get
{
string elementValue = EdmxHelper.GetEdmxElementValue(this.parent, "IsDeleteable", RepositorySettings.Namespace);
bool value;
if (bool.TryParse(elementValue, out value))
{
return value;
}
return false;
}
set
{
EdmxHelper.SetEdmxElementValue(this.parent, "IsDeleteable", RepositorySettings.Namespace, value.ToString(), this.context);
}
}
[DisplayName("Inaktiv-Eigenschaft")]
[Description("Angabe der Eigenschaft, die zur Inaktivsetzung verwendet wird.")]
[Category(RepositorySettings.Category)]
[DefaultValue("")]
public string InactiveProperty
{
get
{
return EdmxHelper.GetEdmxElementValue(
this.parent,
"InactiveProperty",
RepositorySettings.Namespace);
}
set
{
EdmxHelper.SetEdmxElementValue(
this.parent,
"InactiveProperty",
RepositorySettings.Namespace,
value,
this.context);
}
}
[DisplayName("Leserechte")]
[Description("Angabe, welche Rollen Leserechte besitzen")]
[Category(RepositorySettings.Category)]
[DefaultValue("")]
public string ReadRights
{
get
{
return EdmxHelper.GetEdmxElementValue(
this.parent,
"ReadRights",
RepositorySettings.Namespace);
}
set
{
EdmxHelper.SetEdmxElementValue(
this.parent,
"ReadRights",
RepositorySettings.Namespace,
value,
this.context);
}
}
[DisplayName("Schreibrechte")]
[Description("Angabe, welche Rollen create, update und delete Rechte besitzen.")]
[Category(RepositorySettings.Category)]
[DefaultValue("")]
public string CUDRights
{
get
{
return EdmxHelper.GetEdmxElementValue(
this.parent,
"CUDRights",
RepositorySettings.Namespace);
}
set
{
EdmxHelper.SetEdmxElementValue(
this.parent,
"CUDRights",
RepositorySettings.Namespace,
value,
this.context);
}
}
}
}
Mit dieser Klasse werden die Eigenschaften für das Propertygrid festgelegt. Das setzen der Werte im XML habe ich in eine Helper-Klasse ausgelagert. Hierbei handelt es sich um die Zuweisung der Getter- und Setter aus dem Beispielcode der Datei MyNewProperty.cs. Es werden Anzeigename, die Beschreibung, die Kategorie und der Defaultwert attributiert. Anschliessend wird die Factory-Klasse angelegt, die das Interface IEntityDesignerExtendedProperty aus dem Namensraum Microsoft.Data.Entity.Design.Extensibility implementieren muss.
RepositoryPropertiesFactory.cs basierend auf MyNewPropertyFactory.cs (c#)
namespace EdmxExtension.RepositoryProperties
{
using System;
using System.ComponentModel.Composition;
using System.Xml.Linq;
using Microsoft.Data.Entity.Design.Extensibility;
[PartCreationPolicy(CreationPolicy.Shared)]
[Export(typeof(IEntityDesignerExtendedProperty))]
[EntityDesignerExtendedProperty(EntityDesignerSelection.ConceptualModelEntityType)]
class RepositoryPropertiesFactory
: IEntityDesignerExtendedProperty
{
public object CreateProperty(XElement element, PropertyExtensionContext context)
{
return new RepositoryProperties(element, context);
}
}
}
Mit dieser Schnittstelle muss die Methode CreateProperty belebt werden. Innerhalb dieser Methode wird eine Instanz der Klasse erzeugt, die meine benutzerdefinierten Eigenschaften enthält und zurückgegeben.
Um das Ganze zu ermöglichen, sind die MEF-typischen Attribute PartCreationPolicy und Export notwendig. Mit dem Attribut EntityDesignerExtendedProperty lässt sich steuern, auf welcher Ebene das Propertygrid die benutzerdefinierten Eigenschaften anzeigen soll. In meinen Fall soll das auf Ebene der Entität sein und die Enum-Eigenschaft dafür heisst EntityDesignerSelection.ConceptualModelEntityType. Es ist auch möglich die Eigenschaften auf Ebene der Properties oder Funktionen zu definieren.
Nach dem erfolgreichen Kompilieren der Erweiterung kann diese installiert werden. Das Nachteilige an den Visual Studio Erweiterungen ist der Neustart, falls eine VS Instanz geöffnet ist.
Innerhalb des EF Model Designer für Entitäten stehen nun die benutzerdefinierten Eigenschaften zur Verfügung.
Diese Eigenschaften reichen mir für dieses Projekt aus, um die Repositories, später auch die Präsentationsmuster mit den notwendigen Methoden und Schnittstellen zu definieren. Es ist zwar sehr einfach gehalten und es lassen sich durchaus komplexere Eigenschaften und Masken definieren, aber das ist dann eine Frage des Aufwands.
Wie diese Eigenschaften in einer T4-Vorlage genutzt werden können, beschreibe ich im nächsten Beitrag. ;-)
- 0 Kommentar(e)




Mein Kommentar