<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Billy Mumby's Blog]]></title><description><![CDATA[Billy Mumby's Blog]]></description><link>https://billy-mumby-dev.com</link><generator>RSS for Node</generator><lastBuildDate>Sun, 12 Apr 2026 09:08:08 GMT</lastBuildDate><atom:link href="https://billy-mumby-dev.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[NSubstitute from Moq]]></title><description><![CDATA[A concise and straightforward guide to transitioning from Moq to NSubstitute.
Foreword
There has been a recent issue with version 4.2.0 of the popular mocking library Moq. This is covered in many places by the community already, there is a great vide...]]></description><link>https://billy-mumby-dev.com/nsubstitute-from-moq</link><guid isPermaLink="true">https://billy-mumby-dev.com/nsubstitute-from-moq</guid><category><![CDATA[unit testing]]></category><category><![CDATA[unit tests]]></category><category><![CDATA[Mocking]]></category><category><![CDATA[NSubstitute]]></category><category><![CDATA[Moq]]></category><dc:creator><![CDATA[Billy Mumby]]></dc:creator><pubDate>Tue, 22 Aug 2023 07:52:07 GMT</pubDate><content:encoded><![CDATA[<p>A concise and straightforward guide to transitioning from Moq to NSubstitute.</p>
<h2 id="heading-foreword">Foreword</h2>
<p>There has been a recent issue with version 4.2.0 of the popular mocking library <a target="_blank" href="https://github.com/moq/moq">Moq</a>. This is covered in many places by the community already, there is a <a target="_blank" href="https://www.youtube.com/watch?v=A06nNjBKV7I&amp;t=312s">great video by Nick Chapsas</a> here if you prefer to read, <a target="_blank" href="https://snyk.io/blog/moq-package-exfiltrates-user-emails/">this blog post</a> is good as well.</p>
<p>It is worth noting that the issues explained above have since been reverted and are currently not an issue, however, there is a large shift for new and existing projects to look at using alternatives for mocking, <a target="_blank" href="https://nsubstitute.github.io/">NSubstitute</a> being one of them.</p>
<p>The post aims to give a simple guide showing examples of performing the same type of mocking in both libraries to hopefully aid a conversion if required.</p>
<h2 id="heading-the-example-solution">The Example Solution</h2>
<p>The example solution is used to demonstrate the differences between Moq and NSubstitute. It is supposed to simulate a very simple order processing pipeline. It focuses solely on the business logic and does not implement any external dependencies such as messaging services or databases. These are expressed as interfaces and these will be mocked using the two mocking libraries.</p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text">The full code solution can be found on <a target="_blank" href="https://github.com/mumby0168/moq-vs-nsubstitute">GitHub</a>.</div>
</div>

<p>The majority of this post will be showing the differences between the two libraries while unit testing the below piece of code:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// OrderService.Place.cs</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">OrderProcessor.Application.Services</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">OrderService</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task&lt;Guid&gt; <span class="hljs-title">PlaceOrderAsync</span>(<span class="hljs-params">
        PlaceOrderDto dto</span>)</span>
    {
        <span class="hljs-keyword">var</span> (customerId, orderLines) = dto;

        <span class="hljs-keyword">var</span> orderId = _idGenerator.NewOrderId();

        <span class="hljs-keyword">var</span> lines = orderLines.Select(
                x =&gt; <span class="hljs-keyword">new</span> OrderLine(
                    _idGenerator.NewOrderLineId(),
                    x.ProductId,
                    x.Price))
            .ToList();

        <span class="hljs-keyword">var</span> expectedDeliveryDate = _orderDateService
            .CalculateExpectedOrderDate(
                customerId,
                lines);

        <span class="hljs-keyword">var</span> order = <span class="hljs-keyword">new</span> Order(
            orderId,
            customerId,
            expectedDeliveryDate,
            lines);

        <span class="hljs-keyword">await</span> _orderRepository.SaveAsync(order);
        <span class="hljs-keyword">await</span> _eventPublisher.PublishOrderPlacedEventAsync(order);

        <span class="hljs-keyword">return</span> orderId;
    }
}
</code></pre>
<p>This code has been written to use many injected services which are great candidates for being mocked, this will be demonstrated in the following sections.</p>
<h2 id="heading-examples">Examples</h2>
<p>This section will work through a concise set of examples showing how you can achieve different mocking capabilities in both libraries.</p>
<h3 id="heading-creating-mocks">Creating Mocks</h3>
<p>The first port of call for interacting with any mocking library is being able to create an instance of a set of mock objects. This is very simple in both libraries, see below:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Moq --&gt; OrderServiceTests.cs</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">OrderProcessor.MoqTests.Application</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">OrderServiceTests</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> Mock&lt;IOrderRepository&gt; _orderRepository;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> Mock&lt;IEventPublisher&gt; _eventPublisher;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> Mock&lt;IIdGenerator&gt; _idGenerator;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> Mock&lt;IOrderDateService&gt; _orderDateService;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">OrderServiceTests</span>(<span class="hljs-params"></span>)</span>
    {
        _orderRepository = <span class="hljs-keyword">new</span> Mock&lt;IOrderRepository&gt;();
        _eventPublisher = <span class="hljs-keyword">new</span> Mock&lt;IEventPublisher&gt;();
        _idGenerator = <span class="hljs-keyword">new</span> Mock&lt;IIdGenerator&gt;();
        _orderDateService = <span class="hljs-keyword">new</span> Mock&lt;IOrderDateService&gt;();
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> IOrderService <span class="hljs-title">CreateSut</span>(<span class="hljs-params"></span>)</span> =&gt;
        <span class="hljs-keyword">new</span> OrderService(
            _orderRepository.Object,
            _eventPublisher.Object,
            _idGenerator.Object,
            _orderDateService.Object);
}
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-comment">// NSubstitute --&gt; OrderServiceTests.cs</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">OrderProcessor.NSubstituteTests.Application</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">OrderServiceTests</span>
{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IOrderRepository _orderRepository;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IEventPublisher _eventPublisher;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IIdGenerator _idGenerator;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> IOrderDateService _orderDateService;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">OrderServiceTests</span>(<span class="hljs-params"></span>)</span>
    {
        _orderRepository = Substitute.For&lt;IOrderRepository&gt;();
        _eventPublisher = Substitute.For&lt;IEventPublisher&gt;();
        _idGenerator = Substitute.For&lt;IIdGenerator&gt;();
        _orderDateService = Substitute.For&lt;IOrderDateService&gt;();
    }

    <span class="hljs-function"><span class="hljs-keyword">private</span> IOrderService <span class="hljs-title">CreateSut</span>(<span class="hljs-params"></span>)</span> =&gt;
        <span class="hljs-keyword">new</span> OrderService(
            _orderRepository,
            _eventPublisher,
            _idGenerator,
            _orderDateService);
}
</code></pre>
<p>There are only a few key differences between creating objects using both libraries:</p>
<ul>
<li><p>Moq has an object that is created using the <code>new</code> keyword, whereas NSubsitute uses a static class to create its objects.</p>
</li>
<li><p>Moq returns a sort of wrapper class around the real object, whereas NSubsitute returns the real interface, meaning there is no need for the <code>.Object</code> call as there is with Moq.</p>
</li>
</ul>
<h3 id="heading-returning-values">Returning Values</h3>
<p>It is possible to return values from both methods and properties in both mocking libraries, see below for some examples.</p>
<p>One of the most common tasks with mocking is returning values from a method, see how this is done below using both libraries when the method has no arguments.</p>
<p><strong>Methods with no arguments</strong></p>
<p>Setting up return values for method calls in both libraries differs quite a bit, see below:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Moq</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">OrderProcessor.MoqTests.Application</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">OrderServiceTests</span>
{
    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">PlaceOrderAsync_Order_ReturnsOrderId</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> sut = CreateSut();

        <span class="hljs-keyword">var</span> orderId = Guid.NewGuid();
        _idGenerator.Setup(x =&gt; x.NewOrderId()).Returns(orderId);

        <span class="hljs-keyword">var</span> placeOrderDto = <span class="hljs-keyword">new</span> PlaceOrderDto(
            Guid.NewGuid(),
            <span class="hljs-keyword">new</span> List&lt;PlaceOrderDto.OrderLineDto&gt;
            {
                <span class="hljs-keyword">new</span>(
                    Guid.NewGuid(),
                    <span class="hljs-number">10.0</span>)
            });

        <span class="hljs-comment">// Act</span>
        <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> sut.PlaceOrderAsync(placeOrderDto);

        <span class="hljs-comment">// Assert</span>
        result.Should().Be(orderId);
    }
}
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-comment">// NSubstitute</span>
<span class="hljs-keyword">namespace</span> <span class="hljs-title">OrderProcessor.NSubstituteTests.Application</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">OrderServiceTests</span>
{
    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">PlaceOrderAsync_Order_ReturnsOrderId</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> sut = CreateSut();

        <span class="hljs-keyword">var</span> orderId = Guid.NewGuid();
        _idGenerator.NewOrderId().Returns(orderId);

        <span class="hljs-keyword">var</span> placeOrderDto = <span class="hljs-keyword">new</span> PlaceOrderDto(
            Guid.NewGuid(),
            <span class="hljs-keyword">new</span> List&lt;PlaceOrderDto.OrderLineDto&gt;
            {
                <span class="hljs-keyword">new</span>(
                    Guid.NewGuid(),
                    <span class="hljs-number">10.0</span>)
            });

        <span class="hljs-comment">// Act</span>
        <span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> sut.PlaceOrderAsync(placeOrderDto);

        <span class="hljs-comment">// Assert</span>
        result.Should().Be(orderId);
    }
}
</code></pre>
<p>The key differences here come back to the fact that Moq uses a wrapper object whereas NSubsitutes API starts with the real interface which in a lot of ways makes it easier. Moq require a <code>.Setup</code> method to be called before getting to the real interface that you are working with. Both, however, use a <code>Return</code> method off the back of the setup to provide your return value.</p>
<p><strong>Methods with arguments</strong></p>
<p>The setting up of methods with arguments is also quite different, see below:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">namespace</span> <span class="hljs-title">OrderProcessor.MoqTests.Application</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">OrderServiceTests</span>
{
    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">PlaceOrderAsync_Order_SavesOrderWithCorrectDeliveryDate</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> sut = CreateSut();
        <span class="hljs-keyword">var</span> customerId = Guid.NewGuid();

        _idGenerator.Setup(x =&gt; x.NewOrderId()).Returns(Guid.NewGuid);

        <span class="hljs-keyword">var</span> orderDeliveryDate = DateTime.UtcNow.AddDays(<span class="hljs-number">1</span>);

        _orderDateService.Setup(
                o =&gt; o.CalculateExpectedOrderDate(
                    customerId,
                    It.IsAny&lt;IEnumerable&lt;OrderLine&gt;&gt;()))
            .Returns(orderDeliveryDate);

        <span class="hljs-keyword">var</span> placeOrderDto = <span class="hljs-keyword">new</span> PlaceOrderDto(
            customerId,
            <span class="hljs-keyword">new</span> List&lt;PlaceOrderDto.OrderLineDto&gt;
            {
                <span class="hljs-keyword">new</span>(
                    Guid.NewGuid(),
                    <span class="hljs-number">10.0</span>)
            });

        <span class="hljs-comment">// Act</span>
        <span class="hljs-keyword">await</span> sut.PlaceOrderAsync(placeOrderDto);

        <span class="hljs-comment">// Assert</span>
        _orderRepository.Verify(
            o =&gt; o.SaveAsync(
                It.Is&lt;Order&gt;(
                    order =&gt;
                        order.ExpectedDeliveryDate == orderDeliveryDate)),
            Times.Once);
    }
}
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-keyword">namespace</span> <span class="hljs-title">OrderProcessor.NSubstituteTests.Application</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">OrderServiceTests</span>
{
    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">PlaceOrderAsync_Order_SavesOrderWithCorrectDeliveryDate</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> sut = CreateSut();
        <span class="hljs-keyword">var</span> customerId = Guid.NewGuid();

        _idGenerator.NewOrderId().Returns(Guid.NewGuid());

        <span class="hljs-keyword">var</span> orderDeliveryDate = DateTime.UtcNow.AddDays(<span class="hljs-number">1</span>);

        _orderDateService.CalculateExpectedOrderDate(
                customerId,
                Arg.Any&lt;IEnumerable&lt;OrderLine&gt;&gt;())
            .Returns(orderDeliveryDate);

        <span class="hljs-keyword">var</span> placeOrderDto = <span class="hljs-keyword">new</span> PlaceOrderDto(
            customerId,
            <span class="hljs-keyword">new</span> List&lt;PlaceOrderDto.OrderLineDto&gt;
            {
                <span class="hljs-keyword">new</span>(
                    Guid.NewGuid(),
                    <span class="hljs-number">10.0</span>)
            });

        <span class="hljs-comment">// Act</span>
        <span class="hljs-keyword">await</span> sut.PlaceOrderAsync(placeOrderDto);

        <span class="hljs-comment">//Assert</span>
        <span class="hljs-keyword">await</span> _orderRepository.Received(<span class="hljs-number">1</span>).SaveAsync(
            Arg.Is&lt;Order&gt;(
                o =&gt;
                    o.ExpectedDeliveryDate == orderDeliveryDate));
    }
}
</code></pre>
<p>The interaction with setting up return values is very similar with or without arguments in both libraries, but there is extra syntax required to provide the expected arguments. In this example the <code>IOrderDateService</code> is set up to return a specific date based on solely a customer ID in these examples.</p>
<p>This is done in both libraries by just calling the method via the mocking library and passing the expected customer ID, and then using the respective <code>It</code> or <code>Arg</code> classes simply accept any order lines passed in as the second argument. These two static classes are the key difference really between the two in terms of accepting arguments when setting up mocked objects.</p>
<p><strong>Properties</strong></p>
<p>It is possible to set up return values from properties in both libraries as well, in a very similar fashion. The tests below do not test anything useful, they simply demonstrate how to set up return values of properties.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">namespace</span> <span class="hljs-title">OrderProcessor.MoqTests.Domain</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">OrderTests</span>
{
    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Id_Mock_ReturnsMockedId</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> id = Guid.NewGuid();
        <span class="hljs-keyword">var</span> sut = <span class="hljs-keyword">new</span> Mock&lt;IOrder&gt;();
        sut.SetupGet(o =&gt; o.Id).Returns(id);

        <span class="hljs-comment">// Act</span>
        <span class="hljs-keyword">var</span> result = sut.Object.Id;

        <span class="hljs-comment">// Assert</span>
        result.Should().Be(id);
    }
}
</code></pre>
<pre><code class="lang-csharp"><span class="hljs-keyword">namespace</span> <span class="hljs-title">OrderProcessor.NSubstituteTests.Domain</span>;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">OrderTests</span>
{
    [<span class="hljs-meta">Fact</span>]
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Id_Mock_ReturnsMockedId</span>(<span class="hljs-params"></span>)</span>
    {
        <span class="hljs-comment">// Arrange</span>
        <span class="hljs-keyword">var</span> id = Guid.NewGuid();
        <span class="hljs-keyword">var</span> sut = Substitute.For&lt;IOrder&gt;();
        sut.Id.Returns(id);

        <span class="hljs-comment">// Act</span>
        <span class="hljs-keyword">var</span> result = sut.Id;

        <span class="hljs-comment">// Assert</span>
        result.Should().Be(id);
    }
}
</code></pre>
<p>I think this example, shows clearly the more concise API that is provided by NSubstitute without the need for in some ways the noise provided by Moq.</p>
<h3 id="heading-verifying-method-calls">Verifying method calls</h3>
<p>Verifying that a method has been called as part of a unit test is also a common task and is possible in both libraries again with quite a difference in syntax, this repeats a test from an earlier section, so for brevity, the calls to check that an order has been saved to the database has been extracted solely in the snippets below:</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">Fact</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">PlaceOrderAsync_Order_SavesOrderWithCorrectDeliveryDate</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-comment">// Excluded for brevity ...</span>
    <span class="hljs-comment">// Assert</span>
    _orderRepository.Verify(
        o =&gt; o.SaveAsync(
            It.Is&lt;Order&gt;(
                order =&gt;
                    order.ExpectedDeliveryDate == orderDeliveryDate)),
            Times.Once);
}
</code></pre>
<pre><code class="lang-csharp">[<span class="hljs-meta">Fact</span>]
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">async</span> Task <span class="hljs-title">PlaceOrderAsync_Order_SavesOrderWithCorrectDeliveryDate</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-comment">// Excluded for brevity ...</span>
    <span class="hljs-comment">//Assert</span>
    <span class="hljs-keyword">await</span> _orderRepository.Received(<span class="hljs-number">1</span>).SaveAsync(
            Arg.Is&lt;Order&gt;(
                o =&gt;
                    o.ExpectedDeliveryDate == orderDeliveryDate));
}
</code></pre>
<p>As shown above, there are some key differences to the approach taken by each library, one thing that stands out, is how the amount of calls is specified to a given method. It's always a good idea to be explicit about the number of calls expected to a given method in a unit test. NSubstitute puts this up front as the first thing required via the <code>.Received()</code> call before getting at the method you want to check. Moq on the other hand has it as an optional argument that isn't always apparent to someone who is just starting with the library.</p>
<p>The ceremony involved in mock with the expressions and extra method calls can make these verify steps a little hard to read, whereas the concise syntax of NSubsitutue can be a lot easier to read.</p>
<p>Both libraries again make use of there respective <code>It</code> and <code>Arg</code> classes for checking arguments as shown in the examples above.</p>
<h1 id="heading-summary">Summary</h1>
<p>In summary, NSubstitue's API surface is much closer to that of the interface that is being tested, which makes a big difference in not having to go through the ceremony of the additional method calls in something like Moq. The verifying of method calls is also a little cleaner in terms of the syntax when being precise about the number of calls made to a method, this is something that feels a little clunky in Moq.</p>
<p>In terms of a potential transition from Moq to NSubstitute, the differences are very minor and should be a trivial task in terms of complexity. Depending on the size of the code base, however, this may be a bit of a laborious task.</p>
]]></content:encoded></item><item><title><![CDATA[Paging in Azure Cosmos DB]]></title><description><![CDATA[Foreword
Cosmos DB is a popular NoSQL database offering from Microsoft's cloud provider Azure. It offers an extremely scalable, super-fast, and highly available platform as a service database. All data is stored as JSON, in a logical grouping called ...]]></description><link>https://billy-mumby-dev.com/paging-in-azure-cosmos-db</link><guid isPermaLink="true">https://billy-mumby-dev.com/paging-in-azure-cosmos-db</guid><category><![CDATA[Azure]]></category><category><![CDATA[dotnet]]></category><category><![CDATA[dotnetcore]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Billy Mumby]]></dc:creator><pubDate>Sun, 16 Jan 2022 09:30:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/E0AHdsENmDg/upload/v1642186196840/c3Vp1WEvd.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-foreword">Foreword</h1>
<p>Cosmos DB is a popular NoSQL database offering from Microsoft's cloud provider Azure. It offers an extremely scalable, super-fast, and highly available platform as a service database. All data is stored as JSON, in a logical grouping called a container.  Containers can scale independently of one another and configure independent partitioning strategies.</p>
<blockquote>
<p>If containers, partitioning, and NoSQL databases are new to you, I would recommend reading the Cosmos DB documentation found <a target="_blank" href="https://docs.microsoft.com/en-us/azure/cosmos-db/">here</a>. </p>
</blockquote>
<h1 id="heading-paging-use-cases">Paging use cases</h1>
<p>Why do you need to page data? There are many cases for paging data. But I believe there are two examples that best explain the different paging options provided by Cosmos DB. However, these use cases apply to all databases.</p>
<p>The first is a common scenario you've probably used in social media, <em>infinite scrolling</em>. Think of Twitter, for example, you load some tweets then scroll down the feed. As you read the first set of tweets more are loaded for you to read, and so on. This is a form of paging. You can scroll back up the tweets but it won't re-query the database for those tweets again. The tweets are already loaded onto your device so you just scroll back up, and they are still there. The fact it does not re-query the database is important in this example. If you think of this from an implementation point of view, this is like a marker, and as you read the next page that marker moves. However, this "marker" always moves forward's never backward in this example. This is important to remember for later.</p>
<p>The second example I like to use, is a typical table, in an application that has common features. Such as <em>next</em> and <em>previous</em> buttons, a dropdown to allow you to specify the maximum number of results returned, and so on. Something like the table below taken from <a target="_blank" href="https://mdbootstrap.com/docs/b4/jquery/tables/pagination/">MD Bootstraps docs</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642187327324/YrJZi8gdY.png" alt="image.png" /></p>
<p>This allows a user to have random access to any page, moving back and forth through the pages, potentially changing the sorting and filtering of the data and the page size of the given results. This as you can imagine, requires the database do a lot more work, but also offers a lot more flexibility in terms of what you can offer to a user.</p>
<h1 id="heading-the-cost-of-paging">The Cost of Paging</h1>
<p>Paging in any database is an expensive feature in terms of resources that will be used up by a database if it is frequently paging data. In the random-access scenario, a lot of implementations rely on either <em>skip</em> and <em>take</em>, or <em>limit</em> and <em>offset</em> approaches. A common example of this could be making a query to retrieve page 5 of some data with a page size of 25. You would have a query that skips the first 100 records but limits the returned records of the query to 25 records. This is supported by Cosmos DB and many other databases but this means the database has to skip 100 rows which it still has to process, it can't just jump to a point in time or to a cursor for example. When you translate this into Cosmos DB terms this skip will add to the number of RUs that your query costs. In a lot of cases making some queries very expensive if run frequently.</p>
<blockquote>
<p>What are "RU's"? RU's or request units are the cost of an operation, think of this as the currency of a Cosmos database read more on this <a target="_blank" href="https://docs.microsoft.com/en-us/azure/cosmos-db/request-units">here</a>.</p>
</blockquote>
<p>Taking a step back to the infinite scroll scenario described earlier if you were to implement something like this in Cosmos DB there is an awesome feature that can be utilized. Cosmos DB implements continuation tokens for its paging operations, this is a stateless marker in a database of where a current query is at. This means when provided back to Cosmos DB it can pick up right where you left off with a mostly consistent RU charge. The beauty of these being stateless means you could make a query and then not come back and request the next page of data for a few hours or days and it will pick up just where you left off. </p>
<blockquote>
<p>Want to know more about continuation tokens? Check out the  <a target="_blank" href="https://docs.microsoft.com/en-us/azure/cosmos-db/sql/sql-query-pagination#continuation-tokens">Cosmos DB docs</a>!</p>
</blockquote>
<h1 id="heading-paging-code-samples">Paging Code Samples</h1>
<p>The following code examples come in the form of .NET 6 Web API projects, both of which can be found <a target="_blank" href="https://github.com/mumby0168/cosmos-db-paging">here</a>. The first example uses the raw  <a target="_blank" href="https://github.com/Azure/azure-cosmos-dotnet-v3">.NET Cosmos SDK</a> and the second uses the <a target="_blank" href="https://github.com/IEvangelist/azure-cosmos-dotnet-repository">Azure Cosmos Repository</a>. Some of the code from these samples will be excluded in this post. This is to focus on paging implementation using both libraries.</p>
<blockquote>
<p>The Azure Cosmos Repository wraps the .NET Cosmos SDK, aiming to take away some of the ceremony involved with the .NET SDK when working with containers.</p>
</blockquote>
<h2 id="heading-paging-in-cosmos-db-using-the-net-sdk">Paging in Cosmos DB using the .NET SDK</h2>
<h3 id="heading-the-skip-take-approach">The <em>skip</em>, <em>take</em> approach</h3>
<p>The first code example is an API endpoint that allows a client to page through a container with a set of people records.</p>
<pre><code class="lang-c#">app.MapGet(<span class="hljs-string">"/skipTake"</span>, <span class="hljs-keyword">async</span> (
    [<span class="hljs-meta">FromServices</span>] CosmosClient client,
    [<span class="hljs-meta">FromQuery</span>] <span class="hljs-keyword">int</span> pageNumber,
    [<span class="hljs-meta">FromQuery</span>] <span class="hljs-keyword">int</span> pageSize) =&gt;
{
    <span class="hljs-keyword">var</span> container = <span class="hljs-keyword">await</span> GetPeopleContainer(client);

    QueryRequestOptions queryOptions = <span class="hljs-keyword">new</span>()
    {
        MaxItemCount = pageSize
    };

    IQueryable&lt;Person&gt; query = container
        .GetItemLinqQueryable&lt;Person&gt;(requestOptions: queryOptions)
        .Where(x =&gt; x.PartitionKey == <span class="hljs-keyword">nameof</span>(Person))
        .Skip(pageSize * (pageNumber - <span class="hljs-number">1</span>))
        .Take(pageSize);

    <span class="hljs-keyword">var</span> (items, charge, _) =
        <span class="hljs-keyword">await</span> GetAllItemsAsync(query, pageSize);

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span>
    {
        requestUnits = charge,
        count = items.Count,
        people = items,
    };
});
</code></pre>
<blockquote>
<p>In the above example, there is a method called <code>GetAllItemsAsync(...)</code> this does a lot with the SDK to properly get the results and map them into a list. This can be found <a target="_blank" href="https://github.com/mumby0168/cosmos-db-paging/blob/904363b9d74d0dc8f49269f92d9575315c453360/CosmosDbSdkPagingSample/Program.cs#L42">here</a></p>
</blockquote>
<p>This example work's as expected making a request such as <code>GET https://{{host}}:{{port}/skipTake?pageSize=25&amp;pageNumber=1</code> returns 25 people and costs <code>3.6 RUs</code>. See the response below.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"requestUnits"</span>: <span class="hljs-number">3.6</span>,
  <span class="hljs-attr">"count"</span>: <span class="hljs-number">25</span>,
  <span class="hljs-attr">"people"</span>: <span class="hljs-comment">//excluded for brevity</span>
}
</code></pre>
<p>3.6 RUs isn't certainly not an expensive query in Cosmos DB terms. However, when you query for page 10 with a request such as <code>GET https://{{host}}:{{port}/skipTake?pageSize=25&amp;pageNumber=10</code>. This request costs nearly <code>10 RUs</code>, see below.</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"requestUnits"</span>: <span class="hljs-number">9.86</span>,
  <span class="hljs-attr">"count"</span>: <span class="hljs-number">25</span>,
  <span class="hljs-attr">"people"</span>: <span class="hljs-comment">//excluded for brevity</span>
}
</code></pre>
<p>This links back to some of the problems with the <em>skip</em>, <em>take</em> approach explained earlier. You can imagine as the page size increases and you begin to query for page 50 or 100 on a larger data set, this charge keeps on increasing.</p>
<h3 id="heading-paging-with-continuation-tokens">Paging with continuation tokens</h3>
<p>The next example shows how the infinite scroll scenario can be implemented using the .NET SDK.</p>
<pre><code class="lang-csharp">app.MapGet(<span class="hljs-string">"/tokenBased"</span>, <span class="hljs-keyword">async</span> (
    HttpContext context,
    [<span class="hljs-meta">FromServices</span>] CosmosClient client,
    [<span class="hljs-meta">FromQuery</span>] <span class="hljs-keyword">int</span> pageSize) =&gt;
{
    <span class="hljs-keyword">string</span>? continuationToken = 
        context.Request.Headers[<span class="hljs-string">"X-Continuation-Token"</span>];

    <span class="hljs-keyword">var</span> container = <span class="hljs-keyword">await</span> GetPeopleContainer(client);

    QueryRequestOptions queryOptions = <span class="hljs-keyword">new</span>()
    {
        MaxItemCount = pageSize
    };

    IQueryable&lt;Person&gt; query = container
        .GetItemLinqQueryable&lt;Person&gt;(
            requestOptions: queryOptions, 
            continuationToken: continuationToken)
        .Where(x =&gt; x.PartitionKey == <span class="hljs-keyword">nameof</span>(Person));

    <span class="hljs-keyword">var</span> (items, charge, newContinuationToken) =
        <span class="hljs-keyword">await</span> GetAllItemsAsync(query, pageSize);


    context.Response.Headers[<span class="hljs-string">"X-Continuation-Token"</span>] =
        newContinuationToken;

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span>
    {
        count = items.Count,
        requestUnits = charge,
        people = items,
    };
});
</code></pre>
<p>The above example tries to read a token from a header named <code>X-Continuation-Token</code>. This is not required to read the first page. This will then return a token stored in the same header for the response if there is another page.</p>
<p>The header <code>X-Continuation-Token</code> was chosen to return the token to a client because, if this was returned in a JSON object the quotes would have to be escaped. This when provided back to Cosmos DB is rejected as an invalid continuation token. See an example of a valid continuation token below.</p>
<pre><code class="lang-json">[
    {
        <span class="hljs-attr">"token"</span>: <span class="hljs-string">"+RID:~47VbALuANL8ZAAAAAAAAAA==#RT:1#TRC:25#ISV:2#IEO:65567#QCF:4#FPC:ARkAAAAAAAAALAEAAAAAAAA="</span>,
        <span class="hljs-attr">"range"</span>: {
            <span class="hljs-attr">"min"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-attr">"max"</span>: <span class="hljs-string">"FF"</span>
        }
    }
]
</code></pre>
<p>Running the samples and working through the pages of data, for each request the RU charge remains consistent. There is not a large increase in cost as you work further through the pages. This is in large contrast to the <em>skip</em>, <em>take</em> approach that costs more the further through the pages you go.</p>
<p>Up to now continuation tokens are looking like the best option. However, there are a few things you cannot do with continuation tokens. The first thing is you can only move forward, you cannot go back a page. The second is that you cannot change the query. Let's say you ordered by age and read the first page. You then got a continuation token back from that request. If you then tried to change the ordering to order by name and passed the same continuation token. Then you would get an error from Cosmos DB. This was something I found the hard way, see  <a target="_blank" href="https://github.com/Azure/azure-cosmos-dotnet-v3/issues/2657">this GitHub issue</a> for more information.</p>
<h3 id="heading-note-on-query-execution-in-cosmos-db">Note on query execution in Cosmos DB</h3>
<p>There is one part of the two examples I wanted to draw attention to and that is the method that is used in both <code>GetAllItemsAsync(query, pageSize);</code>. The implementation of this is below.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">static</span> <span class="hljs-keyword">async</span> Task&lt;(
    List&lt;Person&gt; items, 
    <span class="hljs-keyword">double</span> charge, 
    <span class="hljs-keyword">string</span>? continuationToken
    )&gt; GetAllItemsAsync(
    IQueryable&lt;Person&gt; query,
    <span class="hljs-keyword">int</span> pageSize)
{
    <span class="hljs-keyword">string</span>? continuationToken = <span class="hljs-literal">null</span>;
    List&lt;Person&gt; results = <span class="hljs-keyword">new</span>();
    <span class="hljs-keyword">int</span> readItemsCount = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">double</span> charge = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">using</span> FeedIterator&lt;Person&gt; iterator = query.ToFeedIterator();

    <span class="hljs-keyword">while</span> (readItemsCount &lt; pageSize &amp;&amp; iterator.HasMoreResults)
    {
        FeedResponse&lt;Person&gt; next = 
            <span class="hljs-keyword">await</span> iterator.ReadNextAsync();

        <span class="hljs-keyword">foreach</span> (Person result <span class="hljs-keyword">in</span> next)
        {
            <span class="hljs-keyword">if</span> (readItemsCount == pageSize)
            {
                <span class="hljs-keyword">break</span>;
            }

            results.Add(result);
            readItemsCount++;
        }

        charge += next.RequestCharge;
        continuationToken = next.ContinuationToken;
    }

    <span class="hljs-keyword">return</span> (results, charge, continuationToken);
}
</code></pre>
<p>The main points to note in this method are the <code>while</code> loop and the <code>readItemsCount</code> variable that allows this method to make sure it returns all the available results back from a given paging query.</p>
<p>The reason this is needed is that even though we are paging. The .NET SDK will also perform some paging for larger results sets. The other reason is that when we set the <code>MaxItemCount</code> property on the <code>QueryRequestOptions</code> object, this is <em>specifically</em> a <em>maximum</em> item count. This means in some cases Cosmos DB may return fewer results, even though there are more available. Learn more about this by reading <a target="_blank" href="https://docs.microsoft.com/en-us/azure/cosmos-db/sql/sql-query-pagination">the docs</a></p>
<h2 id="heading-paging-in-cosmos-db-using-the-cosmos-repository-library">Paging in Cosmos DB using the Cosmos Repository Library</h2>
<p>The next set of examples shows how you can achieve paging with the two methods shown using the .NET SDK, using the Cosmos Repository. This library aims to take away a lot of the complexity and boilerplate that is included in the  <a target="_blank" href="https://github.com/mumby0168/cosmos-db-paging/blob/main/CosmosDbSdkPagingSample/Program.cs">.NET SDK sample</a>.</p>
<p>Hopefully, you will see how concise these examples are and furthermore, how the amount of code you are required to write decreases by using this library. See the <a target="_blank" href="https://github.com/mumby0168/cosmos-db-paging/blob/main/CosmosRepositoryPagingSample/Program.cs">full sample here</a>.</p>
<blockquote>
<p>You can find the Azure Cosmos Repository on <a target="_blank" href="https://github.com/IEvangelist/azure-cosmos-dotnet-repository">GitHub</a>  and on [Nuget].(https://www.nuget.org/packages/IEvangelist.Azure.CosmosRepository) </p>
</blockquote>
<h3 id="heading-cosmos-repository-setup">Cosmos Repository Setup</h3>
<p>In order to set up the Cosmos Repository, you can use a simple extension provided by the library, which extends the <code>ServiceCollection</code> provided by Microsoft. See below.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">using</span> CosmosRepositoryPagingSample;
<span class="hljs-keyword">using</span> Microsoft.Azure.CosmosRepository;

<span class="hljs-keyword">var</span> builder = WebApplication.CreateBuilder(args);

<span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> databaseName = <span class="hljs-string">"people-database"</span>;
<span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> peopleContainerName = <span class="hljs-string">"people-container"</span>;
<span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> partitionKey = <span class="hljs-string">"/partitionKey"</span>;

builder.Services.AddCosmosRepository(options =&gt;
{
    <span class="hljs-comment">// In order to set this connection string run</span>
    <span class="hljs-comment">// dotnet user-secrets set RepositoryOptions:CosmosConnectionString "&lt;your-connection-string&gt;</span>
    <span class="hljs-comment">// options.CosmosConnectionString = "&lt;taken-from-config&gt;";</span>

    options.DatabaseId = databaseName;
    options.ContainerPerItemType = <span class="hljs-literal">true</span>;

    options.ContainerBuilder
        .Configure&lt;Person&gt;(containerOptionsBuilder =&gt;
    {
        containerOptionsBuilder
            .WithContainer(peopleContainerName)
            .WithPartitionKey(partitionKey);
    });
});

<span class="hljs-keyword">var</span> app = builder.Build();
</code></pre>
<p>The only other thing you need to do is ensure that the model you are wanting to use implements the <code>IItem</code> interface. There are a set of base classes that are provided by the library that also implements this interface, such as <code>FulllItem</code>. This is used in the example of the <code>Person</code> object below.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span> : <span class="hljs-title">FullItem</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Person</span>(<span class="hljs-params">
        <span class="hljs-keyword">string</span> name, 
        <span class="hljs-keyword">int</span> age, 
        <span class="hljs-keyword">string</span> address</span>)</span>
    {
        Id = name;
        Name = name;
        Age = age;
        Address = address;
        PartitionKey = <span class="hljs-keyword">nameof</span>(Person);
    }

    [<span class="hljs-meta">JsonConstructor</span>]
    <span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">Person</span>(<span class="hljs-params">
        <span class="hljs-keyword">string</span> id, 
        <span class="hljs-keyword">string</span> name, 
        <span class="hljs-keyword">int</span> age, 
        <span class="hljs-keyword">string</span> address, 
        <span class="hljs-keyword">string</span> partitionKey</span>)</span>
    {
        Id = id;
        Name = name;
        Age = age;
        Address = address;
        PartitionKey = partitionKey;
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Name { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> Age { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Address { <span class="hljs-keyword">get</span>; }
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> PartitionKey { <span class="hljs-keyword">get</span>; }

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">override</span> <span class="hljs-keyword">string</span> <span class="hljs-title">GetPartitionKeyValue</span>(<span class="hljs-params"></span>)</span> =&gt;
        PartitionKey;
</code></pre>
<p>The final thing to do is to inject an instance of the <code>IRepository&lt;Person&gt;</code> into any consumer and use the extensive set of methods provided. See this <em>interfaces</em> definition <a target="_blank" href="https://github.com/IEvangelist/azure-cosmos-dotnet-repository/blob/main/src/Microsoft.Azure.CosmosRepository/DefaultRepository.cs">here</a>.</p>
<h3 id="heading-continuation-token-paging-using-the-cosmos-repository">Continuation token paging using the Cosmos Repository</h3>
<p>The following endpoint demonstrates how you can use the Cosmos Repository to implement continuation token paging.</p>
<pre><code class="lang-csharp">app.MapGet(<span class="hljs-string">"/tokenBased"</span>, <span class="hljs-keyword">async</span> (
    HttpContext context,
    [<span class="hljs-meta">FromServices</span>] IRepository&lt;Person&gt; repository,
    [<span class="hljs-meta">FromQuery</span>] <span class="hljs-keyword">int</span> pageSize) =&gt;
{
    <span class="hljs-keyword">string</span>? continuationToken = 
        context.Request.Headers[<span class="hljs-string">"X-Continuation-Token"</span>];

    IPage&lt;Person&gt; result = <span class="hljs-keyword">await</span> repository.PageAsync(
        x =&gt; x.PartitionKey == <span class="hljs-keyword">nameof</span>(Person),
        pageSize,
        continuationToken);

    context.Response.Headers[<span class="hljs-string">"X-Continuation-Token"</span>] =
        result.Continuation;

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span>
    {
        requestUnits = result.Charge,
        count = result.Total,
        people = result.Items
    };
});
</code></pre>
<h3 id="heading-skip-take-paging-using-the-cosmos-repository"><em>Skip</em>, <em>take</em> paging using the Cosmos Repository</h3>
<p>The <em>skip</em>, <em>take</em> approach using the Azure Cosmos Repository is shown below.</p>
<pre><code class="lang-csharp">app.MapGet(<span class="hljs-string">"/skipTake"</span>, <span class="hljs-keyword">async</span> (
    [<span class="hljs-meta">FromServices</span>] IRepository&lt;Person&gt; repository,
    [<span class="hljs-meta">FromQuery</span>] <span class="hljs-keyword">int</span> pageNumber,
    [<span class="hljs-meta">FromQuery</span>] <span class="hljs-keyword">int</span> pageSize) =&gt;
{

    IPageQueryResult&lt;Person&gt; result = <span class="hljs-keyword">await</span> repository.PageAsync(
            x =&gt; x.PartitionKey == <span class="hljs-keyword">nameof</span>(Person),
            pageNumber,
            pageSize);

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span>
    {
        requestUnits = result.Charge,
        count = result.Total,
        pages = result.TotalPages,
        people = result.Items
    };
});
</code></pre>
<p>The response that reading the 10th page resulted in is also shown below.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"requestUnits"</span>: <span class="hljs-number">14.100000000000001</span>,
    <span class="hljs-attr">"count"</span>: <span class="hljs-number">400</span>,
    <span class="hljs-attr">"pages"</span>: <span class="hljs-number">16</span>,
    <span class="hljs-attr">"people"</span>: [
        {
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Harry Pouros"</span>,
            <span class="hljs-attr">"age"</span>: <span class="hljs-number">26</span>,
            <span class="hljs-attr">"address"</span>: <span class="hljs-string">"635 Predovic Heights"</span>,
            <span class="hljs-attr">"partitionKey"</span>: <span class="hljs-string">"Person"</span>,
            <span class="hljs-attr">"etag"</span>: <span class="hljs-string">"\"0600097c-0000-0d00-0000-61e309330000\""</span>,
            <span class="hljs-attr">"timeToLive"</span>: <span class="hljs-literal">null</span>,
            <span class="hljs-attr">"lastUpdatedTimeUtc"</span>: <span class="hljs-string">"2022-01-15T17:49:39"</span>,
            <span class="hljs-attr">"lastUpdatedTimeRaw"</span>: <span class="hljs-number">1642268979</span>,
            <span class="hljs-attr">"createdTimeUtc"</span>: <span class="hljs-literal">null</span>,
            <span class="hljs-attr">"id"</span>: <span class="hljs-string">"Harry Pouros"</span>,
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"Person"</span>
        },
        <span class="hljs-comment">//Excluded the rest of the results for brevity</span>
    ]
}
</code></pre>
<h3 id="heading-why-use-the-cosmos-repository">Why use the Cosmos Repository?</h3>
<p>I hope that the code samples above, look concise and succinct that was the aim. Furthermore, the <code>IPage&lt;TItem&gt;</code> and <code>IPageQueryResult&lt;TItem</code> <em>interfaces</em> offer some extra properties returned back from a paging operation, such as how many items there are in total, how many pages there are, and it also includes the RU charge. These paging implementations also handle all of the query execution bits that were discussed earlier. </p>
<p>I may be slightly biased, being a passionate maintainer of the Cosmos Repository, but I feel this library offers a super-thin wrapper around the .NET SDK that includes a ton of great features! These features took a lot of time and effort to implement, this is time saved for any consumer of this package in my opinion. I would love anyone reading to try this out and offer any feedback via the <a target="_blank" href="https://github.com/IEvangelist/azure-cosmos-dotnet-repository">GitHub repository</a>.</p>
<blockquote>
<p>It is worth also noting that there has been a lot of work by the EF Core team to integrate Cosmos DB into EF Core. It is certainly also worth considering this an option. See <a target="_blank" href="https://devblogs.microsoft.com/dotnet/taking-the-ef-core-azure-cosmos-db-provider-for-a-test-drive/">this blog post</a> on some of the new features they have added.</p>
</blockquote>
<h1 id="heading-final-words">Final words</h1>
<p>Thank you for taking the time to read this and I certainly hope it has helped explain some of the options you have available for paging in Cosmos DB. I am happy to answer any questions feel free to reach out via the comments or tweet me <a target="_blank" href="https://twitter.com/billydev5">@billydev5</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Fluent .NET Apis - Guiding A Developer]]></title><description><![CDATA[Fluent Apis - Guiding A Developer
Sometimes when creating an object or performing a task via an API the creator of that API may want to ensure certain things happen sometimes in a specific order. It may also be the case that some things are required ...]]></description><link>https://billy-mumby-dev.com/fluent-dotnet-apis-guiding-a-developer</link><guid isPermaLink="true">https://billy-mumby-dev.com/fluent-dotnet-apis-guiding-a-developer</guid><category><![CDATA[dotnet]]></category><category><![CDATA[dotnetcore]]></category><category><![CDATA[api]]></category><dc:creator><![CDATA[Billy Mumby]]></dc:creator><pubDate>Thu, 13 Jan 2022 15:03:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/b2Cf5xAsmLQ/upload/v1642086192212/9cT4mfzWD.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-fluent-apis-guiding-a-developer">Fluent Apis - Guiding A Developer</h1>
<p>Sometimes when creating an object or performing a task via an API the creator of that API may want to ensure certain things happen sometimes in a specific order. It may also be the case that some things are required to happen and some things are not. A fluent API is a great way to guide a developer into a direction and also provides a great experience for them while being a client of your API.</p>
<p>There is a high likelihood that if you are reading this and have heard of the term <em>fluent API</em> or not that you will have used one before. It is highly likely that you will have made an API or seen an API made in ASPNET Core. If you have then you will have seen the <code>Startup</code> class. This class has two methods as shown below. Note that this example, shown below does not make separate calls to <code>services</code> or <code>app</code> it chains all of these calls together.</p>
<pre><code class="lang-c#"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Startup</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">ConfigureServices</span>(<span class="hljs-params">IServiceCollection services</span>)</span> =&gt;
        services.AddCosmosRepository(o =&gt;
            {
                o.DatabaseId = <span class="hljs-string">"dogs-db"</span>;
                o.ContainerId = <span class="hljs-string">"dogs"</span>;
            })
            .AddSwaggerGen()
            .AddMvc();

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Configure</span>(<span class="hljs-params">IApplicationBuilder app, IWebHostEnvironment env</span>)</span> =&gt;
        app.UseSwagger()
            .UseSwaggerUI()
            .UseRouting()
            .UseEndpoints(endpoints =&gt;
            {
                endpoints.MapControllers();
                endpoints.MapGet(<span class="hljs-string">"/"</span>, <span class="hljs-keyword">async</span> context =&gt;
                {
                    <span class="hljs-keyword">await</span> context.Response.WriteAsync(<span class="hljs-string">"Dog API"</span>);
                });
            });
}
</code></pre>
<h3 id="heading-what-makes-an-api-fluent">What makes an API fluent?</h3>
<p>In the example of the <code>Startup</code> class shown above, we can see that every call to register some <code>services</code> or to configure some middleware on the <code>app</code> return an instance of the interface back from the method call. <code>IServiceCollection</code> and <code>IApplicationBuilder</code> respectively. The example shown above is one of the more flexible fluent APIs as it just returns an instance of the class over &amp; over it does not guide a user down a specific path.</p>
<h2 id="heading-guiding-a-developer">Guiding a developer</h2>
<p>In order to guide a developer down a specific path when creating a fluent API, interfaces are used to restrict the next method that the user has access to when chaining the methods calls together. This is best shown via an example. This example will show an <code>AccountBuilder</code>, this has a few requirements.</p>
<ol>
<li>An account must have an email address.</li>
<li>An account must have a password.</li>
<li>An account must verify the password used.</li>
<li>An account must have a password with a length of at least 6 characters.</li>
<li>An account must have a password with at least one of the special characters <code>@$</code>.</li>
<li>An account can optionally have a username.</li>
<li>An account can optionally have many roles.</li>
</ol>
<p>In order to meet these requirements, an object has been defined for an account that exposes all of these properties to meet the requirements above. See below.</p>
<pre><code class="lang-c#"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Account</span>
{ 
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Id { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = Guid.NewGuid().ToString();

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Email { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = <span class="hljs-keyword">string</span>.Empty;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span> Password { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = <span class="hljs-keyword">string</span>.Empty;

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">string</span>? Username { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; }

    <span class="hljs-keyword">public</span> List&lt;<span class="hljs-keyword">string</span>&gt; Roles { <span class="hljs-keyword">get</span>; <span class="hljs-keyword">set</span>; } = <span class="hljs-keyword">new</span>();
}
</code></pre>
<p>This class is all well and good but defines a public constructor. This means a client to this class could create it has not met any of the requirements listed above. What would be better is if we made this constructor internal and made the client create an account via a fluent API named <code>AccountBuilder</code>. This is going to make a user first, provide their email address, then provide a password, then verify that password before optionally providing a username and some roles. In order to let a user start the process, we will define a factory that returns an interface that only allows the user to initially provide their email address see the example below.</p>
<pre><code class="lang-c#"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IAccountBuilderWithEmail</span>
{
    <span class="hljs-function">IAccountBuilderWithPassword <span class="hljs-title">WithEmailAddress</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> email</span>)</span>;
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">AccountFactory</span>
{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> IAccountBuilderWithEmail <span class="hljs-title">AccountBuilder</span>(<span class="hljs-params"></span>)</span> =&gt; <span class="hljs-keyword">new</span> AccountBuilder();
}
</code></pre>
<p>The example above show's how the factory initially only lets the user of the api call the method <code>WithEmailAddress(...)</code>. Until they have done this they do not get access to the interface named <code>IAccountBuilderWithPassword</code> which will then let them provide a password. See below.</p>
<pre><code class="lang-c#"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IAccountBuilderWithPassword</span>
{
    <span class="hljs-function">IAccountBuilderWithVerifiedPassword <span class="hljs-title">WithPassword</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> password</span>)</span>;
}
</code></pre>
<p>This method then lets them verify this password via the interface <code>IAccountBuilderWithVerifiedPassword</code> but again nothing more. What this example ends up with is an interface per method that must be called and once completed, the method returns the next method that must be called. This is in essence guiding a developer through the process of creating an account. See all of the interfaces below to see how this starts to guide the user up until the optional part of the process is defined by the interface <code>IAccountBuilder</code>.</p>
<pre><code class="lang-c#"><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IAccountBuilderWithEmail</span>
{
    <span class="hljs-function">IAccountBuilderWithPassword <span class="hljs-title">WithEmailAddress</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> email</span>)</span>;
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IAccountBuilderWithPassword</span>
{
    <span class="hljs-function">IAccountBuilderWithVerifiedPassword <span class="hljs-title">WithPassword</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> password</span>)</span>;
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IAccountBuilderWithVerifiedPassword</span>
{
    <span class="hljs-function">IAccountBuilder <span class="hljs-title">VerifyPassword</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> password</span>)</span>;
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title">IAccountBuilder</span>
{
    <span class="hljs-function">IAccountBuilder <span class="hljs-title">WithUsername</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> username</span>)</span>;

    <span class="hljs-function">IAccountBuilder <span class="hljs-title">WithRole</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> role</span>)</span>;

    <span class="hljs-function">IAccountBuilder <span class="hljs-title">WithRoles</span>(<span class="hljs-params"><span class="hljs-keyword">params</span> <span class="hljs-keyword">string</span>[] roles</span>)</span>;

    <span class="hljs-function">IAccountBuilder <span class="hljs-title">WithRoles</span>(<span class="hljs-params">IEnumerable&lt;<span class="hljs-keyword">string</span>&gt; roles</span>)</span>;

    <span class="hljs-function">Account <span class="hljs-title">Build</span>(<span class="hljs-params"></span>)</span>;
}
</code></pre>
<p>See how this is starting to come together? I hope so. The final interface that is returned once all the required methods have been called is <code>IAccountBuilder</code>. This then optionally allows the user to define roles &amp; a user name before getting access to the <code>Account</code> object by calling <code>Build()</code> which completes the process of building an account.</p>
<p>The neat part about this is that all of the implementations for our fluent API can live inside a single class called <code>AccountBuilder</code> even though there are many interfaces guiding a client through this process. See the implementation below.</p>
<pre><code class="lang-c#"><span class="hljs-keyword">class</span> <span class="hljs-title">AccountBuilder</span> : 
        <span class="hljs-title">IAccountBuilderWithEmail</span>, 
        <span class="hljs-title">IAccountBuilderWithPassword</span>, 
        <span class="hljs-title">IAccountBuilderWithVerifiedPassword</span>, 
        <span class="hljs-title">IAccountBuilder</span>
    {
        <span class="hljs-keyword">readonly</span> Account _account = <span class="hljs-keyword">new</span>();

        <span class="hljs-function"><span class="hljs-keyword">public</span> IAccountBuilderWithPassword <span class="hljs-title">WithEmailAddress</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> email</span>)</span>
        {
            _account.Email = email;
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> IAccountBuilderWithVerifiedPassword <span class="hljs-title">WithPassword</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> password</span>)</span>
        {
            <span class="hljs-keyword">if</span> (password.Length &lt; <span class="hljs-number">6</span>)
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidOperationException(<span class="hljs-string">"A password must be at least 6 characters."</span>);

            <span class="hljs-keyword">if</span> (password.Contains(<span class="hljs-string">'@'</span>) <span class="hljs-keyword">is</span> <span class="hljs-literal">false</span> &amp;&amp; password.Contains(<span class="hljs-string">'$'</span>) <span class="hljs-keyword">is</span> <span class="hljs-literal">false</span>)
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidOperationException(<span class="hljs-string">"A password must contain one of the special characters '@$'."</span>);

            _account.Password = password;
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> IAccountBuilder <span class="hljs-title">VerifyPassword</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> password</span>)</span>
        {
            <span class="hljs-keyword">if</span> (_account.Password != password)
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> InvalidOperationException(<span class="hljs-string">"The passwords provided do not match."</span>);

            <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> IAccountBuilder <span class="hljs-title">WithUsername</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> username</span>)</span>
        {
            _account.Username = username;
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> IAccountBuilder <span class="hljs-title">WithRole</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> role</span>)</span>
        {
            _account.Roles.Add(role);
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> IAccountBuilder <span class="hljs-title">WithRoles</span>(<span class="hljs-params"><span class="hljs-keyword">params</span> <span class="hljs-keyword">string</span>[] roles</span>)</span>
        {
            _account.Roles.AddRange(roles);
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> IAccountBuilder <span class="hljs-title">WithRoles</span>(<span class="hljs-params">IEnumerable&lt;<span class="hljs-keyword">string</span>&gt; roles</span>)</span>
        {
            _account.Roles.AddRange(roles);
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>;
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> Account <span class="hljs-title">Build</span>(<span class="hljs-params"></span>)</span> =&gt; _account;
    }
</code></pre>
<p>Notice how this class implements all of the interfaces and after each method call it just returns <code>this</code> or itself back to the client to carry on building that account.</p>
<h2 id="heading-putting-it-all-together">Putting it all together</h2>
<p>The sample for this post can be found <a target="_blank" href="https://github.com/mumby0168/blog-samples/tree/main/FluentAccountApi.Sample">here</a> and contains a console application which builds an <code>Account</code> and generates some JSON for that <code>Account</code>. See below.</p>
<h3 id="heading-console-application">Console Application</h3>
<pre><code class="lang-c#"><span class="hljs-keyword">using</span> System;
<span class="hljs-keyword">using</span> System.Text.Json;
<span class="hljs-keyword">using</span> FluentAccountApi.Sample;

Console.WriteLine(<span class="hljs-string">"Starting the account builder demo."</span>);

Account account = AccountFactory
    .AccountBuilder()
    .WithEmailAddress(<span class="hljs-string">"joe.bloggs@gmail.com"</span>)
    .WithPassword(<span class="hljs-string">"Test123@"</span>)
    .VerifyPassword(<span class="hljs-string">"Test123@"</span>)
    .WithRoles(<span class="hljs-string">"admin"</span>, <span class="hljs-string">"super"</span>)
    .WithUsername(<span class="hljs-string">"joe123"</span>)
    .Build();

Console.WriteLine(<span class="hljs-string">"Built account -&gt;"</span>);
Console.WriteLine(JsonSerializer.Serialize(account, <span class="hljs-keyword">new</span> JsonSerializerOptions{WriteIndented = <span class="hljs-literal">true</span>}));
</code></pre>
<h3 id="heading-output">Output</h3>
<pre><code>Starting the account builder demo.
Built account <span class="hljs-operator">-</span><span class="hljs-operator">&gt;</span>
{
  <span class="hljs-string">"Id"</span>: <span class="hljs-string">"dc2cc20f-153c-4371-a053-caa071012773"</span>,
  <span class="hljs-string">"Email"</span>: <span class="hljs-string">"joe.bloggs@gmail.com"</span>,
  <span class="hljs-string">"Password"</span>: <span class="hljs-string">"Test123@"</span>,
  <span class="hljs-string">"Username"</span>: <span class="hljs-string">"joe123"</span>,
  <span class="hljs-string">"Roles"</span>: [
    <span class="hljs-string">"admin"</span>,
    <span class="hljs-string">"super"</span>
  ]
}
</code></pre><h2 id="heading-final-words">Final Words</h2>
<p>Fluent APIs can really help a developer through the process of building an object from your library. They can be used in many places and a lot of developers really like using them. Please try out the example provided and get to grips with how it works. Hopefully, you will start to notice fluent APIs in the .NET ecosystem or maybe even create your own!</p>
<p>Find the source code here: https://github.com/mumby0168/blog-samples/tree/main/FluentAccountApi.Sample</p>
]]></content:encoded></item><item><title><![CDATA[Nuget Packages & Github Actions]]></title><description><![CDATA[Deploying Nuget Packages (GitHub Actions)
Nuget packages are great and allow developers to make use of useful libraries put together by other developers. The version number is often important to developers many use the common pattern of Major.Minor.P...]]></description><link>https://billy-mumby-dev.com/github-actions-and-nuget-packages</link><guid isPermaLink="true">https://billy-mumby-dev.com/github-actions-and-nuget-packages</guid><category><![CDATA[dotnet]]></category><category><![CDATA[dotnetcore]]></category><category><![CDATA[GitHub]]></category><category><![CDATA[github-actions]]></category><dc:creator><![CDATA[Billy Mumby]]></dc:creator><pubDate>Thu, 13 Jan 2022 13:21:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/q8kR_ie6WnI/upload/v1642079787303/5Od4up4oG.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-deploying-nuget-packages-github-actions">Deploying Nuget Packages (GitHub Actions)</h1>
<p>Nuget packages are great and allow developers to make use of useful libraries put together by other developers. The version number is often important to developers many use the common pattern of <code>Major.Minor.Patch</code> in order to communicate changes to a package with other developers.</p>
<p><code>Major</code> version increase usually represents a change to the public API surface that would likely require a developer to update their code that uses the API provided. <code>Minor</code> version increases, maybe a new feature to the package that maybe adds to the API surface, while maintaining backward compatibility with the previous versions. <code>Patch</code> usually contains a set of bug fixes that again are backward compatible.</p>
<h2 id="heading-integrating-with-cicd">Integrating with CI/CD</h2>
<p>There are many, many nuget packages which are open source, and authors often encourage contributors to help maintain &amp; evolve their packages. The most common way for this to be done currently is usually via GitHub. This allows developers, who are using a package, to see the source code, raise issues, fix bugs, and also fork the repository to later be merged back into the main repository with any changes they might make.</p>
<p>GitHub also offers Actions that allow you to perform continuous integration (CI) &amp; continuous delivery (CD) of a piece of software. We can make use of GitHub tools to help us manage, deploying a package to <a target="_blank" href="https://www.nuget.org/">nuget.org</a>. </p>
<h2 id="heading-the-deployment-plan">The Deployment Plan</h2>
<p>So in order to do this, we want to really be creating a release of the package and as this happens and the version is specified by the individual making the release along with, detailing what that release includes. Then a workflow is triggered to build, test &amp; deploy the package to nuget.org. GitHub offers this in the form of releases which at the same time tags a commit on a branch usually <code>main/master</code>  with the version number specified. This <a target="_blank" href="https://guides.github.com/activities/citable-code/">article</a> details how to create a release in GitHub here is a sneak peek at what that form looks like and what information it can include. The final step once we have created this release we want a GitHub Actions workflow to run, build, test, and pack our source code, apply for the version number and push it up to <a target="_blank" href="https://www.nuget.org/">nuget.org</a> which we will do in the next section.</p>
<blockquote>
<p>Note where the version is specified.</p>
</blockquote>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642079608657/s1r054BWb.png" alt="Screenshot 2022-01-13 at 13.13.24.png" /></p>
<h2 id="heading-putting-the-plan-into-github-actions">Putting the Plan into (GitHub) Action(s)</h2>
<p>So in order to implement this, we need a new pipeline within the repository that contains the library we want to package up. This is triggered when a tag is applied to a branch, more than likely the main branch. A sample pipeline is shown below, each step is explained after this snippet.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">name:</span> <span class="hljs-string">Release</span> <span class="hljs-string">Package</span> <span class="hljs-string">Version</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>  
    <span class="hljs-attr">tags:</span>
     <span class="hljs-bullet">-</span> <span class="hljs-string">v*</span>     
<span class="hljs-attr">jobs:</span> 
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">if:</span> <span class="hljs-string">github.event.base_ref</span> <span class="hljs-string">==</span> <span class="hljs-string">'refs/heads/main'</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>    

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Print</span> <span class="hljs-string">Tag</span> <span class="hljs-string">Ref</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">$GITHUB_REF</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Extract</span> <span class="hljs-string">Version</span> <span class="hljs-string">Number</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions-ecosystem/action-regex-match@v2</span>
      <span class="hljs-attr">id:</span> <span class="hljs-string">regex-match</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">text:</span> <span class="hljs-string">${{</span> <span class="hljs-string">github.ref</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">regex:</span> <span class="hljs-string">'[0-9.]+'</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Print</span> <span class="hljs-string">Version</span> <span class="hljs-string">Number</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">'$<span class="hljs-template-variable">{{ steps.regex-match.outputs.match }}</span>'</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v2</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">.NET</span>  
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-dotnet@v1</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">dotnet-version:</span> <span class="hljs-number">5.0</span><span class="hljs-string">.x</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Restore</span> <span class="hljs-string">dependencies</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">dotnet</span> <span class="hljs-string">restore</span> <span class="hljs-string">./Fruitless/Fruitless.csproj</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">dotnet</span> <span class="hljs-string">build</span> <span class="hljs-string">./Fruitless/Fruitless.csproj</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Pack</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">dotnet</span> <span class="hljs-string">pack</span> <span class="hljs-string">./Fruitless/Fruitless.csproj</span> <span class="hljs-string">-p:PackageVersion='${{</span> <span class="hljs-string">steps.regex-match.outputs.match</span> <span class="hljs-string">}}'</span> <span class="hljs-string">--output</span> <span class="hljs-string">packages</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Publish</span> <span class="hljs-string">Package</span>
      <span class="hljs-attr">run:</span> <span class="hljs-string">nuget</span> <span class="hljs-string">push</span> <span class="hljs-string">**\*.nupkg</span> <span class="hljs-string">-NoSymbols</span> <span class="hljs-string">-Source</span> <span class="hljs-string">'https://api.nuget.org/v3/index.json'</span> <span class="hljs-string">-ApiKey</span> <span class="hljs-string">${{secrets.NUGET_API_KEY}}</span>

    <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Upload</span> <span class="hljs-string">Package</span>
      <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/upload-artifact@v2</span>
      <span class="hljs-attr">with:</span>
        <span class="hljs-attr">name:</span> <span class="hljs-string">fruitless-pkg-v${{</span> <span class="hljs-string">steps.regex-match.outputs.match</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">path:</span> <span class="hljs-string">packages/</span>
</code></pre>
<h3 id="heading-the-pipeline-explained">The pipeline explained</h3>
<ol>
<li><p>So the initial section of this pipeline is stating that this pipeline will only be triggered when a tag is applied to a branch that starts with a <code>v</code>.</p>
</li>
<li><p>The next section details a single job to run called <code>build</code> and this build has an if the condition that means if the branch is not <code>main</code> then this job will not run and will be skipped. This contains a set of steps detailed below.</p>
</li>
<li><p>It then prints the tag name that has triggered this pipeline.</p>
</li>
<li><p>It is then using a community provided regex tool to match the <code>Major.Minor.Patch</code> section from a string. (A tag of <code>v1.2.3</code> would extract <code>1.2.3</code>).</p>
</li>
<li><p>It then simply prints out the extracted version number extract via the match step.</p>
</li>
<li><p>Next it is just setting up the dotnet sdk version to use.</p>
</li>
<li><p>Then the project is built using <code>dotnet build</code> (tests can also be run at this point using <code>dotnet test</code>).</p>
</li>
<li><p>Then the package is packed using the package version extracted from step 4.</p>
</li>
<li><p>Last but not least, the package is pushed using an API key for <a target="_blank" href="https://www.nuget.org/">nuget.org</a> stored as a secret for this GitHub repository.</p>
</li>
<li><p>Finally, the <code>.nupkg</code> file/files are uploaded as an artifact for that build inside of GitHub actions.</p>
</li>
</ol>
<h2 id="heading-final-words">Final Words</h2>
<p>This is a simple way to get started with versioning packages and having them published via GitHub actions. The repository that this sample comes from is <a target="_blank" href="https://github.com/mumby0168/tag_release_package">here</a>. You can also see my trial and error here (everything doesn't always work the first time).</p>
<p>This can be expanded on in order to remove the regex part of the pipeline is to make use of the nuget package <a target="_blank" href="https://github.com/adamralph/minver">MinVer</a>. This approach has been taken on one of my OSS projects and can be found <a target="_blank" href="https://github.com/IEvangelist/azure-cosmos-dotnet-repository">here</a>.</p>
<p>There are a few concepts glossed over here in terms of how GitHub Actions works and some of its syntax the documentation can be found <a target="_blank" href="https://docs.github.com/en/actions">here</a>.</p>
]]></content:encoded></item></channel></rss>