<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://dotnetos.org/feed.xml" rel="self" type="application/atom+xml" /><link href="https://dotnetos.org/" rel="alternate" type="text/html" /><updated>2024-08-14T20:52:17+00:00</updated><id>https://dotnetos.org/feed.xml</id><title type="html">Dotnetos - courses &amp;amp; conferences about .NET</title><subtitle>Jekyll source for dotnetos.org</subtitle><entry><title type="html">Dictionary implementation in C#</title><link href="https://dotnetos.org/blog/2022-03-28-dictionary-implementation/" rel="alternate" type="text/html" title="Dictionary implementation in C#" /><published>2022-03-28T00:00:00+00:00</published><updated>2022-03-28T00:00:00+00:00</updated><id>https://dotnetos.org/blog/dictionary-implementation</id><content type="html" xml:base="https://dotnetos.org/blog/2022-03-28-dictionary-implementation/"><![CDATA[<p>In the previous post we explained the <a href="https://dotnetos.org/blog/2022-03-07-list-implementation/">implementation details</a> of <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code>. This time we will look at another generic collection defined in <code class="language-plaintext highlighter-rouge">System.Collection.Generic</code> namespace which is <code class="language-plaintext highlighter-rouge">Dictionary&lt;TKey TValue&gt;</code>.</p>

<h2 id="implementation">Implementation</h2>

<p>The most important implementation elements of the <code class="language-plaintext highlighter-rouge">Dictionary&lt;TKey, TValue&gt;</code>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">buckets</code> - set of elements with similar hashes</li>
  <li><code class="language-plaintext highlighter-rouge">entries</code> - elements of the <code class="language-plaintext highlighter-rouge">Dictionary&lt;TKey, TValue&gt;</code></li>
  <li><code class="language-plaintext highlighter-rouge">freeList</code> - index of the first free place</li>
  <li><code class="language-plaintext highlighter-rouge">freeCount</code> - the number of empty spaces in the array, not at the end</li>
  <li><code class="language-plaintext highlighter-rouge">count</code> - the number of elements that are currently in the <code class="language-plaintext highlighter-rouge">Dictionary&lt;TKey, TValue&gt;</code></li>
  <li><code class="language-plaintext highlighter-rouge">version</code> - changes as the <code class="language-plaintext highlighter-rouge">Dictionary&lt;TKey, TValue&gt;</code> is modified</li>
</ul>

<p><img src="/assets/images/posts/DictionaryImplementation.jpg" alt="Dictionary Implementation" /></p>

<p>…and a few more equally important elements that <code class="language-plaintext highlighter-rouge">Entry</code> contains:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">_key</code> - key to identify element with <code class="language-plaintext highlighter-rouge">TKey</code> type</li>
  <li><code class="language-plaintext highlighter-rouge">_value</code> - value of an element with <code class="language-plaintext highlighter-rouge">TValue</code> type</li>
  <li><code class="language-plaintext highlighter-rouge">_hashCode</code> - numeric value used to identify an object in hash-based collection</li>
  <li><code class="language-plaintext highlighter-rouge">_next</code> - describes the next item in the <code class="language-plaintext highlighter-rouge">bucket</code></li>
</ul>

<p><img src="/assets/images/posts/EntryStruct.png" alt="Entry" /></p>

<p>The dictionary uses an array of <code class="language-plaintext highlighter-rouge">Entry</code> structures to store data. To get a better understanding of how this really works, you need to know what exactly a hash table is.
<strong>Hash table</strong> (also called hash map) is a data structure that implements an associative array which allows you to store pairs - each pair contains a key and a value.
<strong>With the key, we are able to quickly find the value associated with the key.</strong> According to the dictionary’s equality comparer the key is unique within the entire associative array.
In .NET the hash table contains a list of <code class="language-plaintext highlighter-rouge">buckets</code> to store values. A hash table uses a <strong>hash function</strong> to compute an index based on the key. This allows us, for example, to find the correct <code class="language-plaintext highlighter-rouge">bucket</code> with the value we are looking for.
The same is the situation with other operations - by calculating the hashcode we can add an element to the appropriate <code class="language-plaintext highlighter-rouge">bucket</code> with the index designated for it. We will continue to explain this in more detail later in this post.
The value in the <code class="language-plaintext highlighter-rouge">bucket</code> indicates the index in <code class="language-plaintext highlighter-rouge">entries</code> +1. As it is in infographic: a value of 3 points to index 2. And a value of 2 points to index 1.
The <code class="language-plaintext highlighter-rouge">next</code> property points to the next item that is in the same bucket. In the picture it is additionally marked with an arrow. When <code class="language-plaintext highlighter-rouge">next</code> is equal to -1, it means that it is the last item in the <code class="language-plaintext highlighter-rouge">bucket</code>.</p>

<h2 id="adding-an-item-when-the-key-doesnt-exist">Adding an item when the key doesn’t exist</h2>

<p><img src="/assets/images/posts/DictionaryKeyDoesNotExist.jpg" alt="DictionaryKeyDoesNotExist" /></p>

<p>In the previous section, we mentioned that the key must be unique. Now let’s look at an example in which we want to add a new <code class="language-plaintext highlighter-rouge">Entry</code> to our list. When the key doesn’t exist and we have one free space not at the end of the array. The first operation in this case is to compute <code class="language-plaintext highlighter-rouge">hashcode</code> and find a suitable <code class="language-plaintext highlighter-rouge">bucket</code> using the formula: <code class="language-plaintext highlighter-rouge">hashCode</code> % <code class="language-plaintext highlighter-rouge">buckets.Length</code>. When we find this <code class="language-plaintext highlighter-rouge">bucket</code>, we compare <code class="language-plaintext highlighter-rouge">hashCode</code> of the new element we want to add to the array with the <code class="language-plaintext highlighter-rouge">hashCode</code> of the first <code class="language-plaintext highlighter-rouge">Entry</code>, then move on to the next one (pointed to by <code class="language-plaintext highlighter-rouge">next</code>) and repeat the comparison.
<strong>If none of the existing <code class="language-plaintext highlighter-rouge">hashCodes</code> are the same then we add a new element to the first empty space.</strong> Our <code class="language-plaintext highlighter-rouge">version</code> grows and the value of <code class="language-plaintext highlighter-rouge">bucket</code> points to the last added element. If the modulo result points to a <code class="language-plaintext highlighter-rouge">bucket</code> that already contains an item (we call this situation a hash collision), after adding a new element its index from the <code class="language-plaintext highlighter-rouge">entries</code> array is set to the value of <code class="language-plaintext highlighter-rouge">bucket</code> and the <code class="language-plaintext highlighter-rouge">next</code> field is set to point to the previous item, resulting in a chain of items.☝️</p>

<h2 id="adding-an-item-when-the-key-exist">Adding an item when the key exist</h2>

<p>Let’s look at the case where we want to change the value of an existing <code class="language-plaintext highlighter-rouge">Entry</code>. In the first steps, nothing changes from the previous example - we calculate the <code class="language-plaintext highlighter-rouge">hashCode</code> and find the appropriate <code class="language-plaintext highlighter-rouge">bucket</code>. After that, the <code class="language-plaintext highlighter-rouge">hashCodes</code> of the elements in the <code class="language-plaintext highlighter-rouge">buckets</code> are compared. When we have verified that the <code class="language-plaintext highlighter-rouge">hashCode</code> of the new <code class="language-plaintext highlighter-rouge">Entry</code> is the same as the existing one, keys comparision occurs. If the keys are the same, <strong>value is overwritten</strong> and the <code class="language-plaintext highlighter-rouge">version</code> is incremented by 1. It’s so simple and interesting at the same time! ✨</p>

<p><img src="/assets/images/posts/DictionaryKeyExist.jpg" alt="DictionaryKeyExist" /></p>

<h2 id="resize">Resize</h2>

<p>Let’s look at another interesting situation. If we want to add an element to the dictionary and there is no more space in it, before this operation we should resize the array. <strong>The first step is to create an empty enlarged array whose size is equal to the nearest Prime Number of doubled the initial size of the array.</strong> We use Prime Numbers to minimalize probability of hash collisions. The next step is to copy the <code class="language-plaintext highlighter-rouge">entries</code> and calculate the <code class="language-plaintext highlighter-rouge">hashcode</code> for the new element and find the right <code class="language-plaintext highlighter-rouge">bucket</code>. Then, as in the previous examples, add the element to the first free spot of the <code class="language-plaintext highlighter-rouge">entries</code> array.👇</p>

<p><img src="/assets/images/posts/ResizeDictionary.jpg" alt="ResizeDictionary" /></p>

<h2 id="remove">Remove</h2>

<p>We’ve already learned how adding elements to the dictionary looks like, it’s high time to know the implementation details of removing them. The initial steps remain the same - the <code class="language-plaintext highlighter-rouge">hashCode</code> is calculated and the appropriate <code class="language-plaintext highlighter-rouge">bucket</code> is found. Then a comparison of <code class="language-plaintext highlighter-rouge">hashCodes</code> takes place and a check is made to see if the keys are the same.
After that, the key is removed and the <code class="language-plaintext highlighter-rouge">version</code> grows. <strong>When an element from the array is removed, the space it occupies goes into the chain <code class="language-plaintext highlighter-rouge">freelist</code></strong>. The Dictionary stores the index of the next element using the <code class="language-plaintext highlighter-rouge">next</code> property of the Entry structure. This way we know, in case we want to add a new element after deletion, which space it will occupy first - <code class="language-plaintext highlighter-rouge">entry[1]</code> and when adding one more element in turn - <code class="language-plaintext highlighter-rouge">entry[0]</code>.👇</p>

<p><img src="/assets/images/posts/DictionaryRemove.jpg" alt="DictionaryRemove" /></p>

<p>If you enjoyed this post and want to keep learning more, check out our social channels💜
<a href="https://twitter.com/dotnetosorg">Twitter</a>
<a href="https://discord.com/invite/X9FNmdVmyA">Discord</a>
<a href="https://www.instagram.com/dotnetosorg/">Instagram</a></p>

<h3 id="sources">Sources</h3>

<p><a href="https://github.com/microsoft/referencesource/blob/master/mscorlib/system/collections/generic/dictionary.cs">https://github.com/microsoft/referencesource/blob/master/mscorlib/system/collections/generic/dictionary.cs</a>
<a href="https://docs.microsoft.com/pl-pl/dotnet/api/system.collections.generic.dictionary-2?view=net-6.0">https://docs.microsoft.com/pl-pl/dotnet/api/system.collections.generic.dictionary-2?view=net-6.0</a></p>]]></content><author><name>Paula Wojciechowska</name></author><category term="Dotnetos" /><category term=".NET Collections" /><category term=".NET in Pictures" /><category term="Dotnetos" /><category term=".NET in Pictures" /><category term="Generic Collections" /><category term="Dictionary&lt;TKey,TValue&gt;" /><summary type="html"><![CDATA[Did you know that a Dictionary was introduced in .Net Framework 2.0 - that is almost 20 years ago! This collection is mainly used to store key-value pairs. In this post you will learn how the hash algorithm is used in the Dictionary implementation and you will get an in-depth look at the basic methods this class uses.]]></summary></entry><entry><title type="html">A few words about the implementation of List in C#</title><link href="https://dotnetos.org/blog/2022-03-07-list-implementation/" rel="alternate" type="text/html" title="A few words about the implementation of List in C#" /><published>2022-03-07T00:00:00+00:00</published><updated>2022-03-07T00:00:00+00:00</updated><id>https://dotnetos.org/blog/list-implementation</id><content type="html" xml:base="https://dotnetos.org/blog/2022-03-07-list-implementation/"><![CDATA[<h2 id="a-few-words-about-the-implementation-of-list-in-c">A few words about the implementation of List<T> in C#</T></h2>

<p>In C# <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> is a generic collection that is used to store any number of strongly typed objects as a list, where T is the type of objects. It allows us to perform a number of operations to find individual list items and modify them with operations such as adding, deleting or sorting.
The <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> class implements: <code class="language-plaintext highlighter-rouge">ICollection&lt;T&gt;</code>, <code class="language-plaintext highlighter-rouge">IEnumerable&lt;T&gt;</code>, <code class="language-plaintext highlighter-rouge">IList&lt;T&gt;</code>, <code class="language-plaintext highlighter-rouge">IReadOnlyCollection&lt;T&gt;</code>, <code class="language-plaintext highlighter-rouge">IReadOnlyList&lt;T&gt;</code>, <code class="language-plaintext highlighter-rouge">ICollection</code>, <code class="language-plaintext highlighter-rouge">IEnumerable</code> and <code class="language-plaintext highlighter-rouge">IList</code> interface. <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> class is defined in the <code class="language-plaintext highlighter-rouge">System.Collection.Generic</code> namespace.</p>

<p>If we look deeper, internally the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> stores all elements as a reference to a single array of elements of T type.</p>

<h2 id="inside-of-list">Inside of List<T></T></h2>

<p>The most important implementation elements of the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">items</code> - elements of the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code></li>
  <li><code class="language-plaintext highlighter-rouge">size</code> - the number of items that are currently in the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code></li>
  <li><code class="language-plaintext highlighter-rouge">version</code> - changes as the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> is modified</li>
</ul>

<p><img src="/assets/images/posts/ListImplementation2.png" alt="List Implementation" /></p>

<p>The List implementation uses an underlying array for storing items. This underlying array length is called Capacity.</p>

<h2 id="adding-an-item-to-a-list">Adding an item to a List<T></T></h2>

<p>Let’s analyze the first operation that adds items to the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code>. For a complete explanation, you will need to look at Capacity in depth. <strong>The Capacity value for the default constructor is equal to 4</strong>. Even if you add less elements as in our example - there is room for 4 elements in the internal array.</p>

<p><img src="/assets/images/posts/AddElementsToList2.png" alt="Add elements to List" /></p>

<p>When we want to add a new element to the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code>, the first step is to check - if there is enough space for it in the array. If there is, we add item to the end of our list and the version is incremented, because the array has changed.☝️</p>

<p>You may ask the question, what if I want to add more than 4 elements to the array? What now, since we have no more space for new items? In this case, before adding an element, the array should be <strong>resized</strong>.</p>

<h2 id="adding-an-element-with-resize">Adding an element with resize</h2>

<p><img src="/assets/images/posts/Resize2.png" alt="Resize" /></p>

<p>In this case, the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> <strong>Capacity is doubled from the previous array</strong>. A new array is created and <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> is modified, so version is increased by 1. In the next step, the values from the old array are copied to a new array with larger Capacity in our example equal to 8.
After all these operations we have enough space to add a new element to the list.</p>

<h2 id="removing-items-from-the-list">Removing items from the List<T></T></h2>

<p>Another operation we can perform is to remove an element from the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code>. If we remove item with a particular index from the middle of the list, then all subsequent elements change their indexes 👇</p>

<p><img src="/assets/images/posts/RemoveElementsFromList2.png" alt="Remove" /></p>

<p>Just like in the example, the index of element D has changed after deleting C. The size of the array decreases and the versioning mechanism remains the same. When we remove an element, as in the previous examples our version grows.</p>

<h2 id="how-asspan-method-works">How AsSpan method works</h2>

<p>The last issue we will touch upon in the context of the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> is the <strong>AsSpan()</strong> method.</p>

<p><code class="language-plaintext highlighter-rouge">Span&lt;T&gt;</code> is a <code class="language-plaintext highlighter-rouge">ref struct</code> that can be stored only on the stack. This structure contains a <strong>pointer</strong> to a specific memory location memory and <strong>length</strong> that describes how many elements from the memory location given span has.</p>

<p><img src="/assets/images/posts/Span2.png" alt="Span" /></p>

<p>As we can see on the image the <strong>AsSpan()</strong> method creates a span and sets a pointer to the first element of the array that stores values of the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code>. By wrapping the elements of the <code class="language-plaintext highlighter-rouge">List&lt;T&gt;</code> in <code class="language-plaintext highlighter-rouge">Span&lt;T&gt;</code> structure, we can operate on a subset of data without allocating additional memory. This is a great example of how using special types that allow slicing can increase the performance of our code ✨</p>

<p>Want to know more? Follow our social channels!💜
<a href="https://twitter.com/dotnetosorg">Twitter</a>
<a href="https://discord.com/invite/X9FNmdVmyA">Discord</a>
<a href="https://www.instagram.com/dotnetosorg/">Instagram</a></p>

<h3 id="sources">Sources</h3>

<p><a href="https://github.com/microsoft/referencesource/blob/master/mscorlib/system/collections/generic/list.cs">https://github.com/microsoft/referencesource/blob/master/mscorlib/system/collections/generic/list.cs</a>
<a href="https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=net-6.0">https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=net-6.0</a></p>]]></content><author><name>Paula Wojciechowska</name></author><category term="Dotnetos" /><category term=".NET Collections" /><category term=".NET in Pictures" /><category term="Dotnetos" /><category term=".NET in Pictures" /><category term="Generic Collections" /><category term="List&lt;T&gt;" /><summary type="html"><![CDATA[We all use C# List class everyday but have we ever thought about how it really works? In this post we will look at the implementation details of the List and it’s most important methods.]]></summary></entry><entry><title type="html">Discord phenomenon… dragged us in as well</title><link href="https://dotnetos.org/blog/2021-12-27-discord-phenomenon/" rel="alternate" type="text/html" title="Discord phenomenon… dragged us in as well" /><published>2021-12-27T00:00:00+00:00</published><updated>2021-12-27T00:00:00+00:00</updated><id>https://dotnetos.org/blog/discord-phenomenon</id><content type="html" xml:base="https://dotnetos.org/blog/2021-12-27-discord-phenomenon/"><![CDATA[<h2 id="discord-phenomenon-dragged-us-in-as-well">Discord phenomenon… dragged us in as well</h2>

<p>What exactly is the Discord mentioned by us in the title? Let it answer by itself:</p>

<blockquote>
  <p><em>Discord is the easiest way to talk over voice, video, and text. Talk, hang out, and create a place to belong with your friends and communities.</em></p>
</blockquote>

<p>Let us explain-  Discord is a free application for voice, video or text communication. It sounds a bit enigmatic, therefore we will briefly describe the history of the uprising and more importantly, where the idea for it came from.</p>

<h3 id="the-first-need-arose">The first need arose</h3>
<p>The first need arose: it’s fun to play, it’s even better to play and talk. The possibility of talking during an online game existed before, it was possible to communicate using the available solutions like IRC and then skype. None of these solutions fully satisfied the players. Out of need, an idea appeared, followed by a project. As the creator-  Jason Citron writes himself:</p>

<blockquote>
  <p><em>Discord was born from the need to create a better way to chat and spend time online while playing video games with friends. […] We designed Discord for talking. There’s no endless scrolling, no news feed, and no tracking likes. No algorithms decide what you “should” see.</em></p>
</blockquote>

<p>Since 2015, Discord has been constantly evolving and gaining an increasing number of users who surprise even the creators themselves with how they use their service.</p>

<h3 id="is-discord-a-perfect-platform">Is Discord a perfect platform?</h3>
<p>Looking at the entries of dissatisfied users, definitely not! The author himself admits, that:</p>
<blockquote>
  <p><em>the first few interactions someone has with our service could be intimidating because Discord is complex with many features.</em></p>
</blockquote>

<p>The complexity of the platform itself is not its only downside. Some users accuse the platform of imperfect anti-intrusion security and delay in introducing improvements.
Apart from the above-mentioned disadvantages, the desire to create</p>

<h4 id="dotnetos--net-community"><a href="https://discord.gg/X9FNmdVmyA">Dotnetos- .NET community</a></h4>

<p>a server through which the .NET community can share their thoughts and experiences in one place.</p>

<p>In order to organize this place, we created several thematic channels e.g.: code design, code review, architecture, performance and many others.</p>

<p><img src="/assets/images/posts/general.png" alt="General channel" /> <img src="/assets/images/posts/development.png" alt="Development channel" /> <img src="/assets/images/posts/languages.png" alt="Languages channel" /></p>

<p>It’s <strong>not a forum</strong> nor a question-and-answer page like StackOverflow - no tags, no boards, no threads (though you can create them!).</p>

<p><img src="/assets/images/posts/discord.png" alt="Discord chat" /></p>

<p>We have experienced what over 350 million (!) Discord users have already experienced:</p>

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">me irl: 😐<br />me on discord: 🚦💺🗽✈️🚥🚉🎡🚉🗿🚀🏟🚀🚦🚉🚊🗼🗼🧢🗼😓😓😓😍💀🧢🚦😚💪😉🗿🥺🚉😍🚦🧢😚😉🚥👋💺😫🗿🐈🚀😍😳🧢💪😉🚦😫🚦🐈💪😕😓😚😓😂🗽🤒😓👿😓🤕👶🧔‍♂️🧜‍♂️🤱🧚‍♂️🗽🚀🗿✈️🏟🚈✒️🗳🧮📋📎📋📐📆✒️🗃🈶⛔️🈚️🛑♑️🈲♏️🧂🧃🧂🥤🏐🍼🥏🍯</p>&mdash; Joney (@Joneyology) <a href="https://twitter.com/Joneyology/status/1443999591448432642?ref_src=twsrc%5Etfw">October 1, 2021</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<p>and we must admit that <strong>it is worth trying</strong>, because this place is a pleasure to stay in touch and talk to other living people.</p>

<p>Are you interested? <strong>Jump into our Dotnetos server!</strong>  👇</p>

<p><a href="https://discord.gg/X9FNmdVmyA"><img src="/assets/images/posts/discord-logo.png" alt="Dicord logo" /></a></p>

<h4 id="sources">Sources:</h4>

<blockquote>
  <p>https://blog.discord.com/how-were-making-discord-more-welcoming-for-everyone-ee152f198c60</p>

  <p>https://blog.discord.com/your-place-to-talk-a7ffa19b901b</p>

  <p>https://discord.com/blog/an-update-on-our-business</p>

  <p>https://twitter.com/discord</p>
</blockquote>]]></content><author><name>Ewelina Zawadzka</name></author><category term="Dotnetos" /><category term="Trends" /><category term="Discord" /><category term="Dotnetos" /><summary type="html"><![CDATA[Discord is a free application for voice, video or text communication. It sounds a bit enigmatic, therefore we will briefly describe the history of the uprising and more importantly, where the idea for it came from.]]></summary></entry><entry><title type="html">Keep an employee. The challenge for 2022</title><link href="https://dotnetos.org/blog/2021-12-13-keep-an-employee/" rel="alternate" type="text/html" title="Keep an employee. The challenge for 2022" /><published>2021-12-13T00:00:00+00:00</published><updated>2021-12-13T00:00:00+00:00</updated><id>https://dotnetos.org/blog/keep-an-employee</id><content type="html" xml:base="https://dotnetos.org/blog/2021-12-13-keep-an-employee/"><![CDATA[<h2 id="keep-an-employee-the-challenge-for-2022">Keep an employee. The challenge for 2022</h2>

<p>The year is coming to an end and with it we expected a return to the pre-pandemic state, but reality presented a different scenario. <strong>It turns out that some changes have entered our everyday life for good and returning to the standards from before 2020 is simply impossible.</strong> Those changes affect all aspects of life, but in this article we will focus mainly on the labor market, narrowing down to the IT industry. We will try to analyze them in terms of both the employee and the employer.</p>

<p>In 2020 the pandemic surprised the whole world and forced, if not to introduce, then to accelerate certain changes. The unstable situation and uncertainty about what the upcoming months will bring stopped all those who planned to change jobs. This, as a consequence, had an impact on the current year, and will probably leave its mark in the following years. Remote work, data migration to the cloud or the technological revolution taking place in front of our eyes have superimposed on an increased amount of work that can be done only by qualified specialists, and those are disproportionately small to the current demand on the IT market. The growing competition for an employee has only confirmed the excellent situation that IT specialists currently have. Everything indicates that in the near future many companies will have to face the challenge of employee retention. <strong>This should not come as a surprise, because the estimated value of the IT services market for 2022 is expected to be about 5.3 trillion US dollars</strong>. The upward trend is expected to continue until 2024, with an <strong>increase of 5%</strong> per year, which in the face of the global crisis is particularly important information for companies operating in this industry. Companies with a strong specialist background will be able to compete for the highest profits. <strong>What do employers have to prepare in order to retain their specialists and thus gain new clients?</strong> To answer this question, it is necessary to find out what motivates employees.</p>

<p>We will start with the theory presented by the American researcher Abraham Maslow.</p>

<p><img src="/assets/images/posts/maslow-pyramid.png" alt="Maslow pyramid" /></p>

<p><em>Maslow’s classic hierarchy of needs.</em></p>

<p>We see that on its top are the needs of self-development, belonging or appreciation of work satisfaction. The IT industry employees are one of the few groups with a relatively high degree of satisfaction with their salary. <strong>Thus, for most, the financial aspect is no longer the decisive impulse to change jobs</strong>. The most common factors influencing the willingness to change jobs are:</p>

<ul>
  <li>repetitive, non-developing and unambitious projects</li>
  <li>lack of the synergy on the teamwork, frequent conflicts and the lack of possibility of codecision</li>
</ul>

<p>Important aspect in this discussion is working out the <strong>work-life balance</strong>, because this significantly affects productivity, job satisfaction and commitment to it. There is also a growing awareness of jobseekers, choosing employers who have clear values, consistent  with their own.</p>

<p>The State of Octoverse quotes interesting data: <strong>over 86% of respondents from the IT industry after the pandemic expect only remote or hybrid work</strong>, and thus the recruitment process itself enables this form of interview.</p>

<p><a href="https://octoverse.github.com/#improving-how-we-work"><img src="/assets/images/posts/octoverse-report.png" alt="Results from The State of Octoverse" /></a></p>

<p>Starting a new fully remote job causes some trouble. The most common problem during the onboarding of new employees is communication and bonding with teammates. This can result in decreased productivity. The solution seems to be to set a transparent onboarding process, taking into account 1:1 meetings, assigning an onboarding buddy and among others, providing up-to-date documentation. However, despite these difficulties, no change of direction is to be expected, as the possibility of remote and even hybrid work has become a new practice. The changes that are taking place are also evidenced by the analysis presented by <a href="https://awareson.com">Awareson</a>, which indicates that 9 out of 10 surveyed IT employees plan to improve their technological competences next year. <strong>It only confirms the need for self-improvement that the group we are talking about presents.</strong></p>

<p>The information we have provided above should make employers reflect and take some steps now. <strong>We want to focus especially on the aspect of the need to raise qualifications</strong> by specialists, because we have prepared some excellent on-line courses on .NET related topics:</p>
<ul>
  <li><strong><a href="https://asyncexpert.com/?utm_source=blog&amp;utm_medium=post&amp;utm_campaign=2021fall">Async Expert EN</a></strong>, <strong><a href="https://asyncexpert.pl/?utm_source=blog&amp;utm_medium=post&amp;utm_campaign=2021fall">Async Expert PL</a></strong></li>
  <li><strong><a href="https://diagnosticsexpert.com/?utm_source=blog&amp;utm_medium=post&amp;utm_campaign=2021fall">.NET Diagnostics Expert</a></strong></li>
  <li><strong><a href="https://dotnetmemoryexpert.com/?utm_source=blog&amp;utm_medium=post&amp;utm_campaign=2021fall">.NET Memory Expert</a></strong></li>
  <li><strong><a href="https://procsharp9.com/?utm_source=blog&amp;utm_medium=post&amp;utm_campaign=2021fall">C# 9.0 Professional</a></strong></li>
</ul>

<p>within which we discuss each of the topics listed above in a detailed way. <strong>The course authors are brilliant specialists, repeatedly awarded with MVP titles</strong>. The courses are dedicated to specialists at intermediate and advanced / expert levels. They provide students with the latest solutions and enable contact and exchange of experiences via a specially prepared platform and discussion forum. This is an excellent solution, especially nowadays when on-site training is difficult due to the remote / hybrid aspect of work. <strong>What’s more, we provide special discounts for groups of more than 10 people.</strong></p>

<p>To sum up, the upcoming months and years will be a challenge for employers, especially from the IT industry. On the one hand, the fight for the client, on the other, for the employee. As the forecasts show, there is something to fight for, as the value of the market is still growing and there is no indication of changes. <strong>Investing in a highly qualified specialist is not just a cost, it is an investment for years</strong>. For employees, it will be an excellent time to start salary negotiations or look for a place where they will be able to pursue their ambitions in highly challenging technical work or count on additional profits, such as training and courses.</p>

<hr />

<p>Source materials:</p>

<blockquote>
  <p><a href="https://www.statista.com/statistics/507365/worldwide-information-technology-industry-by-region">Global information technology industry forecast 2019-2022, by region</a></p>

  <p><a href="https://www.researchgate.net/publication/258023302_Motivation_of_Software_Engineers_A_Qualitative_Case_Study_of_a_Research_and_Development_Organisation">Motivation of Software Engineers: A Qualitative Case Study of a Research and Development Organisation</a></p>

  <p><a href="https://www.emerald.com/insight/content/doi/10.1108/EJMS-12-2020-0003/full/pdf?title=worklife-balance-retention-of-professionals-and-psychological-empowerment-an-empirical-validation">Work–life balance, retention of professionals and psychological empowerment: an empirical validation</a></p>

  <p><a href="https://arxiv.org/pdf/2011.08130.pdf">Please Turn Your Cameras On: Remote Onboarding of Software Developers during a Pandemic</a></p>

  <p><a href="https://www.viirj.org/vol13issue1/44.pdf">TALENT MANAGEMENT: EMPLOYEE RETENTION STRATEGIES</a></p>

  <p><a href="https://doi.org/10.31033/ijemr.9.6.11">A Study of Employee Motivation in Organization</a></p>
</blockquote>]]></content><author><name>Ewelina Zawadzka</name></author><category term="Dotnetos" /><category term="Trends" /><category term="Dotnetos" /><summary type="html"><![CDATA[The year is coming to an end and with it we expected a return to the pre-pandemic state, but reality presented a different scenario. It turns out that some changes have entered our everyday life for good and returning to the standards from before 2020 is simply impossible.]]></summary></entry><entry><title type="html">Configuring dotnet-monitor with Prometheus and Grafana</title><link href="https://dotnetos.org/blog/2021-11-22-dotnet-monitor-grafana/" rel="alternate" type="text/html" title="Configuring dotnet-monitor with Prometheus and Grafana" /><published>2021-11-22T00:00:00+00:00</published><updated>2021-11-22T00:00:00+00:00</updated><id>https://dotnetos.org/blog/dotnet-monitor-grafana</id><content type="html" xml:base="https://dotnetos.org/blog/2021-11-22-dotnet-monitor-grafana/"><![CDATA[<h2 id="configuring-dotnet-monitor-with-prometheus-and-grafana">Configuring <code class="language-plaintext highlighter-rouge">dotnet-monitor</code> with Prometheus and Grafana</h2>

<p><img src="/assets/images/posts/grafana-dotnet-monitor.png" alt="Grafana dashboard example" /></p>

<p>Everyone likes dashboards! So let’s make one! <code class="language-plaintext highlighter-rouge">dotnet-monitor</code> <a href="https://devblogs.microsoft.com/dotnet/introducing-dotnet-monitor/">was announced</a> last year as an experimental tool for <strong>exposing REST endpoints</strong> to make diagnostics/measuring of your apps simpler. It could be seen as a <em>“simple”</em> ASP.NET Core app that wraps Diagnostic IPC Protocol to communicate with the target .NET application - exactly the same which is used when we use CLI Diagnostic tools like <code class="language-plaintext highlighter-rouge">dotnet-trace</code> or <code class="language-plaintext highlighter-rouge">dotnet-counters</code>. I personally perceive it simply as the REST wrapper for those tools. So, for example, we have <code class="language-plaintext highlighter-rouge">/trace</code> endpoint to start a session or <code class="language-plaintext highlighter-rouge">/logs</code> endpoint to capture logs. And finally, very recently <code class="language-plaintext highlighter-rouge">dotnet-trace</code> <a href="https://devblogs.microsoft.com/dotnet/announcing-dotnet-monitor-in-net-6/">was announced</a> as production-ready part of .NET 6 😍</p>

<p>But we are interested in <code class="language-plaintext highlighter-rouge">/metrics</code> endpoint. As the docs says, it <em>“captures a snapshot of metrics of the default process in <strong>the Prometheus exposition format</strong>“</em>. Awesome! BTW, it’s pretty simple format so such endpoint returns text data like:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># HELP systemruntime_cpu_usage_ratio CPU Usage
# TYPE systemruntime_cpu_usage_ratio gauge
systemruntime_cpu_usage_ratio 0 1632929076109
systemruntime_cpu_usage_ratio 0 1632929076111
systemruntime_cpu_usage_ratio 0 1632929086110
# HELP systemruntime_working_set_bytes Working Set
# TYPE systemruntime_working_set_bytes gauge
systemruntime_working_set_bytes 1529000000 1632929066112
systemruntime_working_set_bytes 1529000000 1632929076110
systemruntime_working_set_bytes 1529000000 1632929076112
...
# HELP systemruntime_time_in_gc_ratio % Time in GC since last GC
# TYPE systemruntime_time_in_gc_ratio gauge
systemruntime_time_in_gc_ratio 0 1632929066112
systemruntime_time_in_gc_ratio 0 1632929076110
systemruntime_time_in_gc_ratio 0 1632929076112
</code></pre></div></div>

<p>We simply see here all <code class="language-plaintext highlighter-rouge">System.Runtime</code> counters (the same as observed by <code class="language-plaintext highlighter-rouge">dotnet-counters</code>) with their values. Having said all that, let’s see <strong>how we can configure all necessary pieces together to have a working Grafana dashboard with some GC metrics</strong>👀.</p>

<h2 id="step-1---preparing-the-app">Step 1 - Preparing the app</h2>

<p>Let’s use <a href="https://github.com/sebastienros/memoryleak">memoryleak</a> .NET 5 app by Sébastien Ros as I love it and I’m using it a lot in <a href="https://dotnetmemoryexpert.com/?utm_source=blog&amp;utm_medium=link&amp;utm_campaign=general">our .NET Memory Expert course</a> - because of its self-measuring capabilities. So, just do:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ git clone https://github.com/sebastienros/memoryleak.git
</code></pre></div></div>

<p>… and we are almost ready! To make things more clean and fun, I will put this app into a container using Docker. So, here’s my sample <code class="language-plaintext highlighter-rouge">Dockerfile</code> for doing it (and I add <code class="language-plaintext highlighter-rouge">procps</code> just as an example that you can😇):</p>

<pre><code class="language-Docker"># https://hub.docker.com/_/microsoft-dotnet
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /source

COPY . .
RUN dotnet restore
RUN dotnet publish -c release -o /app --no-restore

# final stage/image
FROM mcr.microsoft.com/dotnet/aspnet:5.0
RUN apt-get update &amp;&amp; apt-get install -y procps
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "MemoryLeak.dll"]
</code></pre>

<p>Then build it:</p>

<pre><code class="language-cmd">❯ docker build --pull -t memoryleak-image -f Dockerfile .
</code></pre>

<p>Because we will be running <code class="language-plaintext highlighter-rouge">dotnet-monitor</code> from a sidecar container, we need to have some shared volume to represent <code class="language-plaintext highlighter-rouge">/tmp</code> folder (used by the IPC communication protocol), so let’s create one:</p>

<pre><code class="language-cmd">❯ docker volume create dotnet-tmp
</code></pre>

<p>And now we are ready to run our image, mount the shared volume and expose its <code class="language-plaintext highlighter-rouge">80</code> port as port <code class="language-plaintext highlighter-rouge">5000</code>:</p>

<pre><code class="language-cmd">❯ docker run -it --rm -p 5000:80 --mount "source=dotnet-tmp,target=/tmp" memoryleak-image
</code></pre>

<p>Now, you should be able to visit <code class="language-plaintext highlighter-rouge">http://localhost:5000/</code> to see the app drawing its own memory usage.</p>

<h2 id="step-2---dotnet-monitor">Step 2 - <code class="language-plaintext highlighter-rouge">dotnet-monitor</code></h2>

<p>We can install <code class="language-plaintext highlighter-rouge">dotnet-monitor</code> as a global tool, but let’s stay with containers. There is up and ready container image available on Microsoft Container Registry so it is as easy as the following command:</p>

<pre><code class="language-cmd">❯ docker run -it --rm -p 52323:52323 --mount "source=dotnet-tmp,target=/tmp" \
             mcr.microsoft.com/dotnet/monitor --urls http://*:52323 --no-auth
</code></pre>

<p>Note we are mounting here the same shared volume to have IPC communication possible. Now <code class="language-plaintext highlighter-rouge">http://localhost:52323/processes</code> should print only a single process with <em>pid = 1</em> because <strong>it is observing the application container thanks to the shared /tmp</strong> volume. And <code class="language-plaintext highlighter-rouge">http://localhost:52323/metrics</code> should return similar metrics like presented before. BTW, I’m using <code class="language-plaintext highlighter-rouge">--no-auth</code> just to get rid of any authentication/certificates issues for such a simple demo.</p>

<h2 id="step-3---prometheus">Step 3 - Prometheus</h2>

<p><a href="https://prometheus.io/">Prometheus</a> is a free monitoring system and time series database. We need it to consume just prepared <code class="language-plaintext highlighter-rouge">/metrics</code> endpoint and store results in it. Again, we could run/install Prometheus in a various ways but let’s go funny and use container again. First of all, we need a configuration file, let’s call it <code class="language-plaintext highlighter-rouge">prometheus.yml</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">global</span><span class="pi">:</span>
  <span class="na">scrape_interval</span><span class="pi">:</span> <span class="s">15s</span>
  <span class="na">scrape_timeout</span><span class="pi">:</span> <span class="s">10s</span>
  <span class="na">evaluation_interval</span><span class="pi">:</span> <span class="s">15s</span>
<span class="na">alerting</span><span class="pi">:</span>
  <span class="na">alertmanagers</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">scheme</span><span class="pi">:</span> <span class="s">http</span>
    <span class="na">timeout</span><span class="pi">:</span> <span class="s">10s</span>
    <span class="na">api_version</span><span class="pi">:</span> <span class="s">v1</span>
    <span class="na">static_configs</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="na">targets</span><span class="pi">:</span> <span class="pi">[]</span>
<span class="na">scrape_configs</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">job_name</span><span class="pi">:</span> <span class="s">prometheus</span>
  <span class="na">honor_timestamps</span><span class="pi">:</span> <span class="no">true</span>
  <span class="na">scrape_interval</span><span class="pi">:</span> <span class="s">15s</span>
  <span class="na">scrape_timeout</span><span class="pi">:</span> <span class="s">10s</span>
  <span class="na">metrics_path</span><span class="pi">:</span> <span class="s">/metrics</span>
  <span class="na">scheme</span><span class="pi">:</span> <span class="s">http</span>
  <span class="na">static_configs</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">targets</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">localhost:9090</span>
<span class="pi">-</span> <span class="na">job_name</span><span class="pi">:</span> <span class="s">memoryleak</span>
  <span class="na">honor_timestamps</span><span class="pi">:</span> <span class="no">true</span>
  <span class="na">scrape_interval</span><span class="pi">:</span> <span class="s">2s</span>
  <span class="na">scrape_timeout</span><span class="pi">:</span> <span class="s">2s</span>
  <span class="na">metrics_path</span><span class="pi">:</span> <span class="s">/metrics</span>
  <span class="na">scheme</span><span class="pi">:</span> <span class="s">http</span>
  <span class="na">static_configs</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">targets</span><span class="pi">:</span>
    <span class="pi">-</span> <span class="s">host.docker.internal:52323</span>
</code></pre></div></div>

<p>Nothing magical is happening here. Most important parts are the last 9 lines - configuring the job <code class="language-plaintext highlighter-rouge">memoryleak</code> that scrapes the <code class="language-plaintext highlighter-rouge">http://host.docker.internal:52323/metrics</code> endpoint every 2 seconds. The magical <code class="language-plaintext highlighter-rouge">host.docker.internal</code> hostname allows to communicate from one container to another container exposed on localhost. This is networking stuff and for sure it would be done differently on real setup. Good enough for a demo.</p>

<p>Prometheus is available as <code class="language-plaintext highlighter-rouge">ubuntu/prometheus</code> image, so let’s use it! Having configuration file we need to map it to the internal <code class="language-plaintext highlighter-rouge">/etc/prometheus/prometheus.yml</code> so the final command for running it is:</p>

<pre><code class="language-cmd">❯ docker run -d --name prometheus-container -e TZ=UTC -p 30090:9090 \
         -v c:\your\path\to\prometheus.yml:/etc/prometheus/prometheus.yml ubuntu/prometheus
</code></pre>

<p>And… that’s it! Go to the <code class="language-plaintext highlighter-rouge">http://localhost:30090/</code> and you will see Prometheus dashboard. By going to <em>Status/Targets</em> you should see <em>Up</em> state for the defined target:</p>

<p><img src="/assets/images/posts/grafana-prometheus.png" alt="Prometheus dashboard" /></p>

<p>And on the <em>Table/Graph</em> panel you can sneak peak into the gathered measurements, for example by typing <code class="language-plaintext highlighter-rouge">systemruntime_</code> to see the suggestions for all recorded metrics:</p>

<p><img src="/assets/images/posts/grafana-prometheus2.png" alt="Prometheus dashboard" /></p>

<h2 id="step-4---grafana">Step 4 - Grafana</h2>

<p>And eventually, Grafana itself 😍 Let’s use the prepared image again:</p>

<pre><code class="language-cmd">❯ docker run -d -p 3000:3000 grafana/grafana
</code></pre>

<p>After going to <code class="language-plaintext highlighter-rouge">http://localhost:3000/</code> you need to login as <code class="language-plaintext highlighter-rouge">admin</code>/<code class="language-plaintext highlighter-rouge">admin</code>. Then go to the <em>Configuration/Add data source</em> and add Prometheus endpoint as one of the sources:</p>

<ul>
  <li>set URL to <code class="language-plaintext highlighter-rouge">http://host.docker.internal:30090/</code></li>
  <li>leave everything else as default</li>
  <li>click <em>Save &amp; test</em></li>
</ul>

<p><img src="/assets/images/posts/grafana-source.png" alt="Grafana - adding source" /></p>

<p>Now we are ready to <em>Explore</em>. As a <em>Metric browser</em> start typing again <code class="language-plaintext highlighter-rouge">systemruntime</code>… and you should see the suggestions of already available metrics:</p>

<p><img src="/assets/images/posts/grafana-explore.png" alt="Grafana - explore metrics" /></p>

<p>And… <strong>that’s it! Everything is ready</strong>. Obviously, what we would like to achieve is to have a nice dashboard(s), so we need to create one. This is not the article about creating dashboards for sure. You can find many tutorials out there. To leave you with something useful, here’s a JSON file you can import in <em>Dashboards/Import</em> to have such a nice graphs as at the beginning of the article:</p>

<p><a href="/assets/files/posts/sample-dotnet-monitor-dashboard.json"><code class="language-plaintext highlighter-rouge">sample-dotnet-monitor dashboard.json</code></a></p>

<p>Have a nice dashboarding!</p>

<p>PS. For me it is just <strong>mindblowing</strong> how containerization and .NET on Linux makes it all possible 🤯 We have <strong>four</strong> containers that can run on Windows that run Ubuntu - one for .NET 5 app, one for <code class="language-plaintext highlighter-rouge">dotnet-monitor</code>, one for Prometheus and one for Grafana. And it all just works!💜</p>]]></content><author><name>Konrad Kokosa</name></author><category term="monitoring" /><category term="monitoring" /><category term="grafana" /><category term="prometheus" /><category term="CLI tools" /><summary type="html"><![CDATA[Let's be clear, open source software developers build the tools we all use. The world runs on GitHub’ s open source code, each of us uses its benefits, often unconsciously. In this blog, we are not going to focus only on code, because it is the result of someone's work, and we would like to emphasize the value of work, and specifically of the people who do it.]]></summary></entry><entry><title type="html">Why should we care about the OSS community?</title><link href="https://dotnetos.org/blog/2021-11-04-dotnetoss-grants/" rel="alternate" type="text/html" title="Why should we care about the OSS community?" /><published>2021-11-04T00:00:00+00:00</published><updated>2021-11-04T00:00:00+00:00</updated><id>https://dotnetos.org/blog/dotnetoss-grants</id><content type="html" xml:base="https://dotnetos.org/blog/2021-11-04-dotnetoss-grants/"><![CDATA[<h2 id="why-should-we-care-about-the-oss-community">Why should we care about the OSS community?</h2>

<p>Let’s be clear, open source software developers build the tools we all use. The world runs on GitHub’ s open source code, each of us uses its benefits, often unconsciously. In this blog, we are not going to focus only on code, because it is the result of someone’s work, and we would like to emphasize the value of work, and specifically of the people who do it. Without a team, even the most interesting project will not develop. For maintainers, participation in projects is often the realization of their passion. Who if not enthusiasts spread their passion and commitment with others! Thanks to them, the world takes color and flavor, regardless of the field in which they operate. However, while appreciating the work of the creator seems to be simple, because you can go to a concert or buy a book, appreciating someone whose work is not signed, causes more difficulties. Should ‘invisible’ work not be visibly appreciated? We thought for a long time how to solve this and honour those who work for the OSS community and launched <strong>DotetOSS Grants</strong>.</p>

<p><strong>DotnetOSS Grants</strong> is a six-month Github sponsorship. Having in mind that it is difficult to convert passion into money, we found it a nice and universal way of appreciating the work that has been put into OSS development. In the dream world we would distinguish everyone in this way, but in reality we do not have such possibilities. Guided by our own opinions and observations, we selected a few people who made the greatest impression on us. Who are they and a little more about the entire initiative, you can read on our <a href="https://dotnetos.org/initiatives/grants/">website</a></p>

<p>At the end we would like to thank all who transform their ideas into code! No matter your role on the project, you are changing the world, your commitment is inspirational for all of us.</p>]]></content><author><name>Ewelina Zawadzka</name></author><category term="Dotnetos" /><category term="Branding" /><category term="Dotnetos" /><summary type="html"><![CDATA[Let's be clear, open source software developers build the tools we all use. The world runs on GitHub’ s open source code, each of us uses its benefits, often unconsciously. In this blog, we are not going to focus only on code, because it is the result of someone's work, and we would like to emphasize the value of work, and specifically of the people who do it.]]></summary></entry><entry><title type="html">How we built Dotnetos Platform using Azure Functions, Cloudflare Workers and Jekyll</title><link href="https://dotnetos.org/blog/2021-08-16-dotnetos-platform/" rel="alternate" type="text/html" title="How we built Dotnetos Platform using Azure Functions, Cloudflare Workers and Jekyll" /><published>2021-08-16T00:00:00+00:00</published><updated>2021-08-16T00:00:00+00:00</updated><id>https://dotnetos.org/blog/dotnetos-platform</id><content type="html" xml:base="https://dotnetos.org/blog/2021-08-16-dotnetos-platform/"><![CDATA[<blockquote>
  <p>This platform does not support…</p>
</blockquote>

<p>are the <strong>famous last words</strong> an engineer can hear before they start to think about implementing it on their own. This was the case, when we thought about releasing our very first online course <a href="https://asyncexpert.com/?utm_source=blog&amp;utm_medium=link&amp;utm_campaign=general">Async Expert</a>. After considering various options, we agreed to use a set of existing components and gluing them on our own. Having a three engineers on board, that are capable of using <code class="language-plaintext highlighter-rouge">C#</code> and serverless approach, the choice for a platform was simple.</p>

<h2 id="what-are-the-things-to-integrate">What are the things to integrate</h2>

<p>You could ask what are the things to integrate anyway. How many moving pieces do you use to provide an online experience to attendees? Actually, there are some.</p>

<p>The first tool, that we use to provide our attendees and subscribers with emails, is <a href="https://scooletz.com/links/convertkit">ConvertKit</a>. This is the mailing platform that we selected a while ago. With its tag, segments, sequences and automations, it’s a perfect tool to deliver meaningful content using emails. This is the very same tool that we use to send broadcasts, whenever we announce a new opening for our course.</p>

<p>The second part is related to <strong>hosting videos and downloads</strong> for our attendees. After reviewing multiple options, we selected <code class="language-plaintext highlighter-rouge">Thinkific</code> to deliver these materials. Initially, we used the discussion forum in there, but as our samples and discussions between attendees often include pasting formatted code, we changed the approach and moved to a different option for hosting them.</p>

<p>To make the discussions fruitful and easy to follow, we chose the <code class="language-plaintext highlighter-rouge">Discourse</code>. It’s an awesome tool, if you want to share code snippets. Additionally, it allows attendees to have a good well-formatted interaction between them and authors/mentors.</p>

<p>The payment gateway that our Platform uses is provided by <code class="language-plaintext highlighter-rouge">Stripe</code>. The process is augmented to apply VAT tax properly. This is the part that we spent a lot of time. Dealing with various cases of inverse VAT, EU VAT (that is verified against the EU database) and other cases isn’t that easy! Finally, whenever the payment is done, an invoice is generated and sent to the person who purchased licenses. This, especially for bulk business orders, might be a different person from the attendees.</p>

<h2 id="where-are-azure-functions">Where are Azure Functions</h2>

<p>All the parts mentioned above are integrated by a single serverless <strong>Azure Functions app</strong>. To interact with specific components we use API and webhooks. This is done mostly by using the <a href="https://restsharp.dev/">RestSharp</a> and performing specific calls against specific endpoints. We tend to use a limited part of the provided API. Usually it’s sufficient to make a call or two to a specific component.</p>

<p>The whole ordering process is designed to not fail. All the parts that are related to the <strong>integration aspects are separated</strong> from performing the actual order. After all, it’s ok to accept an order and fix something later on. At the same time it would be a terrible mistake, if the order failed due to a third party service being unavailable at the moment.</p>

<p>It’s worth to mention that the Azure Functions app is run in a <a href="https://docs.microsoft.com/en-us/azure/azure-functions/consumption-plan">consumption plan</a>. The orders are not done every single minute (we wish they were!) so we’re much better with having it started on demand.</p>

<p>Now, you could think that with the consumption plan the ordering process may suffer from the cold start problem. The cold start means that the app is loaded for the very first time and it takes a while to get it ready. We address it by sending a warming up request. It ensures that once the <strong>order button</strong> is hit, the app is warm and ready.</p>

<h2 id="cloudflare-workers">Cloudflare Workers</h2>

<p>There are places where we use <a href="https://workers.cloudflare.com">Cloudflare Workers</a>, which an edge, a.k.a <strong>Region Earth</strong>, serverless platform. It provides interesting capabilities. One of them allows you to intercept the incoming traffic and augment the returned html. This is used to inject cross-promotions of our products. You can see it on your vising <a href="https://goodies.dotnetos.org?utm_source=blog&amp;utm_medium=link&amp;utm_campaign=general">Dotnetos Goodies page</a> and scroll to the bottom of it. If you visit other courses, like <a href="https://diagnosticsexpert.com/?utm_source=blog&amp;utm_medium=link&amp;utm_campaign=general">.NET Diagnostics Expert</a> and scroll again, you’ll see a similar section. This is done on the fly by a specific Cloudflare Worker.</p>

<h2 id="jekyll">Jekyll</h2>

<p>The majority of our pages uses <a href="https://jekyllrb.com">Jekyll</a> and is hosted on GitHub Pages. This allows us to use <strong>MarkDown</strong> (this is how this post is written at the moment). Then, if it’s needed, we can always augment the output with Cloudflare Workers.</p>

<p>One could think about using a bit more enterprisey tool for creating pages, but so far, Jekyll is the one that we were able to leverage in every single this. It’s worth to mention, that the injection made by Cloudflare Workers are also based on the Jekyll-ed output.</p>

<h2 id="summary">Summary</h2>

<p>During this year a lot of things happened. There are many more platforms and services that help online creators to support their attendees. There are things that were introduced in the payment gateways that allow to lift off the burden of the tax calculations and many more. At the same time, by augmenting piece by piece our <strong>Dotnetos Platform</strong>, connecting different dots and addressing pain points that arise, we were able to make it a really good tool to work with.</p>

<p>We wish you frequent usages of it, either by joining our courses or simply visiting pages with all the content that we provide.</p>]]></content><author><name>Szymon Kulec</name></author><category term="Azure" /><category term="Azure Functions" /><category term="serverless" /><category term="Cloudflare" /><category term="Azure" /><category term="Azure Functions" /><category term="serverless" /><category term="Cloudflare" /><summary type="html"><![CDATA[This platform does not support... are the famous last words an engineer can hear before they start to think about implementing it on their own. This was the case, when we thought about releasing our very first online course - Async Expert!]]></summary></entry><entry><title type="html">Hello World!</title><link href="https://dotnetos.org/blog/2021-07-01-hello-world/" rel="alternate" type="text/html" title="Hello World!" /><published>2021-07-01T00:00:00+00:00</published><updated>2021-07-01T00:00:00+00:00</updated><id>https://dotnetos.org/blog/hello-world</id><content type="html" xml:base="https://dotnetos.org/blog/2021-07-01-hello-world/"><![CDATA[<p>Hello everyone and welcome to Dotnetos blog! This is our latest initiative and although we already share our stories with you through various portals such as Twitter or monthly newsletter, creating a blog has been our dream for a long time. We can promise you that it will be different from the news we share with you on a daily basis! In our company blog you will find:</p>

<h2 id="net-news">.NET news</h2>

<p>We live and breathe .NET. We’ll be providing you with good news from the .NET world.</p>

<h2 id="courses">Courses</h2>

<p>We produced a lot of courses and we’re not stopping here! More good things will come!</p>

<h2 id="guest-entries">Guest entries</h2>

<p>We’re working on bringing interesting people to this place and we can assure you that it will be awesome ;).</p>

<p>Make yourself at home and enjoy this place! As Brian Clark once said <em>“Don’t focus on having a great blog. Focus on producing a blog that’s great for your readers.”</em> - and that’s what we’re aiming for. This blog was made for <strong>you</strong>.</p>]]></content><author><name>Paulina Snarska</name></author><category term="Dotnetos" /><category term="Branding" /><category term="Dotnetos" /><summary type="html"><![CDATA[Hello everyone and welcome to Dotnetos blog! This is our latest initiative and although we already share our stories with you through various portals such as Twitter or monthly newsletter, creating a blog has been our dream for a long time. We can promise you that it will be different from the news we share with you on a daily basis!]]></summary></entry></feed>