{"id":5417,"date":"2023-08-08T05:34:33","date_gmt":"2023-08-08T12:34:33","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/odata\/?p=5417"},"modified":"2023-08-08T05:34:33","modified_gmt":"2023-08-08T12:34:33","slug":"working-with-media-resources-in-odata-part-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/odata\/working-with-media-resources-in-odata-part-2\/","title":{"rendered":"Working with media resources in OData &#8211; Part 2"},"content":{"rendered":"<p>In <a href=\"\/odata\/working-with-media-resources-in-odata-part-1\">Part 1<\/a> of this blog post, we demonstrated how to implement an OData service that serves media resources. In Part 2, we look at how to implement a client app that interacts with the OData binary large object (BLOB) feed to both retrieve and post binary data along with the metadata for the media resource.<\/p>\n<p>The OData BLOB feed created in <a href=\"\/odata\/working-with-media-resources-in-odata-part-1\">Part 1<\/a> supported the following functionality:<\/p>\n<ul>\n<li><a href=\"\/odata\/working-with-media-resources-in-odata-part-1\/#posting-a-media-resource\">Posting a media resource<\/a><\/li>\n<li><a href=\"\/odata\/working-with-media-resources-in-odata-part-1\/#retrieving-a-media-resource\">Retrieving a media resource<\/a><\/li>\n<li><a href=\"\/odata\/working-with-media-resources-in-odata-part-1\/#patching-a-media-link-entry\">Patching a media link entry<\/a><\/li>\n<li><a href=\"\/odata\/working-with-media-resources-in-odata-part-1\/#retrieving-a-media-link-entry\">Retrieving a media link entry<\/a><\/li>\n<li><a href=\"\/odata\/working-with-media-resources-in-odata-part-1\/#updating-a-media-resource\">Updating a media resource<\/a><\/li>\n<\/ul>\n<p>The service also exposed a single media link entry (MLE) named <strong><code>Asset<\/code><\/strong> that is linked to a media resource (MR).<\/p>\n<p>In the following sections, we implement a client app to interact with that BLOB feed.<\/p>\n<h2>Create a Console application<\/h2>\n<ul>\n<li>Start Visual Studio 2022 and select <strong>Create a new project<\/strong>.<\/li>\n<li>In the <strong>Create a new project<\/strong> dialog:\n<ul>\n<li>Enter <code>Console<\/code> in the <strong>Search for templates<\/strong> search box.<\/li>\n<li>Select <strong>Console App<\/strong> project template and select <strong>Next<\/strong>.<\/li>\n<\/ul>\n<\/li>\n<li>Name the project <em>ODataMRSample.Client<\/em> and select <strong>Next<\/strong>.<\/li>\n<li>In the <strong>Additional information<\/strong> dialog:\n<ul>\n<li>Select <strong>.NET 6.0 (Long Term Support)<\/strong>.<\/li>\n<li>Check <strong>Do not use top-level statements<\/strong> &#8211; not necessary if you&#8217;re comfortable with using top-level statements that limits you to using local variables and local functions.<\/li>\n<li>Select <strong>Create<\/strong>.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2>Install required packages<\/h2>\n<p>In our client application, we are going to use the <code>Microsoft.OData.Client<\/code> library that offers APIs for interacting with OData BLOB feeds.<\/p>\n<p>Run the following command on the Visual Studio <strong>Package Manager Console<\/strong> to install the <code><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.OData.Client\">Microsoft.OData.Client<\/a><\/code>\u00a0nuget package:<\/p>\n<p><code><span style=\"font-size: 1rem; text-align: var(--bs-body-text-align);\">Install-Package Microsoft.OData.Client<\/span><\/code><\/p>\n<h2>Implement the client functionality<\/h2>\n<p>The BLOB feed implemented in <a href=\"\/odata\/working-with-media-resources-in-odata-part-1\">Part 1<\/a> needs to be running for the endpoints that the client app will be interacting with to be available. Other than for the media resources, the service does not make use of a persistent storage. This means that if the service stopped, any MLEs associated with the media resources are lost. It makes sense therefore to keep the service running throughout the following exercises.<\/p>\n<p>Start by making the following changes to the <em>ODataMRSample.Client<\/em> project:<\/p>\n<ul>\n<li>Add a folder named <strong>Resources<\/strong> to the project. We&#8217;ll stash media resources that we want to post to the BLOB feed in this folder.<\/li>\n<li>Add a folder named <strong>Store<\/strong> to the project. We&#8217;ll persist media resources that we retrieve from the BLOB feed in this folder.<\/li>\n<li>Add a static class variable named <code>serviceRoot<\/code> to <code>Program<\/code> class &#8211; in <em>Program.cs<\/em> file. This variable will hold the root endpoint of the BLOB feed we implemented in <a href=\"\/odata\/working-with-media-resources-in-odata-part-1\">Part 1<\/a>.<\/li>\n<\/ul>\n<pre class=\"prettyprint language-cs language-csharp\" style=\"padding-left: 40px;\"><code class=\"language-cs language-csharp\">namespace ODataMRSample.Client\r\n{\r\n    internal class Program\r\n    {\r\n        static readonly Uri serviceRoot = new Uri(\"http:\/\/localhost:5000\");\r\n\r\n        static void Main(string[] args)\r\n        {\r\n            \/\/ ...\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Next, generate a strongly-typed OData client proxy using the <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=marketplace.ODataConnectedService2022\">OData Connected Service<\/a> extension.<\/p>\n<p>If <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=marketplace.ODataConnectedService2022\">OData Connected Service<\/a> extension is not installed, follow the steps below to install from the <a href=\"https:\/\/marketplace.visualstudio.com\/\">Visual Studio marketplace<\/a>:<\/p>\n<ul>\n<li>From the Visual Studio <strong>Extensions<\/strong> menu, select <strong>Manage Extensions<\/strong>.<\/li>\n<li>From the <strong>Manage Extensions<\/strong> window, type &#8220;OData Connected Service&#8221; on the <strong>Search<\/strong> box.<\/li>\n<li>The <strong>OData Connected Service 2022+<\/strong> extension should appear on the list if you&#8217;re using Visual Studio 2022.<\/li>\n<\/ul>\n<p style=\"padding-left: 40px;\"><a href=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-install-ocs-extension.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5436\" src=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-install-ocs-extension.png\" alt=\"Image media resource odata client install ocs extension\" width=\"941\" height=\"653\" srcset=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-install-ocs-extension.png 941w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-install-ocs-extension-300x208.png 300w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-install-ocs-extension-768x533.png 768w\" sizes=\"(max-width: 941px) 100vw, 941px\" \/><\/a><\/p>\n<ul>\n<li>A button labelled <strong>Download<\/strong>\u00a0should appear on the list item if the extension is not installed. Click the button to install the extension.<\/li>\n<\/ul>\n<h3>Generate the client proxy<\/h3>\n<p>Ensure that the ASP.NET Core application created in <a href=\"\/odata\/working-with-media-resources-in-odata-part-1\">Part 1<\/a> is running before proceeding with the following steps to generate the OData client proxy:<\/p>\n<ul>\n<li>In <strong>Solution Explorer<\/strong>, right-click <strong>ODataMRSample.Client<\/strong> project. Click <strong>Add<\/strong> &gt; <strong>Connected Service<\/strong> to open the <strong>Connected Services<\/strong> view.<\/li>\n<li>Click <strong>OData Connected Service<\/strong> (under <strong>Other Services<\/strong>) to launch the OData Connected Service wizard.<\/li>\n<li>On the <strong>Endpoint<\/strong> wizard page, provide a suitable service name (e.g., <em>ODataMRService<\/em>) and enter the endpoint which the OData service from <a href=\"\/odata\/working-with-media-resources-in-odata-part-1\">Part 1<\/a> is listening on, e.g., <a href=\"http:\/\/localhost:5000\">http:\/\/localhost:5000<\/a>. If you specified a route prefix for the OData service, make sure to append it to the endpoint, e.g., <a href=\"http:\/\/localhost:5000\/ROUTE_PREFIX\">http:\/\/localhost:5000\/ROUTE_PREFIX<\/a>.<\/li>\n<\/ul>\n<p style=\"padding-left: 40px;\"><a href=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-install-ocs-configure-endpoint-1.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5440\" src=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-install-ocs-configure-endpoint-1.png\" alt=\"Image media resource odata client install ocs configure endpoint\" width=\"804\" height=\"552\" srcset=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-install-ocs-configure-endpoint-1.png 804w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-install-ocs-configure-endpoint-1-300x206.png 300w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-install-ocs-configure-endpoint-1-768x527.png 768w\" sizes=\"(max-width: 804px) 100vw, 804px\" \/><\/a><\/p>\n<ul>\n<li>Click <strong>Next<\/strong> to move to the <strong>Schema Types Selection<\/strong> wizard page. Leave everything unchanged.<\/li>\n<li>Click <strong>Next<\/strong> again to move to the <strong>Function\/Action Imports Selection<\/strong> wizard page. Leave everything unchanged.<\/li>\n<li>Click <strong>Next<\/strong> again to move to the <strong>Settings<\/strong> wizard page. If you wish, you can provide your own name for the client proxy, e.g. <em>ODataMRProxy<\/em>. Leave the other settings unchanged.<\/li>\n<li>Click <strong>Finish<\/strong> to trigger the generation of the client proxy.<\/li>\n<\/ul>\n<p style=\"padding-left: 40px;\"><a href=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-connected-service-configured.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5442\" src=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-connected-service-configured.png\" alt=\"Image media resource odata client connected service configured\" width=\"1023\" height=\"622\" srcset=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-connected-service-configured.png 1023w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-connected-service-configured-300x182.png 300w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-connected-service-configured-768x467.png 768w\" sizes=\"(max-width: 1023px) 100vw, 1023px\" \/><\/a><\/p>\n<p>If you inspect the file containing the proxy classes generated using the <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=marketplace.ODataConnectedService2022\">OData Connected Service<\/a> extension, you should find a class named <code>Asset<\/code>. Let&#8217;s augment the class with additional properties to hold the MR metadata:<\/p>\n<ul>\n<li>Add a folder named <strong>Models<\/strong>.<\/li>\n<li>Add a C# code file named <em>Partials.cs<\/em> to the <strong>Models<\/strong> folder.<\/li>\n<li>Replace the contents of the <em>Partials.cs<\/em> file with the following:<\/li>\n<\/ul>\n<pre class=\"prettyprint language-cs language-csharp\" style=\"padding-left: 40px;\"><code class=\"language-cs language-csharp\">namespace ODataMRSample.Models\r\n{\r\n    public partial class Asset\r\n    {\r\n        public string? Description { get; set; }\r\n        public string? Color { get; set; }\r\n    }\r\n}<\/code><\/pre>\n<p>An important thing to note here is that the namespace in the code block above is the same as the one for the <code>Asset<\/code> class in the client proxy &#8211; <em>ODataMRProxy.cs<\/em>.<\/p>\n<p>At this point, we can proceed with the implementation of the logic for interacting with the BLOB feed.<\/p>\n<h3>Posting a media resource<\/h3>\n<p>Choose an image to upload to the service and add it to the <strong>Resources<\/strong> folder. For this exercise, we&#8217;ll upload a black square image &#8211; <em>black.png<\/em>.<\/p>\n<p>Posting an MR from the client app to the BLOB feed requires the following steps:<\/p>\n<ul>\n<li>Create an MLE object and set its properties.<\/li>\n<li>Read an MR from the disk into a stream and then pass the stream together with the MLE object into the <code>SetSaveStream<\/code> method of the <code>DataServiceContext<\/code> instance.<\/li>\n<li>Call <code>SaveChanges<\/code> method (or <code>SaveChangesAsync<\/code>\u00a0in async scenarios) of the <code>DataServiceContext<\/code> instance to send the MR as a binary stream in a <code>POST<\/code> request and the MLE properties in a <code>PATCH<\/code> request.<\/li>\n<\/ul>\n<p>The <code>CreateMLE<\/code> method below shows the logic for posting an MR:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">static void CreateMLE()\r\n{\r\n    var dataServiceContext = new Default.Container(serviceRoot);\r\n\r\n    var asset = new Asset\r\n    {\r\n        Description = \"Square\",\r\n        Color = \"Black\"\r\n    };\r\n\r\n    dataServiceContext.AddToAssets(asset);\r\n\r\n    var fileName = \"black.png\";\r\n    var path = $\"..\\\\..\\\\..\\\\Resources\\\\{fileName}\";\r\n\r\n    using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))\r\n    {\r\n        var requestArgs = new DataServiceRequestArgs();\r\n        requestArgs.ContentType = \"image\/png\";\r\n        requestArgs.Headers.Add(\"Content-Disposition\", $\"inline;filename={fileName}\");\r\n\r\n        dataServiceContext.SetSaveStream(asset, fileStream, closeStream: false, args: requestArgs);\r\n        dataServiceContext.BuildingRequest += DataServiceContext_BuildingRequest;\r\n\r\n        \/\/ The response for the POST request contains the Asset MLE\r\n        \/\/ Retrieve the value of the Id property and assign it to Id property of the Asset object\r\n        dataServiceContext.Configurations.ResponsePipeline.OnEntryStarted((args) =&gt;\r\n        {\r\n            if (args.Entry.TypeName == typeof(Asset).FullName)\r\n            {\r\n                var idProperty = args.Entry.Properties.Single(p =&gt; p.Name == \"Id\");\r\n                asset.Id = idProperty.Value.ToString();\r\n            }\r\n        });\r\n\r\n        dataServiceContext.SaveChanges();\r\n    }\r\n}\r\n\r\nprivate static void DataServiceContext_BuildingRequest(object? sender, BuildingRequestEventArgs e)\r\n{\r\n            \r\n}<\/code><\/pre>\n<p>There are a few things to note about the above block of code:<\/p>\n<ul>\n<li>We are using a <code>DataServiceRequestArgs<\/code> object to specify the <code>Content-Type<\/code> of the MR together with any other request headers.<\/li>\n<li>Since the <code>Id<\/code> is server-generated, we are hooking into the response pipeline to capture the entry materialized from the response payload whereupon we retrieve the value of the <code>Id<\/code> property and assign it to the `Id` property of the <code>Asset<\/code> object.<\/li>\n<li>The event handler attached to the <code>BuildRequest<\/code> event is only for the purpose of helping us inspect the 2 requests that will be sent to the service when <code>SaveChanges<\/code> method is executed.<\/li>\n<\/ul>\n<p>Modify the <code>Main<\/code> method to call the <code>CreateMLE<\/code> method:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">static void Main(string[] args)\r\n{\r\n    CreateMLE();\r\n}<\/code><\/pre>\n<p>Put a breakpoint in the <code>DataServiceContext_BuildingRequest<\/code> method and run the app &#8211; ensure that the ASP.NET Core application created in <a href=\"\/odata\/working-with-media-resources-in-odata-part-1\">Part 1<\/a> is running.<\/p>\n<p>The first time the breakpoint is hit, inspecting the <code>BuildingRequestEventArgs<\/code> parameter shows that the request is a <code>POST<\/code> to the <code>\/Assets<\/code> endpoint:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-1-1.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5453\" src=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-1-1.png\" alt=\"Image media resource odata client create mle request 1\" width=\"1037\" height=\"259\" srcset=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-1-1.png 1037w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-1-1-300x75.png 300w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-1-1-1024x256.png 1024w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-1-1-768x192.png 768w\" sizes=\"(max-width: 1037px) 100vw, 1037px\" \/><\/a><\/p>\n<p>The second time the breakpoint is hit, the request is a <code>PATCH<\/code> to the <code>\/Assets({key})<\/code> endpoint:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-2.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5449\" src=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-2.png\" alt=\"Image media resource odata client create mle request 2\" width=\"1035\" height=\"277\" srcset=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-2.png 1035w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-2-300x80.png 300w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-2-1024x274.png 1024w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-create-mle-request-2-768x206.png 768w\" sizes=\"(max-width: 1035px) 100vw, 1035px\" \/><\/a><\/p>\n<h3>Retrieving a media resource<\/h3>\n<p>A client app retrieves a media resource from the BLOB feed in the following steps:<\/p>\n<ul>\n<li>Retrieve the MLE entry linked to the MR from the service.<\/li>\n<li>Pass the MLE entry into the <code>GetReadStream<\/code> method of the <code>DataServiceContext<\/code> instance to retrieve the linked MR.<\/li>\n<\/ul>\n<p>The <code>GetMLE<\/code> method below shows the logic for retrieving an MR:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">static void GetMLE()\r\n{\r\n    var dataServiceContext = new Default.Container(serviceRoot);\r\n\r\n    var asset = dataServiceContext.Assets.ByKey(\"47bc49a\").GetValue();\r\n\r\n    dataServiceContext.BuildingRequest += DataServiceContext_BuildingRequest;\r\n    var dataServiceStreamResponse = dataServiceContext.GetReadStream(asset);\r\n\r\n    var fileName = \"black.png\";\r\n    var path = $\"..\\\\..\\\\..\\\\Store\\\\{fileName}\";\r\n\r\n    using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))\r\n    {\r\n        dataServiceStreamResponse.Stream.CopyTo(fileStream);\r\n    }\r\n}<\/code><\/pre>\n<p class=\"prettyprint language-cs language-csharp\">Just like in the previous section, the event handler attached to the <code>BuildingRequest<\/code> event will help us inspect the request that is sent to the service when <code>GetReadStream<\/code> method is executed.<\/p>\n<p>Before running the app, modify the <code>Main<\/code> method to call the <code>GetMLE<\/code> method and put a breakpoint in the <code>DataServiceContext_BuildingRequest<\/code> method:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">static void Main(string[] args)\r\n{\r\n    GetMLE();\r\n}<\/code><\/pre>\n<p>When the app is ran, the MR uploaded in the previous section is retrieved and saved to the <strong>Store<\/strong> folder. Execution is paused at the breakpoint in the <code>DataServiceContext_BuildingRequest<\/code>\u00a0method. Inspecting the <code>BuildingRequestEventArgs<\/code> parameter shows that the request for the MR is a <code>GET<\/code> to the <code>\/Assets({key}\/$value<\/code> endpoint:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-get-mle-request.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5455\" src=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-get-mle-request.png\" alt=\"Image media resource odata client get mle request\" width=\"1036\" height=\"248\" srcset=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-get-mle-request.png 1036w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-get-mle-request-300x72.png 300w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-get-mle-request-1024x245.png 1024w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-get-mle-request-768x184.png 768w\" sizes=\"(max-width: 1036px) 100vw, 1036px\" \/><\/a><\/p>\n<h3>Updating a media resource<\/h3>\n<p>Choose an image to replace the one uploaded previously and add it to the <strong>Resources<\/strong> folder. For this exercise, we&#8217;ll replace the uploaded MR with a purple square image &#8211; <em>purple.png<\/em>.<\/p>\n<p>Updating an MR from the client app involves the following steps:<\/p>\n<ul>\n<li>Retrieve the MLE entry linked to the MR from the service.<\/li>\n<li>Optionally modify the MLE&#8217;s properties and pass the MLE object to the <code>UpdateObject<\/code> method of the <code>DataServiceContext<\/code> instance.<\/li>\n<li>Read the replacement MR from the disk into a stream and then pass the stream together with the MLE object into the <code>SetSaveStream<\/code> method of the <code>DataServiceContext<\/code> instance.<\/li>\n<li>Call <code>SaveChanges<\/code> method (or <code>SaveChangesAsync<\/code> in async scenarios) of the <code>DataServiceContext<\/code> instance to send the replacement MR as a binary stream in a <code>PUT<\/code> request, and the MLE properties in a <code>PATCH<\/code> request &#8211; if the MLE was modified.<\/li>\n<\/ul>\n<p>The <code>UpdateMLE<\/code> method below shows the logic for updating an MR:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">static void UpdateMLE()\r\n{\r\n    var dataServiceContext = new Default.Container(serviceRoot);\r\n    var asset = dataServiceContext.Assets.ByKey(\"47bc49a\").GetValue();\r\n\r\n    asset.Color = \"Purple\";\r\n    dataServiceContext.BuildingRequest += DataServiceContext_BuildingRequest;\r\n    dataServiceContext.UpdateObject(asset);\r\n\r\n    var fileName = \"purple.png\";\r\n    var path = $\"..\\\\..\\\\..\\\\Resources\\\\{fileName}\";\r\n\r\n    using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))\r\n    {\r\n        var requestArgs = new DataServiceRequestArgs();\r\n        requestArgs.ContentType = \"image\/png\";\r\n        requestArgs.Headers.Add(\"Content-Disposition\", $\"inline;filename={fileName}\");\r\n\r\n        dataServiceContext.SetSaveStream(asset, fileStream, closeStream: false, args: requestArgs);\r\n\r\n        dataServiceContext.SaveChanges();\r\n    }\r\n}<\/code><\/pre>\n<p>The event handler attached to the <code>BuildingRequest<\/code> event will help us inspect the request that is sent to the service when <code>SaveChanges<\/code> method is called.<\/p>\n<p>Before running the app, modify the <code>Main<\/code> method to call the <code>UpdateMLE<\/code> method and put a breakpoint in the <code>DataServiceContext_BuildingRequest<\/code> method:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">static void Main(string[] args)\r\n{\r\n    UpdateMLE();\r\n}<\/code><\/pre>\n<p>When the app is ran, execution will pause at the breakpoint and inspecting the <code>BuildingRequestEventArgs<\/code> parameter the first time execution is paused shows that the request is a <code>PUT<\/code> to the <code>\/Assets({key})\/$value<\/code> endpoint:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-update-mle-request.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-5458\" src=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-update-mle-request.png\" alt=\"Image media resource odata client update mle request\" width=\"1035\" height=\"247\" srcset=\"https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-update-mle-request.png 1035w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-update-mle-request-300x72.png 300w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-update-mle-request-1024x244.png 1024w, https:\/\/devblogs.microsoft.com\/odata\/wp-content\/uploads\/sites\/23\/2023\/06\/media-resource-odata-client-update-mle-request-768x183.png 768w\" sizes=\"(max-width: 1035px) 100vw, 1035px\" \/><\/a><\/p>\n<h3 id=\"summary\">Summary<i class=\"fabric-icon fabric-icon--Link\" aria-hidden=\"true\"><\/i><\/h3>\n<p>It is my hope that this blog post helps you understand how the OData client library supports working with media resources.<\/p>\n<p>If you have any questions, comments, or if you run into any issues, feel free to reach out on this blog post or report the issue on <a href=\"https:\/\/github.com\/OData\/odata.net\/issues\" target=\"_blank\" rel=\"noopener\">GitHub repo<\/a> for OData client.<\/p>\n<p>Find the source code for this blog post\u00a0<a href=\"https:\/\/github.com\/gathogojr\/ODataMRSample\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<p><code class=\"language-cs language-csharp\"><\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Part 1 of this blog post, we demonstrated how to implement an OData service that serves media resources. In Part 2, we look at how to implement a client app that interacts with the OData binary large object (BLOB) feed to both retrieve and post binary data along with the metadata for the media [&hellip;]<\/p>\n","protected":false},"author":25333,"featured_media":3253,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-5417","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-odata"],"acf":[],"blog_post_summary":"<p>In Part 1 of this blog post, we demonstrated how to implement an OData service that serves media resources. In Part 2, we look at how to implement a client app that interacts with the OData binary large object (BLOB) feed to both retrieve and post binary data along with the metadata for the media [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/5417","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/users\/25333"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/comments?post=5417"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/posts\/5417\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media\/3253"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/media?parent=5417"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/categories?post=5417"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/odata\/wp-json\/wp\/v2\/tags?post=5417"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}