Erweiterung zum Entity Framework CTP5 (EF Persistence Framework)

Wer OO-Programmierer mit Leib und Seele ist, wird wahrscheinlich schon von der neuen CTP 5 des Entity Framework gehört haben. Das Zauberwort lautet code-only. Man kann also einfach mit seinem Modell anfangen, so wie man es auch OOP-Entwickler gewohnt ist. Mit ein paar Annotationen oder Beachtung einiger Konventionen kommt man sehr schnell zu einem schönen Modell. Nun fehlt noch ein Kontext, der sich einfach von der Klasse DbContext ableiten lässt und die Sets werden als generische DbSet-Eigenschaften angelegt.
[csharp]
//eine Klasse im Modell
public class Person
{
[ReadOnly(true)]
[Key]
public virtual int PersonId { get; set; }

[DisplayName(„Vorname“)]
[Required]
[StringLength(100)]
public virtual string Firstname { get; set; }

[DisplayName(„Nachname“)]
[Required]
public virtual string Lastname { get; set; }

[Range(1,200)]
public virtual int Nummer { get; set; }
}

//der Kontext
public class PersonContext : DbContext
{
public DbSet Persons { get; set; }
public DbSet Ratings { get; set; }
}
[/csharp]

Soweit erstmal zum Thema EF CTP5. Mehr dazu gibt es im oben angegebenen Blog.
Nun gibt es aber mehr als ein paar Modellklassen und den Kontext. Beim Erstellen von Anwendungen wird man mitbekommen, dass man ein paar Dinge an verschiedenen Stellen wiederholt oder ähnlich macht. Da klingt dem guten Programmierer eigentlich gleich eine Stimme im Ohr. Ich habe auf die Stimme gehört und ein kleines Paket Namens EFPersistenceFramework geschnürt. In dem Paket habe ich versucht, eine Umgebung zum komfortableren Verwendung des EF zur Verfügung zu stellen. Dazu habe ich die Patterns „Repository“ und eine angepasste Version von „Unit of Work“ implementiert.

Hier die Kernpunkte des Frameworks:

  • Bereitstellung der Basisumgebung mittels Interfaces (notwendig für Dependency Injection)
  • Standardrepository mit Implementierung der CRUD-Funktionalität
  • UnitOfWork mit Transaktionssteuerung über TransactionScope
  • Verwendung von Ninject als DI-Container

Möchte man das Framework in eigenen Projekten verwenden, sollte man sich an ein paar Richtlinien halten.
Normalerweise reicht die CRUD-Funktion nicht aus, um alle Anforderung der Geschäftslogik umzusetzen. Man sollte also eine Schnittstelle für die Anforderungen schreiben. In meinem Beispiel nenne ich diese Schnittstelle mal IPersonRepository. Diese erbt von IRepository und wird gleich mit dem entsprechenden Typ der zu verarbeitenden Klasse des Respositories implementiert.
[csharp]
public interface IPersonRepository : IRepository
{
IEnumerable GetNewPersons();
}
[/csharp]

Dann wird noch eine Implementierung der Schnittstelle benötigt. Diese sollte von BaseRespository erben und muss die selbst erstellte Repositoryschnittstelle implementieren. Erbt man hier nicht von BaseRepository hat man einiges mehr zu implementieren, da in dieser abstrakten Basisklasse, wie oben schon erwähnt, alle CRUD-Funktionen schon implementiert sind.
[csharp]
public class PersonRepository : BaseRepository, IPersonRepository
{
public PersonRepository(IUnitOfWork unitOfWork)
: base(unitOfWork)
{
}

public IEnumerable GetPersons()
{
return GetMany(person => person.Nummer > 100);
}
}
[/csharp]

Wie man sieht, wird im Konstruktor ein IUnitOfWork-Objekt erwartet. Dieses kann man mittels IoC auflösen. Dazu aber später mehr.
Im Framework ist eine UnitOfWork-Klasse enthalten. Diese bietet Transaktionssteuerung über TransactionScope (s.o.). Man kann aber auch eine eigene Implementierung des Interfaces vornehmen und diese verwenden.
[csharp]
public interface IUnitOfWork
{
DbContext Context { get; }
void BeginTransaction();
void BeginTransaction(IsolationLevel isolationLevel, TimeSpan timeout);
void Commit();
void Rollback();
}
[/csharp]

Wenn man nun alles beisammen hat, kann man auch schon mit der Verwendung in der Businesslogik beginnen. In meinem Beispiel verwende ich den Controller als Anlaufstelle für die Geschäftslogik. Normalerweise würde das in einer anderen Schicht erfolgen.
Der Controller bekommt im Konstruktor das Repository übergeben (wieder per DI/IoC). Mehr ist hier eigentlich nicht zu sagen. Die Verwendung vom IRatingRepository im Controller kann außer Acht gelassen werden.
[csharp]
public class PersonController : Controller
{
private IPersonRepository _personRepos;
private IRatingRepository _ratingRepos;

public PersonController(IPersonRepository repository, IRatingRepository ratingRepos)
{
this._personRepos = repository;
this._ratingRepos = ratingRepos;
}

public ActionResult Index()
{
ViewBag.Ratings = _ratingRepos.GetAll().ToList();
return View(_personRepos.GetPersons());
}

public ActionResult Details(int id)
{
var person = _personRepos.GetOne(pers => pers.PersonId == id);
return View(person);
}

public ActionResult Create()
{
return View();
}

[HttpPost]
public ActionResult Create(Person person)
{
try
{
_personRepos.Insert(person);
return RedirectToAction(„Index“);
}
catch
{
return View();
}
}

public ActionResult Edit(int id)
{
var person = _personRepos.GetOne(pers => pers.PersonId == id);
return View(person);
}

[HttpPost]
public ActionResult Edit(int id, Person person)
{
try
{
person.PersonId = id;
_personRepos.UnitOfWork.BeginTransaction();

Rating newRating = new Rating() { Created = DateTime.Now, RateComment = „Der Eintrag“ };
_ratingRepos.Insert(newRating);

_personRepos.Update(person);
_personRepos.UnitOfWork.Commit();

return RedirectToAction(„Index“);
}
catch
{
_personRepos.UnitOfWork.Rollback();
return View();
}
}
}
[/csharp]

Zum Abschluss noch kurz die Konfiguration des IoC-Containers. Bei Ninject wird alles im Code erstellt und dieser spricht eigentlich für sich. Mehr zu Ninject gibt es auch auf Codeplex.
[csharp]
public class ProductionModule : NinjectModule
{
public override void Load()
{
Bind().To().InRequestScope();
Bind().To().InRequestScope();
Bind().To().InRequestScope();
Bind().To().InRequestScope();
}
}
[/csharp]

Das Framework kann von Google Code geladen werden. Für Hinweise und Erweiterungsvorschläge bin ich immer dankbar. Hier ist auch der komplette Quellcode einsehbar.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden /  Ändern )

Google Foto

Du kommentierst mit Deinem Google-Konto. Abmelden /  Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden /  Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden /  Ändern )

Verbinde mit %s