Microsoft

Blog Categories

Subscribe to RSS feed

Archives

Business Connectivity Services: Implementing a StreamAccessor in Windows Communication Foundation using a .NET Assembly Wrapper

Storing documents on a file server and connecting to them with metadata stored in a database is rather common. Let’s talk about the situations in which you’d like to surface the metadata attached to the file in Business Connectivity Services (BCS) and elsewhere using a single Windows Communication Foundation (WCF) service. To return a file to SharePoint through BCS using any connector, you need to use a StreamAccessor method instance. The StreamAccessor gives SharePoint access to the underlying file for use in web parts and, most importantly, search indexing. The file will be served to the crawler through BCS as the metadata is crawled.

According to Microsoft, you can return a Stream object from a WCF service1, 2 and everything will work easy-peasy. Regrettably, I have not found this to be the case when returning a Stream to a remote file from either a file server or a web page. Instead, the connection to the Stream is closed by the server before the client can access it.

A simple solution would be a temporary file, but because it would be I/O intensive (not to mention a security risk) to copy the contents of the Stream to a temporary file on the WCF server and return a Stream to that file instead, we need another way to accomplish this task. The only solution I’ve been able to find is to create a .NET Assembly connector for BCS and return the Stream from there.

To do this, change your WCF service to return an array of bytes instead of a Stream. From your connector assembly, return a MemoryStream of the incoming byte array from the service. This wrapper will ensure that the Stream is copied into RAM for the connector before BCS gets to it, eliminating the connection closed error. However, the caveat here is that if you have any other methods the make up this content type or are associated to it, they’ll need to be wrapped inside this assembly as well. It’s duplication of code, but you still have the flexibility brought about by the WCF service. Here’s how I’ve implemented it for a Document class below:

public static Stream GetDocumentStream(int id)

{

using (ChannelFactory<IPublicWebService> factory = PublicWebServiceConnector.GetChannelFactory())

{

IPublicWebService service = factory.CreateChannel();

byte[] bytes = service.GetDocumentBytes(id);

return new MemoryStream(bytes);

}

}

private static ChannelFactory<IPublicWebService> GetChannelFactory()

{

XmlDictionaryReaderQuotas readerQuotas = new XmlDictionaryReaderQuotas()

{

MaxArrayLength = 10485760,

MaxStringContentLength = 10485760

};

 

return new ChannelFactory<IPublicWebService>(new BasicHttpBinding()

{

MaxBufferPoolSize = 10485760,

MaxBufferSize = 10485760,

MaxReceivedMessageSize = 10485760,

ReaderQuotas = readerQuotas

}, new EndpointAddress(“<Endpoint Address>”));

}

Fortunately, the XML for your StreamAccessor doesn’t need to change and can simply be copied over from the BCS model for the WCF service. There’s no need to worry about MIME types or the like, unless you wish to retain the file name. Windows and search indexing will pick up the MIME type from the Stream automatically. Here’s the pertinent BCS model for the GetDocumentStream method above:

<MethodName=GetDocumentStreamDefaultDisplayName=Get Document StreamIsStatic=true>

<Parameters>

<ParameterName=idDirection=InDefaultDisplayName=Id>

<TypeDescriptorName=IdTypeName=System.Int32IsCollection=falseIdentifierName=IdDefaultDisplayName=Id />

</Parameter>

<ParameterName=GetDocumentStreamDirection=ReturnDefaultDisplayName=Get Document Stream>

<TypeDescriptorName=DocumentStreamTypeName=System.IO.StreamDefaultDisplayName=Document Stream />

</Parameter>

</Parameters>

<MethodInstances>

<MethodInstanceName=GetDocumentStreamInstanceType=StreamAccessorReturnParameterName=GetDocumentStreamReturnTypeDescriptorPath=DocumentStreamDefaultDisplayName=Get Document StreamDefault=true />

</MethodInstances>

</Method>

 

Examples

Eample Code

Citations:

  1. Code Snippet: Implementing a StreamAccessor [microsoft.com]
  2. XML Snippet: Modeling a StreamAccessor Method [microsoft.com]

Tags: ,

Leave a Reply