Visual Studio

Entity Framework 4.0 und WCF Data Services 4.0 in Visual Studio 2010

Elisa Flasko

Beispielcode herunterladen.

Neben vielen neuen Verbesserungen werden in Visual Studio 2010 die lang erwarteten Versionen Entity Framework 4.0 und WCF Data Services 4.0 (zuvor ADO.NET Data Services) eingeführt, die den Prozess der Datenmodellierung, -verarbeitung und -erstellung vereinfachen.

Entity Framework 4.0 (EF 4.0) soll im Wesentlichen zwei grundlegende Szenarien ermöglichen und vereinfachen: die domänenzentrierte Anwendungsentwicklung und die traditionellen datenzentrierten "Formulare über Daten". Es enthält Features wie die Model-first-Entwicklung, die es ermöglicht, ein Modell zu erstellen und angepasstes T-SQL generieren zu lassen, Unterstützung für Persistenzignoranz, Fremdschlüssel, Lazy Loading und Generierung von benutzerdefiniertem Code für Entitäten.

Im Zentrum von WCF Data Services 4.0 steht das Ermöglichen von Updates des Open Data-Protokolls (odata.org) und seiner neuen Features. Dazu gehören beispielsweise die bidirektionale Datenbindung für Windows Presentation Foundation (WPF) und Silverlight, Zeilenanzahl, servergesteuertes Paging, erweiterte BLOB-Unterstützung (Binary Large Object) und Unterstützung für Projektionen.

Anhand einer Beispiel-Webloganwendung (MyBlog) werde ich die neuen Features in EF und WCF Data Services demonstrieren und erläutern, wie die Technologien gemeinsam die Modellierung und Verarbeitung von Daten vereinfachen. Die Beispielanwendung verfügt über eine ASP.NET-Webanwendung, die eine schreibgeschützte Ansicht von Blogbeiträgen bereitstellt, und einen Silverlight-Blogadministratorclient, mit dem der Blogbesitzer Beiträge bearbeiten kann. Ich beginne nach dem Prinzip "Model First" mit dem Erstellen eines Entity Data Model (EDM) und generiere dann eine Datenbank und den Code zur Interaktion mit der Datenbank. In diesem Beispiel wird auch das ADO.NET Data Services-Update für Silverlight 3 CTP 3 verwendet.

Erste Schritte mit EF 4.0

Ich beginne mit dem ASP.NET-Webanwendungsprojekt. (Die Anwendung hat den Namen BlogModel. Sie können den zugehörigen Code unter code.msdn.microsoft.com/mag201004VSData herunterladen.) Für den Einstieg in EF füge ich mit dem Assistenten zum Hinzufügen eines neuen Elements ein ADO.NET-EDM hinzu und wähle ein leeres Modell aus, das ich ebenfalls BlogModel nenne. Durch Rechtsklicken auf die leere Designer-Oberfläche und Auswählen von Eigenschaften können Sie den Standardnamen für leere Container, in diesem Fall BlogModelContainer, anzeigen. Zuerst ändere ich den Namen in BlogContext, und dann erstelle ich das Modell.

MyBlog erfordert drei Entitäten, Blog, Post und Tag, wie in Abbildung 1 dargestellt. Um diese zu erstellen, ziehe ich eine Entität aus der Toolbox auf die Entwurfsoberfläche, klicke mit der rechten Maustaste und wähle Eigenschaften aus, um die Entitätseigenschaften zu bearbeiten. Außerdem benötige ich einige skalare Eigenschaften für jede Entität (Rechtsklicken auf die Entität und dann Hinzufügen | Skalare Eigenschaft auswählen).

Abbildung: Blog-, Post- und Tag-Entitäten und zugehörige Eigenschafteneinstellungen

Abbildung 1 Blog-, Post- und Tag-Entitäten und zugehörige Eigenschafteneinstellungen

Unterstützung für Fremdschlüssel in EF 4.0

Als Nächstes füge ich Beziehungen zwischen diesen Entitäten hinzu. Klicken Sie mit der rechten Maustaste auf die Entwurfsoberfläche, und wählen Sie wie in Abbildung 2 dargestellt Hinzufügen | Zuordnung aus. EF unterstützt nun Fremdschlüssel, sodass Fremdschlüsseleigenschaften für eine Entität hinzugefügt werden können. Beachten Sie, dass der Post-Entität mit der Beziehung eine BlogBlogID-Eigenschaft (der Fremdschlüssel) hinzugefügt wurde.

Abbildung: Zuordnungen zwischen Blog-, Post- und Tag-Entitäten

Abbildung 2 Zuordnungen zwischen Blog-, Post- und Tag-Entitäten

Der Einschluss von Fremdschlüsseleigenschaften für Entitäten vereinfacht einige wesentliche Kodiermuster, wie Datenbindung, dynamische Daten, Parallelitätssteuerung und n-Schichtenentwicklung. Wenn ich beispielsweise eine Datenbindung für ein Raster mit Produkten einrichte und sich die CategoryID (ein Fremdschlüsselwert) in einem Raster befindet, über dessen entsprechendes Category-Objekt ich nicht verfüge, muss ich aufgrund der Fremdschlüsselunterstützung in EF keine zusätzliche Abfrage mehr ausführen, um das Category-Objekt zurückzuholen.

Model First mit EF 4.0

Nachdem das Modell erstellt wurde (siehe Abbildung 3), benötigt die Anwendung eine Datenbank. In diesem Fall ist MyBlog eine neue Anwendung und verfügt noch nicht über eine Datenbank. Ich möchte die Datenbank nicht selbst erstellen, aber ich kann sie für mich erstellen lassen. Dank "Model First" in EF 4.0 kann Visual Studio nicht nur benutzerdefinierten Code für die Entitäten, sondern auch eine auf dem Modell basierende Datenbank generieren.

Abbildung: Blog-Modell

Abbildung 3 Blog-Modell

Zuerst muss ich die leere Datenbank erstellen, auf die ich das generierte Schema anwenden werde. Dazu öffne ich den Server-Explorer, klicke mit der rechten Maustaste auf den Knoten Datenverbindungen, und wähle Neue SQL Server-Datenbank erstellen aus (siehe Abbildung 4). Nachdem die leere Datenbank erstellt wurde, klicke ich mit der rechten Maustaste auf die Modellentwurfsoberfläche und wähle Generate Database from Model (Datenbank aus Modell generieren) aus. Der Datenbankgenerierungs-Assistent führt schrittweise durch das Erstellen einer Datei BlogModel.edmx.sql. Wenn diese neue Datei geöffnet ist, muss ich lediglich mit der rechten Maustaste auf die Datei klicken und das SQL-Skript ausführen, um das Schema für die Datenbank zu erstellen.

Abbildung: Erstellen einer neuen leeren Datenbank und Generieren eines Datenbankschemas aus EDM

Abbildung 4 Erstellen einer neuen leeren Datenbank und Generieren eines Datenbankschemas aus EDM

Generierung von benutzerdefiniertem Code mit EF 4.0

Hier ist eine Fortsetzung mit verschiedenen Schritten möglich. Einer davon ist die Anpassung des von EF generierten Codes mit T4-Vorlagen. Obwohl EF in Visual Studio 2008 SP1 einige Möglichkeiten zur Anpassung der Codegenerierung bot, war dies relativ unflexibel und schwer handhabbar. EF 4.0 macht Gebrauch von T4-Vorlagen, die eine wesentlich einfachere, flexiblere und leistungsstärkere Möglichkeit zur Anpassung von generiertem Code bieten.

Um dem Projekt eine neue T4-Vorlage hinzuzufügen, klicken Sie mit der rechten Maustaste auf die Entitäts-Designeroberfläche und wählen Add Code Generation Item (Codegenerierungselement hinzufügen) aus. Wählen Sie dann eine beliebige installierte Vorlage als Ausgangspunkt aus, oder zeigen Sie die verfügbaren Vorlagen im Onlinekatalog an. Da ich die EF-Standardvorlage als Ausgangspunkt für das Projekt verwenden möchte, wähle ich die Vorlage ADO.NET EntityObject Generator aus. Die Vorlage hat standardmäßig den Namen Model1.tt. Wenn auf diese Weise eine Codegenerierungsvorlage hinzugefügt wird, wird die Standardcodegenerierung für das Modell automatisch von EF deaktiviert. Der generierte Code wird von BlogModel.Designer.cs entfernt und ist nun in Model1.cs vorhanden. An dieser Stelle kann die Vorlage bearbeitet werden, um die zu generierenden Entitäten anzupassen. Bei jedem Speichern der TT-Datei wird der abhängige Code neu generiert. Weitere Informationen zum Bearbeiten und Verwenden von T4-Vorlagen mit EF 4.0 finden Sie im ADO.NET-Teamblog unter blogs.msdn.com/adonet.

Verwenden von POCO-Entitäten mit EF 4.0

In Visual Studio 2008 SP1 bestanden zahlreiche Einschränkungen für Entitätsklassen, die das Erstellen von Klassen, die tatsächlich unabhängig von Persistenzproblemen waren, äußerst schwierig machten. Eines der am häufigsten gewünschten Features in EF 4.0 ist die Möglichkeit, POCO-Typen (Plain Old CLR Object) für Entitäten zu erstellen, die mit EF funktionsfähig sind und nicht die Einschränkungen wie in Visual Studio 2008 SP1 aufweisen.

Zurück zum MyBlog-Beispiel. Ich werde nun POCO-Objekte für die drei Entitäten Blog, Post und Tag erstellen. Zuerst muss die Codegenerierung deaktiviert werden, und ich muss die im letzten Abschnitt erstellte TT-Datei entfernen. Zur Überprüfung der Eigenschaften des Modells klicken Sie mit der rechten Maustaste auf die Entitäts-Designeroberfläche. Wie in Abbildung 5 dargestellt, muss eine Eigenschaft namens Code Generation Strategy auf None festgelegt werden, um die Codegenerierung zu deaktivieren.

Abbildung: Code Generation Strategy-Eigenschaft

Abbildung 5 Code Generation Strategy-Eigenschaft

Beachten Sie, dass diese Eigenschaft automatisch auf None festgelegt wird, wenn Sie ein Codegenerierungselement (T4-Vorlage) hinzufügen. Ist in das Projekt aktuell eine TT-Datei eingeschlossen, müssen Sie sie vor der Verwendung von POCO-Objekten entfernen. Von hier aus können die Klassen für die POCO-Objekte, Blog.cs, Post.cs und Tag.cs, hinzugefügt werden, wie in den Abbildungen 6, 7 und 8 dargestellt.

Abbildung 6 POCO-Objekt für die Blog-Entität

public class Blog

{

  public intBlogID

  {

    get;

    set;

  }

  public string Name

  {

    get;

    set;

  }

  public string Owner

  {

    get;

    set;

  }

  public List<Post> Posts

  {

    get { return _posts; }

    set { _posts = value; }

  }

  List<Post> _posts = new List<Post>();

}

Abbildung 7 POCO-Objekt für die Tag-Entität

public class Tag

{

  public int TagID

  {

    get;

    set;

  }

  public string Name

  {

    get;

    set;

  }

  public List<Post> Posts

  {

    get { return _posts; }

    set { _posts = value; }

  }

  List<Post> _posts = new List<Post>();

}

Abbildung 8 POCO-Objekt für die Post-Entität

public class Post

{

  public int PostID

  {

    get;

    set;

  }

  public DateTime CreatedDate

  {

    get;

    set;

  }

  public DateTime ModifiedDate

  {

    get;

    set;

  }

  public string Title

  {

    get;

    set;

  }



  public string PostContent

  {

    get;

    set;

  }

  public Blog Blog

  {

    get;

    set;

  }

  public int BlogBlogID

  {

    get;

    set;

  }

  public Boolean Public

  {

    get;

    set;

  }

  public List<Tag> Tags

  {

    get { return _tags; }

    set { _tags = value; }

  }

  private List<Tag> _tags = new List<Tag>();

}

Schließlich muss ich die Kontextklasse erstellen, die stark der Implementierung von ObjectContext ähnelt, die mit der Standardcodegenerierung erstellt wird. Ich werde sie jedoch BlogContext nennen. Sie erbt von der ObjectContext-Klasse. Der Kontext ist die Klasse, die die Persistenz beachtet. Sie ermöglicht die Komposition von Abfragen, Materialisierung von Entitäten und Speicherung von Änderungen in der Datenbank (siehe Abbildung 9).

Abbildung 9 BlogContext

public class BlogContext : ObjectContext 

{

  public BlogContext() 

    : base("name=BlogContext", "BlogContext") 

      { 

      }

  public ObjectSet<Blog> Blogs 

  {

    get 

    {

      if (_Blogs == null) 

      { 

         _Blogs = 

           base.CreateObjectSet<Blog>("Blogs"); 

      }

    return _Blogs; 

  } 

}

private ObjectSet<Blog> _Blogs;

public ObjectSet<Post> Posts 

{

  get 

  {

    if (_Posts == null) 

    { 

      _Posts = 

        base.CreateObjectSet<Post>("Posts"); 

    }

  return _Posts; 

  } 

}

private ObjectSet<Post> _Posts;

public ObjectSet<Tag> Tags 

{

  get 

  {

    if (_Tags == null) 

  { 

    _Tags = base.CreateObjectSet<Tag>("Tags"); 

  }

  return _Tags; 

  } 

}

private ObjectSet<Tag> _Tags; 

}

Lazy Loading

In Visual Studio 2008 SP1 wurden von EF zwei grundlegende Arten zum Laden verwandter Entitäten unterstützt, die beide sicherstellten, dass die Anwendung nur auf ausdrückliche Anweisung auf die Datenbank zugreift. Dabei wurde entweder die Load-Methode zum expliziten Laden der verwandten Entitäten oder die Include-Methode zum vorzeitigen Laden (Eager Loading) verwandter Entitäten in einer Abfrage verwendet. Ein weiteres häufig gefordertes Feature in EF 4.0 ist Lazy Loading. Wenn explizites Laden nicht erforderlich ist, können Sie Lazy Loading verwenden (auch als verzögertes Laden bezeichnet), um verwandte Entitäten zu laden, wenn auf eine Navigationseigenschaft zum ersten Mal zugegriffen wird. In Visual Studio 2010 erfolgt dies, indem die Navigationseigenschaften zu virtuellen Eigenschaften erklärt werden.

Im MyBlog-Beispiel würde die public List<Post> Posts-Eigenschaft in Blog.cs und Tag.cs zu public virtual List<Post> Posts, und die public List<Tag> Tags-Eigenschaft in Post.cs würde zu public virtual List<Tag> Tags. EF würde dann zur Laufzeit einen Proxytyp erstellen, der einen Ladevorgang ausführen kann, sodass keine weiteren Codeänderungen erforderlich sind. Da im MyBlog-Beispiel jedoch mit WCF Data Services Entitäten über einen OData-Dienst (Open Data-Protokoll) verfügbar gemacht werden, macht die Anwendung keinen Gebrauch von Lazy Loading.

Erstellen eines WCF Data Service in Visual Studio 2010

MyBlog nutzt die nahezu gebrauchsfertige Lösung von WCF Data Services, um einen OData-Dienst über ein EDM bereitzustellen, und enthält einen Silverlight-Blogadministratorclient, der den OData-Dienst verwendet. Das Open Data-Protokoll ist ein Datenfreigabestandard, der Silos aufschlüsselt und ein leistungsstarkes, interoperatives Ökosystem für Daten-Consumer (Clients) und -produzenten (Dienste) fördert, sodass mehr Anwendungen einen umfassenderen Satz von Daten sinnvoll nutzen können.

Nach der Einrichtung eines EDM und einer Datenbank ist das Hinzufügen eines neuen WCF Data Service zu der Anwendung problemlos. Ich füge mithilfe des Assistenten zum Hinzufügen eines neuen Elements einen WCF Data Service (namens BlogService) hinzu. Dadurch wird eine Datei BlogService.svc generiert, die das Gerüst des Diensts darstellt und auf das EDM verwiesen wird, indem ihm der zuvor erstellte Kontext zugewiesen wird. Da der Dienst standardmäßig völlig gesperrt ist, muss der Zugriff auf die Entitätenmengen, die über den Dienst verfügbar gemacht werden sollen, explizit mithilfe von config.SetEntitySetAccessRule zugelassen werden. Dazu wird für jede verfügbar gemachte Entitätenmenge (EntitySet) eine Regel festgelegt, wie in Abbildung 10 dargestellt.

Abbildung 10 BlogService.svc

public class BlogService : DataService<BlogContext> 

{ 

    // This method is called only once to initialize service-wide policies. 

    public static void InitializeService(DataServiceConfiguration config) 

    { 

       // TODO: set rules to indicate which entity sets and service 

       // operations are visible, updatable, etc. 

       // Examples: 

       config.SetEntitySetAccessRule("Blogs", EntitySetRights.All); 

       config.SetEntitySetAccessRule("Posts", EntitySetRights.All); 

       config.SetEntitySetAccessRule("Tags", EntitySetRights.All); 

       // config.SetServiceOperationAccessRule("MyServiceOperation", 

       // ServiceOperationRights.All); 

       config.DataServiceBehavior.MaxProtocolVersion =      

       DataServiceProtocolVersion.V2; 

    } 

}

* (Hinweis: Wenn Sie den Beispielcode für den Artikel herunterladen, werden Sie feststellen, dass ein sehr einfaches Formularauthentifizierungsschema zur Sicherung der Website verwendet wird. Die verbleibenden Beispiele verwenden dieses Schema ebenfalls zum Filtern von Daten basierend auf dem aktuell angemeldeten Benutzer. Da das Implementieren der Formularauthentifizierung den Rahmen dieses Artikels sprengen würde, werde ich die Details hier überspringen.)*

Wenn der Dienst betriebsbereit ist, besteht der nächste Schritt im Filtern der Ergebnisse basierend auf dem aktuell angemeldeten Benutzer, sodass nur ihm gehörende Blogs zurückgegeben werden. Sie erreichen dies durch Hinzufügen von Abfrage-Interceptoren, die die von einer Abfrage zurückgegebenen Entitäten einschränken, wie in Abbildung 11 dargestellt.

Abbildung 11 Abfrage-Interceptor

// returns only public posts and posts owned by the current user 

[QueryInterceptor("Posts")]

public Expression<Func<Post, bool>>OnPostQuery() 

{

  return p =>p.Public == true ||

  p.Blog.Owner.Equals(HttpContext.Current.User.Identity.Name); 

}

// returns only the blogs the currently logged in user owns 

[QueryInterceptor("Blogs")]

public Expression<Func<Blog, bool>>OnBlogQuery() 

{

  return b =>

  b.Owner.Equals(HttpContext.Current.User.Identity.Name); 

}

Nutzen eines WCF Data Service in Silverlight

Die Details der Erstellung einer Silverlight-Benutzeroberfläche gehen über den Rahmen dieses Artikels hinaus, daher werde ich einige davon übergehen. Bevor ich jedoch ausführlich erläutere, wie der Datendienst mit einer Silverlight-Anwendung verbunden wird, werde ich dem Projekt eine neue Silverlight-Anwendung hinzufügen, die die Silverlight-Standardseite MainPage.xaml enthält. Dazu füge ich ein grundlegendes DataGrid-, ComboBox- und Button-Steuerelement sowie einige Beschriftungen hinzu. Wenn das Gerüst der Silverlight-Anwendung fertig ist (siehe Abbildung 12), können wir den Datendienst anbinden.

Abbildung: Grundlegendes Layout der Silverlight-Anwendung MyBlog

Abbildung 12 Grundlegendes Layout der Silverlight-Administratoranwendung MyBlog

Zuerst benötigt die Silverlight-Anwendung Objekte, die die einzelnen durch den Datendienst definierten Entitäten darstellen. Zu diesem Zweck verwenden Sie den Assistenten zum Hinzufügen von Dienstverweisen in Visual Studio, um automatisch Clientklassen für den Datendienst zu generieren. (Beachten Sie, dass ich zum Hinzufügen eines Dienstverweises vorübergehend die im Dienst implementierten Autorisierungsprüfungen deaktivieren muss, sodass der Befehl Dienstverweis hinzufügen über Vollzugriff auf den Dienst verfügt. Ich verweise den Assistenten zum Hinzufügen eines Dienstverweises auf den Basis-URI für den Dienst. In MyBlog ist dies localhost:48009/BlogService.svc).

Datenbindung in WCF Data Services 4.0

Die verbesserte Unterstützung für Datenbindung in WCF Data Services 4.0 umfasst einen neuen Auflistungstyp, DataServiceCollection, in der Clientbibliothek. Dieser stellt eine Erweiterung von ObservableCollection dar. In Silverlight 3 ist die Datenbindung jedoch standardmäßig deaktiviert, wenn dem Projekt ein Dienstverweis hinzugefügt wird. Daher muss die Datenbindung aktiviert und der Dienstverweis aktualisiert werden, um die neue Datenbindungsfunktionalität in WCF Data Services zu nutzen. Klicken Sie im Projektmappen-Explorer auf die Schaltfläche Alle Dateien anzeigen, und erweitern Sie unter dem Knoten Dienstverweise das Element BlogService. Doppelklicken Sie auf die Zuordnungsdatei Reference.datasvc, und ersetzen Sie das Parameters-Element durch folgenden XML-Codeausschnitt:

<Parameters>

  <Parameter Name="UseDataServiceCollection" Value="true" />

  <Parameter Name="Version" Value="2.0" />

</Parameters>

Wenn der UseDataServiceCollection-Parameter auf true festgelegt wird, werden automatisch clientseitige Typen generiert, die die Schnittstellen INotifyPropertyChanged und INotifyCollectionChanged implementieren. Somit werden alle Änderungen an Inhalten einer DataServiceCollection-Auflistung oder von Entitäten in der Auflistung im Clientkontext widergespiegelt. Bei einer erneuten Abfrage einer Entität in der Auflistung werden zudem alle Änderungen an dieser Entität in den Entitäten der DataServiceCollection-Auflistung widergespiegelt. Da die DataServiceCollection-Auflistung die Standardbindungsschnittstellen implementiert, kann sie darüber hinaus als DataSource an die meisten WPF- und Silverlight-Steuerelemente gebunden werden.

Zurück zum MyBlog-Beispiel. Im nächsten Schritt wird eine Verbindung mit dem Dienst hergestellt, indem ein neuer DataServiceContext erstellt und zum Abfragen des Diensts verwendet wird. Abbildung 13 enthält MainPage.xaml und MainPage.xaml.cs und zeigt das Erstellen eines neuen DataServiceContext, das Abfragen des Diensts auf alle Blogs – in diesem Fall gibt der Dienst alle Blogs des angemeldeten Benutzers zurück – und das Binden der Blogs an eine ComboBox in der Silverlight-Anwendung. 

Abbildung 13 "MainPage.xaml" und "MainPage.xaml.cs"

MainPage.xaml

<Grid x:Name="LayoutRoot" Background="White" Width="618">

  <data:DataGrid Name="grdPosts" AutoGenerateColumns="False" 

  Height="206" HorizontalAlignment="Left"Margin="17,48,0,0"

  VerticalAlignment="Top" Width="363" ItemsSource="{Binding Posts}">

    <data:DataGrid.Columns>

      <data:DataGridTextColumn Header="Title" Binding="{Binding Title}"/>

      <data:DataGridCheckBoxColumn Header="Public" 

      Binding="{Binding Public}"/>

      <data:DataGridTextColumn Header="Text" 

      Binding="{Binding PostContent}"/>

    </data:DataGrid.Columns>

  </data:DataGrid>

  <Button Content="Save" Height="23" HorizontalAlignment="Left" 

  Margin="275,263,0,0" Name="btnSave" VerticalAlignment="Top"

  Width="75" Click="btnSave_Click_1" />

  <ComboBox Height="23" HorizontalAlignment="Left" 

  Margin="86,11,0,0" Name="cboBlogs" VerticalAlignment="Top"

  Width="199" ItemsSource="{Binding}" DisplayMemberPath="Name" 

  SelectionChanged="cboBlogs_SelectionChanged" />

  <dataInput:Label Height="50" HorizontalAlignment="Left" 

  Margin="36,15,0,0" Name="label1" 

  VerticalAlignment="Top"Width="100" Content="Blogs:" />

  <dataInput:Label Height="17" HorizontalAlignment="Left" 

  Margin="17,263,0,0" Name="lblCount" VerticalAlignment="Top"

  Width="200" Content="Showing 0 of 0 posts"/>

  <Button Content="Load More Posts" Height="23" HorizontalAlignment="Left" Margin="165,263,0,0" Name="btnMorePosts"

VerticalAlignment="Top" Width="100" Click="btnMorePosts_Click" />

</Grid>

MainPage.xaml.cs

public MainPage() 

{

  InitializeComponent(); 

  svc = new BlogContext(new Uri("/BlogService.svc", UriKind.Relative)); 

  blogs = new DataServiceCollection<Blog>(svc);

  this.LayoutRoot.DataContext = blogs;

  blogs.LoadCompleted += 

  new EventHandler<LoadCompletedEventArgs>(blogs_LoadCompleted);

  var q = svc.Blogs.Expand("Posts");

  blogs.LoadAsync(q); 

}

void blogs_LoadCompleted(object sender, LoadCompletedEventArgs e) 

{

  if (e.Error == null) 

  {

    if (blogs.Count> 0) 

    {

      cboBlogs.SelectedIndex = 0; 

    } 

  } 

}

Zum Binden des DataGrid wird eine cboBlogs_SelectionChanged()-Methode hinzugefügt:

private void cboBlogs_SelectionChanged(object sender, SelectionChangedEventArgs e) 

{

  this.grdPosts.DataContext = ((Blog)cboBlogs.SelectedItem); 

}

Die Methode wird jedesmal aufgerufen, wenn das in der ComboBox ausgewählte Element geändert wird.

Als letztes Element wird in die Silverlight-Anwendung eine Schaltfläche zum Speichern eingebunden, die durch Hinzufügen einer btnSave_Click-Methode aktiviert wird, die SaveChanges für den DataServiceContext aufruft (siehe Abbildung 14).

Abbildung 14 Speichern von Änderungen in der Datenbank

private void btnSave_Click_1(object sender, RoutedEventArgs e) 

{

  svc.BeginSaveChanges(SaveChangesOptions.Batch, OnChangesSaved, svc); 

}

private void OnChangesSaved(IAsyncResult result) 

{

  var q = result.AsyncState as BlogContext;

  try 

  {

    // Complete the save changes operation

    q.EndSaveChanges(result); 

  }

  catch (Exception ex) 

  {

    // Display the error from the response.

    MessageBox.Show(ex.Message); 

  } 

}

Servergesteuertes Paging

Häufig muss die Gesamtanzahl der Ergebnisse, die ein Server für eine Abfrage zurückgibt, beschränkt werden, um zu vermeiden, dass eine Anwendung gelegentlich extrem viele Daten zurückerhält. Durch servergesteuertes Paging in WCF Data Services 4.0 kann der Ersteller eines Diensts auflistungsspezifische Grenzen für die Gesamtanzahl von Entitäten festlegen, die ein Dienst für jede Anfrage zurückgibt. Dazu legt er die SetEntitySetPageSize-Eigenschaft in der InitializeService-Methode für jede Auflistung von Entitäten fest. Neben der Beschränkung der zurückgegebenen Anzahl von Entitäten bietet der Datendienst dem Client einen "Weiter"-Link, d. h. einen URI, der angibt, wie der Client die nächste Entitätsmenge aus der Auflistung abrufen kann. Der URI wird durch das AtomPub<link rel="next">-Element angegeben.

Zurück zum MyBlog-Beispiel. Ich lege jetzt die SetEntitySetPageSize-Eigenschaft für den Dienst für die Entitätsmenge Posts auf 5 Ergebnisse fest:

config.SetEntitySetPageSize("Posts", 5);

Dadurch wird die Anzahl der zurückgegebenen Entitäten beschränkt, wenn der Dienst auf Posts abgefragt wird. Ich lege die SetEntitySetPageSize-Eigenschaft auf eine kleine Zahl fest, um das Feature zu demonstrieren. Im Allgemeinen würde für eine Anwendung eine Grenze festgelegt, die die meisten Clients nicht erreichen (der Client würde vielmehr $top und $skip verwenden, um die Menge der angeforderten Daten zu steuern).

Außerdem füge ich der Anwendung eine neue Schaltfläche hinzu,mit der der Benutzer die nächste Seite von Posts von dem Dienst abrufen kann. In dem Ausschnitt greift die btnMorePosts_Click-Methode auf die nächste Menge von Posts zu:

private void btnMorePosts_Click(object sender, RoutedEventArgs e) 

{

  Blog curBlog = cboBlogs.SelectedItem as Blog;

  curBlog.Posts.LoadCompleted += new   

    EventHandler<LoadCompletedEventArgs>(Posts_LoadCompleted);

  curBlog.Posts.LoadNextPartialSetAsync(); 

}

Zeilenanzahl

Eines der meistgeforderten Features nach der Veröffentlichung von ADO.NET Data Services in Visual Studio 2008 SP1 war die Möglichkeit, die Gesamtanzahl der Entitäten in einer Menge zu bestimmen, ohne alle aus der Datenbank abzurufen. In WCF Data Services 4.0 wurde dazu das Feature Zeilenanzahl hinzugefügt.

Beim Erstellen einer Abfrage auf dem Client kann die IncludeTotalCount-Methode aufgerufen werden, um das count-Tag in die Antwort aufzunehmen. Auf den Wert kann dann, wie in Abbildung 15 dargestellt, mit der TotalCount-Eigenschaft im QueryOperationResponse-Objekt zugegriffen werden.

Abbildung 15 Verwenden der Zeilenanzahl

private void cboBlogs_SelectionChanged(object sender, SelectionChangedEventArgs e) 

{

  Blog curBlog = this.cboBlogs.SelectedItem as Blog;

  this.grdPosts.DataContext = curBlog;

  var q = (from p in svc.Posts.IncludeTotalCount()

  where p.BlogBlogID == curBlog.ID

  select p) as DataServiceQuery<Post>;

  curBlog.Posts.LoadCompleted += new     

  EventHandler<LoadCompletedEventArgs>(Posts_LoadCompleted);

  curBlog.Posts.LoadAsync(q); 

}

void Posts_LoadCompleted(object sender, LoadCompletedEventArgs e) 

{

  if (e.Error == null) 

  {

    Blog curBlog = cboBlogs.SelectedItem as Blog;

    totalPostCount = e.QueryOperationResponse.TotalCount;

    string postsCount = string.Format("Displaying {0} of {1} posts",

    curBlog.Posts.Count, totalPostCount);

    this.lblCount.Content = postsCount;

    curBlog.Posts.LoadCompleted -= Posts_LoadCompleted; 

  } 

}

Projektionen

Ein weiteres häufig gefordertes Feature in WCF Data Services 4.0 sind Projektionen, d. h. die Möglichkeit, eine Teilmenge der Eigenschaften einer Entität für die Rückgabe nach einer Abfrage anzugeben. Dadurch können Anwendungen hinsichtlich des Bandbreiten- und Speicherbedarfs optimiert werden. In Visual Studio 2010 wurde das Data Services URI-Format um die Abfrageoption $select erweitert. Damit können Clients die Teilmenge der Eigenschaften angeben, die von der Abfrage zurückgegeben werden sollen. Bei MyBlog könnte ich beispielsweise mit dem folgenden URI auf die Title- und PostContent-Eigenschaften aller Posts abfragen: BlogService.svc/Posts?$select=Title,PostContent. Auf der Clientseite können Sie nun auch LINQ für Abfragen mit Projektionen verwenden.

Weitere Informationen

Dieser Artikel enthält die wichtigsten Informationen, um Ihnen die ersten Schritte mit Entity Framework 4.0 und WCF Data Services 4.0 in Visual Studio 2010 zu ermöglichen. Darüber hinaus gibt es zahlreiche andere interessante Themen und neue Features. Weitere Informationen finden Sie im MSDN Data Development Center unter msdn.microsoft.com/data.  

Elisa Flasko ist Program Manager im Data Programmability-Team bei Microsoft und befasst sich mit den Modellierungsdiensttechnologien ADO.NET Entity Framework, WCF Data Services, M, Quadrant und SQL Server. Sie kann unter blogs.msdn.com/elisaj erreicht werden.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Jeff Derstadt und Mike Flasko