.NET-Clientbibliothek (ADO.NET Data Services-Framework)
ADO.NET Data Services enthält die .NET-Clientbibliothek für Anwendungen, die .NET Framework und ADO.NET Data Services verwenden. Anwendungen, die die Clientbibliothek verwenden, arbeiten mit Ergebnissen aus dem Datendienst als .NET-Objekte. Das Durchlaufen von Zuordnungen wird durch CLR-Objekte (Common Language Runtime) verwaltet.
Die .NET-Clientbibliothek verwendet HTTP und das AtomPub-Format. Sie eignet sich gut für Unternehmensnetzwerke und Internetumgebungen. Es ist lediglich eine Konnektivität zum Datendienst auf HTTP-Ebene erforderlich, entweder direkt oder über Proxys.
System.Data.Services.Client
Fügen Sie zur Verwendung der Clientbibliothek Ihrem Projekt einen Verweis auf die System.Data.Services.Client-Assembly hinzu. Die Clientbibliothek kann von jedem Projekttyp aus verwendet werden, einschließlich Windows Forms, Windows Presentation Foundation und Website-Projekten.
Die beiden Hauptkonstrukte der Clientbibliothek sind die DataServiceContext
-Klasse und die DataServiceQuery
-Klasse. DataServiceContext
stellt den Laufzeitkontext mit einem angegebenen Datendienst dar. Im Gegensatz zu Datendiensten ist der Kontext nicht statusfrei. Der Status auf dem Client wird zwischen Interaktionen beibehalten, um Features wie das Änderungsmanagement zu unterstützen.
Die DataServiceQuery
-Klasse stellt eine Abfrage des Speichers dar, die mit der URI-Syntax von ADO.NET Data Services angegeben wird. Zum Ausführen einer Abfrage und Abrufen der Ergebnisse in Form von .NET-Objekten muss das Abfrageobjekt aufgelistet werden, beispielsweise mit dem foreach
-Konstrukt in C# oder For Each
in Visual Basic.
Damit alle im Datendienst definierten Entitäten als .NET-Objekte dargestellt werden, müssen entsprechende Klassen für die Clientanwendung definiert werden. Eine Möglichkeit ist das manuelle Definieren der Klassen. Die andere Möglichkeit ist das DataSvcUtil.exe-Tool, das im nächsten Abschnitt erläutert wird.
Das folgende Beispiel zeigt eine von Hand geschriebene Definition für die Address
-Klasse auf Grundlage von Daten in der AdventureWorks-Beispieldatenbank, die in SQL Server 2005 enthalten ist. Für das Beispiel werden die Address
-Entität sowie ein kleines Codesegment verwendet, mit dem eine Abfrage des Diensts ausgeführt wird. Als Ausgabe werden Eigenschaften der zurückgegebenen Address
angezeigt.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Data.Services.Client;
namespace DataServiceClient
{
public class Address
{
public int AddressID { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public DateTime ModifiedDate { get; set; }
public string PostalCode { get; set; }
public Guid rowguid { get; set; }
public int StateProvinceID { get; set; }
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button_Click(object sender, EventArgs e)
{
DataServiceContext ctx = new
DataServiceContext(new Uri("https://localhost:1492/AdvWksSales.svc/"));
// This example expects the user to enter
// an integer representing AddressID in textBox1.
DataServiceQuery<Address> query =
ctx.CreateQuery<Address>(
"SalesOrderHeader(45678)/Address");
StringBuilder output = new StringBuilder();
foreach (Address address in query)
{
output.Append("Id: " + address.AddressID +
" Line 1: " + address.AddressLine1 + "\r\n");
}
richTextBox1.Text = output.ToString();
}
Nachfolgend wird die Ausgabe gezeigt.
Id: 70 Address Line1: 1792 Belmont Rd. City: Monroe
Verwendung des DataSvcUtil-Tools
Das manuelle Schreiben der Klassen ist bei einer kleinen Anzahl von Typen möglich. Wenn das Datendienstschema jedoch komplexer ist, wachsen Anzahl und Größe der zu verwaltenden Klassen, sodass eine manuelle Verwaltung nicht mehr möglich ist. Eine bessere Möglichkeit ist die Verwendung des Tools zur Codeerstellung mit dem Namen DataSvcUtil.exe, das in ADO.NET Data Services enthalten ist. Das Tool generiert .NET-Klassen anhand der Datendienstdefinition.
DataSvcUtil.exe befindet sich im Verzeichnis \WINDOWS\Microsoft.NET\Framework\v3.5\. In der Befehlszeile wird die Basis-URL zum Datendienst, für den Typen generiert werden sollen, als Argument angegeben. Wenn der Northwind-Dienst beispielsweise auf dem Visual Studio-Entwicklungsserver unter https://localhost:1234/Northwind.svc ausgeführt wird, lautet die Befehlszeile zum Generieren von Klassen folgendermaßen:
C:\Program Files\Microsoft Visual Studio 9.0\VC>"C:\WINDOWS\Microsoft.NET\Framework\v3.5\DataSvcUtil.exe"
/out:c:\northwind.cs /uri:https://localhost:1365/Northwind.svc
Die Ausgabe des Befehls ist eine C#-Datei (Visual Basic-Typen können mit dem Schalter /language:VB
generiert werden), die für jeden Entitätstyp im Datendienst eine Klasse enthält.
Die generierten Klassen enthalten Member zur Darstellung von primitiven Werten und Zuordnungen, die die Navigation des Objektmodells ermöglichen.
LINQ to ADO.NET Data Services
Die .NET-Clientbibliothek unterstützt neben den im Abschnitt "Microsoft-Datenwebclient" dargestellten Datendienstabfragen in einem Aufruf von DataServiceContext.CreateQuery
auch Datendienstabfragen mithilfe von LINQ (Language Integrated Queries). Weitere Informationen über LINQ finden Sie unter LINQ to Entities. Die Clientbibliothek verwaltet die Details beim Zuordnen der LINQ-Anweisung zu einem URI im Zieldatendienst und beim Abrufen der angegeben Ressourcen als .NET-Objekte. Im folgenden Beispiel wird veranschaulicht, wie alle Kunden in der Stadt London abgerufen werden und das Resultset nach Unternehmensname geordnet zurückgegeben wird.
using System;
using System.Data.Services.Client;
using System.Linq;
using NorthwindModel;
namespace TestApplication
{
class Program
{
static void Main(string[] args)
{
NorthwindEntities ctx = new
NorthwindEntities(
new Uri("https://localhost:1365/Northwind.svc"));
var q = from c in ctx.Customers
where c.City == "London"
orderby c.CompanyName
select c;
foreach (var cust in q)
{
Console.WriteLine(cust.CompanyName);
}
}
}
}
Hinweis |
---|
Der Satz an Abfragen, der in der LINQ-Syntax ausgedrückt werden kann, ist umfangreicher als bei der von Datendiensten verwendeten REST-basierten (Representational State Transfer) URI-Syntax. Wenn die Abfrage keinem URI im Zieldatendienst zugeordnet werden kann, wird eine Ausnahme ausgelöst. Weitere Informationen zu REST finden Sie unter REST-Dienste und -Semantik (ADO.NET Data Services-Framework). |
Hinweis |
---|
Für die Beispiele in diesem und den nächsten beiden Abschnitten wird die Northwind-Beispieldatenbank verwendet. Das Northwind-Beispiel kann unter Beispieldatenbanken für SQL Server heruntergeladen werden. |
Zuordnungen
Zuordnungen zwischen Objekten werden von der DataServiceContext
-Klasse nachverfolgt und verwaltet. Zugeordnete Objekte können vorzeitig oder erst bei Bedarf geladen werden. Vorzeitiges und verzögertes Laden werden in der Dokumentation zu ADO.NET Entity Framework unter Querying Data as Objects and Shaping Query Results erläutert. Die URL-Formate werden unter Einfaches Adressierungsschema für Daten mit URIs (ADO.NET Data Services-Framework) erläutert.
Zum Laden zugeordneter Entitäten nach Bedarf wird die LoadProperty
-Methode der DataServiceContext
-Klasse verwendet. Im folgenden Beispiel wird gezeigt, wie Category
-Entitäten zugeordnete Product
-Entitäten verzögert geladen werden.
using System;
using System.Data.Services.Client;
using NorthwindModel;
namespace TestApplication
{
class Program
{
static void Main(string[] args)
{
DataServiceContext ctx = new
DataServiceContext(
new Uri("https://localhost:1365/Northwind.svc"));
DataServiceQuery<Categories> categories =
ctx.CreateQuery<Categories>("/Categories");
foreach (Categories c in categories)
{
Console.WriteLine(c.CategoryName);
ctx.LoadProperty(c, "Products");
foreach (Products p in c.Products)
{
Console.WriteLine("\t" + p.ProductName);
}
}
}
}
}
}
In einigen Szenarien benötigen Sie möglicherweise zugeordnete Objekte und möchten die Verzögerung durch eine zusätzliche Abfrage zum Abrufen der Objekte vermeiden. In diesem Fall können Sie die Option expand
in der URL verwenden. Die Clientbibliothek erkennt, dass die Ergebnisse sowohl Entitäten der obersten Ebene als auch zugeordnete Entitäten enthalten und materialisiert alle diese Entitäten als Objektdiagramm. Dies wird als vorzeitiges Laden bezeichnet. Vorzeitiges Laden ähnelt dem vorherigen Beispiel, jedoch werden die zugehörigen Produkte in einem einzigen Roundtrip zum Datendienst geladen.
Im folgenden Beispiel wird die Option "expand" veranschaulicht, mit der mit Kategorien verknüpfte Produkte eingeschlossen werden.
using System;
using System.Data.Services.Client;
using NorthwindModel;
namespace TestApplication
{
class Program
{
static void Main(string[] args)
{
DataServiceContext ctx = new
DataServiceContext(
new Uri("https://localhost:1365/Northwind.svc"));
// get a single category
DataServiceQuery<Categories> categories =
ctx.CreateQuery<Categories>("/Categories(1)?$expand=Products");
foreach (Categories c in categories)
{
//Console.WriteLine(c.CategoryName);
richTextBox1.Text = c.CategoryName;
foreach (Products p in c.Products)
{
//Console.WriteLine("\t" + p.ProductName);
richTextBox1.Text = richTextBox1.Text +
"\r\n\t" + p.ProductName ;
}
}
}
}
}
Aktualisierungsunterstützung
Zur Erstellung einer neuen Instanz im Datendienst erstellen Sie zunächst das .NET-Objekt und rufen dann AddObject
für die verwendete DataServiceContext
-Instanz auf. Übergeben Sie dabei, wie im folgenden Codeausschnitt gezeigt, das Objekt und die Zielentitätenmenge.
using System;
using Microsoft.Data.WebClient;
using NorthwindModel;
namespace TestApplication
{
class Program
{
static void Main(string[] args)
{
DataServiceContext ctx = new
DataServiceContext(
new Uri("https://localhost:1365/Northwind.svc"));
Categories cNew = new Categories();
cNew.CategoryName = "NewCategory1";
cNew.Description = "Add Item Test.";
ctx.AddObject("Categories",cNew);
ctx.SaveChanges();
}
}
}
Nach dem Erstellen oder Ändern einer Entität im Datendienst wird vom Dienst eine neue Kopie der Entität zurückgegeben. Darin sind alle Eigenschaftswerte enthalten, die durch Trigger in der Datenbank oder automatisch generierte Schlüssel möglicherweise aktualisiert wurden. Das .NET-Objekt wird von der Clientbibliothek automatisch mit diesen neuen Werten aktualisiert.
Um eine vorhandene Instanz einer Entität zu ändern, führen Sie zunächst eine Abfrage für das entsprechende Objekt durch und nehmen die gewünschten Änderungen an den zugehörigen Eigenschaften vor. Rufen Sie anschließend die UpdateObject
-Methode auf, um der Clientbibliothek mitzuteilen, dass eine Aktualisierung für dieses Objekt gesendet werden muss.
Hinweis |
---|
Im folgenden Beispiel wird die Kontextklasse |
Das folgende Beispiel zeigt die Aktualisierungssyntax unter Verwendung der UpdateObject
-Methode.
using System;
using Microsoft.Data.WebClient;
using System.Linq;
using NorthwindModel;
namespace TestApplication
{
class Program
{
static void Main(string[] args)
{
NorthwindEntities ctxN =
new NorthwindEntities(
new Uri("https://localhost:1365/Northwind.svc"));
var c1 = (from c in ctxN.Categories
where c.CategoryName == "NewCategory1"
select c).First();
c1.CategoryName = "UpdatedCategory";
//ctxN.AttachTo("Categories", c1);
ctxN.UpdateObject(c1);
ctxN.SaveChanges();
}
}
}
Um eine Instanz einer Entität zu löschen, rufen Sie die Delete
-Methode für das DataServiceContext
-Objekt auf.
Änderungen werden in der DataServiceContext
-Instanz nachverfolgt, jedoch nicht unmittelbar an den Server gesendet. Wenn Sie die erforderlichen Änderungen für eine bestimmte Aktivität vorgenommen haben, rufen Sie SaveChanges
auf, um alle Änderungen an den Datendienst zu übergeben.
Zuordnungsaktualisierungen
Mit der Aktualisierungsinfrastruktur können auch Zuordnungsänderungen verwaltet werden. Es ist möglich, Zuordnungen zwischen .NET-Objekten zu ändern und die Clientbibliothek diese Änderungen als Erstellungs- oder Löschvorgänge von Zuordnungen an der HTTP-Schnittstelle widerspiegeln zu lassen. Zur Veranschaulichung wird im folgenden Beispiel ein Product
in der Northwind-Datenbank erstellt und einer vorhandenen Category
zugeordnet. Kategorien und Produkte sind Teil einer 1:n-Zuordnung. Daher gehört ein bestimmtes Produkt genau einer Kategorie an. Im folgenden Code wird gezeigt, wie die Add
-Methode verwendet wird, um einer Category
eine Product
-Zuordnung hinzuzufügen.
using System;
using Microsoft.Data.WebClient;
using System.Linq;
using NorthwindModel;
namespace TestApplication
{
class Program
{
static void Main(string[] args)
{
NorthwindEntities ctx = new
NorthwindEntities(new Uri(https://localhost:1234/Northwind.svc));
var c1 = (from c in ctx.Categories
where c.CategoryName == "UpdatedCategory"
select c).First();
Products p = new Products();
p.ProductName = "TestProduct";
p.Discontinued = false;
p.QuantityPerUnit = "1";
p.ReorderLevel = 100;
p.UnitPrice = 1.1M;
p.UnitsInStock = 200;
p.UnitsOnOrder = 0;
ctx.AddObject("Products", p);
// Add binding between product and category
p.Categories = c1;
ctx.SetLink(p, "Categories", c1);
ctx.SaveChanges();
Console.ReadKey();
}
}
Das Verwalten von Änderungen an beliebigen Objektdiagrammen mit bidirektionalen Zuordnungen ist ein komplexes Problem. Erweiterte Bibliotheken, wie das ADO.NET Entity Framework, beinhalten sehr umfangreiche und konsistente Zustandsmanager zur Verarbeitung teilweise materialisierter Diagramme. Im Gegensatz dazu wurde bei der ADO.NET-Clientbibliothek Wert auf Ressourcenschonung gelegt. Daher werden nur diejenigen Primitiven bereitgestellt, die für die Zuordnung von Datendienstvorgängen zu .NET-Objekten erforderlich sind.
Das folgende Diagramm zeigt Quell- und Zielentitäten sowie Entitätszustände, die verschiedene Methoden zur Zuordnungsaktualisierung erfordern. Entlang der horizontalen und vertikalen Achsen finden Sie Methoden, deren Aufruf zulässig ist.
Ziel → Quelle ↓ |
NULL |
Added |
Modified |
Deleted |
Unchanged |
Added |
SetLink |
AddLink SetLink |
AddLink SetLink |
n/v |
AddLink SetLink |
Modified |
SetLink |
AddLink SetLink |
AddLink AttachLink DeleteLink SetLink |
DeleteLink |
AddLink AttachLink DeleteLink SetLink |
Deleted |
SetLink |
n/v |
DeleteLink |
DeleteLink |
DeleteLink |
Unchanged |
SetLink |
AddLink |
AddLink AttachLink DeleteLink SetLink |
DeleteLink |
AddLink AttachLink DeleteLink SetLink |
AttachLink kann nur verwendet werden, wenn sich sowohl Quelle als auch Ziel im unveränderten Zustand befinden. Beziehungen werden von AttachLink nicht beibehalten. Verwenden Sie AddLink nur, wenn Sie wünschen, dass der Link bei einem Aufruf von SavingChanges nicht gespeichert wird.
Verwenden Sie objCtx.AddLink(prod, "Categories", c1)
, wenn eine Beziehung zwischen Products
und Catagories
vorliegt und die Eigenschaft Products.Categories
eine Auflistung ist.
Verwenden Sie objCtx.SetLink(prod, "Categories", c1);
, wenn es sich bei Products.Cateories
nur um einen Verweis handelt.
Während des Aufrufs von SaveChanges für eine Quellentität im Zustand Added
schließt ein HTTP POST Links zu anderen Entitäten ein, die sich im geänderten oder nicht geänderten Zustand befinden.
Die AttachLink-Methode erstellt einen Link im Zustand EntityStates.Unchanged
. Hierbei handelt es sich um einen Mechanismus für Benutzer zur Erstellung eines vom Kontext nachverfolgten Links, der nicht an den Server gesendet wird.
Authentifizierung in der Clientbibliothek
Die .NET-Clientbibliothek verwendet die .NET Framework-Unterstützung für das HTTP-Protokoll. Neben anderen Dingen behandelt die Infrastruktur des Frameworks Authentifizierungsschemas über HTTP automatisch, wenn ein Satz mit Anmeldeinformationen bereitgestellt wird.
In der Standardeinstellung werden von der Clientbibliothek keine Anmeldeinformationen für den HTTP-Stapel bereitgestellt. Sie können jedoch die Credentials-Eigenschaft in DataServiceContext
festlegen, um auf ein Objekt zu verweisen, das die ICredentials
-Schnittstelle implementiert. Weitere Informationen zu Anmeldeinformationen finden Sie unter WebRequest.Credentials.
Asynchrone Interaktionen mit dem Datendienst
Bei Webanwendungen muss die Latenzzeit zwischen Client und Server berücksichtig werden, die im Vergleich zu innerhalb interner Netzwerke ausgeführten Anwendungen höher ist. Durch die Verwendung asynchroner Interaktionen mit dem Server kann gewährleistet werden, dass die Benutzeroberfläche reagiert, während die Anwendung auf eine Antwort des Servers wartet.
In dieser Version der ADO.NET-Clientbibliothek wird für viele in der DataServiceContext
-Klasse enthaltene Vorgänge, wie das Abrufen und Speichern von Änderungen, ein asynchroner Betriebsmodus unterstützt. Das folgende Beispiel verdeutlicht, wie die asynchrone API der Clientbibliothek in Code eingesetzt wird.
using System;
using Microsoft.Data.WebClient;
using System.Linq;
using NorthwindModel;
namespace TestApplication
{
class Program
{
static void Main(string[] args)
{
NorthwindEntities ctx = new
NorthwindEntities(new Uri(https://localhost:51905/nw.svc));
DataServiceQuery<Customers> q = ctx.Customers;
q.BeginExecute(
delegate(IAsyncResult ar)
{
foreach (Customers c in q.EndExecute(ar))
{
Console.WriteLine(c.CompanyName);
}
},
null);
Console.ReadKey();
}
}
}
Siehe auch
Konzepte
HttpWebRequest-GET (ADO.NET Data Services-Framework)
Erstellen von ADO.NET Data Services