Working with Binary Data (ADO.NET Data Services)
Note
This topic describes new functionality in ADO.NET Data Services that is available as an update to the .NET Framework version 3.5 Service Pack 1. You can download and install the update from the Microsoft Download Center.
The ADO.NET Data Services client library enables you to retrieve and update binary data from a data service in one of the following ways:
As a primitive type property of an entity. This is the recommended method for working with small binary data objects that can be easily loaded into memory. In this case, the binary property is an entity property exposed by the data model, and the data service serializes the binary data as base-64 binary encoded XML in the response message.
As a separate binary data stream. This is the recommended method for accessing and changing binary large object (BLOB) data that may represent a photo, video or any other type of binary encoded data. In this method, the binary data is not included data service model and is requested as a stream directly from the data service.
ADO.NET Data Services implements the streaming of binary data by using HTTP as defined in the Open Data Protocol (OData) specifications. In this mechanism, binary data is treated as a media resource that is separate from but related to an entity, which is called a media link entry. For more information, see Streaming Provider (ADO.NET Data Services).
Media Link Entry Metadata
An entity that is a media link entry is indicated by the HasStream attribute in the metadata returned by the data service by the URI, such as https://localhost:12345/Northwind.svc/$metadata. In the following example, the Employee entity is a media resource that has a related media resource. This media resource is a bitmap image that is stored in the Photo column of the Employees table in the Northwind database as a media resource.
<EntityType Name="Employees" m:HasStream="true">
<Key>
<PropertyRef Name="EmployeeID" />
</Key>
<Property Name="EmployeeID" Type="Edm.Int32" Nullable="false" />
<Property Name="LastName" Type="Edm.String" Nullable="false" MaxLength="20" Unicode="true" FixedLength="false" />
<Property Name="FirstName" Type="Edm.String" Nullable="false" MaxLength="10" Unicode="true" FixedLength="false" />
<Property Name="Title" Type="Edm.String" Nullable="true" MaxLength="30" Unicode="true" FixedLength="false" />
<Property Name="TitleOfCourtesy" Type="Edm.String" Nullable="true" MaxLength="25" Unicode="true" FixedLength="false" />
<Property Name="BirthDate" Type="Edm.DateTime" Nullable="true" />
<Property Name="HireDate" Type="Edm.DateTime" Nullable="true" />
<Property Name="Address" Type="Edm.String" Nullable="true" MaxLength="60" Unicode="true" FixedLength="false" />
<Property Name="City" Type="Edm.String" Nullable="true" MaxLength="15" Unicode="true" FixedLength="false" />
<Property Name="Region" Type="Edm.String" Nullable="true" MaxLength="15" Unicode="true" FixedLength="false" />
<Property Name="PostalCode" Type="Edm.String" Nullable="true" MaxLength="10" Unicode="true" FixedLength="false" />
<Property Name="Country" Type="Edm.String" Nullable="true" MaxLength="15" Unicode="true" FixedLength="false" />
<Property Name="HomePhone" Type="Edm.String" Nullable="true" MaxLength="24" Unicode="true" FixedLength="false" />
<Property Name="Extension" Type="Edm.String" Nullable="true" MaxLength="4" Unicode="true" FixedLength="false" />
<Property Name="Notes" Type="Edm.String" Nullable="true" MaxLength="Max" Unicode="true" FixedLength="false" />
<Property Name="PhotoPath" Type="Edm.String" Nullable="true" MaxLength="255" Unicode="true" FixedLength="false" />
<NavigationProperty Name="Employees1" Relationship="NorthwindModel.FK_Employees_Employees" FromRole="Employees" ToRole="Employees1" />
<NavigationProperty Name="Employees2" Relationship="NorthwindModel.FK_Employees_Employees" FromRole="Employees1" ToRole="Employees" />
</EntityType>
In this case, notice that the Photo property is not included in the definition of the Employee entity. This Photo data is instead accessed as a binary stream from the data service. The remaining examples in this topic show how to access and change this media resource using a binary data stream. Project files from this streaming sample application can be downloaded from the WCF Data Services Documentation Samples page.
Getting and Setting a Media Resource's Stream
When retrieving a media resource as a binary data stream, you must call the GetReadStream(Object) method on the DataServiceContext instance that is tracking the entity. This method sends a request to the data service that returns a DataServiceStreamResponse object, which has a reference to the stream that contains the media resource. The following example shows how to call the GetReadStream(Object) method to retrieve the bitmap image that belongs to a specified employee from a data service that is defined to expose data from the Northwind database:
' Get the read stream for the Media Resource for the currently selected
' entity (Media Link Entry).
Using response As DataServiceStreamResponse = _
context.GetReadStream(currentEmployee, "image/bmp")
' Read the returned stream into new memory stream.
Dim reader As BinaryReader = New BinaryReader(response.Stream)
Using imageStream As MemoryStream = _
New MemoryStream()
Dim buffer As Byte() = New Byte(999) {}
Dim count As Integer = 0
' Read the returned stream into the new memory stream.
If response.Stream.CanRead Then
Do
count = response.Stream.Read(buffer, 0, buffer.Length)
imageStream.Write(buffer, 0, count)
Loop While 0 < count
End If
' Use the returned bitmap stream to create a bitmap image that is
' the source of the image control.
employeeImage.Source = CreateBitmapFromStream(imageStream)
End Using
End Using
// Get the read stream for the Media Resource for the currently selected
// entity (Media Link Entry).
using (DataServiceStreamResponse response =
context.GetReadStream(currentEmployee, "image/bmp"))
{
using (MemoryStream imageStream =
new MemoryStream())
{
byte[] buffer = new byte[1000];
int count = 0;
// Read the returned stream into the new memory stream.
while (response.Stream.CanRead && (0 < (
count = response.Stream.Read(buffer, 0, buffer.Length))))
{
imageStream.Write(buffer, 0, count);
}
// Use the returned bitmap stream to create a bitmap image that is
// the source of the image control.
employeeImage.Source = CreateBitmapFromStream(imageStream);
}
}
To insert or update a media resource, you must call the SetSaveStream(Object, Stream, Boolean, DataServiceRequestArgs) method on the DataServiceContext instance that is tracking the entity. This method sends a request to the data service that contains the media resource read from the supplied stream. The following example shows how to call the SetSaveStream(Object, Stream, Boolean, String, String) method to send a bitmap image that belongs to a specific employee to the data service:
' Use a FileStream to open the existing image file.
Using imageStream As FileStream = _
New FileStream(openImage.FileName, FileMode.Open)
' Set the file stream as the source of binary stream
' to send to the data service.
context.SetSaveStream(currentEmployee, _
imageStream, False, "image/bmp", _
currentEmployee.LastName)
' Confirm that we should save changes; we need to do this
' while the file stream still exists.
If MessageBox.Show("Send changes to the service?") _
= MessageBoxResult.OK Then
' Upload the binary stream to the data service.
response = context.SaveChanges()
' Since we still have an open filestream, reset the steam and
' set the filestream as the source of the image control
' in the client UI, before the stream is closed.
imageStream.Position = 0
employeeImage.Source = CreateBitmapFromStream(imageStream)
End If
End Using
// Use a FileStream to open the existing image file.
using (FileStream imageStream =
new FileStream(openImage.FileName, FileMode.Open))
{
// Set the file stream as the source of binary stream
// to send to the data service. The slug header only
// needs to be provided when a new media link entry is created.
context.SetSaveStream(currentEmployee,
imageStream, false, "image/bmp",
string.Empty);
// Confirm that we should save changes; we need to do this
// while the file stream still exists.
if (MessageBox.Show("Send changes to the service?")
== MessageBoxResult.OK)
{
// Upload the binary stream to the data service.
context.SaveChanges();
// Since we still have an open filestream, reset the steam and
// set the filestream as the source of the image control
// in the client UI, before the stream is closed.
imageStream.Position = 0;
employeeImage.Source = CreateBitmapFromStream(imageStream);
}
}
Note that the stream is not sent to the data service until SaveChanges() is called.
In these examples, the streams are created in a using block to make sure that the stream resources are disposed of correctly.
See Also
Other Resources
Using a Data Service in a .NET Framework Application (ADO.NET Data Services)