<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>WarNov Developer Evangelist</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/warnov/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/warnov/feed.xml</link>
    <description>Open mind 4 a Different Coding</description>
    <lastBuildDate>Wed, 27 Feb 2019 04:13:54 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Working with AZCopy 10 and Azure Storage Blob Access Tiers</title>
      <link>https://docs.microsoft.com/archive/blogs/warnov/working-with-azcopy-10-and-azure-storage-blob-access-tiers</link>
      <pubDate>Tue, 26 Feb 2019 22:54:34 GMT</pubDate>
      <dc:creator><![CDATA[WarNov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/warnov/?p=4075</guid>
      <description><![CDATA[As you may know, today the Azure Blob Storage offers three different access tiers that allow us to...]]></description>
      <content:encoded><![CDATA[<p>As you may know, today the Azure Blob Storage offers three different access tiers that allow us to save money depending on our storage requirements: If we need our data immediately (hot), eventually (cool) or rarely (archive). More info about the features of each tier here. </p>
<p>Actually, we can configure an Azure Storage Account to have an access tier by default: hot or cool. Archive is not an option for an entire account though; so you just can specify if a given blob will be in archive mode.</p>
<p>When you mark a storage account to be cool by default, any single blob uploaded to that account will have its access tier set to cool if no other access tier is specified during the upload operation.</p>
<p>This means, that even when you have declared a default access tier for a storage account it can contain blobs with different access tires among them (i.e. a cool storage account can have hot and archive blobs as well).</p>
<p>Working with these blob features is pretty straightforward using the Azure Portal and its GUI. But what if you need to upload some millions of blobs in hot mode to a storage account that has been configured with a cool access tier?</p>
<p>Enter <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10">AzCopy</a>: the legendary console tool that always has been there to help us with uploading blobs to the Azure Storage. Today, this tool is in its 10th version and in preview mode. This version represents a radical change in how we execute commands: now they are easier and cleaner to use. Its architecture has also been modified for high-performance reliable data transfers. Besides this, it now ships the ability to work with access tiers (this feature wasn’t available in previous versions because access tiers are relatively new).</p><p>So, let’s take a look to how take advantage of these new features to achieve our goal of upload blobs specifying their access tiers explicitly:</p><ol><li><a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10#latest-preview-version-v10">Download</a> the v10 (preview or further) version of <font face="Courier New">AzCopy</font>. It now consists only of one *.exe that you can save in your preferred location. You can even change its name (I did it in order to keep parallel versions: the 8.1 along the v10 preview; so now I call the latest one by typing <font face="Courier New">azc10pre</font>)</li><li>To work with your storage account Azure requires that you either Login into the tool using Azure Active Directory or use SAS Tokens. For this example I will be using SAS tokens: There are <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-dotnet-shared-access-signature-part-1?toc=%2fazure%2fstorage%2fblobs%2ftoc.json">many ways to get a SAS token</a>. Once you have it you just need to append it at the end of the path of the blob you are working with. In a nutshell, the SAS token contains a passport to work with the storage account for a limited time and under some conditions.</li><li>Let’s create a container for our example:</li><ul><li><font face="Courier New">azcopy make https://<em>account</em>.blob.core.windows.net/<em>container-name</em><strong><em>sastoken</em></strong><strong><em><a></a></em></strong></font></li><ul><li><font face="Segoe UI">Observe how we have appended the sas token.</font></li></ul></ul><li>Now let’s use the CP command to copy two files from our computer to the cloud:</li><ul><li><font face="Courier New">azcopy cp "C:\local\path\myfile" "https://account.blob.core.windows.net/container/myfile<em></em></font><strong><a></a><em><a><font face="Courier New">sastoken</font></a><font face="Courier New">”</font> </em></strong></li><ul><li>If we operate in this way the resulting uploaded blob, will be in cool tier as it is the account´s default and we didn’t specified anything else. But, if we need to override that default, we should use:</li></ul><li><font face="Courier New">azcopy cp "C:\local\path\myfile" "https://account.blob.core.windows.net/container/myfile<em></em><a></a><em><a><strong>sastoken</strong></a><strong>” </strong>--block-blob-tier="hot"</em></font></li></ul></ol><p><font face="Segoe UI">So this is the way you can have mixed access tiers inside a given storage account using the AzTool that works for Windows, Linux and MacOS</font></p>]]></content:encoded>
    </item>
    <item>
      <title>Replacing Keen.io functionalities with Azure Functions and Cosmos DB</title>
      <link>https://docs.microsoft.com/archive/blogs/warnov/replacing-keen-io-functionalities-with-azure-functions-and-cosmos-db</link>
      <pubDate>Sun, 18 Nov 2018 00:17:55 GMT</pubDate>
      <dc:creator><![CDATA[WarNov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/warnov/?p=4055</guid>
      <description><![CDATA[One of my customers consulted me about how to optimize the costs they were having by receiving...]]></description>
      <content:encoded><![CDATA[<p align="justify">One of my customers consulted me about how to optimize the costs they were having by receiving SendGrid notifications with the state of the emails they sent. After thinking about a possible solution in Azure I came up with an idea that allowed my customer to reduce their costs from USD$500 to less than USD$50 each month. Keep reading and find out how.<p align="justify"><img width="247" height="300" style="margin-right: auto;margin-left: auto;float: none" alt="Save Money with Multiple Insurance Policies from Same Company" src="https://www.insurancebyjoed.com/wp-content/uploads/2013/10/savings-ahead-street-sign-247x300.jpg"></p><p align="justify"><a href="https://sendgrid.com/partners/azure/">SendGrid</a> is a very good email sending service that allows you to send millions of emails through their platform. One of the mechanisms they have to inform you about what was the result of sending your massive emails (opened, reported as spam, deleted without being read, etc.) is <a href="https://sendgrid.com/docs/API_Reference/Event_Webhook/event.html">by sending a HTTP POST to a URL you can provide</a>. The <font face="Courier New">HTTP POST</font> will then contain a <font face="Courier New">JSON</font> body with details about the result of the emails sent. <p align="justify">You could be interested in store this information in a repository where you could be able to query these reports. For example, to know what messages were reported as spam during the last three days.<p align="justify">My customer was using a service called Keen.io that provided him with the webhook so SendGrid could send the information there, and with a repository and a platform to query the data gathered by the webhook.<p align="justify">But after a couple of months of being using this service, the costs started to rise notoriously. Hitting USD$500 just for some thousands of notifications received.<p align="justify">So, they consulted me, described to me the situation and I immediately imagined a solution based on Azure that will be obviously a way cheaper.<p align="justify">The main components would be the almighty Azure Functions on the receiving URL side, and Azure Cosmos DB as a repository.<p align="justify"><a href="https://azure.microsoft.com/en-us/services/functions/">Azure Functions</a> can be deployed on the consumption plan model i.e. they will charge just for the seconds they are running. Not to mention that Azure gives you a million of Azure Functions calls per month for free!<p align="justify">On the other hand, <a href="https://azure.microsoft.com/en-us/services/cosmos-db/">Cosmos DB</a> can store your JSON documents natively and make advanced and fast queries over them without too much effort. And since all the information could be stored in just one collection, you can keep the costs very low, as the price for just one collection of 400 RUs (you won't need anything else for a use case like these) is $21.92.<p align="justify">The other considerable cost associated with the Cosmos DB will be the storage per se. It is $0.25 GB/month. Let's exaggerate and say you have one GB of notifications per month. On the first month, your total costs will be near to $22+$0.25=$22.25. On the second, you'll pay $22 plus what you were already storing: $0.25 plus another GB: $0.25. A total of $22.5. And so on. <p align="justify">All in all, Keep.io is a very good service that keeps you away from the implementation details; but you could do it by yourself on your own Azure subscription while keeping the costs far down from one-tenth of the original cost of an already available service.<h3 align="justify">Let's get down to work</h3><p align="justify">The following diagram depicts the solution for this case:<p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/2018/11/image124.png"><img width="468" height="255" title="image" alt="image" src="https://msdnshared.blob.core.windows.net/media/2018/11/image_thumb115.png" border="0"></a></p><p align="justify">First, you would like to <a href="https://docs.microsoft.com/en-us/azure/cosmos-db/sql-api-dotnetcore-get-started">create a Cosmos DB on your subscription</a> or maybe getting a <a href="https://azure.microsoft.com/en-us/try/cosmosdb/">free testing Cosmos DB</a>. We are so flexible, that you could use even a <a href="https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator">Cosmos DB emulator</a>.<p align="justify">Once you have created your Cosmos DB, you will have its endpoint and connection key (required to make the connection - like the connection string for a SQL Database). Keep them at hand as we are going to configure our function with them.<p align="justify">Now, using your Visual Studio, create an HTTP Triggered Azure Function. A new function with a predefined template will be created. Remove the get method from the function header as we are going to need just the post method. It is a good practice to reduce the attack surface a possible intruder could have. <p align="justify">By now, November 2018, the template will come a little outdated (the tracer for logging is now being deprecated and replaced by the <font face="Courier New">ILogger</font>). But not a problem. The code I'm showing you is already adjusted to being on the cutting edge (for now). Another thing to update is how we access the configuration settings. Observe that I have added the <font face="Courier New">ExecutionContext </font>parameter as input for the template. It will help us to set up the <font face="Courier New">config </font>object to get those variables in the way that is used in the .NET Core world. <p align="justify">So, if we are in the development environment, app settings will be loaded from <font face="Courier New">local.settings.json</font>; but in production, these variables will be loaded from the appsettings tab of the app service.<p align="justify">The data sent from the client (SendGrid or another source) to Azure Functions is supposed to come in raw JSON format inside the body of the post message as an array of JSON documents. That's why I am using the <font face="Courier New">JArray</font> object to store these documents.<p align="justify">Once you get the documents in memory it is time for us to communicate to the Cosmos DB and insert that info in the required collection. Apart from the Cosmos DB URI and Key, we are going to need the name of the DB and collection in which we need to store the data. All these values should be provided as configuration variables, so you can change the repository whenever you want, without changing your code.<p align="justify">The <font face="Courier New">DocumentClient</font> object will do the magic for us and when it is configured as we need a simple <font face="Courier New">foreach</font> will be required to traverse the <font face="Courier New">JArray</font> object and for each entry (a <font face="Courier New">JObject</font>) it will be inserted in the Cosmos DB using the operation <font face="Courier New">CreateDocumentAsync</font>.<p align="justify">If there are errors when inserting a document, in this case, the execution continues with the other documents. So, this insertion is not transactional. To make this execution transactional you should create a Cosmos DB stored procedure containing the transaction logic as right now there are no means to control transactions from Cosmos DB clients. Just inside the engine. The failed insertions will be reported in the log, and at the end of the execution, in the response, it will be noted that not all the documents were inserted. This could help administrators to check what went wrong.<p>This is the code that summarizes my previous instructions:<pre style="background: black;color: lime;font-family: ocr a extended, courier new, monospace;font-size: 12px"><span style="color: gainsboro">[</span><span style="color: cyan">FunctionName</span><span style="color: gainsboro">(</span><span style="color: yellow">"JsonInserter"</span><span style="color: gainsboro">)]</span><br><span style="color: white">public</span>&nbsp;<span style="color: white">static</span>&nbsp;<span style="color: white">async</span>&nbsp;<span style="color: cyan">Task</span><span style="color: gainsboro">&lt;</span><span>IActionResult</span><span style="color: gainsboro">&gt;</span> Run<br>&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">([</span><span style="color: cyan">HttpTrigger</span><span style="color: gainsboro">(</span><span>AuthorizationLevel</span><span style="color: yellow">.</span>Function<span style="color: gainsboro">,</span>&nbsp;<span style="color: yellow">"post"</span><span style="color: gainsboro">,</span> Route <span style="color: yellow">=</span>&nbsp;<span style="color: white">null</span><span style="color: gainsboro">)]</span><span style="color: cyan">HttpRequest</span> req<span style="color: gainsboro">,</span><br>&nbsp;&nbsp;&nbsp; <span>ILogger</span> log<span style="color: gainsboro">,</span><br>&nbsp;&nbsp;&nbsp; <span style="color: cyan">ExecutionContext</span> context<span style="color: gainsboro">)</span>&nbsp; <br><span style="color: gainsboro">{</span><br> <br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//This is the proper .net core way to get the config object to access configuration variables</span><br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//If we are in the development environment, app settings will be loaded from local.settings.json</span><br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//In production, these variables will be loaded from the appsettings tab of the app service</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">var</span> config <span style="color: yellow">=</span>&nbsp;<span style="color: white">new</span>&nbsp;<span style="color: cyan">ConfigurationBuilder</span><span style="color: gainsboro">()</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: yellow">.</span>SetBasePath<span style="color: gainsboro">(</span>context<span style="color: yellow">.</span>FunctionAppDirectory<span style="color: gainsboro">)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: yellow">.</span>AddJsonFile<span style="color: gainsboro">(</span><span style="color: yellow">"local.settings.json"</span><span style="color: gainsboro">,</span> optional<span style="color: gainsboro">:</span>&nbsp;<span style="color: white">true</span><span style="color: gainsboro">,</span> reloadOnChange<span style="color: gainsboro">:</span>&nbsp;<span style="color: white">true</span><span style="color: gainsboro">)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: yellow">.</span>AddEnvironmentVariables<span style="color: gainsboro">()</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: yellow">.</span>Build<span style="color: gainsboro">();</span><br> <br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//Getting the configuration variables (names are self explanatory)</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">var</span> cosmosDBEndpointUri <span style="color: yellow">=</span> config<span style="color: gainsboro">[</span><span style="color: yellow">"cosmosDBEndpointUri"</span><span style="color: gainsboro">];</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">var</span> cosmosDBPrimaryKey <span style="color: yellow">=</span> config<span style="color: gainsboro">[</span><span style="color: yellow">"cosmosDBPrimaryKey"</span><span style="color: gainsboro">];</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">var</span> cosmosDBName <span style="color: yellow">=</span> config<span style="color: gainsboro">[</span><span style="color: yellow">"cosmosDBName"</span><span style="color: gainsboro">];</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">var</span> cosmosDBCollectionName <span style="color: yellow">=</span> config<span style="color: gainsboro">[</span><span style="color: yellow">"cosmosDBCollectionName"</span><span style="color: gainsboro">];</span><br> <br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//The data sent from the client to Azure Functions is supossed to come</span><br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//in raw json format inside the body of the post message; </span><br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//specifically, as an array of json documents.</span><br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//That's why we are using the JArray object. To store these documents</span><br>&nbsp;&nbsp;&nbsp; <span style="color: cyan">JArray</span> data<span style="color: gainsboro">;</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">string</span> requestBody <span style="color: yellow">=</span>&nbsp;<span style="color: white">new</span>&nbsp;<span style="color: cyan">StreamReader</span><span style="color: gainsboro">(</span>req<span style="color: yellow">.</span>Body<span style="color: gainsboro">)</span><span style="color: yellow">.</span>ReadToEnd<span style="color: gainsboro">();</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">try</span><br>&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">{</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data <span style="color: yellow">=</span>&nbsp;<span style="color: gainsboro">(</span><span style="color: cyan">JArray</span><span style="color: gainsboro">)</span><span style="color: cyan">JsonConvert</span><span style="color: yellow">.</span>DeserializeObject<span style="color: gainsboro">(</span>requestBody<span style="color: gainsboro">);</span><br>&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">}</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">catch</span><span style="color: gainsboro">(</span><span style="color: cyan">Exception</span> exc<span style="color: gainsboro">)</span><br>&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">{</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//As we relay on the json info correctly beign parsed, </span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//if we have an exception trying to parse it, then we must finish the execution</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//Exceptions in this functions are handled writing a message in the log</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//so we could trace in which part of the fucntion it was originated</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//And returning the most suitable error message with the exception text</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//as the function response</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log<span style="color: yellow">.</span>LogError<span style="color: gainsboro">(</span><span style="color: yellow">$"Problem with JSON input: </span><span style="color: gainsboro">{</span>exc<span style="color: yellow">.</span>Message<span style="color: gainsboro">}</span><span style="color: yellow">"</span><span style="color: gainsboro">);</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//Bad request since the format of the message is not as expected</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: white">return</span>&nbsp;<span style="color: white">new</span>&nbsp;<span style="color: cyan">BadRequestObjectResult</span><span style="color: gainsboro">(</span>exc<span style="color: gainsboro">);</span>&nbsp;<br>&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">}</span><br> <br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//This is going to be the object that allows us to communicate with cosmosDB</span><br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//It comes in the Microsoft.Azure.DocumentDB.Core package, so be sure to include it</span><br>&nbsp;&nbsp;&nbsp; <span style="color: cyan">DocumentClient</span> client<span style="color: gainsboro">;</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">try</span><br>&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">{</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//Client must be initialized with the cosmosdb uri and key</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; client <span style="color: yellow">=</span>&nbsp;<span style="color: white">new</span>&nbsp;<span style="color: cyan">DocumentClient</span><span style="color: gainsboro">(</span><span style="color: white">new</span>&nbsp;<span style="color: cyan">Uri</span><span style="color: gainsboro">(</span>cosmosDBEndpointUri<span style="color: gainsboro">),</span> cosmosDBPrimaryKey<span style="color: gainsboro">);</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//If the desired database doesn't exist it is created</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: white">await</span> client<span style="color: yellow">.</span>CreateDatabaseIfNotExistsAsync<span style="color: gainsboro">(</span><span style="color: white">new</span>&nbsp;<span style="color: cyan">Database</span>&nbsp;<span style="color: gainsboro">{</span> Id <span style="color: yellow">=</span> cosmosDBName <span style="color: gainsboro">});</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//If the desired collection doesn't exist it is created</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: white">await</span> client<span style="color: yellow">.</span>CreateDocumentCollectionIfNotExistsAsync<span style="color: gainsboro">(</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: cyan">UriFactory</span><span style="color: yellow">.</span>CreateDatabaseUri<span style="color: gainsboro">(</span>cosmosDBName<span style="color: gainsboro">),</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: white">new</span>&nbsp;<span style="color: cyan">DocumentCollection</span>&nbsp;<span style="color: gainsboro">{</span> Id <span style="color: yellow">=</span> cosmosDBCollectionName <span style="color: gainsboro">});</span><br>&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">}</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">catch</span>&nbsp;<span style="color: gainsboro">(</span><span style="color: cyan">Exception</span> exc<span style="color: gainsboro">)</span><br>&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">{</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log<span style="color: yellow">.</span>LogError<span style="color: gainsboro">(</span><span style="color: yellow">$"Problem communicating with CosmosDB: </span><span style="color: gainsboro">{</span>exc<span style="color: yellow">.</span>Message<span style="color: gainsboro">}</span><span style="color: yellow">"</span><span style="color: gainsboro">);</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: white">return</span>&nbsp;<span style="color: white">new</span>&nbsp;<span style="color: cyan">ExceptionResult</span><span style="color: gainsboro">(</span>exc<span style="color: gainsboro">,</span>&nbsp;<span style="color: white">true</span><span style="color: gainsboro">);</span><br>&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">}</span><br> <br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//Now that we have the db context we can proceed to insert the json documents in data</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">uint</span> successfullInsertions <span style="color: yellow">=</span>&nbsp;<span style="color: white">0</span><span style="color: gainsboro">;</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">foreach</span>&nbsp;<span style="color: gainsboro">(</span><span style="color: white">var</span> jobj <span style="color: white">in</span> data<span style="color: gainsboro">)</span><br>&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">{</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: white">try</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">{</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: white">await</span> client<span style="color: yellow">.</span>CreateDocumentAsync<span style="color: gainsboro">(</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: cyan">UriFactory</span><span style="color: yellow">.</span>CreateDocumentCollectionUri<span style="color: gainsboro">(</span>cosmosDBName<span style="color: gainsboro">,</span> cosmosDBCollectionName<span style="color: gainsboro">),</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; jobj<span style="color: gainsboro">);</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; successfullInsertions<span style="color: yellow">++</span><span style="color: gainsboro">;</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">}</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: white">catch</span><span style="color: gainsboro">(</span><span style="color: cyan">Exception</span> exc<span style="color: gainsboro">)</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">{</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//We don't finish the execution here. If there are errors, the execution continues</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//with the other documents. So this insertion is not transactional.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//To make this execution transactional you must create a cosmosdb stored procedure</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//with the transaction logic as there are no means right now to control transactions</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//from cosmosdb clients. Just inside the engine.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//The failed insertions will be reported in the log, and at the end of the execution,</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//in the response, it will be noted that not all the documents were inserted.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: silver">//This could help administrators to check what went wrong.</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; log<span style="color: yellow">.</span>LogError<span style="color: gainsboro">(</span><span style="color: yellow">$"Problem inserting document: </span><span style="color: gainsboro">{</span>exc<span style="color: yellow">.</span>Message<span style="color: gainsboro">}</span><span style="color: yellow">\n"</span>&nbsp;<span style="color: yellow">+</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: yellow">$"Content: </span><span style="color: gainsboro">{</span>jobj<span style="color: yellow">.</span>ToString<span style="color: gainsboro">()}</span><span style="color: yellow">"</span><span style="color: gainsboro">);</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">}</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; <span style="color: gainsboro">}</span><br> <br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//A little report is generated as response if no exceptions were found</span><br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//This report indicates how many documents were inserted and what was the total</span><br>&nbsp;&nbsp;&nbsp; <span style="color: silver">//of documents passed to the function to be processed</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">var</span> endingMessage <span style="color: yellow">=</span>&nbsp;<span style="color: yellow">$"</span><span style="color: gainsboro">{</span>successfullInsertions<span style="color: gainsboro">}</span><span style="color: yellow"> / </span><span style="color: gainsboro">{</span>data<span style="color: yellow">.</span>Count<span style="color: gainsboro">}</span><span style="color: yellow"> "</span>&nbsp;<span style="color: yellow">+</span><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: yellow">$"documents inserted in CosmosDB"</span><span style="color: gainsboro">;</span><br>&nbsp;&nbsp;&nbsp; log<span style="color: yellow">.</span>LogInformation<span style="color: gainsboro">(</span>endingMessage<span style="color: gainsboro">);</span><br>&nbsp;&nbsp;&nbsp; <span style="color: white">return</span>&nbsp;<span style="color: white">new</span>&nbsp;<span style="color: cyan">OkObjectResult</span><span style="color: gainsboro">(</span>endingMessage<span style="color: gainsboro">);</span><br><span style="color: gainsboro">}</span></pre>




<p>You can also find this project on GitHub and collaborate if you want to add more features or improvements to it:<p>
<iframe width="160" height="30" src="https://ghbtns.com/github-btn.html?user=warnov&amp;repo=AzFunc_PlainJsonCosmosDBWriter&amp;type=watch&amp;count=false&amp;size=large&amp;v=2" frameborder="0"></iframe>
<iframe width="220" height="30" src="https://ghbtns.com/github-btn.html?user=warnov&amp;type=follow&amp;count=false&amp;size=large" frameborder="0"></iframe><p><strong>UPDATE</strong><p align="justify">After the first deployment, my customer told me that sometimes they required the information from <font face="Courier New">SendGrid</font> to be stored on a <font face="Courier New">MongoDB</font> database they had outside Azure.<p align="justify">The code base basically will be the same as for receiving the data in the Azure Function. Thus, I decided to make the function configurable so you can choose when you want to store the received data in <font face="Courier New">CosmosDB</font> either using the <font face="Courier New">SQL Native</font> model or the <font face="Courier New">MongoDB</font> model. Using the <font face="Courier New">MongoDB</font> model the data could be stored on any <font face="Courier New">MongoDB</font> i.e. Not just <font face="Courier New">CosmosDB</font>. Then, the configuration file for the Azure Function will look like:<pre style="background: black;color: lime;font-family: ocr a extended, courier new, monospace;font-size: 12px"><span style="color: yellow">{</span><br>&nbsp; <span>"IsEncrypted"</span><span style="color: yellow">:</span>&nbsp;<span style="color: white">false</span><span style="color: yellow">,</span><br>&nbsp; <span>"Values"</span><span style="color: yellow">:</span>&nbsp;<span style="color: yellow">{</span><br>&nbsp;&nbsp;&nbsp; <span>"FUNCTIONS_WORKER_RUNTIME"</span><span style="color: yellow">:</span>&nbsp;<span style="color: yellow">"dotnet"</span><span style="color: yellow">,</span><br>&nbsp;&nbsp;&nbsp; <span>"AzureWebJobsStorage"</span><span style="color: yellow">:</span>&nbsp;<span style="color: yellow">"UseDevelopmentStorage=true"</span><span style="color: yellow">,</span><br>&nbsp;&nbsp;&nbsp; <span>"AzureWebJobsDashboard"</span><span style="color: yellow">:</span>&nbsp;<span style="color: yellow">"UseDevelopmentStorage=true"</span><span style="color: yellow">,</span><br>&nbsp;&nbsp;&nbsp; <span>"cosmosDbEndpointUri"</span><span style="color: yellow">:</span>&nbsp;<span style="color: yellow">"https://…"</span><span style="color: yellow">,</span><br>&nbsp;&nbsp;&nbsp; <span>"cosmosDbPrimaryKey"</span><span style="color: yellow">:</span>&nbsp;<span style="color: yellow">"…=="</span><span style="color: yellow">,</span><br>&nbsp;&nbsp;&nbsp; <span>"mongoDbConnectionString"</span><span style="color: yellow">:</span>&nbsp;<span style="color: yellow">"mongodb://….documents.azure.com:10255/?ssl=true&amp;replicaSet=globaldb"</span><span style="color: yellow">,</span><br>&nbsp;&nbsp;&nbsp; <span>"dbName"</span><span style="color: yellow">:</span>&nbsp;<span style="color: yellow">"…"</span><span style="color: yellow">,</span><br>&nbsp;&nbsp;&nbsp; <span>"dbCollectionName"</span><span style="color: yellow">:</span>&nbsp;<span style="color: yellow">"sendgrid"</span><span style="color: yellow">,</span><br>&nbsp;&nbsp;&nbsp; <span>"useMongoDb"</span><span style="color: yellow">:</span>&nbsp;<span style="color: yellow">"true"</span><br>&nbsp; <span style="color: yellow">}</span><br><span style="color: yellow">}</span>&nbsp;</pre><p align="justify">The key variable here is <font face="Courier New">useMongoDb</font> if it is set to true, it means that the function will be storing the data in <font face="Courier New">MongoDB </font>(or <font face="Courier New">CosmosDB </font>for <font face="Courier New">MongoDB</font>) so the values for the variables related to <font face="Courier New">CosmosDB </font>will be omitted.</p><p align="justify">The C# code also changed, specially to include the instructions to store the data in <font face="Courier New">MongoDB</font>. For this, you have to include the <font face="Courier New">MongoDB.Driver</font> nuget package. That code updated is published in the <font face="Courier New">GitHub </font>repository and you could use it to compare how is the experience of working with the <font face="Courier New">Native SQL </font>mode and the <font face="Courier New">MongoDB</font> mode in <font face="Courier New">CosmosDB</font>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Por qué el Uptime Institute no mide los datacenters de los cloud providers como Azure</title>
      <link>https://docs.microsoft.com/archive/blogs/warnov/por-qu-el-uptime-institute-no-mide-los-datacenters-de-los-cloud-providers-como-azure</link>
      <pubDate>Fri, 05 Jan 2018 17:01:05 GMT</pubDate>
      <dc:creator><![CDATA[WarNov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/warnov/?p=4015</guid>
      <description><![CDATA[Hace poco a uno de mis partners de desarrollo colombianos, uno de sus clientes de EEUU le envió un...]]></description>
      <content:encoded><![CDATA[<p>Hace poco a uno de mis partners de desarrollo colombianos, uno de sus clientes de EEUU le envió un RFP en el cuál le preguntaba cuál era la calificación que el Uptime Institute le daba al proveedor de hosting de la solución que el Partner estaba ofreciendo. Mi Partner no tenía la respuesta así que me preguntó esto, para poder ayudarles a ganar el negocio.</p><p>Me puse a investigar y sucede que el <a href="https://uptimeinstitute.com/tier-certification">Uptime Institute</a> ofrece unas certificaciones de disponibilidad de servicio en un datacenter. Estas certificaciones clasifican al datacenter como Tier I, II, III o IV, donde el IV es la categoría de los datacenters con más disponibilidad del servicio (de acuerdo a la clasificación propuesta por la <a title="https://www.tia-942.org/" href="https://www.tia-942.org/">https://www.tia-942.org/</a>) con un 99.999% de disponibilidad.</p><p><a href="https://msdnshared.blob.core.windows.net/media/2018/01/image66.png"><img width="547" height="500" title="image" style="border: 0px currentcolor" alt="image" src="https://msdnshared.blob.core.windows.net/media/2018/01/image_thumb66.png" border="0"></a></p><p>Sin embargo, dado que la arquitectura de Azure y otros grandes proveedores de cloud es mucho más moderna y a escala que la de un datacenter tradicional de los que mide el Uptime Institute, los mecanismos usados por este instituto para hacer las mediciones, no aplican para proveedores de cloud, dado que el Uptime Institute trata de medir el tiempo que un gran servidor permanece arriba sin caerse; mientras que la nube está diseñada para que existan muchos servidores en redundancia, de tal suerte que cuando uno cae, los otros siguen funcionando en su lugar. Es por esto que ningún proveedor importante de cloud aparece en el <a href="https://uptimeinstitute.com/TierCertification/certMaps.php">listado</a> del instituto.<p>Sin embargo, en conclusión, la experiencia de un usuario de Azure en términos de disponibilidad del servicio, al final es equiparable a la que ofrecería un datacenter tradicional estando en Tier 3 o 4, dependiendo de los servicios de Azure que se estén usando.<p>Es muy útil tener esto en cuenta cuando potenciales clientes de nuestra solución cloud traten de medirnos usando un mecanismo que no aplica ya en éste entorno.]]></content:encoded>
    </item>
    <item>
      <title>Se puede justificar el uso de goto?</title>
      <link>https://docs.microsoft.com/archive/blogs/warnov/se-puede-justificar-el-uso-de-goto</link>
      <pubDate>Sat, 16 Sep 2017 16:49:36 GMT</pubDate>
      <dc:creator><![CDATA[WarNov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/warnov/?p=3985</guid>
      <description><![CDATA[goto&nbsp; es tal vez una de los statements más cuestionados en los lenguajes de programación...]]></description>
      <content:encoded><![CDATA[<p><em>goto</em>&nbsp; es tal vez una de los statements más cuestionados en los lenguajes de programación modernos.</p><p>Por años fue la pasta causante del código espagueti y miles de developers han denigrado de el pobre.</p><p>&nbsp;<img alt="Image result for goto spaghetti code" src="https://www.i-programmer.info/images/stories/Core/Theory/Goto/goto.PNG"></p><p>Pero en realidad... no habrán formas en las que usarlo sea adecuado?</p><p>Consideremos el siguiente loop:</p><pre><span>while</span> <span>(</span>currentFibonacciNumber <span>&lt;</span><span>=</span> maxLimit<span>)</span>  
<span>{</span>  
    <span>var</span> lastCurrentFibonacciNumber <span>=</span> currentFibonacciNumber<span>;</span>  
    currentFibonacciNumber <span>+</span><span>=</span> previousFibonacciNumber<span>;</span>  
    previousFibonacciNumber <span>=</span> lastCurrentFibonacciNumber<span>;</span>  
    <span>if</span> <span>(</span>currentFibonacciNumber <span>%</span> <span>2</span> <span>=</span><span>=</span> <span>0</span> &amp;&amp;  
        currentFibonacciNumber <span>&lt;</span><span>=</span> maxLimit<span>)</span>  
        sum <span>+</span><span>=</span> currentFibonacciNumber<span>;</span>  
<span>}</span>
</pre>
<p>

Es un sencillo loop que suma los números pares de la serie de Fibonacci, tendiendo en cuenta que hay un límite superior, donde la suma se detiene (maxLimit).</p><p>Con este loop, dándole a maxLimit un valor de 4000000000, el algoritmo se resuelve en promedio en 550 ticks.</p><p>Obsérvese que por cada iteración, se hacen 3 comparaciones.</p><p>Podemos reducir el número de comparaciones a 2:</p><pre><span>while</span> <span>(</span><span>true</span><span>)</span>
<span>{</span>
     <span>var</span> lastCurrentFibonacciNumber <span>=</span> currentFibonacciNumber<span>;</span>
     currentFibonacciNumber <span>+</span><span>=</span> previousFibonacciNumber<span>;</span>
     <span>if</span> <span>(</span>currentFibonacciNumber <span>&lt;</span><span>=</span> maxLimit<span>)</span>
     <span>{</span>
         previousFibonacciNumber <span>=</span> lastCurrentFibonacciNumber<span>;</span>
         <span>if</span> <span>(</span>currentFibonacciNumber <span>%</span> <span>2</span> <span>=</span><span>=</span> <span>0</span><span>)</span>
             sum <span>+</span><span>=</span> currentFibonacciNumber<span>;</span>
     <span>}</span>
     <span>else</span> <span>break</span><span>;</span>
 <span>}</span>

 <span>return</span> sum<span>;</span>
</pre><p>Curiosamente, el tiempo empleado es muy similar: 545 ticks.</p><p>Al parecer la mera estructura del loop implica esfuerzo de proceso.</p><p>Y que tal si traemos al no muy querido <em>goto </em>a escena?</p><pre>IterateFibonacci<span>:</span>
<span>var</span> lastCurrentFibonacciNumber <span>=</span> currentFibonacciNumber<span>;</span>
currentFibonacciNumber <span>+</span><span>=</span> previousFibonacciNumber<span>;</span>
<span>if</span> <span>(</span>currentFibonacciNumber <span>&lt;</span><span>=</span> maxLimit<span>)</span>
<span>{</span>
    previousFibonacciNumber <span>=</span> lastCurrentFibonacciNumber<span>;</span>
    <span>if</span> <span>(</span>currentFibonacciNumber <span>%</span> <span>2</span> <span>=</span><span>=</span> <span>0</span><span>)</span>
        sum <span>+</span><span>=</span> currentFibonacciNumber<span>;</span>
    <span>goto</span> IterateFibonacci<span>;</span>
<span>}</span>

<span>return</span> sum<span>;</span>
</pre><p>En este caso, el tiempo promedio se reduce a 455 ticks!</p><p>Esto en loops mucho más grandes o con comparaciones más complejas, podría significar una gran diferencia!</p><p>De aquí también puedo rescatar que el uso de goto fue muy específico y con un alcance muy bien delimitado. Y en tiempos de ejecución está dando mejor resultado que los otros métodos. Además es más entendible a primera vista que por ejemplo el segundo método donde con lo que uno se encuentra al principio es con un loop infinito. Algo que no es realmente el objetivo del algoritmo.</p><p>Sin embargo, como siempre hay que poner todo en una balanza. Es en verdad la mejora que se gana con goto justificable para usarlo? eso depende de cada caso de uso.</p><p>Cuando no es así, el primer procedimiento da más claridad para los posteriores lectores del código que por ejemplo el segundo y teniendo un tiempo de ejecución muy similar, podría pensarse en preferirlo.</p><p>Todos estos son simples hallazgos que no determinan ninguna regla pero si amplían nuestro horizonte para poder escoger la mejor opción según sea el caso.</p>]]></content:encoded>
    </item>
    <item>
      <title>Splash: Ejecución de scripts Python preexistentes dentro de Azure Machine Learning</title>
      <link>https://docs.microsoft.com/archive/blogs/warnov/splash-ejecucin-de-scripts-python-preexistentes-dentro-de-azure-machine-learning</link>
      <pubDate>Thu, 14 Sep 2017 22:23:06 GMT</pubDate>
      <dc:creator><![CDATA[WarNov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/warnov/?p=3975</guid>
      <description><![CDATA[En ocasiones sí es adecuado subir los propios modelos creados con Python o R a Azure Machine...]]></description>
      <content:encoded><![CDATA[En ocasiones sí es adecuado subir los propios modelos creados con Python o R a Azure Machine Learning (AML). Esto es completamente soportado y es una práctica que llevan a cabo muchos usuarios.

Las ventajas de incluir el modelo en AML es que podemos usar todas las funcionalidades que vienen predefinidas allí como la posibilidad de especificar visualmente cuáles van a hacer los orígenes de datos, ejecutar tareas de selección y limpieza de datos, aplicar el modelo subido de Python, hacer llamados a componentes R, SQLite, etc, hacer evaluación del modelo, publicar una vez ya estemos contentos con el modelo y luego acceder a éste a través de servicios web, todo sin programar nada. En el editor de AML existe un componente llamado “Execute Python Script” que permite ejecutar un modelo previo que tengamos creado en Python.

<a href="https://msdnshared.blob.core.windows.net/media/2017/09/execpython.png"><img src="https://msdnshared.blob.core.windows.net/media/2017/09/execpython-300x69.png" alt="" width="300" height="69" class="alignnone size-medium wp-image-3986" /></a>

Existen sin embargo, condiciones para que decidamos correctamente montar nuestro modelo pre existente en AML; por ejemplo, el bloque de “Execute Python Script” permite hasta tres inputs. Dos de éstos inputs deben ser del tipo Pandas Frames y el otro recibe en texto plano el código Python a ejecutarse cuando sea sencillo, o la referencia al Zip con todo el código Python a ejecutar (aquí se incluirían las librerías de apoyo que no son tan comunes como para estar en la distribución Anaconda estándar; otras como <em>scikit-learn</em> o <em>scipy</em> ya están disponibles en AML). Y se pueden generar hasta dos salidas; una, como un dataframe empaquetado dentro de una secuencia Python como una tupla, una lista o un arreglo NumPy y la otra salida puede ser usada para visualización usando MatplotLib en cuyo caso se genera un png que se puede descargar o visualizar fácilmente.

Por otro lado, el modelo pre existente debe correr sin problemas sobre la distribución de Python Anaconda, ya que ésta es en la que está basada AML para ejecutar modelos Python. Aquí precisamente recomiendo que primero se prepare el script y se vea que ejecuta sin problemas localmente, antes de subirlo a AML, porque allí no hay optimización para una experiencia de debugging.

Si luego de leer éste Splash tienes más interés en los detalles específicos de esta implementación, visita este <a href="https://docs.microsoft.com/en-us/azure/machine-learning/machine-learning-execute-python-scripts">artículo</a>.

De lo contrario, tal vez quieras contemplar la posibilidad de poner el modelo a correr tu script sobre <a href="https://prmadi.com/running-python-code-on-azure-functions-app/">Azure Functions</a> donde a través de http podrías enviar datos y a través del mismo mecanismo recibir resultados. En este caso habría que subir toda librería extra usada y cargarla en cada ejecución. Esto cuando usamos Pandas puede ser un problema, porque siempre tiene un tiempo alto carga dada la cantidad de imports que hace. En ese caso, tal vez quieras poner la Azure Function mejor en un App Service en lugar de un Consumption Plan.

Me preguntaban si se podría poner el código en un worker role, pero habría que crear algo así como un servicio WCF para pasar los datos. Habría menos complejidad en este caso, usando una Web App y exponiendo todo a través de HTTP.]]></content:encoded>
    </item>
    <item>
      <title>Splash: Active Directory B2C</title>
      <link>https://docs.microsoft.com/archive/blogs/warnov/splash-active-directory-b2c</link>
      <pubDate>Fri, 01 Sep 2017 22:25:00 GMT</pubDate>
      <dc:creator><![CDATA[WarNov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/warnov/?p=3965</guid>
      <description><![CDATA[Con mi serie de artículos "Splash", planeo ahorrarles mucho tiempo de investigación online acerca de...]]></description>
      <content:encoded><![CDATA[<p align="justify">Con mi serie de artículos "Splash", planeo ahorrarles mucho tiempo de investigación online acerca de tecnologías Microsoft. Sobretodo aquellas relacionadas con Azure.</p> <p align="justify">En esta ocasión, les estaré escribiendo de Azure Active Directory B2C (Business to Consumer).</p> <p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/2017/04/image407.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/04/image_thumb394.png" width="799" height="203"></a></p> <p align="justify">Éste es un servicio que siempre me imaginé sería muy cool de existir: Poder delegar la repetitiva y no funcional tarea de manejo de usuarios (registro, login, passwords, perfiles, integración con redes sociales, etc.).</p> <p align="justify">Azure hoy lo ofrece. Y dispone de toda la infraestructura y robustez que siempre ha caracterizado al Active Directory, ahora a disposición de aplicaciones o servicios dirigidos al consumidor; no solamente a usuarios corporativos como era costumbre.</p> <p align="justify">Así que si estás desarrollando tu startup o tienes ya una aplicación SaaS que maneja múltiples usuarios, con ADB2C, tienes una opción.</p> <p align="justify">En este caso, Azure te ofrece una base de datos propia para almacenar la información de tus usuarios. Ésta DB cuenta con la misma alta disponibilidad que es ofrecida a servicios como Outlook . Es una base de datos que está tan abstraída que difícilmente te darás cuenta de que existe. Tu solamente usarás el servicio para poder registrar tus usuarios y que ellos puedan luego hacer login, cambiar sus passwords, etc.</p> <p align="justify">Es tan abstraído el servicio, que ya te ofrece interfaces web previamente creadas que son mostradas en tu aplicación cada vez que un usuario se va a registrar, loguear, editar su perfil, etc.</p> <p align="justify">Así que lo único que hay que hacer, es configurar el servicio de ADB2C en tu suscripción de Azure, configurar la aplicación o servicio propio para usar ADB2C y comenzar a usarlo!</p> <p align="justify">Se incluyen 50 autenticaciones al mes gratis. Y un millón cuestan la tercera parte de un centavo. O sea, es muy barato.</p> <p align="justify">Aunque también se ofrece Multi-Factor Authentication sin que tengamos que desarrollar ni una línea de código. Así pues para un usuario que se loguee correctamente con usuario y password, también se podría emitir un SMS o una llamada telefónica en la cual se transmitiría un código, para que sea ingresado en el login y tener un acceso aún más seguro. Cada autenticación de este tipo sí vale 3 centavos de dólar.</p> <p align="justify">Hace casi un año intenté usar ésta tecnología, pero aún estaba muy incipiente. Por ejemplo, en ese entonces no estaba disponible para aplicaciones de escritorio. Hoy en día, sí que lo está. También está disponibles para aplicaciones móviles y de hecho está hasta para sitios web asp.net core.</p> <h2 align="justify">No todo es color de rosa… aún<br><br></h2> <p align="justify"><img style="float: left" alt="Image result for active directory b2c logo" src="https://azure.microsoft.com/svghandler/active-directory?width=600&amp;height=315" width="354" align="left" height="186"></p> <p align="justify">Así que decidí darle otra oportunidad, precisamente con un sitio web asp.net core. Luego de estudiar bastante (no es una tecnología muy directa de usar; hay que entender varios conceptos con una curva un poco más alta que la del promedio de servicios de Azure) llegué a hacer funcionar mi sitio con la ayuda de este servicio.</p> <p align="justify">De hecho el servicio ofrece una buena <a href="https://docs.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-devquickstarts-graph-dotnet">API</a> para hacer operaciones en Batch, por ejemplo para hacer una migración desde un sistema de manejo de usuarios anterior, o para ingresar un conjunto de nuevos usuarios con una sola operación.</p> <p align="justify">Sin embargo, aunque la prueba concepto estuvo adecuada y funcionó bien, el ir a producción con ADB2C me presentó varios inconvenientes insalvables por ahora, pues dependen de funcionalidades que aún no están en el servicio pero que claramente están en el roadmap. Yo le calcularía unos 6 meses más para salir (no es un compromiso de ninguna manera):<br></p> <ol> <li> <div align="justify">Páginas personalizadas: Como lo mencioné ADB2C ofrece interfaces de usuario prefabricadas para que los usuarios se registren, hagan login, actualicen sus permisos, etc. Éstas interfaces son muy parecidas a las que aparecen cada vez que te vas a loguear en un servicio de Microsoft como Outlook, Onedrive, etc. <br><br>Pero a veces, los requerimientos del negocio exigen que el look and feel permanezca uniforme durante toda la aplicación. Así que sería ideal que los desarrolladores pudiesen ofrecer sus propias plantillas para el manejo de cuentas de usuarios.<br><br>Aquí aunque se ha avanzado y algunas experiencias se pueden personalizar (por ejemplo la de registro), otras como el sign in no permiten que usemos nuestras propias páginas para el login, por lo que quedamos limitados a la experiencia que se ofrece por defecto, aún cuando en ésta se pueden cambiar logos, imágenes y colores. Pero siempre bajo la misma apariencia en general.<br><br>Este es un punto muy importante en el que el equipo está trabajando, para que podamos personalizar cada experiencia de usuarios en nuestras aplicaciones. Pero por ahora no es posible.<br></div> <li> <div align="justify">Localización: Esta falencia fue la que definitivamente me frenó. Ya que las experiencias de usuario vienen solo en inglés y no es posible cambiarlas. Así que por ejemplo donde se pide el correo electrónico aparece: Email Address y si se te olvidó el password aparece: Forgot your Password? Y cosas por el estilo.<br></div> <li> <div align="justify">Falta de acceso a datos de Redes Sociales: A pesar de que un usuario se puede loguear por ejemplo con su cuenta de Facebook a través de OAuth 2.0 soportado por ADB2C, no se pueden extraer datos de su perfil ni acceder a otros permisos requeridos por Facebook. Solo se puede obtener el email. La implementación de éstas características también está en progreso.<br></div> <li> <div align="justify">No veo un manejo de roles: Por tal motivo, éste asunto debería manejarse dentro de la aplicación. No es algo del otro mundo, pero por ejemplo la autenticación asp.net, tenía sus tablas de roles en la db.</div></li></ol> <h2 align="justify">Conclusiones</h2> <p align="justify">En conclusión podría mencionar que si necesitamos generar una aplicación o servicio que maneje usuario de una manera súper rápida donde no importa mucho el look and feel de las experiencias de usuario para login registro, emails de verificación, etc. Y tampoco que éstas experiencias estén en inglés, podríamos obtener mucho beneficio de ADB2C.</p> <p align="justify">Pero para usos más reales en nuestra industria considero que debemos esperar un poco más. Aunque sea hasta poder tener las experiencias personalizadas para todos los casos y hacer una localización que se ajuste a los gustos de nuestros usuarios.</p> <p align="justify">Sin duda una tecnología muy prometedora que en el cercano futuro estaré usando en mis proyectos para ser mucho más productivo.</p>]]></content:encoded>
    </item>
    <item>
      <title>Creación automatizada de proyectos en Visual Studio Team Services</title>
      <link>https://docs.microsoft.com/archive/blogs/warnov/creacin-automatizada-de-proyectos-en-visual-studio-team-services</link>
      <pubDate>Mon, 30 Jan 2017 16:09:48 GMT</pubDate>
      <dc:creator><![CDATA[WarNov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/warnov/?p=3845</guid>
      <description><![CDATA[Una de las cosas que más me gustan por ejemplo de GIT, es la facilidad con la cual añades un origen...]]></description>
      <content:encoded><![CDATA[<p align="justify">Una de las cosas que más me gustan por ejemplo de GIT, es la facilidad con la cual añades un origen externo (por ejemplo desde Team Services, Azure o GitHub), bajas el código y comienzas a trabajar de manera segura sobre tu solución; y todo sin salirte de una simple consola de comandos.</p> <p align="justify">Sin embargo una de las piezas faltantes que no podía ejecutar desde la consola, es la creación del repositorio o proyecto como tal en Team Services. Entonces tenía que hacer una pausa, ir al portal, loguearme si no lo estaba, crear el proyecto, buscar la url del git y luego sí conectar todo con mi cliente de desarrollo.</p> <p align="justify">Obviamente Team Services posee una API Rest para poder ejecutar operaciones programáticamente. Así que decidí investigar como hacer una aplicación de consola que me permitiera crear el repositorio en Team Services de manera automática sin entrar al portal.</p> <p align="justify">Una forma muy sencilla de poder acceder al API de Team Services es a través de los “Personal Access Token” (PAT). Si tienes una cuenta de Team Services, lo puedes obtener desde el portal (solo hay que hacerlo una vez). Entrando a la parte de seguridad dentro de tu cuenta:</p> <p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/2017/01/image715.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image_thumb619.png" width="455" height="377"></a></p> <p align="justify">&nbsp;</p> <p align="justify">Allí de una vez encontrarás el acceso a la administración de los PAT relativos a tu cuenta de Team Services. Para un mejor control recomiendo generar un PAT único para tu aplicación de creación de proyectos. Puedes escoger qué permisos otorgará ese PAT sobre los servicios de Team Services y por cuánto tiempo será válido. La idea es que luego uses el PAT para hacer tus llamados a la API sin necesidad de un usuario y contraseña o tal vez de doble autenticación. </p> <p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/2017/01/image716.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image_thumb620.png" width="459" height="354"></a></p> <p align="justify">Quise desarrollar este proyecto usando .NETCore, (si no estás familiarizado con .NETCore, puedes ver <a title="Introducci&oacute;n r&aacute;pida a .NET core en Linux" href="https://warnov.com/@netcorelinux" target="_blank">este corto video</a> que creé para mostrar de qué se trata muy rápidamente), de manera que cuando esté en Linux o acaso en MacOS, igual pueda usar la herramienta. Es un proyecto que hace uso extensivo del archivo de settings para .NETCore, que como ya es estándar, he llamado appsettings.json:</p> <div align="justify"><pre style="font-family: ocr a extended, courier, monospace;background: black;color: yellow"><p>  {</p><p>&nbsp;&nbsp; <span style="color: #d7ba7d">"domains"</span>: {<br>&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #d7ba7d">"warnov"</span>: "YOUR PAT",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #d7ba7d">"sample"</span>: "SampleAccountPATlaxs3zhqf74rcflkxqhufbmhnhpmma"&nbsp;&nbsp; <br>     },<br>&nbsp;&nbsp; <span style="color: #d7ba7d">"templates"</span>: {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #d7ba7d">"scrum"</span>: "6B724908-EF14-45CF-84F8-768B5384DA45",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #d7ba7d">"agile"</span>: "ADCC42AB-9882-485E-A3ED-7678F01F66BC", <br>&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #d7ba7d">"CMMI"</span>: "27450541-8E31-4150-9947-DC59F998FC01"&nbsp;&nbsp; <br>     },<br>&nbsp;&nbsp; <span style="color: #d7ba7d">"sourceControl"</span>: [ "Git", "Tfvc" ],&nbsp; <br>    <span style="color: #d7ba7d">"defaults"</span>: { <br>&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #d7ba7d">"sourceControlProvider"</span>: "git",</p><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="color: #d7ba7d">"template"</span>: "6B724908-EF14-45CF-84F8-768B5384DA45"&nbsp;&nbsp; </p><p>    }, </p><p>&nbsp;&nbsp; <span style="color: #d7ba7d">"apiPath"</span>: "https://{0}.visualstudio.com/_apis/projects?api-version=2.2"
}</p></pre></div>
<p align="justify">&nbsp;</p>
<p align="justify">El trabajo con este tipo de archivos en .NETCore es algo especial comparado a como hacíamos antes con archivos como el Web.Config o App.Config. Empezando porque ya no son archivos XML sino JSON. Para una guía detallada para aprender a manejar estos tipos de archivos, consulta este <a title=".NET Core: C&oacute;mo usar App Settings en una aplicaci&oacute;n de consola" href="https://warnov.com/@coresettings" target="_blank">post</a> que escribí al respecto.</p>
<p align="justify">En primera instancia encontramos una sección de dominios, donde especificamos el PAT de cada uno de ellos. Esto quiere decir que por ejemplo en el código anterior, puedo trabajar con Team Services en <em>warnov.visualstudio.com</em> y <em>sample.visualstudio.com</em>, pues se supone que allí he especificado los PAT válidos para cada uno de ellos.</p>
<p align="justify">Luego tenemos los templates; como lo puedes ver en las llaves, hacen referencia a si vamos a trabajar con scrum, agile o CMMI el ciclo de vida de nuestro proyecto de software. Generalmente escojo scrum.</p>
<p align="justify">Después vienen las opciones que tendremos para administrar el código fuente. Hoy en día Team Services ofrece Git y Tfvc (Team Foundation Version Control). Últimamente estoy optando por usar más git por lo simplista de su acercamiento.</p>
<p align="justify">También creé una sección de valores por defecto tanto para el proveedor de source control y el template a usar; esto para que mi aplicación pueda funcionar sin necesidad de que yo especifique todos los parámetros cuando la llame.</p>
<p align="justify">Para finalizar, especifico el formato de la ruta del API a usar. Sucede que ésta API está en constante evolución y me ha pasado que la URL cambia ligeramente. O acaso quieras usar otra versión de la API. En ese caso, basta con modificar este formato. Observa el {0}: éste va a ser reemplazado con uno de los nombres de dominios que has especificado arriba y que será aquel que escojas para trabajar.</p>
<p align="justify">Ahora sí, let’s get fun!</p>
<p align="justify">&nbsp;</p>
<div align="justify"><pre style="border-top-style: none;overflow: visible;border-left-style: none;font-family: 'OCR A Extended', courier, monospace;background: black;border-bottom-style: none;color:;padding-bottom: 0px;padding-top: 0px;border-right-style: none;padding-left: 0px;line-height: normal;padding-right: 0px"><span style="color:"><font color="#ffffff"><font style="font-size: 9pt">if</font></font></span><font style="font-size: 9pt"><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#00ff00">args</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Length</font></span><font color="#ffff00"> &gt; </font><span style="color:"><font color="#ffffff">0</font></span><span style="color:"><font color="#dcdcdc">)</font></span>
<span style="color:"><font color="#dcdcdc">{</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">workingDomain</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">args</font></span><span style="color:"><font color="#dcdcdc">[</font></span><span style="color:"><font color="#ffffff">0</font></span><span style="color:"><font color="#dcdcdc">];</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">workingProject</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">args</font></span><span style="color:"><font color="#dcdcdc">[</font></span><span style="color:"><font color="#ffffff">1</font></span><span style="color:"><font color="#dcdcdc">];</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">description</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">args</font></span><span style="color:"><font color="#dcdcdc">[</font></span><span style="color:"><font color="#ffffff">2</font></span><span style="color:"><font color="#dcdcdc">];</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">var</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">defaults</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">Config</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">GetSection</font></span><span style="color:"><font color="#dcdcdc">(</font></span><font color="#ffff00">"defaults"</font><span style="color:"><font color="#dcdcdc">);</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">sourceControlProvider</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">defaults</font></span><span style="color:"><font color="#dcdcdc">[</font></span><font color="#ffff00">"sourceControlProvider"</font><span style="color:"><font color="#dcdcdc">];</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">templateId</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">defaults</font></span><span style="color:"><font color="#dcdcdc">[</font></span><font color="#ffff00">"template"</font><span style="color:"><font color="#dcdcdc">];</font></span>
</font><span style="color:"><font style="font-size: 9pt" color="#dcdcdc">}</font></span></pre></div>
<p align="justify">Sencillamente evalúo si he pasado argumentos a la herramienta. En el primero vendría el dominio… por ejemplo <em>warnov</em> si quiero trabajar con <em>warnov.visualstudio.com</em>. Luego el nombre del proyecto. Por ejemplo: <em>Picas y Fijas on the Cloud</em> y finalmente la descripción del proyecto: “<em>Maravilloso y revolucionario juego interactivo</em>”. Como se aprecia, los otros valores son extraídos de la sección de defaults que definí en mi appsettings. de hecho, esto les da la idea de cómo se trabaja con ese archivo.</p>
<p align="justify">Si no paso parámetros, los pido de manera interactiva:</p>
<div align="justify"><pre style="border-top-style: none;overflow: visible;border-left-style: none;font-family: ocr a extended, courier, monospace;background: black;border-bottom-style: none;color:;padding-bottom: 0px;padding-top: 0px;border-right-style: none;padding-left: 0px;line-height: normal;padding-right: 0px"><span style="color:"><font color="#ffffff"><font style="font-size: 9pt">else</font></font></span><font style="font-size: 9pt">
<span style="color:"><font color="#dcdcdc">{</font></span>
<span style="color:"><font color="#c0c0c0">    //Domain</font></span>
<span style="color:"><font color="#00ff00">    workingDomain</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">GetDomain</font></span><span style="color:"><font color="#dcdcdc">();</font></span>
</font><font style="font-size: 9pt"><span style="color:"><font color="#c0c0c0">    //Project Name</font></span>
<span style="color:"><font color="#4ec9b0">    Console</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">WriteLine</font></span><span style="color:"><font color="#dcdcdc">(</font></span><font color="#ffff00">"Project Name: "</font><span style="color:"><font color="#dcdcdc">);</font></span>
<span style="color:"><font color="#00ff00">    workingProject</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#4ec9b0">Console</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">ReadLine</font></span><span style="color:"><font color="#dcdcdc">();</font></span>
<span style="color:"><font color="#c0c0c0">    //Project Description</font></span>
<font color="#ffff00">    </font><span style="color:"><font color="#4ec9b0">Console</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">WriteLine</font></span><span style="color:"><font color="#dcdcdc">(</font></span><font color="#ffff00">"Project Description: "</font><span style="color:"><font color="#dcdcdc">);</font></span>
<font color="#ffff00">    </font><span style="color:"><font color="#00ff00">description</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#4ec9b0">Console</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">ReadLine</font></span><span style="color:"><font color="#dcdcdc">();</font></span>
<font color="#ffff00"> 
    </font><span style="color:"><font color="#c0c0c0">//SOURCE CONTROL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span>
<font color="#ffff00">    </font><span style="color:"><font color="#00ff00">sourceControlProvider</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">GetSourceControlProvider</font></span><span style="color:"><font color="#dcdcdc">();</font></span>
<font color="#ffff00"> 
    </font><span style="color:"><font color="#c0c0c0">TEMPLATE ID</font></span>
<font color="#ffff00">    </font><span style="color:"><font color="#00ff00">templateId</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">GetTemplateId</font></span><span style="color:"><font color="#dcdcdc">();</font></span></font><font style="font-size: 9pt"><font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></font><span style="color:"><font style="font-size: 9pt" color="#dcdcdc">}</font></span></pre></div>
<p align="justify">y eso es todo. Luego pedimos los otros valores requeridos para la conexión, como el PAT; armamos el mensaje a enviar al API, lo enviamos usando POST y luego con el resultado mostramos un mensaje al final de la ejecución del programa.</p>
<div align="justify"><pre style="border-top-style: none;overflow: visible;border-left-style: none;font-family: ocr a extended, courier, monospace;background: black;border-bottom-style: none;color:;padding-bottom: 0px;padding-top: 0px;border-right-style: none;padding-left: 0px;line-height: normal;padding-right: 0px"><span style="color:"><font color="#c0c0c0"><font style="font-size: 9pt">//PAT</font></font></span><font style="font-size: 9pt">
<span style="color:"><font color="#ffffff">var</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">pat</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">domainsSection</font></span><span style="color:"><font color="#dcdcdc">[</font></span><span style="color:"><font color="#00ff00">workingDomain</font></span><span style="color:"><font color="#dcdcdc">];</font></span>
</font><font style="font-size: 9pt"><span style="color:"><font color="#c0c0c0">//Message Body</font></span>
<span style="color:"><font color="#ffffff">var</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">messageBody</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">GetMessageBody</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#00ff00">workingProject</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">description</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">sourceControlProvider</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">templateId</font></span><span style="color:"><font color="#dcdcdc">);</font></span>
<span style="color:"><font color="#c0c0c0">//PostExecution</font></span>
<span style="color:"><font color="#ffffff">var</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">postResult</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">ExecutePost</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#00ff00">workingDomain</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">pat</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">messageBody</font></span><span style="color:"><font color="#dcdcdc">);</font></span>
<span style="color:"><font color="#4ec9b0">Console</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">WriteLine</font></span><span style="color:"><font color="#dcdcdc">(</font></span><font color="#ffff00">$"</font><span style="color:"><font color="#dcdcdc">{</font></span><span style="color:"><font color="#00ff00">postResult</font></span><span style="color:"><font color="#dcdcdc">}</font></span><font color="#ffff00">\n\nPress Enter to Finish..."</font><span style="color:"><font color="#dcdcdc">);</font></span>
</font><font style="font-size: 9pt"><span style="color:"><font color="#4ec9b0">Console</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">ReadLine</font></span></font><span style="color:"><font style="font-size: 9pt" color="#dcdcdc">();</font></span></pre></div>
<p align="justify"><strong>Mensaje a enviar:</strong></p>
<div align="justify"><pre style="border-top-style: none;overflow: visible;border-left-style: none;font-family: ocr a extended, courier, monospace;background: black;border-bottom-style: none;color:;padding-bottom: 0px;padding-top: 0px;border-right-style: none;padding-left: 0px;line-height: normal;padding-right: 0px"><span style="color:"><font color="#4ec9b0"><font style="font-size: 9pt">ProjectCreator</font></font></span><font style="font-size: 9pt"><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">body</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#ffffff">new</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#4ec9b0">ProjectCreator</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#00ff00">workingProject</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">description</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">sourceControlProvider</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">templateId</font></span><span style="color:"><font color="#dcdcdc">);</font></span>
<span style="color:"><font color="#ffffff">var</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">jsonBody</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#4ec9b0">JsonConvert</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">SerializeObject</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#00ff00">body</font></span><span style="color:"><font color="#dcdcdc">);</font></span>
<span style="color:"><font color="#ffffff">return</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#ffffff">new</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#4ec9b0">StringContent</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#00ff00">jsonBody</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#4ec9b0">Encoding</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">UTF8</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00"> "application/json"</font></font><span style="color:"><font style="font-size: 9pt" color="#dcdcdc">);</font></span></pre></div>
<p align="justify">Observa que hay una clase modelo que se llama <font face="Courier New">ProjectCreator</font>. Desarrollé ésta clase a partir de la documentación del API, usando la metodología que he descrito en este <a title="Api Toolbox" href="https://warnov.com/@ApiToolbox">post</a>. Es una metodología que ayuda mucho a poder conectarse a todas las APIs modernas basadas en REST y JSON.</p>
<p align="justify">Pues bien, una ves inicializo ésta clase con los valores requeridos para la creación de mi proyecto en Team Services, la convierto a un JSON que luego paso a un <font face="Courier New">StringContent</font> que es el objeto provisto por&nbsp; el framework que prepara los datos y los deja listos para ser incluidos en el body de un mensaje post que incluye data en formato texto para ser enviada al servidor.</p>
<p align="justify">Finalmente pasamos al envío del mensaje:</p>
<div align="justify"><pre style="border-top-style: none;overflow: visible;border-left-style: none;font-family: ocr a extended, courier, monospace;background: black;border-bottom-style: none;color:;padding-bottom: 0px;padding-top: 0px;border-right-style: none;padding-left: 0px;line-height: normal;padding-right: 0px"><span style="color:"><font color="#ffffff"><font style="font-size: 9pt">private</font></font></span><font style="font-size: 9pt"><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#ffffff">static</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#ffffff">string</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">ExecutePost</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#ffffff">string</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">workingDomain</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#ffffff">string</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">pat</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#4ec9b0">StringContent</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">messageBody</font></span><span style="color:"><font color="#dcdcdc">)</font></span>
<span style="color:"><font color="#dcdcdc">{</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">string</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">responseBody</font></span><font color="#ffff00">=</font><span style="color:"><font color="#ffffff">string</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Empty</font></span><span style="color:"><font color="#dcdcdc">;</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">using</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#4ec9b0">HttpClient</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">client</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#ffffff">new</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#4ec9b0">HttpClient</font></span><span style="color:"><font color="#dcdcdc">())</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#dcdcdc">{</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">string</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">_credentials</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#4ec9b0">Convert</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">ToBase64String</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#00ff00">System</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Text</font></span><font color="#ffff00">.</font><span style="color:"><font color="#4ec9b0">ASCIIEncoding</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">ASCII</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">GetBytes</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#ffffff">string</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Format</font></span><span style="color:"><font color="#dcdcdc">(</font></span><font color="#ffff00">"{0}:{1}"</font><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00"> ""</font><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">pat</font></span><span style="color:"><font color="#dcdcdc">)));</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">client</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">DefaultRequestHeaders</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Accept</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Clear</font></span><span style="color:"><font color="#dcdcdc">();</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">client</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">DefaultRequestHeaders</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Accept</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Add</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#ffffff">new</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">System</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Net</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Http</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Headers</font></span><font color="#ffff00">.</font><span style="color:"><font color="#4ec9b0">MediaTypeWithQualityHeaderValue</font></span><span style="color:"><font color="#dcdcdc">(</font></span><font color="#ffff00">"application/json"</font><span style="color:"><font color="#dcdcdc">));</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">client</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">DefaultRequestHeaders</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Authorization</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#ffffff">new</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#4ec9b0">AuthenticationHeaderValue</font></span><span style="color:"><font color="#dcdcdc">(</font></span><font color="#ffff00">"Basic"</font><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">_credentials</font></span><span style="color:"><font color="#dcdcdc">);</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">var</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">url</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">Config</font></span><span style="color:"><font color="#dcdcdc">[</font></span><font color="#ffff00">"apiPath"</font><span style="color:"><font color="#dcdcdc">];</font></span>
</font><font style="font-size: 9pt"><font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">var</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">request</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#ffffff">new</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#4ec9b0">HttpRequestMessage</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#4ec9b0">HttpMethod</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Post</font></span><span style="color:"><font color="#dcdcdc">,</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">string</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Format</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#00ff00">url</font></span><span style="color:"><font color="#dcdcdc">,</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">workingDomain</font></span><span style="color:"><font color="#dcdcdc">))</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#dcdcdc">{</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">Content</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">messageBody</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#dcdcdc">};</font></span><font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">try</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#dcdcdc">{</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">var</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">response</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">client</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">SendAsync</font></span><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#00ff00">request</font></span><span style="color:"><font color="#dcdcdc">)</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Result</font></span><span style="color:"><font color="#dcdcdc">;</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">response</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">EnsureSuccessStatusCode</font></span><span style="color:"><font color="#dcdcdc">();</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">var</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">code</font></span><font color="#ffff00"> = </font><span style="color:"><font color="#00ff00">response</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">StatusCode</font></span><span style="color:"><font color="#dcdcdc">;</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">if</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#00ff00">response</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">IsSuccessStatusCode</font></span><span style="color:"><font color="#dcdcdc">)</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#dcdcdc">{</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">responseBody</font></span><font color="#ffff00"> = $"</font><span style="color:"><font color="#dcdcdc">{</font></span><span style="color:"><font color="#00ff00">code</font></span><span style="color:"><font color="#dcdcdc">}</font></span><font color="#ffff00">: </font><span style="color:"><font color="#dcdcdc">{</font></span><span style="color:"><font color="#00ff00">response</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Content</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">ReadAsStringAsync</font></span><span style="color:"><font color="#dcdcdc">()</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Result</font></span><span style="color:"><font color="#dcdcdc">}</font></span><font color="#ffff00">"</font><span style="color:"><font color="#dcdcdc">;</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#dcdcdc">}</font></span></font><font style="font-size: 9pt"><font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#dcdcdc">}</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">catch</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#dcdcdc">(</font></span><span style="color:"><font color="#4ec9b0">Exception</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">exc</font></span><span style="color:"><font color="#dcdcdc">)</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#dcdcdc">{</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#00ff00">responseBody</font></span><font color="#ffff00"> = $"ERROR: </font><span style="color:"><font color="#dcdcdc">{</font></span><span style="color:"><font color="#00ff00">exc</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">Message</font></span><span style="color:"><font color="#dcdcdc">}</font></span><font color="#ffff00">. </font><span style="color:"><font color="#dcdcdc">{</font></span><span style="color:"><font color="#00ff00">exc</font></span><font color="#ffff00">.</font><span style="color:"><font color="#00ff00">InnerException</font></span><font color="#ffff00">?.</font><span style="color:"><font color="#00ff00">Message</font></span><span style="color:"><font color="#dcdcdc">}</font></span><font color="#ffff00">"</font><span style="color:"><font color="#dcdcdc">;</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#dcdcdc">}</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#dcdcdc">}</font></span>
<font color="#ffff00">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffffff">return</font></span><font color="#ffff00">&nbsp;</font><span style="color:"><font color="#00ff00">responseBody</font></span><span style="color:"><font color="#dcdcdc">;</font></span>
</font><span style="color:"><font style="font-size: 9pt" color="#dcdcdc">}</font></span></pre></div>
<p align="justify">En primera medida ajustamos las credenciales de acceso al API, encodificando el PAT que hemos obtenido. Luego ajustamos el header de autorización con esta data. También indicamos que el API maneja mensajes JSON. </p>
<p align="justify">Después armamos la url de envío de nuestro request y adicionamos el mensaje que armamos previamente.</p>
<p align="justify">Finalmente enviamos el request y le pedimos al sistema que si no nos responden con éxito lance una excepión, a través de <font color="#000000"><font face="Courier New"><span style="color:">response</span>.<span style="color:">EnsureSuccessStatusCode</span><span style="color:">();</span> </font></font></p>
<p align="justify">Observa que la respuesta inicial no va a ser un 200, porque la operación no finaliza en bien recibimos la respuesta. La creación de un proyecto toma algún tiempo así que lo que es retornado por el servidor de Team Services es el código 202 que nos indica que todo está bien, pero que debemos esperar a que la operación termine (cosa que por lo general podemos hacer sin tomar acción especial distinta a esperar algunos segundos). También se nos retorna una URL de seguimiento del estado del request por si queremos automatizar la herramienta para que ella misma cree el repositorio local una vez haya quedado creado en la nube. Pero eso haría parte de otro post.</p>
<p align="justify">Así que después solo resta inicializar nuestro repositorio git, y agregarle el remote con dirección similar a esta: <br>https://[<strong>yourdomain</strong>].visualstudio.com/DefaultCollection/_git/[<strong>yourprojectname</strong>]&lt;/STRONG]<br></p>
<p align="justify">y voilá… habremos podido crear todo lo necesario para nuestro ambiente de desarrollo local soportado por Team Services, sin necesidad de salir de la consola!</p>
<p align="justify">Todo el código de esta solución lo pueden encontrar en mi Github. Pueden agregarle más funcionalidades basados en el <a href="https://www.visualstudio.com/en-us/docs/integrate/api/overview" target="_blank">API de Team Services</a> o modificar su comportamiento. Estaré atento a sus push requests!</p>
<p align="justify"><a title="WhizTeamServices" href="https://github.com/warnov/WhizTeamServices" target="_blank"><img title="github-512" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="github-512" src="https://msdnshared.blob.core.windows.net/media/2017/01/github-512.png" width="82" height="82"></a></p>]]></content:encoded>
    </item>
    <item>
      <title>Toolbox de desarrollo de APIs REST</title>
      <link>https://docs.microsoft.com/archive/blogs/warnov/toolbox-de-desarrollo-de-apis-rest</link>
      <pubDate>Thu, 26 Jan 2017 19:54:57 GMT</pubDate>
      <dc:creator><![CDATA[WarNov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/warnov/?p=3905</guid>
      <description><![CDATA[O también de desarrollo sobre APIs. Ya no es ningún secreto que las APIs de hoy se construyen con la...]]></description>
      <content:encoded><![CDATA[<p align="justify">O también de desarrollo <strong>sobre</strong> APIs.</p> <p align="justify">Ya no es ningún secreto que las APIs de hoy se construyen con la simplicidad de REST&nbsp; y JSON.</p> <p align="justify">Hay tal flexibilidad ya, que en todo lado encontramos herramientas muy apropiadas tanto para ayudarnos en el desarrollo de estas APIs, como para sus pruebas.</p> <p align="justify">De los temas más acostumbrados hoy en día, es que la API reciba requests POST, donde los parámetros de ejecución vienen embebidos en un JSON que es transmitido dentro del content de dicho post.</p> <p align="justify"><strong>Trabajando con JSON en .NET</strong></p> <p align="justify">Entonces para manejar JSON especialmente dentro de nuestras aplicaciones .NET, nada mejor que el gran framework, <a href="https://www.newtonsoft.com/json" target="_blank">Json.NET</a> creado por el talentoso James Newton-King. Quien lo ha dispuesto de manera free aún para el uso comercial. Y desde mi modo de ver, es la mejor librería para trabajar con JSON con .NET. Y además viendo que ya <a href="https://warnov.com/@netcorelinux" target="_blank">.NET es multiplataforma</a>, entonces sería la mejor librería para trabajar JSON sobre cualquier plataforma. Te puedes bajar la dll a la vieja usanza o sencillamente incluir el paquete desde Nuget con el wizard o desde la consola <a href="https://www.nuget.org/packages/Newtonsoft.Json" target="_blank">NPM</a>.</p> <p align="justify"><strong>Creando modelos para nuestras APIs JSON con C#</strong></p> <p align="justify">Digamos eso sí, que lo anterior fue lo más obvio de este post. Pero bueno, ya teniendo como manipular JSON desde nuestro C#, lo siguiente que puede ocurrir entonces es que quisiéramos conectarnos a un API que nos pide le enviemos un json con la siguiente estructura:</p> <div align="justify"><pre style="border-top-style: none;overflow: visible;border-left-style: none;font-family: 'OCR A Extended','Courier New', courier, monospace;background: black;border-bottom-style: none;color:;padding-bottom: 0px;padding-top: 0px;border-right-style: none;padding-left: 0px;line-height: normal;padding-right: 0px"><span style="color:"><font color="#ffff00"><font style="font-size: 9pt">{</font></font></span><font style="font-size: 9pt">
<font color="#dadada">&nbsp; </font><span style="color:"><font color="#d7ba7d">"name"</font></span><span style="color:"><font color="#ffff00">:</font></span><font color="#dadada">&nbsp;</font><font color="#ffff00"><span style="color:">"FabrikamTravel"</span><span style="color:">,</span></font>
<font color="#dadada">&nbsp; </font><span style="color:"><font color="#d7ba7d">"description"</font></span><span style="color:"><font color="#ffff00">:</font></span><font color="#dadada">&nbsp;</font><font color="#ffff00"><span style="color:">"Frabrikam travel app for Windows Phone"</span><span style="color:">,</span></font>
<font color="#dadada">&nbsp; </font><span style="color:"><font color="#d7ba7d">"capabilities"</font></span><span style="color:"><font color="#ffff00">:</font></span><font color="#dadada">&nbsp;</font><span style="color:"><font color="#ffff00">{</font></span>
<font color="#dadada">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#d7ba7d">"versioncontrol"</font></span><span style="color:"><font color="#ffff00">:</font></span><font color="#dadada">&nbsp;</font><span style="color:"><font color="#ffff00">{</font></span>
<font color="#dadada">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#d7ba7d">"sourceControlType"</font></span><span style="color:"><font color="#ffff00">:</font></span><font color="#dadada">&nbsp;</font><span style="color:"><font color="#ffff00">"Git"</font></span>
<font color="#dadada">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffff00">},</font></span>
<font color="#dadada">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#d7ba7d">"processTemplate"</font></span><span style="color:"><font color="#ffff00">:</font></span><font color="#dadada">&nbsp;</font><span style="color:"><font color="#ffff00">{</font></span>
<font color="#dadada">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#d7ba7d">"templateTypeId"</font></span><span style="color:"><font color="#ffff00">:</font></span><font color="#dadada">&nbsp;</font><span style="color:"><font color="#ffff00">"6b724908-ef14-45cf-84f8-768b5384da45"</font></span>
<font color="#dadada">&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#ffff00">}</font></span>
<font color="#dadada">&nbsp; </font><span style="color:"><font color="#ffff00">}</font></span>
</font><span style="color:"><font style="font-size: 9pt" color="#ffff00">}</font></span></pre></div>
<p align="justify">Podríamos ponernos a construir las clases a mano y emplear un montón de tiempo en esto.</p>
<p align="justify">Pero mejor tomar el ejemplo del api que nos da el developer y pasarlo por esta <a title="json2csharp" href="https://json2csharp.com/" target="_blank">tool online</a>:</p>
<p align="justify"><a title="json2csharp" href="https://json2csharp.com/"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image730.png" width="416" height="502"></a></p>
<p align="justify">&nbsp;</p>
<p align="justify">Como se aprecia, el modelo generado en C# es muy bueno. Nos ahorramos montones de tiempo y si no es exactamente lo que queremos, al menos tenemos una gran aproximación a lo que requerimos.</p>
<p align="justify">Luego ya con esas clases y la ayuda de JSON.NET, podemos hacer serializaciones que nos emiten automáticamente el JSON requerido.</p>
<p align="justify"><strong>Generando datos de prueba JSON de manera masiva</strong></p>
<p align="justify">Algunas APIs pueden requerir que enviemos registros para ser insertados en una base de datos. Estos registros, deberían ir representados en JSON. Pero generalmente la edición de éstos&nbsp; es mucho más sencilla por ejemplo en Excel, que poniéndose a escribir el JSON a mano. Mi recomendación entonces es seguir usando Excel para crear los datos de prueba y luego los grabamos en formato CSV. Cuando ya los tenemos así, podemos copiar y pegar el contenido de este archivo y ponerlo en esta otra <a title="Convert CSV to JSON" href="https://www.convertcsv.com/csv-to-json.htm">tool online</a>:</p>
<p align="justify"><a title="Convert CSV to JSON" href="https://www.convertcsv.com/csv-to-json.htm"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image732.png" width="394" height="567"></a></p>
<p align="justify">El resultado es un muy bien estructurado JSON que podremos agregar a nuestras pruebas al API con la aplicación que estemos construyendo.</p>
<p align="justify">Y hablando de pruebas:</p>
<p align="justify"><strong>Prueba de APIs propias y de terceros</strong></p>
<p align="justify">Una vez creamos un API generalmente ésta quedará expuesta en nuestra máquina de desarrollo a través del localhost. Y para no tener que escribir una aplicación web por nuestra propia cuenta, tenemos varias opciones.</p>
<p align="justify"><em>curl:</em></p>
<p align="justify">Es una herramienta de consola que proviene del mundo Unix y nos permite hacer llamados HTTP y recibir respuesta en la misma consola. Hay muchas formas de obtenerlo. Existe la versión <a title="Curl for Windows" href="https://curl.haxx.se/download.html">stand alone para Windows</a> y también se puede obtener dentro del paquete de <a href="https://cygwin.com/">Cygwin</a> que es uno de los intentos más notables hasta ahora de traer el mundo de la consola Linux a Windows. Si han instalado <a title="Git para Windows" href="https://git-for-windows.github.io/">Git para Windows</a>, ésta instalación también instala una emulación de la consola Bash del mundo *nix, que incluye por supuesto curl. Pero la opción más interesante para mí sin embargo, es usarlo desde <a title="Bash on Ubuntu on Windows" href="https://msdn.microsoft.com/en-us/commandline/wsl/about">Bash en Windows</a>, luego de que ya es posible tener un subsistema de Linux formal dentro de Windows, gracias a las herramientas para desarrollador que vienen para Windows 10. Así que el bash que se ejecuta no es emulado sino realmente el bash de Linux (con algunas limitantes):</p>
<p align="justify">Por ejemplo, con una línea de comando consulté un API que tengo desplegada en Azure que sencillamente te retorna la IP actual con la que te reconocen desde internet:</p>
<p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/2017/01/image733.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image_thumb634.png" width="595" height="89"></a></p>
<p align="justify">Claro está que si el término azul no es lo tuyo y te gustan las tools más elaboradas, podrías usar el muy amigable <a href="https://www.getpostman.com/">PostMan</a> que comenzó siendo una extensión de Chrome y ahora ya tiene su instalador standalone para Windows y otros sistemas operativos:</p>
<p align="justify"><a title="Postman" href="https://www.getpostman.com/"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image734.png" width="515" height="358"></a></p>
<p align="justify">Aquí hago un llamado a la misma API anterior. Se pueden armar llamados HTTP de cualquier tipo y hay muchas ayudas visuales para crear el cuerpo de los mensajes y para formatear las respuestas desde el server de manera que sean fácilmente entendibles por el developer.</p>
<p align="justify">Pero definitivamente la navaja suiza para trabajar con APIs es Fiddler. Su interfaz para nada tan amigable con el usuario tal vez nos haga sentir como si estuviéramos frente a los controles de un Boeing 747. Pero por ejemplo a diferencia de PostMan, Fiddler permite rastrear todo el trafico web desde y hacia nuestra máquina de desarrollo. Nos permite simular certificados https, correr scripts de automatización avanzados y muchos más. Puedes leer más de fiddler en <a href="https://blogs.msdn.microsoft.com/warnov/2012/06/22/debugging-de-windows-phone-apps-con-fiddler/">este blog post</a> que escribí hace años.</p>
<p align="justify"><strong>Visualizando JSON entendiblemente</strong></p>
<p align="justify">Puede que luego de probar alguna API recibamos un montón de JSON que queramos ver de manera estructurada, sabiendo la cantidad de nodos que hay en cada nivel y demás. Para eso, nada mejor que esta gran <a title="JSON Viewer" href="https://codebeautify.org/jsonviewer">tool online</a>:</p>
<p align="justify"><a title="JSON Viewer" href="https://codebeautify.org/jsonviewer"><img title="image4" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image4" src="https://msdnshared.blob.core.windows.net/media/2017/01/image4102.png" width="520" height="524"></a></p>
<p align="justify"><strong>Generación de esquemas JSON</strong></p>
<p align="justify">Sí, hay <a href="https://json-schema.org/">esquemas JSON</a>. Así como habían esquemas XSD, también hay documentos que pueden describir un JSON. Y esto es muy útil para poder desarrollar otras herramientas que manejen JSON o para poder validar la estructura de un documento JSON en general. La herramienta que les mostraré a continuación, permite que le pasemos un ejemplo de un documento JSON y en vez de generarnos por ejemplo clases C#, lo que nos genera es el dichoso esquema que obviamente viene escrito en JSON. Plataformas como <a href="https://azure.microsoft.com/en-us/services/logic-apps/">Logic Apps</a> en Azure por ejemplo, requieren que especifiquemos un esquema JSON para los datos de entrada de las aplicaciones y qué mejor que contar con una <a href="https://jsonschema.net/#/">herramienta online</a> como ésta:</p>
<p align="justify"><a title="JsonSchema.net is a tool that automatically generates JSON schema from JSON according to the IETF JSON Schema Internet Draft Version 4" href="https://jsonschema.net/#/"><img title="image" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image935.png" width="519" height="358"></a></p>
<p align="justify">En conclusión este es el pequeño arsenal de tools con el que me defiendo cuando trabajo bien sea creando o consumiendo APIs. Si tienes otras tools para recomendar, bienvenidas en los comentarios!</p>]]></content:encoded>
    </item>
    <item>
      <title>Moviendo recursos entre suscripciones de Azure</title>
      <link>https://docs.microsoft.com/archive/blogs/warnov/moviendo-recursos-entre-suscripciones-de-azure</link>
      <pubDate>Fri, 20 Jan 2017 14:04:29 GMT</pubDate>
      <dc:creator><![CDATA[WarNov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/warnov/?p=3785</guid>
      <description><![CDATA[El movimiento de recursos en Azure se ha tratado de llevar al máximo a ser una operación de...]]></description>
      <content:encoded><![CDATA[<p>El movimiento de recursos en Azure se ha tratado de llevar al máximo a ser una operación de autoservicio a través de las funciones del portal, para evitar posibles esperas al equipo de soporte. Sin embargo, no todos los recursos se pueden mover de esta manera y algunos poseen ciertas limitaciones a la hora de moverse.</p> <p>La principal limitante para mover recursos entre suscripciones sin requerir soporte, es que dichas suscripciones pertenezcan al mismo tenant de Active Directory (o a la misma cuenta de Azure – una cuenta de Azure puede ser dueña de varias suscripciones de Azure. En este caso, todas las suscripciones pertenecientes a esa cuenta, también pertenecen al mismo tenant de Azure Directory que se crea por defecto).</p> <p>Si las suscripciones no pertenecen al mismo tenant, entonces se hace obligatorio pedir un caso de soporte para hacer el movimiento.</p> <p>Hoy en día, esta es una labor sencilla, dado que dentro del portal de administración de Azure, en las opciones de soporte hay una especial para la administración de la suscripción, que permite este tipo de acciones y que viene incluida sin costo. </p> <p>En los otros casos, el movimiento de recursos en general funciona de la siguiente manera:</p> <p>Se selecciona el Resource Group (RG) que tiene los recursos a mover. Allí en la descripción del RG hay una opción de <em>mover</em> el RG. Al seleccionarla, aparecen los recursos incluidos dentro de es RG para escoger aquellos que queremos mover. Luego se selecciona el RG al que se quieren mover.</p> <p>&nbsp;</p> <p><a href="https://msdnshared.blob.core.windows.net/media/2017/01/image493.png"><img title="image" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image_thumb416.png" width="841" height="401"></a></p> <p>&nbsp;</p> <p>Esto casi siempre es necesario antes de mover los recursos a otra suscripción, pues este movimiento se hace basado en todo un RG completo. Así que primero hay que agrupar los elementos que se quieren transferir a un RG separado, si es que no se quiere transferir todo un RG originalmente. </p> <p>Una vez se tienen agrupados los elementos que se quieren transferir procedemos así:</p> <p><a href="https://msdnshared.blob.core.windows.net/media/2017/01/image494.png"><img title="image" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image_thumb417.png" width="836" height="327"></a></p>  <p>Y nos aparecerá una lista de todas las suscripciones pertenecientes al mismo AD Tenant. Solo a esas podremos mover los recursos automáticamente. Recuerde que si se necesita mover los recursos a un tenant diferente, se requerirá entonces de un caso de soporte.</p> <p><strong>Otras limitaciones:</strong></p> <p>Como lo mencioné anteriormente, hay servicios con limitantes. Por ejemplo el App Service posee ciertas reglas a la hora de moverse. O los elementos clásicos que no están manejados a través de un Resource Group. En estos casos, la mayoría de veces también es mejor acudir a soporte. Encuentre <a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-move-resources">aquí</a>, una lista detallada de los elementos que se pueden mover, aquellos que tienen limitantes y los que definitivamente no pueden moverse.</p>]]></content:encoded>
    </item>
    <item>
      <title>Configurando .NET Core 1.1.0 (current) en Windows</title>
      <link>https://docs.microsoft.com/archive/blogs/warnov/configurando-net-core-1-1-0-current-en-windows</link>
      <pubDate>Thu, 19 Jan 2017 23:58:22 GMT</pubDate>
      <dc:creator><![CDATA[WarNov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/warnov/?p=3666</guid>
      <description><![CDATA[Cuando instalamos las herramientas de .NET Core para Visual Studio, aparte del tipo de proyecto se...]]></description>
      <content:encoded><![CDATA[<p align="justify">Cuando instalamos las herramientas de <font style="font-family: 'OCR A Extended', 'Courier New', courier, monospace">.NET Core</font> para Visual Studio, aparte del tipo de proyecto se instala también el SDK y el <span class="auto-style1"><font style="font-family: 'OCR A Extended', 'Courier New', courier, monospace">runtime</font></span>. Pero la versión del SDK no es la más reciente, sino la versión <font style="font-family: 'OCR A Extended', 'Courier New', courier, monospace">LTS </font>(Long Term Support). Dedicada para aquellos que con el fin de tener un soporte extendido de 3 años prefieren sacrificar las últimas innovaciones que se vayan liberando. Sin embargo, la evolución de <font style="font-family: 'OCR A Extended', 'Courier New', courier, monospace">.NET Core</font> continúa permanentemente y se están publicando versiones estables que tienen hasta 3 meses de soporte.</p> <p align="justify">Esta es la filosofía en Windows. Pero en Linux y Mac, las opciones por defecto siempre instalarán la última versión estable.</p> <p align="justify">Así que si en Windows queremos estar a la vanguardia, existen un par de pasos adicionales que necesitamos para quedar con la última versión estable que a Enero de 2017 es la 1.1.0.</p> <p align="justify">La versión 1.1.0 del framework requiere trabajar con una versión de Nuget mínimo 3.4.4. Esto, con el fin de que Nuget pueda configurar bien los archivos del proyecto Net Core de acuerdo a la última versión. Si en el about de Visual Studio se observa una versión menor a 3.4.4:</p> <p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/2017/01/image457.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image_thumb378.png" width="698" height="750"></a></p> <p align="justify">&nbsp;</p> <p align="justify">Entonces lo primero que hay que hacer es actualizar a la última (clic en la imagen para ir al destino):</p> <p align="justify"><a href="https://dist.nuget.org/index.html"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image_thumb373.png" width="735" height="347"></a></p> <p align="justify">Después de esto, procedemos a descargar el .NET Core 1.1 ( o el que se encuentre “current” a la hora de leer este artículo).</p> <p align="justify"><a href="https://www.microsoft.com/net/download/core"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image_thumb374.png" width="742" height="720"></a></p> <p align="justify">&nbsp;</p> <p align="justify">Obsérvese que como queremos desarrollar, se escoge el SDK y no solo el runtime. Solo escogeremos el runtime, para instalarlo en una máquina cliente por ejemplo.</p> <p align="justify">Acto seguido, ejecutamos la instalación del SDK:</p> <p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/2017/01/image454.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image_thumb375.png" width="744" height="529"></a></p> <p align="justify">Ahora, si usamos Visual Studio, procedemos a la creación o modificación de nuestro proyecto .NET Core que queremos tener en la última versión del framework. Y si vemos el archivo de configuración project.json observaremos que en la sección de dependencies, tiene una estructura similar a esta:</p> <div align="justify"><pre style="border-top-style: none;overflow: visible;border-left-style: none;font-family: 'OCR A Extended', 'Courier New', courier, monospace;background: black;border-bottom-style: none;color:;padding-bottom: 0px;padding-top: 0px;border-right-style: none;padding-left: 0px;line-height: normal;padding-right: 0px"><span style="color:"><font color="#d7ba7d"><font style="font-size: 9pt">"dependencies"</font></font></span><font style="font-size: 9pt"><font color="#ffff00">: {<br>&nbsp;&nbsp; </font><span style="color:"><font color="#d7ba7d">"Microsoft.NETCore.App"</font></span><font color="#ffff00">: {<br>&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#d7ba7d">"type"</font></span></font><font style="font-size: 9pt"><font color="#ffff00">: "platform",<br>&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#d7ba7d">"version"</font></span><font color="#ffff00">: "1.0.1"<br>&nbsp;&nbsp; }</font></font></pre></div>
<p align="justify">Aquí se nota que por defecto Visual Studio trabaja con la versión LTS. Por ahora no hay una forma visual para decirle a Visual Studio que trabaje con la versión 1.1.0 o la última estable. Ojo que no basta con cambiar la versión en el archivo de configuración. Es necesario hacer el upgrade a través de Nuget; que como ya está actualizado a la última versión, sabrá cómo actualizar el proyecto para que quede funcionando correctamente. Así que hacemos la actualización a través de Nuget:</p>
<p align="justify">&nbsp;</p>
<p align="justify"><a href="https://msdnshared.blob.core.windows.net/media/2017/01/image458.png"><img title="image" style="border-left-width: 0px;border-right-width: 0px;border-bottom-width: 0px;padding-top: 0px;padding-left: 0px;padding-right: 0px;border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2017/01/image_thumb379.png" width="738" height="471"></a></p>
<p align="justify">Después de esto, entre otras actualizaciones tendremos la del project.json:</p>
<div align="justify"><pre style="border-top-style: none;overflow: visible;border-left-style: none;font-family: 'OCR A Extended', 'Courier New', courier, monospace;background: black;border-bottom-style: none;color:;padding-bottom: 0px;padding-top: 0px;border-right-style: none;padding-left: 0px;line-height: normal;padding-right: 0px"><span style="color:"><font color="#d7ba7d"><font style="font-size: 9pt">"dependencies"</font></font></span><font style="font-size: 9pt"><font color="#ffff00">: {<br>&nbsp;&nbsp; </font><span style="color:"><font color="#d7ba7d">"Microsoft.NETCore.App"</font></span><font color="#ffff00">: {<br>&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#d7ba7d">"type"</font></span></font><font style="font-size: 9pt"><font color="#ffff00">: "platform",<br>&nbsp;&nbsp;&nbsp;&nbsp; </font><span style="color:"><font color="#d7ba7d">"version"</font></span><font color="#ffff00">: "1.1.0"<br>&nbsp;&nbsp; }</font></font></pre></div>
<p align="justify">Con lo que ya quedaríamos listos para correr nuestra aplicación con la última versión del .NET Core Framework.</p>
<p>Encuentra en mi Github un ejemplo muy sencillo de una aplicación de Consola .NET Core corriendo con la versión 1.1.0 del framework:</p>
<p><a href="https://github.com/warnov/NetCoreConsoleSettings"><img alt="Image result for github png logo" src="https://cdn4.iconfinder.com/data/icons/iconsimple-logotypes/512/github-512.png" width="82" align="left" height="82"></a></p>]]></content:encoded>
    </item>
  </channel>
</rss>