30
AdventureWorks OData sample - Named Resource Streams Overview As a continuation of my previous blog post , the AdventureWorks QueryFeed OData sample now shows how to implement Named Resource Streams to stream AdventureWorks product images. In addition, I added the ability to select Named Resource Streams (product images) within a business workflow to the QueryFeed activity, and render a stream in a Word document. The AdventureWorks QueryFeed OData sample illustrates an end to end OData workflow with Office scenario. Starting with SQL Server views, the sample shows how to expose selective AdventureWorks views as an OData service. The AdventureWorks product catalog contains two product images: LargePhoto and ThumbNailPhoto. By implementing WCF Data Services 5.0 Named Resource Streams, the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document. You can select named resources in the QueryFeed workflow activity and select the default named resource that the client host will render in the TablePartPublisher workflow activity.

AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

Embed Size (px)

Citation preview

Page 1: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

AdventureWorks OData sample - Named Resource StreamsOverviewAs a continuation of my previous blog post, the AdventureWorks QueryFeed OData sample now shows how to implement Named Resource Streams to stream AdventureWorks product images. In addition, I added the ability to select Named Resource Streams (product images) within a business workflow to the QueryFeed activity, and render a stream in a Word document.

The AdventureWorks QueryFeed OData sample illustrates an end to end OData workflow with Office scenario. Starting with SQL Server views, the sample shows how to expose selective AdventureWorks views as an OData service. The AdventureWorks product catalog contains two product images: LargePhoto and ThumbNailPhoto. By implementing WCF Data Services 5.0 Named Resource Streams, the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document. You can select named resources in the QueryFeed workflow activity and select the default named resource that the client host will render in the TablePartPublisher workflow activity.

Page 2: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

Source CodeAdventureWorks OData Feed (http://msftdbprodsamples.codeplex.com/releases/view/94064).

OData QueryFeed workflow activity (http://msftdbprodsamples.codeplex.com/releases/view/94486).

Requirements1. Visual Studio .NET 2012

Page 3: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

2. .NET Framework 4.0 (QueryFeed activity).3. .NET Framework 4.5 (AdventureWorks2012 OData Service).4. WCF Data Services 5.0 for OData V3 located at

http://www.microsoft.com/en-us/download/details.aspx?id=29306.5. Open XML SDK located at http://www.microsoft.com/en-us/download/details.aspx?id=5124.6. Microsoft Excel 2010 or above (optional for ExcelAddin).7. Microsoft Word 2010 or above (optional for WordAddin).

Integrated TechnologiesThe sample shows how to integrate the following technologies to address the business scenario of consuming any OData service in an Office application. The sample uses the AdventureWorks database as an example line-of-business database with selective views exposed as OData resources.

SQL Server 2008R2 or above including views with joins, ROW_NUMBER() OVER, functions, xpath queries, and configuring a NT AUTHORITY\NETWORK SERVICE login and role for IIS.

Windows Workflow 4.0 including custom activities, activity designers, OData schema aware expression lists, practical use of ModelItem and ModelItemTree, child activities, XML LINQ projections into entity properties, a custom workflow designer, and variables.

Office (Excel and Word) including hosting an OData related workflow, consuming workflow activity states using tracking participants, extension methods, embedding OData properties into Content Controls, and Open XML.

User stories addressed in the blog seriesThis blog series addressed a partial list of user stories. More detailed user stories would be defined in an agile production application.

As a backend developer, I want to only allow views of the AdventureWorks2012 database to be exposed as an OData public resource so that the underlying schema can be modified without affecting the service.

As a backend developer, I want the AdventureWorks OData service to expose multiple versions of the product photo so that client applications can have a thumbnail view and a detail view with large photos.

As a Developer, I want to create a product catalog view model so that other developers can bind device views to the view model.

As a developer, I want to create an OData feed activity so that an IT Analyst can consume any AdventureWorks OData feed within a business workflow.

As an Office developer, I want to create an Addin that consumes the AdventureWorks OData service product photos so that our marketing department can insert photos into product related documents.

How to install the sample1) Install WCF Data Services 5.0 for OData V3 (WcfDataServices.exe ) located at

http://www.microsoft.com/en-us/download/details.aspx?id=29306.

Page 4: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

2) Install OpenXMLSDKv2.msi located at http://www.microsoft.com/en-us/download/details.aspx?id=5124.

3) Open \AdventureWorks.OData.Service\AdventureWorks.OData.Service.sln in Visual Studio 2012.4) Build the AdventureWorks.OData.Service solution.5) Open \Microsoft.Samples.SqlServer.OData\Microsoft.Samples.SqlServer.Workflows.OData.sln in

Visual Studio 2012.6) Build the solution.7) For this iteration of the sample, you will need to install the activity designer assembly into the

GAC. See How to install the activity designer assemblies into the GAC.

How to install the activity designer assemblies into the GACFrom the Visual Studio Command Prompt, enter:

gacutil /i {FullPath}\Microsoft.Samples.SqlServer.Activities.dll

For Example:

gacutil /i C:\Projects\Microsoft.Samples.SqlServer.OData\Common\Activities\bin\Debug\Microsoft.Samples.SqlServer.Activities.dll

How to remove Microsoft.Samples.SqlServer.Activities from the GAC

From the Visual Studio Command Prompt, enter:

gacutil /u Microsoft.Samples.SqlServer.Activities

Note

If you get the Visual Studio error below, then you will need to install Microsoft.Samples.SqlServer.Activities into the GAC. A future release should resolve this issue.

Page 5: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

How to run the sampleThe AdventureWorks OData service sample is hosted on the ASP.NET Development Server. You can also consume the AdventureWorks OData feed from http://services.odata.org/AdventureWorksV3/AdventureWorks.svc.

To run the service on a local ASP.NET Development Server

1) Download the sample from CodePlex (http://msftdbprodsamples.codeplex.com/releases/view/94064).

2) Attach the AdventureWorks2012 database. The AdventureWorks2012 database can be downloaded from http://msftdbprodsamples.codeplex.com/releases/view/93587.

3) From Microsoft SQL Server Management Studio, run \AdventureWorks.OData.Service\SQL Scripts\Views.sql to create the OData feed SQL views.

4) Open \AdventureWorks.OData.Service\AdventureWorks.OData.Service.sln in Visual Studio 2012.5) In Solution Explorer, select AdventureWorks.svc.6) Press F5 to run the service on http://localhost:1234/AdventureWorks.svc/.

AdventureWorks OData Service Resources

Named Resource Streams and product photosA data service can expose binary data. Starting with version 3 of OData, an entity can have multiple related resource streams, which are accessed by name. The sample illustrates how to implement IDataServiceStreamProvider2 to expose AdventureWorks ThumbNailPhoto and LargePhoto product images.

Configuring a data service to support the streaming of binary data requires four steps.

1. Attribute the target entity that has resource streams.2. Implement the following stream provider interfaces:

IDataServiceStreamProvider2 – required only for named resource streams. IDataServiceStreamProvider – required to support both kinds of binary resource

streams.

Page 6: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

3. Define a data service that implements the IServiceProvider interface. The data service uses the GetService implementation to access the streaming data provider implementation. This method returns the appropriate streaming provider implementation.

4. Enable large message streams in the Web application configuration and access to binary resources on the server or in a data source.

Step 1 - Attribute the target entity that has resource streams.

The AdevntureWorks sample attributes vProductCatalog. See AdventureWorksModel.Extensions.cs within the code sample.

using System.Data.Services.Common;

namespace Microsoft.Samples.SqlServer.AdventureWorksService{ [NamedStream("LargePhoto")] [NamedStream("ThumbNailPhoto")] public partial class vProductCatalog { }}

Step 2 - Implement the following stream provider interfaces:

IDataServiceStreamProvider2 – required only for named resource streams. IDataServiceStreamProvider – required to support both kinds of binary resource streams.

public class ProductCatalogResourceProvider : IDataServiceStreamProvider2{…

public Stream GetReadStream(object entity, ResourceProperty resourceProperty, string etag, bool? checkETagForEquality, DataServiceOperationContext operationContext){

vProductCatalog image = entity as vProductCatalog;

if (image == null){

throw new DataServiceException(500, "Internal Server Error.");}

// Return a stream that contains the requested ThumbnailPhoto or LargePhoto

return ProductPhoto(image.ProductID, resourceProperty.Name);}

//The sample uses .NET Framework 4.5 SqlDataReader.GetStream();private Stream ProductPhoto(int productID, string columnName){ Stream productPhoto = null;

using (SqlConnection connection = new SqlConnection(Properties.Settings.Default.Setting.ToString()))

{ using (SqlCommand command = connection.CreateCommand())

Page 7: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

{ // Setup the command command.CommandText =

string.Format("SELECT {0} FROM Production.vProductCatalogImages WHERE ProductID=@ProductID", columnName);

command.CommandType = CommandType.Text;

// Declare the parameterSqlParameter paramID = new SqlParameter("@ProductID", SqlDbType.Int);

paramID.Value = productID; command.Parameters.Add(paramID);

connection.Open();

try {

using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection))

{ reader.Read(); if (reader.HasRows) productPhoto = reader.GetStream(0); } } catch (SqlException ex) {

//Log the SqlException, such as Invalid column name, in a production application

}

return productPhoto; } }}

Step 3 - Define a data service that implements the IServiceProvider interface.

public object GetService(Type serviceType){ if(serviceType == typeof(IDataServiceStreamProvider2)) { //Return the stream provider to the data service. return new ProductCatalogResourceProvider(); }

return null;}

Step 4 - Enable large message streams in the Web application configuration.

Page 8: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

When you create a data service in an ASP.NET Web application, Windows Communication Foundation (WCF) is used to provide the HTTP protocol implementation. By default, WCF limits the size of HTTP messages to only 65K bytes. To stream large binary data to and from the data service, you configure the Web application to enable large binary files and to use streams for transfer. To do this, add <services> element and <bindings> element to the application's Web.config file:

<system.serviceModel> <services> <!--The name of the service--> <service name="AdventureWorks_ODataService.AdventureWorks"> <!--you can leave the address blank or specify your end point URI--> <endpoint binding="webHttpBinding" bindingConfiguration="higherMessageSize" contract="System.Data.Services.IRequestHandler" /> </service> </services> <bindings> <webHttpBinding> <!-- configure the maxReceivedMessageSize value to suit the max size of the request (in bytes) you want the service to recieve--> <binding name="higherMessageSize" maxReceivedMessageSize="500000" /> </webHttpBinding> </bindings> </system.serviceModel>

See AdventureWorks.OData.Service.sln for a complete sample service. For more information, see Streaming Provider (WCF Data Services).

Example QueryFeed workflowsThe sample includes three example workflows that consume the AdventureWorks Odata service: ProductCatalog Activity Example.xaml, ManufacturingInstructions Activity Example.xaml, and WorkOrderRouting Activity Example.xaml.

How to run the ProductCatalog Activity ExampleTo run the ProductCatalog activity example, you will need to start the sample AdventureWorks OData feed. After the AdventureWorks OData service starts, you can run the ExcelAddin or WordAddin project.

To run the service on a local ASP.NET Development Server

1) Open \Microsoft.Samples.SqlServer.OData\AdventureWorks.OData.Service\AdventureWorks.OData.Service.sln in Visual Studio 2012.

2) In Solution Explorer, select AdventureWorks.svc.3) Press F5 to run the service on http://localhost:1234/AdventureWorks.svc/.

To run the ExcelAddin project

1) Open \Microsoft.Samples.SqlServer.OData\Microsoft.Samples.SqlServer.Workflows.OData.sln in Visual Studio 2012.

2) Right click the Microsoft.Samples.SqlServer.ExcelAddIn project.

Page 9: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

3) Click Set as Startup Project.4) Press F5 to run the sample ExcelAddin.5) Click the Developer ribbon tab.

6) Click the Configuration button.7) Select the path containing ProductCatalog Activity Example.xaml by clicking the (…) button next

to the Workflow property. Click the Close button.

NoteThe example QueryFeed workflow path is \Microsoft.Samples.SqlServer.OData\Configuration

8) Select a cell.9) Click the Get Feed ribbon button and select ProductCatalog Activity Example.xaml.10) The example QueryFeed workflow renders Product Catalog entity properties as an Excel

ListObject table.

Page 10: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

To run the WordlAddin project

1) Open \Microsoft.Samples.SqlServer.OData\Microsoft.Samples.SqlServer.Workflows.OData.sln in Visual Studio 2012.

2) Right click the Microsoft.Samples.SqlServer.WordAddIn project.3) Click Set as Startup Project.4) Press F5 to run the sample WordAddin.5) Click the Developer ribbon tab.

6) Click the Configuration button.7) Select the path containing ProductCatalog Activity Example.xaml by clicking the (…) button next

to the Workflow property. Click the Close button.

NoteThe example QueryFeed workflow path is \Microsoft.Samples.SqlServer.OData\Configuration

8) Click the Get Feed ribbon button and select ProductCatalog Activity Example.xaml.9) The example QueryFeed workflow renders Product Catalog entity properties as a Word table

using Open Xml.

How to render images in the sample Word document

After running the sample QueryFeed workflow, the Word addin host embeds an OData resource Uri in each ProductID content control. The resource Uri is obtained from the TablePartPublisher default resource.

The sample illustrates two methods to render an AdventureWorks image from a content control tag containing a resource uri: Double click the ProductID Content Control or right click the the ProductID Content Control and select a named resource. Named resource context buttons are obtained from the QueryFeed Select expression.

Page 11: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

To insert an OData resource into the sample Word document

After running the ProductCatalog Activity Example.xaml, right click on a ProductID Content Control and select LargePhoto or ThumbNailPhoto.

Page 12: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

How to create a QueryFeed workflow using the hosted Windows Workflow designerYou can open the custom Workflow designer as a stand alone Windows application, from within Excel or from within Word. After following these steps, you will be able to create a new OData QueryFeed workflow, and run the OData workflow to render entity properties in Excel and Word.

When rendering entity properties in Word, you can stream a product catalog ThumbNailPhoto or LargePhoto directly into Word.

To create an AdventureWorks product catalog workflow

1) Open \Microsoft.Samples.SqlServer.OData\Microsoft.Samples.SqlServer.Workflows.OData.sln in Visual Studio 2012.

Page 13: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

2) Right click the Microsoft.Samples.SqlServer.ExcelAddIn or Microsoft.Samples.SqlServer.WordAddIn project.

3) Click Set as Startup Project.4) Press F5 to run the sample ExcelAddin or WordAddin.5) Click the Developer ribbon tab.6) Click the Design button.7) Click the New button.8) Click the Activities button.

9) Drag a QueryFeed activity onto the default Sequence activity.10) Select ProductCatalog for the Resource ComboBox expression.11) Enter 15 for the Top expression12) Drag a Filter activity onto the Drop Filter Here drop zone in the QueryFeed activity.13) Select CultureID, Eq, "En", And on the Filter activity.14) Drag a Filter activity onto the Drop Filter Here drop zone in the QueryFeed activity.15) Select ListPrice, Gt, 1000 on the Filter activity.16) Collapse the two Filter activities.17) Select ListPrice for Order By expression.18) Select ThumbNailPhoto, LargePhoto, ProductID, Description, and ListPrice for Select expression.

Page 14: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

19) Drag an EntityProperties activity under the QueryFeed activity.20) Enter "EntityProperties" for the Properties expression.21) You will see a Type 'EntityProperty' is not defined error. This error demonstrates a Workflow

Designer IValidationErrorService.22) Click Imports, enter "Microsoft.Samples.SqlServer.Activities.Designers.OData" in the Enter or

Select namespace ComboBox control. Click Imports to collapse the namespace list.23) Drag a TablePartPublisher activity onto the EntityProperties ContentPart drop zone.24) Select a Style such as "Medium List 2 - Accent 5" for Word or "TableStyleMedium24" for Excel.25) Select a default Resource such as "ThumbNailPhoto". The default resource is used by the client

hosting the workflow to render one of many named resources.26) Click the Run button.

The new workflow application produces a fully qualified OData uri, and renders entity properties in a Word table using the TablePartPublisher style. In addition, the default resource uri is embedded into the ProductID Content Control. Double click or right click the ProductID Content Control to render an AdventureWorks image stream.

Word table produced from new OData workflow application

ServiceQueryString=http://localhost/AdventureWorks_ODataService/AdventureWorks.svc/ProductCatalog?$filter=CultureID eq 'en' and ListPrice gt 1000&$top=15&$orderby=ListPrice asc&$select=ThumbNailPhoto,LargePhoto,ProductID,Description,ListPrice

Page 15: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document
Page 16: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

Final OData workflow application

Page 17: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

Code SnippetsAn upcoming blog post or article will discuss the source code used for this sample. For now, here are some code snippets.

Production.vProductCatalog

CREATE VIEW [Production].[vProductCatalog]ASSELECT ROW_NUMBER() OVER (ORDER BY [ProductID] DESC) AS ID, P.ProductID, P.ProductNumber, P.Name AS ProductName, PM.Name AS ProductModel, PC.Name AS ProductCategory, PS.Name AS ProductSubcategory, PD.Description, PMPDCL.CultureID, P.Color, P.Size, P.Weight, P.ListPriceFROM Production. Product AS P INNER JOIN Production.ProductSubcategory AS PS INNER JOIN Production.ProductCategory AS PC ON PS.ProductCategoryID = PC.ProductCategoryID ON P.ProductSubcategoryID = PS.ProductSubcategoryID INNER JOIN Production.ProductDescription AS PD INNER JOIN Production.ProductModel AS PM INNER JOIN Production.ProductModelProductDescriptionCulture AS PMPDCL ON PM.ProductModelID = PMPDCL.ProductModelID ON PD.ProductDescriptionID = PMPDCL.ProductDescriptionID ON P.ProductModelID = PM.ProductModelID;

AdventureWorksModel.Extensions.cs

using System.Data.Services.Common;

namespace Microsoft.Samples.SqlServer.AdventureWorksService{ [NamedStream("LargePhoto")] [NamedStream("ThumbNailPhoto")] public partial class vProductCatalog { }}

public object GetService(Type serviceType){ if(serviceType == typeof(IDataServiceStreamProvider2)) { //Return the stream provider to the data service. return new ProductCatalogResourceProvider(); }

return null;}

private Stream ProductPhoto(int productID, string columnName) { Stream productPhoto = null;

Page 18: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

using (SqlConnection connection = new SqlConnection (Properties.Settings.Default.Setting.ToString())) { using (SqlCommand command = connection.CreateCommand()) { // Setup the command command.CommandText = string.Format ("SELECT {0} FROM Production.vProductCatalogImages WHERE ProductID=@ProductID", columnName);

command.CommandType = CommandType.Text;

// Declare the parameter SqlParameter paramID = new SqlParameter("@ProductID", SqlDbType.Int); paramID.Value = productID; command.Parameters.Add(paramID);

connection.Open();

try { using (SqlDataReader reader = command.ExecuteReader (CommandBehavior.CloseConnection)) { reader.Read(); if (reader.HasRows) productPhoto = reader.GetStream(0); } } catch (SqlException ex) { //In Log the SqlException, such as Invalid column name, in a production application }

return productPhoto; } } }

Page 19: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

public Stream GetReadStream(object entity, ResourceProperty resourceProperty, string etag, bool? checkETagForEquality, DataServiceOperationContext operationContext) { vProductCatalog image = entity as vProductCatalog;

if (checkETagForEquality != null) { // This stream provider implementation does not support // ETag headers for media resources. This means that we do not track // concurrency for a media resource and last-in wins on updates. throw new DataServiceException(400, "This sample service does not support the ETag header for a media resource."); }

if (image == null) { throw new DataServiceException(500, "Internal Server Error."); }

// Return a stream that contains the requested ThumbnailPhoto or LargePhoto return ProductPhoto(image.ProductID, resourceProperty.Name); }

How to get the selected model item

private void ActivityDesigner_Loaded(object sender, RoutedEventArgs e){ selectedModelItem = (sender as TablePartPublisherDesigner).ModelItem;}

How to Named Resources expression items

private void resourceCombobox_DropDownOpened(object sender, EventArgs e){ ComboBox resourceCombobox = (sender as ComboBox); QueryFeed queryFeed = null;

resourceCombobox.Items.Clear();

//Get Sequence parent ModelItem sequence = selectedModelItem.GetParent(typeof(Sequence));

//Get QueryFeed Activities var queryFeedActivities = from a in sequence.Properties["Activities"].ComputedValue as Collection<System.Activities.Activity> where a.DisplayName == "QueryFeed" select a; if (queryFeedActivities.Count() > 0) queryFeed = queryFeedActivities.First() as QueryFeed;

Page 20: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

if (queryFeed != null) { if (queryFeed.NamedResources != null) { resourceCombobox.Items.Add(string.Empty); foreach (string item in queryFeed.NamedResources) { resourceCombobox.Items.Add(item); } } }}

QueryFeed Entity Properties – Combining elements to form IEnumerable<EntityProperty>

The sample uses several LINQ queries to combine elements to form IEnumerable<EntityProperty>. I would like to use a single LINQ query, but ran out of time.

1. Get entry elements

IEnumerable<XElement> entries = from element in XElement.Load(serviceQuery).Descendants(xmlns.GetName("entry")) select element;

2. Get properties elements

properties = (from property in entries.Descendants(mxmlns.GetName("properties")) select (from e in property.Descendants() select new EntityProperty { Name = e.Name.LocalName, Value = e.Value, Type = e.HasAttributes ? (e.Attribute(mxmlns.GetName("type")) != null ? e.Attribute(mxmlns.GetName("type")).Value.ToString() : string.Empty) : "Edm.String" }).ToList()).ToList();

Page 21: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

3. Get link (Named Resource) elements

IEnumerable<IEnumerable<EntityProperty>> namedResources = from entry in entries select from l in entry.Descendants(xmlns.GetName("link")) where l.Attribute("type") != null select new EntityProperty { Name = l.Attribute("title").Value.ToString(), Value = l.Attribute("href").Value.ToString(), Type = "Edm.Stream" };

4. Get id elements

var entityIds = from e in entries.Descendants(xmlns.GetName("id")) select new EntityProperty { Name = "id", Value = e.Value, Type = "Edm.String" };

5. Combine LINQ queries

//Add namedResources to propertiesIEnumerable<EntityProperty> entityPropertyEnum;int i = -1;foreach (List<EntityProperty> p in properties){ i++; //Get the link properties entityPropertyEnum = namedResources.ElementAt(i); foreach (var item in entityPropertyEnum) { p.Add(item); } //Add in entity id p.Add(entityIds.ElementAt(i));

How to create $filter from a collection of Filter activities

private string BuildFilter() { StringBuilder filterStringBuilder = new StringBuilder();

if (this.FilterActivities.Count > 0) { filterStringBuilder.Append("$filter=");

//Build foreach (Filter activity in this.FilterActivities)

Page 22: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

{

filterStringBuilder.Append(String.Format("{0} {1} {2}", activity.Name.ToString(), activity.ComparisonOperator.ToString().ToLower(), activity.Value.ToString().Replace("\"", "'"))); if (activity.LogicalOperator != LogicalOperatorEnum.End) filterStringBuilder.Append( String.Format(" {0} ", activity.LogicalOperator.ToString().ToLower())); } }

return filterStringBuilder.ToString(); }

How to set a default OutArgument

private void RefreshEntitySets(){ ODataQuery q = null;

//NOTE: The sample assumes a EntityProperties activity variable as IEnumerable<IEnumerable<EntityProperty>> ModelItem sequence = selectedModelItem.GetParent(typeof(Sequence));

Collection<System.Activities.Variable> variables = sequence.Properties["Variables"].ComputedValue as Collection<System.Activities.Variable>; var variable = (from v in variables where v.Name == "EntityProperties" select v).First();

selectedModelItem.Properties["EntityProperties"].SetValue(new OutArgument<IEnumerable<IEnumerable<EntityProperty>>>(variable));

//Get entity sets if (selectedModelItem.Properties["Uri"].Value != null) { uri = selectedModelItem.Properties["Uri"].Value.ToString(); q = new ODataQuery(uri); entitySet = q.EntitySets; }}

How to add an Office context menu item using entity properties

public void AddMenuItem(){ app.CustomizationContext = app.ActiveDocument; MsoControlType menuItem = MsoControlType.msoControlButton;

if (Globals.Ribbons.WorkflowRibbon.Activity != null)

Page 23: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

{ IEnumerable<IEnumerable<EntityProperty>> entityProperties = Globals.Ribbons.WorkflowRibbon.Activity.EntityProperties; if (entityProperties.Count() > 0) { List<string> namedResources = (from item in entityProperties select item).First<IEnumerable<EntityProperty>>() .Where(n => n.Type == "Edm.Stream").Select(n => n.Name).ToList<string>();

foreach (var resource in namedResources) { CommandBarButton oDataResource = (CommandBarButton)app.CommandBars["Table Text"].Controls.Add (menuItem, missing, missing, 1, true); oDataResource.Style = MsoButtonStyle.msoButtonCaption; oDataResource.Caption = resource; oDataResource.Tag = "#oDataResource";

oDataResource.Click += new Microsoft.Office.Core ._CommandBarButtonEvents_ClickEventHandler (oDataResource_Click); } } }

GC.Collect();

}

How to render an OData Named Resource into an Office document

void app_WindowBeforeDoubleClick(word.Selection Sel, ref bool Cancel){ if (Sel.Range.ParentContentControl != null) { if (Sel.Range.ParentContentControl.Title == key) { Sel.Range.ParentContentControl.Range.Select();

app.Selection.InsertBitmapImage (Sel.Range.ParentContentControl.Tag.Replace("#", string.Empty)); } }}

Word Extension

public static class WordExtensions { public static void InsertBitmapImage(this word.Selection selection, string serviceQuery)

Page 24: AdventureWorks OData-Named Resource Streams.docx€¦  · Web viewStarting with SQL Server views, ... the sample shows how to stream LargePhoto and ThumbNailPhoto into a Word document

{ // Create source. BitmapImage bi = new BitmapImage();

// BitmapImage.UriSource must be in a BeginInit/EndInit block. bi.BeginInit(); bi.DownloadCompleted += new EventHandler(bi_DownloadCompleted); bi.UriSource = new Uri(serviceQuery, UriKind.RelativeOrAbsolute); bi.EndInit(); }

static void bi_DownloadCompleted(object sender, EventArgs e) { string fullPath = System.Reflection.Assembly.GetAssembly (typeof(Microsoft.Samples.SqlServer.WordAddIn.WorkflowRibbon)) .Location;

string gifPath = String.Format(@"{0}$word.odata.gif", Path.GetDirectoryName(fullPath));

using (FileStream stream = new FileStream(gifPath, FileMode.Create)) { var encoder = new GifBitmapEncoder(); encoder.Frames.Add(BitmapFrame.Create((BitmapImage)sender)); encoder.Save(stream); }

Globals.ThisAddIn.Application.Selection .InlineShapes.AddPicture(gifPath, false); }}