Vergleich Entity Framework Objekte und POCO C# Objekte ...

In meinem letzten Blogeintrag "Einfache C# Objekte (POCO) mit dem Entity Framework lesen ..."  habe ich kurz gezeigt, wie man mit dem Entityframework aus einer SQL Server Datenbank einfache Plain Old CLR Objekte mit den Daten lesen/füllen kann. Zu dem Artikel habe ich eine direkte Frage von einem Leser erhalten, wo genau die Unterschiede zwischen einem POCO Objekt und einer Entity Framework generierten Klasse  liegen.
Nach kurzen Nachdenken über die berechtigte Frage habe ich folgende Themen im inhaltlichen Umfeld dieser Themen erkannt, und möchte mit diesen und den nächsten Artikeln darüber bloggen. Hier sind die Themen, die in Bezug auf diese Objekte interessant sein könnten:

  • Vergleich Entity Framework Objekte und POCO Objekte ...
  • Warum sind Entity Framework Objekte und POCO Objekte in einer Architektur wichtig ...
  • Datenbankunabhängig Entwurf einer Schichtenarchitektur ...

Heute geht's um den Vergleich --> Wenn man den Code eines POCO Object betrachtet, sieht das zum Beispiel so aus:

  public class BeOrder {
  public System.Int32 OrderID { get; set; }
  public System.String CustomerID { get; set; }
  public System.Int32? EmployeeID { get; set; }
  public System.DateTime? OrderDate { get; set; }
  public System.DateTime? RequiredDate { get; set; }
  public System.DateTime? ShippedDate { get; set; }
  public System.Int32? ShipVia { get; set; }
  public System.Decimal? Freight { get; set; }
  public System.String ShipName { get; set; }
  public System.String ShipAddress { get; set; }
  public System.String ShipCity { get; set; }
  public System.String ShipRegion { get; set; }
  public System.String ShipPostalCode { get; set; }
  public System.String ShipCountry { get; set; }
}

oder im Klassendiagramm:

image

Die Eigenschaften sind hier dieser extrem einfachen Klasse sind:

-

die Klasse hat keine Basisklasse

  
  • die Klasse hat keine fachlichen Methoden

  • die Klasse hat nur Daten-Properties

  • die Klasse hat keine Verbindung zum DataContext

  • die Klasse unterstützt einfaches Databinding.

Im Gegensatz dazu sieht der Code einer Entity Framework Object so aus:

 /// <summary>
/// There are no comments for TestDbModel.Orders in the schema.
/// </summary>
/// <KeyProperties>
/// OrderID
/// </KeyProperties>
[global::System.Data.Objects.DataClasses.EdmEntityTypeAttribute(NamespaceName="TestDbModel", Name="Orders")]
[global::System.Runtime.Serialization.DataContractAttribute(IsReference=true)]
[global::System.Serializable()]
public partial class Orders : global::System.Data.Objects.DataClasses.EntityObject
{
/// <summary>
/// Create a new Orders object.
/// </summary>
/// <param name="orderID">Initial value of OrderID.</param>
public static Orders CreateOrders(int orderID)
{
    Orders orders = new Orders();
    orders.OrderID = orderID;
    return orders;
}
/// <summary>
/// There are no comments for Property OrderID in the schema.
/// </summary>
[global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[global::System.Runtime.Serialization.DataMemberAttribute()]
public int OrderID
{
    get
    {
        return this._OrderID;
    }
    set
    {
        this.OnOrderIDChanging(value);
        this.ReportPropertyChanging("OrderID");
        this._OrderID = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);
        this.ReportPropertyChanged("OrderID");
        this.OnOrderIDChanged();
    }
}
private int _OrderID;
partial void OnOrderIDChanging(int value);
partial void OnOrderIDChanged();
/// <summary>
/// There are no comments for Property EmployeeID in the schema.
/// </summary>
[global::System.Data.Objects.DataClasses.EdmScalarPropertyAttribute()]
[global::System.Runtime.Serialization.DataMemberAttribute()]
public global::System.Nullable<int> EmployeeID
{
    get
    {
        return this._EmployeeID;
    }
    set
    {
        this.OnEmployeeIDChanging(value);
        this.ReportPropertyChanging("EmployeeID");
        this._EmployeeID = global::System.Data.Objects.DataClasses.StructuralObject.SetValidValue(value);
        this.ReportPropertyChanged("EmployeeID");
        this.OnEmployeeIDChanged();
    }
}

Die Vererbungshierarchie dieses Entity Framework generierten Object sieht so aus:

image

Die Klasse hat die Basisklasse EntityObject definiert im System.Data.Objects.DataClasses Namespace. Die EntityObjects Klasse hat wiederrum das Klasse StructuralObject ebenfalls definiert in System.Objects.DataClasses als Basisklasse. Die Klasse Orders selbst hat folgendes Diagramm:

image

Die Eigenschaften der Entityframework Klasse sind folgende:

-

die Klasse hat Basisklassen

  
  • die Klasse hat keine fachlichen Methoden

  • die Klasse hat nur Daten-Properties

  • die Klasse kann direkt an den DataContext gekoppelt werden (via context.Attach/Detach)

  • die Klasse unterstützt komplexes Databinding.

Der Vergleich der Codemetric zeigt, dass ein Entity Framework Object etwas komplexer als ein typisches POCO ist.

image

Der Vergleich im Profiler zeigt, das Entity Framework Objekte etwas mehr Speicher als unser Beispiel POCO brauchen (- man kann das aus den Inclusive Bytes / Inclusive Allocations errechnen).

imagen

Wenn ich ein Object in einem Usecase aus der Datenbank lesen und in der Anwendung benutzen und  danach dasselbe Object wieder in die Datenbank speichern möchte, ist das Entity Frameobject empfehlenswert. Da es dieses Szenario sehr effizient durch ein Entity Framework Object unterstützt wird.

Abstracter Beispielcode eines solchen Szenarios:

     EntityKey key;
    object originalItem;

    TestDbEntities nwindDbCtx = new TestDbEntities();
    
    // Data layer - get the customer from DB and detach from DB
    Customers customer = (from c in nwindDbCtx.Customers 
                         where c.CustomerID == "ALFKI" 
                         select c).First();
    nwindDbCtx.Detach( customer );

    // .....
    // .....
    
    // UI layer - modify the entity on screen - assign the entity to an UI element and modify it
    modifyForm.DataContext = customer;
 // ..... 
// ..... 
.csharpcode, .csharpcode pre
{
 font-size: small;
   color: black;
   font-family: consolas, "Courier New", courier, monospace;
   background-color: #ffffff;
  /*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt 
{
   background-color: #f4f4f4;
  width: 100%;
    margin: 0em;
}
.csharpcode .lnum { color: #606060; }
     // Data layer - attach the entity back and save the changes
    key = nwindDbCtx.CreateEntityKey( "Customers", customer );
    if ( nwindDbCtx.TryGetObjectByKey( key, out originalItem ) )
    {
         nwindDbCtx.ApplyPropertyChanges( key.EntitySetName, customer ); 
    }

    nwindDbCtx.SaveChanges();

Anmerkung zum Code: Dadurch, dass die Entityframework Klasse "two-way" Data binding unterstützt, ist eine Rücksyncronsation aus den Eingabefeldern der UI Form nicht notwendig, dass geschieht automatisch.

Wenn man allerdings ein anderes Anwendungszenario hat, bei dem zum Beispiel Daten über eine Serviceschnittstelle ausgetauscht werden, können die einfachen POCO Objekte vorteilhaft sein.

Damit kann man zusammenfassen, dass der Entwurf eines Entity Framework Object ein wenig komplexer als ein generisches CLR Object ist. Allerdings sind die zusätzlichen Methoden für Datenzuweisungen und Databinding-Funktionen optimiert.

Das Entity Framework unterstützt die Zusammenarbeit mit beiden Objekttypen.