Schlagwort-Archive: EntityFramework

Wohin mit den Queries, Repositories und UnitOfWorks in meinen Anwendungsschichten

Am vergangenen .NET Open Space gab es eine Session namens „Repositories oder ORM“. Den Titel fand ich ein wenig unglücklich gewählt. Nutzt man nämlich einen OR-Mapper, so verwendet man implizit Repositories. Zumindest ist das beim Entity Framework der Fall. Dort entspricht der DbContext nämlich der UnitOfWork und die darauf implementierte generische Set-Methode liefert die Repositories zu allen Datenbank-Entitäten.

Grob gesagt sollte pro Geschäftsvorfall (aus Sicht der UI) ein DbContext erzeugt, darauf alle Änderungen und Abfragen ausgeführt und im Anschluss per Commit in einer Transaktion ausgeführt werden. Die Transaktion findet implizit statt, sodass ihr sicher sein könnte, dass entweder alles in die Datenbank geschrieben wurde oder nichts.

Nun wurde die Frage gestellt wie mit Abfragen zu Verfahren ist: Sollte man in allen Layern auf die Repositories zugreifen und dann per LINQ beliebig filtern? Ich bin der Meinung, dass man dies tunlichst unterlassen sollte. Stattdessen nutze ich in der Regel eine der zwei Möglichkeiten (welche sich beide gut zum Testen eigenen):

  • Eine dedizierte Repository-Implementierung, die intern die Abfragen kapselt. Das könnte zum Beispiel die Klasse ‚Rechnungen‘ mit der Methode ‚AlleMitDatumGrößerAls(DateTime datum)‘  sein. Idealerweise mappt das Repository ‚Rechnungen‘ dabei die Entitäten auf Business Objekte. AutoMapper kann in dem Fall viel Schreibarbeit abnehmen.
  • Sollten mehrere Repositories die gleiche Abfrage verwenden, so bietet es sich an die Query in eine eigene Klasse dafür zu extrahieren. Zum Beispiel die Klasse ‚FindeBenutzerMitId‘, welche im Konstruktor die Id übergeben bekommt. Auf der UnitOfWork lässt sich dann die Query z.B. in der Form ausführen: ‚uow.ExecuteQuery(new FindeBenutzerMitId(1))‘. Dafür muss allerdings erst die UnitOfWork um einen solchen Mechanismus erweitert werden. Ich hatte diesen Ansatz vor ein paar Jahren in diesem Video beschrieben.

Leider ist ein Blog Beitrag ungeeignet um ein solch komplexes Thema in der notwendigen Tiefe zu besprechen. Ziel war es daher nur Denkanstöße zu geben und eine Diskussion zu starten. Wer nützliche Ressourcen zu dem Thema hat oder wer andere Ansätze verfolgt, kann diese gerne in die Kommentare schreiben.

Werbung

Testen von Datenbankabfragen – Es geht auch einfach

In diesem Screencast zeige ich wie selbst Abfragen mit mehreren Bedingungen und Sortierungen einfach getestet werden können. Unter der Haube kommt das Entity Framework zum Einsatz. Davon kriegt man dank Dependency Injection aber nichts mit.

Die erwähnte Webcast Serie, sowie weiteren Beispielcode und das Projekt auf Github gibt es hier. Feedback oder Fragen nehme ich gerne entgegen. Wenn ihr wollt, dass ich die Serie fortsetze, sprecht mich über einen Kanal eurer Wahl an.

NuGet Packages auf neue NET Version aktualisieren

Aktuell migrieren wir unsere Projekte / Solutions von .NET Framework 4.0 auf 4.5. Die Herausforderung bestand darin alle NuGet Packages, von denen wir viele verwenden, umzustellen. Ein Blick in die packages.config verrät, dass die installierten Pakete immer gegen eine entsprechende .NET Version gebunden sind:

   1: <?xml version="1.0" encoding="utf-8"?>

   2: <packages>

   3:   <package id="EntityFramework" version="5.0.0" targetFramework="net40" />

   4: </packages>

 

Dies lässt sich auch in den zugehörigen Assemblies ablesen:

image

Unter Path wird in diesem .NET 4.0 Projekt beispielsweise auf “packages\EntityFramework.5.0.0\lib\net40” verwiesen. Darüber ist es beim EntityFramework Package so, dass die Installation der aktuellen 5er Version in obigem Projekt lediglich die 4.4er Version referenziert wird. In der packages.config steht allerdings weiterhin 5.0.0.

Nach der Änderung des Target Frameworks auf 4.5, ergibt sich folgende Zeile in der packages.config:

   1: <package id="EntityFramework" version="5.0.0" targetFramework="net40" requireReinstallation="True" />

 

Der requiresReinstallations-Schalter bewirkt Warnungen beim Build. Um nun nicht alle packages von Hand aktualisieren zu müssen, gibt es den Befehl

update-package -reinstall

welcher die komplette Solution samt aller Projekte durchgeht und alle Packages deinstalliert und danach wieder gemäß dem gesetzten Target Framework installiert.

Entity Framework Webcasts – Testing

Im siebten Teil meiner Entity Framework Webcast Serie widme ich mich dem Thema Testing. Zunächst nehmen wir uns die fertigen Queries vor, welche sich auch gut ohne Datenbank testen lassen. Schließlich will nicht jeder zum Testen einer Abfrage immer eine entsprechende Datenbank hochfahren und die Daten dafür erzeugen.

Danach stellen wir in einem Mini-Business-Pseudo-Layer ein Szenario nach, welches uns dedizierte Daten für die weitere Verarbeitung liefert, sodass das Testen der Geschäftslogik ebenfalls ohne Datenbank erfolgen kann.

Als BDD Framework für die Unit Tests verwende ich Machine.Specifications und als Mocking Framework kommt FakeItEasy zum Einsatz.

Hier noch die zwei Code Beispiele:

 

Beispiel 1: Testen der Query

   1: [Subject(typeof(GetAddressByCity))]

   2: public class When_addresses_contains_exactly_one_matching_entry

   3: {

   4:     static GetAddressByCity Sut;

   5:     static List<Address> Addresses;

   6:     static Address Actual;

   7:     static Address TestCity;

   8:  

   9:     Establish context = () =>

  10:     {

  11:         TestCity = new Address {City = "test city"};

  12:         Addresses = new List<Address>

  13:         {

  14:             new Address {City = "Karlsruhe"},

  15:             TestCity

  16:         };

  17:         Sut = new GetAddressByCity("test city");

  18:     };

  19:  

  20:     Because of = () =>

  21:     {

  22:         Actual = Sut.Execute(Addresses.AsQueryable());

  23:     };

  24:  

  25:     It should_return_exactly_this_address = () => 

  26:             Actual.ShouldEqual(TestCity);

  27: }

 

Beispiel 2: Testen von Geschäftslogik

   1: class EmployeeBusinessLogicSpecs

   2: {

   3:     [Subject(typeof(EmployeeBusinessLogic))]

   4:     class When_hire_date_is_unknown

   5:     {

   6:         static IUnitOfWork Uow;

   7:         static EmployeeBusinessLogic Sut;

   8:         static Employee DummyEmployee;

   9:  

  10:         Establish context = () =>

  11:         {

  12:             DummyEmployee = new Employee {EmployeeID = 1, 

  13:                     FirstName = "Uli", LastName = "Armbruster"};

  14:  

  15:             Uow = A.Fake<IUnitOfWork>();

  16:  

  17:             A

  18:                 .CallTo(() => Uow.ExecuteQuery(

  19:                     A<GetEmployeeById>

  20:                     .That

  21:                     .Matches(q => q.EmployeeId == DummyEmployee.EmployeeID)

  22:                                   ))

  23:                 .Returns(DummyEmployee);

  24:  

  25:             Sut = new EmployeeBusinessLogic(Uow);

  26:         };

  27:  

  28:         Because of = () => Sut.EnsureValidHireDate(DummyEmployee.EmployeeID);

  29:  

  30:         It should_update_it = () => DummyEmployee.HireDate.ShouldNotBeNull();

  31:  

  32:         It should_save_the_changed_hire_date = () => A

  33:             .CallTo(() => Uow.Commit())

  34:             .MustHaveHappened(Repeated.Exactly.Once);

  35:     }

  36: }

 

 

Weitere Quellen:

Entity Framework Webcasts – Stored Procedures und SQL Queries

Im sechsten Teil meiner Entity Framework Webcast Serie zeige ich, wie die IUnitOfWork erweitert werden muss, um SQL Abfragen und Befehle auszuführen. Im Fokus stehen Value Functions und Stored Procedures.

 

 

Dafür stellt das Entity Framework 2 Schnittstellen bereit:

Ein wichtiger Hinweis an dieser Stelle, welcher eingehender in meinem Webcast erwähnt wird: Zum Ausführen einer SQL Query existieren 2 Implementierungen:

Der wesentliche Unterschied besteht darin, dass im ersten Fall die zurückgegebenen Entitäten nicht getrackt werden, im zweiten hingegen schon. Das ist leicht nachvollziehbar, so muss bei DbContext.Database.SqlQuery ein generischer Parameter, der den Rückgabewert darstellt, übergeben werden. Im Falle von DbSet.SqlQuery ist dies nicht nötig, schließlich befindet man sich zum Zeitpunkt der Abfrage auf einem konkreten Set an Entitäten, die über den Context aufgelöst wurden.

Meine vorgestellte Lösung geht den gleichen Weg wie die in Teil 3 beschriebenen Queries, d.h. es werden quasi SQL Query und Command Repositories angelegt, um einer Streuung innerhalb der Anwendungslandschaft entgegenzuwirken.

Schließlich noch zwei Sicherheitshinweise:

  • Die SQL Befehle werden mit den Berechtigungen des Contexts bzw. des darunterliegenden Sicherheitstoken ausgeführt
  • Beim Erstellen des SQL Strings sollte aus Sicherheitsgründen gegenüber Injections mit String.Format() oder alternativ mit SqlParameters gearbeitet werden.

 

I wrote this blog post as an answer of a question from the Netherlands, so I want to complete this article with a quote:

“When you execute a SqlQuery from Database, the results are never tracked by the context, even if the query returns types that are in the model an known by the context. If you do not want the results to be changed-tracked, use DbContext.Databae.SqlQuery.

Results of a DbSet.SqlQuery will be tracked by the context. Ensuring that results are change-tracked is the primary reason you would choose to use DbSet.SqlQuery over Database.SqlQuery.” (Programming Entity Framework, Page 226)

 

Weitere Quellen:

Entity Framework Webcasts Serie

In den vergangenen Tagen gingen Teil 4 und 5 meiner Entity Framework Webcast Serie online. Außerdem ist geplant in den kommenden Tagen noch Teil 6 zu veröffentlichen. Das liegt darin begründet, dass ich verstärkt per E-Mail adressiert mit der Bitte wurde, die restlichen Teile nachzuschieben. Außerdem erhielt ich mehrfach Anfragen bzgl. diverser Schwerpunkte, die ich in zukünftigen Beiträgen aufgreifen möge. Den Bitten werde ich so weit mir möglich nachkommen.

Deshalb an dieser Stelle nochmal der Hinweis, dass man mich über die üblichen Kommunikationskanäle gerne ansprechen darf. Das ein oder andere Feedback hilft mir auch dabei mich immer wieder aufzurappeln und in der Freizeit derartigen Inhalt zu produzieren.

 

Hier ein kurzer Auszug aus einer Mail, die ich gerade erst vergangene Woche erhalten habe:

Hallo Herr Armbruster,

erstmal kompliment für die 3 m.E. sehr guten Videos. Gerne hätte ich noch mehr bzgl. Entity Framework gesehen. Die Art und Weise wie die Thematik erläutert wird, ist Ihnen sehr gut gelungen.

Schön wäre z.B. noch:

– Genaue Erläuterung der Konfigurationsdatei

– WCF in Verbindung mit EF

– Architekturgestaltung bis hin zur UI

Die Gestaltung und Auslagerung der Queries im Teil3 ist für mich ein riesen Lernfortschritt gewesen. Probleme habe ich jetzt bekommen, als ich die Abfrage, also eine IQuery über einen WCF Dienst laufen lassen wollte. Dieser akzeptiert ja soweit mir bekannt ist, keine generischen Typen.

Können Sie hierzu eine kurze Info geben, wie dies realisiert werden kann?

 

 

Aktuell stehen folgende 5 Webcasts in Full HD Auflösung bereit:

  • Teil 1 – DbContext und POCOS: Wir wechselt man auf den neuen DbContext des Entity Framework 5 und erzeugt sich POCO Objekte frei von jeglichen Technologie-spezifischen Referenzen.
  • Teil 2 – UnitOfWork und IoC: Lose Kopplung ist in diesem Beitrag das Ziel. Der Client verliert vollständig seine Abhängigkeit zum Entity Framework bzw. zur Persistenzschicht.
  • Teil 3 – Queries: Hier zeige ich wie sich ein Query Repository aufbauen und Redundanz vermeiden lässt.
  • Teil 4 – Eigener Kontext: Dieses Video erläutert die Implementierung eines eigenen Kontexts.
  • Teil 5 – Weitere Datenbanken: Der nächste wichtiger Schritt ist das Einbinden weiterer Datenbanken. Der Vorzug hierbei ist die implizite Auflösung des entsprechenden Modells auf Basis von Konventionen.
  • Teil 6 – Stored Procedures und SQL Queries: In Anlehnung an Teil 3 baue ich hier ein entsprechendes Repository für reine SQL Abfragen und Stored Procedures auf.

Der vollständige Quellcode steht in dem Repository ‘MyCleanEntityFramework’ in meinem Github Account zur Verfügung.

Entity Framework Webcasts – UnitOfWork und IoC

 

Ich habe auf YouTube mit einer Webcast Reihe zum Entity Framework begonnen. Ich stelle dabei einen Lösungsansatz vor, der zum Ziel hat das EF in der Anwendungsarchitektur als Persistenzschicht derart zu integrieren, dass Unit und Integration Tests einfach zu implementieren sind. Darüber hinaus soll der Ansatz gängige Prinzipien wie Inversion of Control (IoC), Single Responsibility Principle (SRP), Open-Closed-Principle (OCP) umsetzen, sowie die Abhängigkeiten zum Framework minimieren.

In diesem zweiten Teil zeige ich wie man durch den Einsatz eines IoC-Containers eine direkte Abhängigkeit zur Persistenzschicht vermeiden kann. Darüber hinaus setze ich hier bereits das Konzept einer UnitOfWork ein, welche ich in einem folgenden Teil näher erläutern werde.

Quellen:

Entity Framework Webcasts – DbContext und POCOs

Ich habe auf YouTube mit einer Webcast Reihe zum Entity Framework begonnen. Ich stelle dabei einen Lösungsansatz vor, der zum Ziel hat das EF in der Anwendungsarchitektur als Persistenzschicht derart zu integrieren, dass Unit und Integration Tests einfach zu implementieren sind. Darüber hinaus soll der Ansatz gängige Prinzipien wie Inversion of Control (IoC), Single Responsibility Principle (SRP),  Open-Closed-Principle (OCP) umsetzen, sowie die Abhängigkeiten zum Framework minimieren.

In diesem ersten Webcasts zeige ich wie man die Entitäten als POCOs in einer separaten Assembly ablegt. Obwohl für diesen Zweck nicht notwendig, verwende ich den neuen DbContext, welcher in Version 5 des Entity Frameworks Einzug halten wird.

 

Quellen:

Entity Framework – Nullable Bug

As I experienced today, there’s a bug in the Entity Framework. Let’s have a look at the following scenario:

Screenshot 1

image

 

Screenshot 2

image

 

In Screenshot 1 you can see a table with two datetime columns. One is marked as nullable whereas the other is not. I used Database First approach to generate the Entity Data Model. Screenshot 2 shows you the auto-generated Entity. Take a look at the following properties:

imageimage

Notice the highlighted setting “Nullable” of the generated property “MyNullableDateTime”: It’s none instead of what it should be – true. Nevertheless, the generated code is all fine:

   1: [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=true)]

   2: [DataMemberAttribute()]

   3: public Nullable<global::System.DateTime> MyNullableDateTime

   4: {

   5:     get

   6:     {

   7:         return _MyNullableDateTime;

   8:     }

   9:     set

  10:     {

  11:         OnMyNullableDateTimeChanging(value);

  12:         ReportPropertyChanging("MyNullableDateTime");

  13:         _MyNullableDateTime = StructuralObject.SetValidValue(value);

  14:         ReportPropertyChanged("MyNullableDateTime");

  15:         OnMyNullableDateTimeChanged();

  16:     }

  17: }

  18: private Nullable<global::System.DateTime> _MyNullableDateTime;

Unfortunately, if you change the column in the database to be no longer nullable and furthermore, you refresh your model, this will cause a build error:

>>Error 3031: Problem in mapping fragments starting at line 51:Non-nullable column >>DateTimeTest.MyNullableDateTime in table DateTimeTest is mapped to a nullable entity property.

Entity Framework FAQs

Da ich mich in Zukunft noch mehr mit dem Thema beschäftigen und in Folge dessen auch vermehrt Content publishen will, sammle ich die am häufigsten gestellten Fragen. Hier ist mein bisheriger Stand.

  • Versionierung: Down- und Upgrades des DB Schemata und das Deployen beim Kunden
  • Stored Procedures: Viele berichten, sie hätten von Problemen diesbezüglich gehört. Was geht denn nun tatsächlich und was nicht
  • Validation
  • Rights Management
  • Performance: Worauf muss ich achten, was sind bekannte Schwächen
  • Unit Tests / Integration Tests: Hier geht es v.a. um die architektonische Implementierung, sowie das Erzeugen von Seed Daten
  • Architektur: Wie implementiere ich das Entity Framework sinnvoll in meine Anwendungsarchitektur
  • Code First: Was ist bereits möglich, wie kann ich eingreifen
  • Interceptors / Extensions: Typischerweise wollen die meisten hier wissen, wie man bei einer Änderung der Entität Logik auslösen kann. Oftmals sollen in dieser Logik die restlichen Entitäten des Models verändert werden
  • Anzeige der Daten / Binding

Falls ein Punkt noch nicht angesprochen wurde oder ihr spezifische Fragen habt, dann schickt mir diese bitte über die gängigen Kommunikationskanäle wie Emails, Tweets oder Facebooks Messages.

Fake Implementation of IDbSet

   1: public class FakeDbSet<T> : IDbSet<T> where T:class 

   2:  {

   3:      readonly HashSet<T> _set;

   4:      private readonly IQueryable<T> _queryableSet; 

   5:  

   6:      public FakeDbSet()

   7:      {

   8:          _set = new HashSet<T>();

   9:          _queryableSet = _set.AsQueryable();

  10:      } 

  11:  

  12:      public IEnumerator<T> GetEnumerator()

  13:      {

  14:          return _set.GetEnumerator();

  15:      }

  16:  

  17:      IEnumerator IEnumerable.GetEnumerator()

  18:      {

  19:          return GetEnumerator();

  20:      }

  21:  

  22:      public Expression Expression

  23:      {

  24:          get { return _queryableSet.Expression; }

  25:      }

  26:  

  27:      public Type ElementType

  28:      {

  29:          get { return _queryableSet.GetType(); }

  30:      }

  31:  

  32:      public IQueryProvider Provider

  33:      {

  34:          get { return _queryableSet.Provider; }

  35:      }

  36:  

  37:      public T Find(params object[] keyValues)

  38:      {

  39:          throw new NotImplementedException();

  40:      }

  41:  

  42:      public T Add(T entity)

  43:      {

  44:          _set.Add(entity);

  45:          return entity;

  46:      }

  47:  

  48:      public T Remove(T entity)

  49:      {

  50:          _set.Remove(entity);

  51:          return entity;

  52:      }

  53:  

  54:      public T Attach(T entity)

  55:      {

  56:          _set.Add(entity);

  57:          return entity;

  58:      }

  59:  

  60:      public T Create()

  61:      {

  62:          throw new NotImplementedException();

  63:      }

  64:  

  65:      public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T

  66:      {

  67:          throw new NotImplementedException();

  68:      }

  69:  

  70:      public ObservableCollection<T> Local

  71:      {

  72:          get { throw new NotImplementedException(); }

  73:      }

  74:  }

Smart overriding of the SaveChanges-Method of the EntityFramework

 

As part of our refactoring of the persistence layer implemented with the Entity Framework, we developed an approach for some tricky problems. Although i just discuss one specific scenario, the following solution is probably useful for many different issues:

We are using some objects which won’t be deleted in the classic way by removing them from the underlying database table. Instead, we mark them as deleted, set the purge date and the user who initiated the action. The same procedure exists for creating and modifing objects. You surely can imagine why we are doing this. So how can we achieve this behaviour in a transparent, implicit manner.

Step 1: Set up some interfaces like IHaveCreationDate, IHavePurgeDate and IHaveModificationDate.

   1: public interface IHavePurgeDate

   2: {

   3:     void SetPurgeDate();

   4: }

 

Step 2: Now it depends on your engineering strategy: Code First, Model First or Database First. According to your strategy you might using POCOs or you don’t. First of all, you can implement this solution in every single case, but you might change a little of the implementation. Here’s the way like we are using it with our POCOs (take a look at the ADO.NET Team Blog about the usage of POCOs) automatically generated by the EF:

Auto generated code:

   1: public partial class Worker

   2: {

   3:     public int ID_Bearbeiter { get; set; }

   4:     //some properties

   5:     public int deleted_from { get; set; }

   6:     public Nullable<System.DateTime> purge_date { get; set; }

   7:     public bool isDeleted { get; set; }

   8: }

 

Second part of the partial worker implementation in a separate file (don’t adapt the auto generated class!)

   1: public partial class worker : IHavePurgeDate 

   2: {

   3:     public void SetPurgeDate()

   4:     {

   5:         isDeleted = true;

   6:         purge_Date = DateTime.Now;

   7:         deleted_from = currentUser.ID;

   8:     }

   9: }

If you use Code First engineering, you won’t need a seperate file – of course!

 

Step 3: Build your own context or extend the existing one

If you use the auto generated context of the EF, you will have to create one more partial class to extend this context. If you use you’re own implementation, you can add the following code directly to your class:

   1: public override int SaveChanges()

   2: {

   3:     HandleEntries(ChangeTracker.Entries().ToList());

   4:     return base.SaveChanges();

   5: }

   6:  

   7: internal void HandleEntries(IList<DbEntityEntry> entries)

   8: {

   9:     HandleModifiedEntries(entries);

  10:  

  11:     HandleDeletedEntries(entries);

  12:  

  13:     HandleAddedEntries(entries);

  14: }

  15:  

  16: internal void HandleAddedEntries(IEnumerable<DbEntityEntry> entries)

  17: {

  18:     var createdEntities = entries

  19:         .Where(e => e.State == EntityState.Added)

  20:         .Where(e => e.Entity is IHaveCreationDate)

  21:         .ToList();

  22:  

  23:  

  24:     foreach (var dbEntityEntry in createdEntities)

  25:     {

  26:         ((IHaveCreationDate) dbEntityEntry.Entity).SetCreationDate();

  27:     }

  28: }

  29:  

  30: internal void HandleDeletedEntries(IEnumerable<DbEntityEntry> entries)

  31: {

  32:     var deletedEntities = entries

  33:         .Where(e => e.State == EntityState.Deleted)

  34:         .Where(e => e.Entity is IHavePurgeDate)

  35:         .ToList();

  36:  

  37:     foreach (var dbEntityEntry in deletedEntities)

  38:     {

  39:         ((IHavePurgeDate) dbEntityEntry.Entity).SetPurgeDate();

  40:         dbEntityEntry.State = EntityState.Modified;

  41:     }

  42: }

  43:  

  44: internal void HandleModifiedEntries(IEnumerable<DbEntityEntry> entries)

  45: {

  46:     var modifiedEntities = entries

  47:         .Where(e => e.State == EntityState.Modified)

  48:         .Where(e => e.Entity is IHaveModificationDate)

  49:         .ToList();

  50:  

  51:     foreach (var dbEntityEntry in modifiedEntities)

  52:     {

  53:         ((IHaveModificationDate) dbEntityEntry.Entity).SetModificationDate();

  54:     }

  55: }

 

That’s all. Be aware of two more things:

  • This code sample works with a DbContext of EF 4.1, but you can also use the ObjectStateManager of the ObjectContext in EF 1.0/4.0.
  • The order of handling modification and creating/deletion depends on your intended use.

Database First Walkthrough with Entity Framework 4.0

This is just a step by step walkthrough for the database approach with the Entity Framework 4.0, which means that i am using the “old” ObjectContext instead of the “new” EF 4.1 DbContext.

Create a new Console Project

image

 

Add a new Item to your project. Take the “ADO.NET Entity Data Model” template. Choose your prefered name. In my case i take myheco because i want to create an model for a database called myheco.

image

 

Choose “Generate from database” in the upcoming window

image

 

Next, choose your connection. In my example, i decide to save the connection settings in the app.config with the name “myhecoEntities”. This will be name for your context in your code if you don’t create your own implementation of the ObjectContext!

image

 

Choose your tables, views and stored Procedures you want to use and set your prefered options

image

 

Take a look at the Solution. It should look like this

image

 

Let’s try to use our OR-Mapper with LINQ-To-Entities

   1: static void Main(string[] args)

   2: {

   3:     using (var context = new myhecoEntities())

   4:     {

   5:         var item = (from users in context.Users

   6:                     where users.Name.StartsWith("Uli")

   7:                     select users).FirstOrDefault();

   8:  

   9:         if (item != null) Console.WriteLine(item.Name);

  10:     }

  11:  

  12:     Console.ReadKey();

  13: }

 

image

 

If you make changes to your Database Schema, you’ll need to update your model. You can do this by opening the Entity Data Model file (edmx) with a double click. Right click on an empty space and choose “Update Model from Database”

image

 

Choose the “Refresh”-Tab on the upcoming dialog and press “Finish”.

image

Now it depends on your database changes you’ve made if your solution still compiles.

Quellen zum Entity Framework Vortrag

Hier ein Überblick zu nützlichen Quellen rund um meinen Vortrag gestern auf der SQLPass und darüber hinaus.

 

Grundsätzlich gibt es folgende 3 Ansätze, um mit dem Entity Framework als OR-Mapper zu entwickeln:

Database First (Reverse Engineering): Aus der vorhandenen Datenbank wird das Entity Data Model (edmx) und der Code (z.B. die Entitäten) generiert. Verfügbar ist der Ansatz seit der ersten Stunde mit Version 1 im .NET Framework 3.5 SP1. Eine Anleitung dazu findet ihr hier.

Model First (Forward Engineering): Eine Anleitung dazu gibt es hier bzw. für Version 4.1 mit dem DbContext hier. Achtung: Out of the box wird keine Unterstützung für DB Versionierung bzw. Migration angeboten. Das erzeugte T-SQL macht immer ein Drop der Daten! Mit VS2010 Premium oder Ultimate gibt es die Funktion “Schema Compare”im Menüpunkt “Data”. Außerdem gibt es noch in der Visual Studio Gallery das “Entity Designer Database Generation Power Pack”, was neue T4-Vorlagen für besagtes Problem mitbringt. Keine der beiden Möglichkeiten wurden von mir vorgestellt oder bisher selbst getestet! Model First ist seit NET Framework 4 verfügbar (entspricht auch Entity Framework Version 4).

Code First: Ist seit Version 4.1 des Entity Frameworks verfügbar, welches aktuell über NuGet bezogen werden kann. 4.1 wurde zwar als Go Live Version mit Support veröffentlicht, allerdings haben sich Bugs eingeschlichen. Darüber hinaus fehlt es noch am ein oder anderen Ende. Folgende 2 Artikel bieten die wichtigsten Informationen: Artikel 1 und Artikel 2. Weiterführende Links sind noch die Möglichkeit zum Überschreiben der Konventionen im Code oder per Annotationen. Den Walkthrough für Code First gibt es hier. Achtung: Zusätzlich zu den ursprünglichen Klassen ObjectContext und ObjectSet (besser IObjectSet verwenden) gibt es jetzt DbContext und DbSet (besser IDbSet verwenden).

 

Es folgen noch ein paar weitere Links zu angesprochenen Themen:

 

Wie man eine testbare und somit gute Architektur basierenden auf dem EF aufbaut, zeigt der folgende, sehr empfehlenswerte Artikel von Scott Allen:

Testability and Entity Framework 4.0

Hierzu werde ich in nächster Zeit noch diverse Blogposts veröffentlichen, da ich Alternativen zu dem ein oder anderen Punkt aufzeigen will. Unter anderem wird das Open Closed Principle in der UnitOfWork verletzt und die Queries werden in der kompletten Anwendungsarchitektur verstreut.

Zu guter Letzt noch für die dotnet Pro Abonnementen unter euch: Im März hat eine 7-teilige Serie über das Entity Framework begonnen, von denen ich v.a. die Teile 1,4 und 5 empfehlen kann. Außerdem hat sich Patrick A. Lorenz in der 7.2011 im Artikel “Erst coden, dann fragen!” mit dem Code First Ansatz befasst.

Inversion of Control and Entity Framework

Today i want to show you my approach to resolve the requested entity framework object context at runtime. Starting point:

  • First, i want to have just one implementation of an IUnitOfWork to resolve any kind of repository like Repository<Order>
  • Second, my unit of work should autodiscover the proper context for my requested type of domain model as i have different databases and object contexts. For example, Order is a domain object of my domain model (namespace Domain.ComWork) according to the context ‘comwork’, which is mapping to the database ‘comwork’. There’s another domain object like Customer in my domain model (namespace Domain.MyHeco) according to the context ‘myheco’, which is mapping to the database ‘myheco’.

So how can i achieve this? I will use an IoC container like Castle.Windsor. You can add it to your Visual Studio project by the nuget command “install-package Castle.Windsor”. I want to mention that this scenario needs a little bit of advanced IoC techniques because the dependent component has to be resolved at runtime. So you’re not able to register a dedicated component for an interface. Instead you’ve got to use a facility. Allright, so we tell the container that we will register a facility:

   1: container.AddFacility<TypedFactoryFacility>();

 

In line 1 we tell the container that it has to load the necessary infrastructure for facilities. Let’s take a look at the registration process. Here’s my method that will return me an IEnumerable of IRegistration.

   1: static IEnumerable<IRegistration> Components()

   2: {

   3:     yield return Component

   4:         .For<IContextFactory>()

   5:         .AsFactory(c => c.SelectedWith<ContextFactoryHandlerSelector>());

   6:     yield return Component

   7:         .For<ContextFactoryHandlerSelector>();

   8:     yield return Component

   9:         .For<IUnitOfWork>()

  10:         .ImplementedBy<UnitOfWork>();

  11:     yield return Component

  12:       .For<ComWorkContext>()

  13:       .Named("comworkcontext");

  14:     yield return Component

  15:       .For<MyHecoContext>()

  16:       .Named("myhecocontext");

  17: }

 

Here’s the good news: Castle Windsor has an out of the box feature that creates you the needed factory. In other words: The container creates automatically the factory. You’re code is unaware of the container. Take a look at this article. If you want to use this you’ve have to follow some conventions. In my approach i decided to develop my own implementation to follow my own convetions. Take a look at line 5.  With “.AsFactory” i tell the container that i don’t register a concrete implementation for that service (IContextFactory). If i would follow the conventions of Castle.Windsor calling AsFactory() would be enough.

Here’s the code for my own implementation of a TypedFactoryComponentSelector (this would be auto generated by using just AsFactory without parameters):

   1: public class ContextFactoryHandlerSelector : ITypedFactoryComponentSelector

   2: {

   3:     public TypedFactoryComponent SelectComponent(MethodInfo method, 

   4:         Type type, object[] arguments)

   5:     {

   6:         var requestFor = method.GetGenericArguments().First();

   7:         

   8:         var @namespace = string.Format("{0}context", 

   9:             requestFor.Namespace.ToLower()

  10:             .Replace("domain.",""));

  11:         

  12:         return new TypedFactoryComponent(@namespace, method.ReturnType, null);

  13:     }

  14: }

 

My convention is pretty simple: The name of my object context is the same as the namespace of my model without the string “domain.” and with “context” appended.

Sample: Full name of my domain object: “domain.comwork.order”

  • use just the namespace –> domain.comwork
  • replace domain –> comwork
  • add context –> comworkcontext

I have two kind of domain models (domain.comwork and domain.myheco) so i need two contexts (comworkcontext and myhecocontext). Every context is registered:

   1: yield return Component

   2:   .For<ComWorkContext>()

   3:   .Named("comworkcontext");

   4: yield return Component

   5:   .For<MyHecoContext>()

   6:   .Named("myhecocontext");

 

Now we’ve got it: We have a facility that invokes a call to my self implemented factory if a service (defined in the interface IContextFactory) is requested. The factory returns the name of the needed context at runtime. My Interface looks like this:

   1: public interface IContextFactory

   2: {

   3:     ObjectContext GetContextFor<T>();

   4: }

And here’s the generic part. If i call GetContextFor<domain.comwork.order> i will get my comworkcontext. If i call GetContextFor<domain.myheco.customer> i will get my myhecocontext.

The last step is to inject the factory in my UnitOfWork. Nothing easier than that:

   1: public class UnitOfWork : IUnitOfWork

   2: {

   3:     private readonly IContextFactory _factory;

   4:  

   5:     public UnitOfWork(IContextFactory factory)

   6:     {

   7:         _factory = factory;

   8:     }

   9:  

  10:     //here you would return an IRepository<T>

  11:     //this is just a sample code

  12:     public void GetRepository<T>() where T : class 

  13:     {

  14:         Console.WriteLine(_factory.GetContextFor<T>().GetContextName());

  15:     }

  16: }

 

And how can i resolve my concrete UnitOfWork in my program?

   1: static class Program

   2: {

   3:     static void Main(string[] args)

   4:     {

   5:             using (var container = new WindsorContainer())

   6:             {

   7:                 container.AddFacility<TypedFactoryFacility>();

   8:                 container.Register(Components().ToArray());

   9:  

10: container.Resolve<IUnitOfWork>()

.GetRepository<Domain.ComWork.Foo>();

11: container.Resolve<IUnitOfWork>()

.GetRepository<Domain.MyHeco.Bar>();

  12:             }

  13:     }

  14:  

  15:     static IEnumerable<IRegistration> Components()

  16:     {

  17:         //Registrations...

  18:     }

  19: }

%d Bloggern gefällt das: