Named Resource Streams

One of the new WCF Data Services features in the October 2010 CTP is something called Named Resource Streams.

Background

Data Services already supports Media Link Entries which allows you to associate a single streamed blob with an entry.  For example, you could have a Photo entry that lists the metadata about the photo and links directly to the photo itself.

But what happens though if you have multiple versions of the Photo?

Today you could model this with multiple MLEs, but doing so requires you to have multiple copies of the metadata for each version of the stream.  Clearly this is not desirable when you have multiple versions of essentially the photo.  

It turns out that this is a very common scenario, common enough that we thought it needed to be supported without forcing people to use multiple MLEs.  So, with this release we’ve allowed an entry to have multiple streams associated with it such that you can now create services that do things such as expose a Photo entry with links to its print, web and thumbnail versions.

Let’s explore how to use this feature.

Client-Side

Once a producer exposes Named Resource Streams you have two ways to manipulate them on the client, the first is via new overloads of GetReadStream(..) and SetSaveStream(..) that take a stream name:

// retrieve a person
Person fred = (from p in context.People
where p.Name == "Fred"
select p).Single(); // set custom headers etc via args if needed.
var args = new DataServiceRequestArgs();
args.Headers.Add(…); // make the request to get the stream ‘Photo’ stream on the Fred entity.
var response = context.GetReadStream(fred, "Photo", args);
Stream stream = response.Stream;

If you want to update the stream you use SetWriteStream(..) something like this:

var fileStream = new FileStream("C:\\fred.jpeg", FileMode.Open);
context.SetSaveStream(person, "Photo", fileStream, true, args);
context.SaveChanges();

The other option is to use the StreamDescriptor which hangs off the entity’s EntityDescriptor, and carries information about the SelfLink, EditLink (often they are the same, but you can sometimes get and update the stream via different urls), ETag and ContentType of the Stream:

// Get the entity descriptor for Fred
var dscptor = context.GetEntityDescriptor(fred);

// Get the ‘Photo’ StreamDescriptor
var photoDscptor = dscptor.StreamDescriptors.Single(s => s.Name == "Photo");
Uri uriOfFredsPhoto = photoDscptor.SelfLink;

With the StreamDescriptor you can do low level network activities or perhaps use this Uri when rendering a webpage in the ‘src’ of an <img> tag.

Server-Side

Adding Named Resource Streams to your Model

The first step to using Named Resource Streams is to add them into your model. Because data services supports 3 different types of data sources (Entity Framework, Reflection, Custom) there are three ways to add Named Resource Streams into your model.

Entity Framework

When using the Entity Framework adding a Named Resource Stream to an EntityType is pretty straight forward, you simply add a structural annotation into your Entity Framework EDMX file something like this:

<EntityType Name="Person">
<Key>
<PropertyRef Name="ID" />
</Key>
<Property Name="ID" Type="Edm.Int32" Nullable="false" />
<Property Name="Name" Type="Edm.String" Nullable="true" />
<m:NamedStreams>
<m:NamedStream Name="PhotoThumbnail" />
<m:NamedStream Name="Photo" />
</m:NamedStreams>
</EntityType>

Here the the m:NamedStreams element (xmlns:m="https://schemas.microsoft.com/ado/2007/08/dataservices/metadata") indicates Person has two Named Resource Streams, Photo and PhotoThumbnail.

Reflection Provider

To add a Named Resource Streams to an EntityType using the Reflection Provider we added a new attribute called NamedStreamAttribute:

[NamedStream(“PhotoThumbnail”)]
[NamedStream(“Photo”)]
public class Person
{
public int ID {get;set;}
public string Name {get;set}
}

This example is the Reflection provider equivalent of the Entity Framework example above.

Custom Provider

When you write a custom provider (see this series for more) you add the Named Resource Streams via your implementation of IDataServiceMetadataProvider.

To support this we added a new ResourceStreamInfo class which you can add to your ResourceType definitions something like this:

ResourceType person = …
person.AddNamedStream(new ResourceStreamInfo("PhotoThumbnail"));
person.AddNamedStream(new ResourceStreamInfo("Photo"));

$metadata

No matter how you tell Data Services about your Named Resource Streams consumers always learn about Named Streams via $metadata, which is an EDMX file, so as you might guess we simply tell consumers about named streams using the same structured annotations we used in the Entity Framework provider example above.

Implementing Named Resource Streams

Next you have to implement a new interface called IDataServiceStreamProvider2

Implementing this interface is very similar to implementing IDataServiceStreamProvider which Glenn explains well in his two part series (Part1 & Part2).

IDataServiceStreamProvider2 extends IDataServiceStreamProvider by adding some overloads to work with Named Resource Streams.

The overloads always take a ResourceStreamInfo from which you can learn which Named Resource Streams is targeted. When reading from or writing to the stream you also get the eTag for the stream and a flag indicating whether the eTag should be checked.

Summary

As you can see Named Resource Streams add a significant new feature to Data Services, which is very useful if you have any entities that contain metadata for anything that has multiple representations, like for example Photos or Videos.

Named Resource Streams are very easy to use client-side and build upon our existing Stream capabilities on the server side.

As always we are keen to hear your thoughts and feedback.

Alex James
Program Manager
Microsoft

Comments

  • Anonymous
    December 26, 2010
    I have implemented a IDataServiceStreamProvider2 but I don't get the methods with the ResourceStreamInfo called.The Method GetService(Type serviceType) on my DataService gets only called when setting the HasStreamAttribute on a entity type. For my understanding the two attributes are not intended to be used on the same entity type (HasStream uses IDataServiceStreamProvider, NamedStream the new IDataServiceStreamProvider2). But the usage of NamedStream does not cause the GetService method to be called.
  • Anonymous
    December 07, 2011
    See msdn.microsoft.com/.../ee960144(v=VS.103).aspx for more details on how to build a service with named resource streams.