{"id":906,"date":"2021-07-19T19:55:49","date_gmt":"2021-07-19T19:55:49","guid":{"rendered":"https:\/\/woitech.eu\/blog\/?p=906"},"modified":"2021-07-21T18:33:03","modified_gmt":"2021-07-21T18:33:03","slug":"linq-ensure-that-all-items-have-the-same-parent-key","status":"publish","type":"post","link":"https:\/\/woitech.eu\/blog\/2021\/07\/19\/linq-ensure-that-all-items-have-the-same-parent-key\/","title":{"rendered":"LINQ: Ensure that all items have the same parent key"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Why C# Geek post series?<\/h2>\n\n\n\n<p class=\"has-text-align-justify wp-block-paragraph\">I love programming and C#. I have been working with it for the last 15 years. While my roles and responsibilities have changed over this time, my passion for C# stayed at the same level, thus I decided to write a set of posts on my personal observations, experiences, and learnings.<\/p>\n\n\n\n<p class=\"has-text-align-justify wp-block-paragraph\"><strong>What to expect from this series?<\/strong><br>Well, I will try to cover various aspects of working with C# and .NET, where I will try to avoid opinionated posts but rather focus on sharing findings and providing comparisons.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">LINQ: Ensure that all items have the same parent key<\/h2>\n\n\n\n<p class=\"has-text-align-justify wp-block-paragraph\">Recently I have encountered an interesting problem. There is a method processing in bulk the collection of items, however, it is only able to process them if all items belong to the same parent object. In order to ensure it is the case, the method in question validates the input collection using the following code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">if (items.GroupBy(i =&gt; i.ParentKey).Count() &gt; 1)\n    throw new InvalidOperationException( \/* ... *\/);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The presented code is perfectly fine and does the job. I know however that LINQ offers various extension methods allowing to achieve the same outcome, thus I got curious to explore them and find out which one would be best suitable for this particular scenario.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Setting up BenchmarkDotNet<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In order to compare the differences, I created a new project following guidelines from <a href=\"https:\/\/benchmarkdotnet.org\/articles\/overview.html\" class=\"ek-link\">BenchmarkDotNet<\/a> overview page with <code>Program<\/code> class:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">public class Program\n{\n    static void Main(string[] args) =&gt; BenchmarkRunner.Run(typeof(Program).Assembly);\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I created a class <code>Item<\/code> with <code>ParentKey<\/code> property:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">public class Item\n{\n    public string ParentKey { get; init; }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then, I created a base benchmark class that will contain all benchmark methods:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">[MemoryDiagnoser]\npublic abstract class BenchmarkBase\n{\n    private readonly IEnumerable&lt;Item&gt; _data;\n\n    protected BenchmarkBase()\n    {\n        _data = GetData().ToArray();\n    }\n\n    protected abstract IEnumerable&lt;Item&gt; GetData();\n\n    \/* Benchmark methods... *\/\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">&#8230; followed by a few child classes that run these benchmarks against collections of 1000 items, where:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>all items have the same key<\/li><li>first item has different key<\/li><li>last item has different key<\/li><li>middle item has different key<\/li><\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">public class AllTheSame : BenchmarkBase\n{\n    protected override IEnumerable&lt;Item&gt; GetData() =&gt; Enumerable.Range(0, 1000)\n        .Select(_ =&gt; new Item { ParentKey = \"key1\" });\n}\n\npublic class FirstDifferent : BenchmarkBase\n{\n    protected override IEnumerable&lt;Item&gt; GetData() =&gt; Enumerable.Range(0, 999)\n        .Select(_ =&gt; new Item { ParentKey = \"key1\" })\n        .Prepend(new Item { ParentKey = \"key2\" });\n}\n\npublic class LastDifferent : BenchmarkBase\n{\n    protected override IEnumerable&lt;Item&gt; GetData() =&gt; Enumerable.Range(0, 999)\n        .Select(_ =&gt; new Item { ParentKey = \"key1\" })\n        .Append(new Item { ParentKey = \"key2\" });\n}\n\npublic class MiddleDifferent : BenchmarkBase\n{\n    protected override IEnumerable&lt;Item&gt; GetData() =&gt; Enumerable.Range(0, 500)\n        .Select(_ =&gt; new Item { ParentKey = \"key1\" })\n        .Append(new Item { ParentKey = \"key2\" })\n        .Concat(Enumerable.Range(0, 499).Select(_ =&gt; new Item { ParentKey = \"key1\" }));\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Validation methods<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Having the benchmark skeleton in place, I created the <code>MultipleParentKeyDetector<\/code> class and implemented a few alternatives of methods detecting the presence of items with different <code>ParentKey<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>GroupBy_Count()<\/code> is the method used in the original code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">public bool GroupBy_Count(IEnumerable&lt;Item&gt; data) \n    =&gt; data.GroupBy(x =&gt; x.ParentKey).Count() &gt; 1;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I followed it by an equivalent that uses <code>Distinct()<\/code> method:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">public bool Select_Distinct_Count(IEnumerable&lt;Item&gt; data) \n    =&gt; data.Select(x =&gt; x.ParentKey).Distinct().Count() &gt; 1;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Having it implemented, I thought that we do not really care about the exact number of different keys. The next method I wrote checks if there are any other parent keys than the first one:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">public bool Select_Distinct_Skip_Any(IEnumerable&lt;Item&gt; data) \n    =&gt; data.Select(x =&gt; x.ParentKey).Distinct().Skip(1).Any();<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, I implemented a reference method using <code>foreach<\/code> and that returns immediately after spotting the first difference:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">public bool Foreach(IEnumerable&lt;Item&gt; data)\n{\n    var first = true;\n    string firstKey = null;\n    foreach (var item in data)\n    {\n        if (firstKey != item.ParentKey &amp;&amp; !first)\n            return true;\n        if (first)\n        {\n            firstKey = item.ParentKey;\n            first = false;\n        }\n    }\n    return false;\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Running benchmarks<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Having validation methods implemented, I came back to <code>BenchmarkBase<\/code> and added the benchmark methods:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">[Benchmark(Baseline = true)]\npublic bool Foreach() =&gt; _detector.Foreach(_data);\n\n[Benchmark]\npublic bool GroupBy_Count() =&gt; _detector.GroupBy_Count(_data);\n\n[Benchmark]\npublic bool Select_Distinct_Count() =&gt; _detector.Select_Distinct_Count(_data);\n\n[Benchmark]\npublic bool Select_Distinct_Skip_Any() =&gt; _detector.Select_Distinct_Skip_Any(_data);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, it was possible to run them with <code>dotnet run -c Release<\/code> command.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The outcome<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The benchmark was run on .NET 5 SDK and runtime on Windows 10:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1083 (20H2\/October2020Update)\nIntel Core i9-9900KS CPU 4.00GHz, 1 CPU, 16 logical and 8 physical cores\n.NET SDK=5.0.302\n  [Host]     : .NET 5.0.8 (5.0.821.31504), X64 RyuJIT\n  DefaultJob : .NET 5.0.8 (5.0.821.31504), X64 RyuJIT<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">AllTheSame<\/h3>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:11px\"><code lang=\"markdown\" class=\"language-markdown\">|                   Method |      Mean |     Error |    StdDev | Ratio | RatioSD |  Gen 0 |  Gen 1 | Gen 2 | Allocated |\n|------------------------- |----------:|----------:|----------:|------:|--------:|-------:|-------:|------:|----------:|\n|                  Foreach |  4.658 \u03bcs | 0.0204 \u03bcs | 0.0181 \u03bcs |  1.00 |    0.00 |      - |      - |     - |      32 B |\n|            GroupBy_Count | 24.903 \u03bcs | 0.1545 \u03bcs | 0.1445 \u03bcs |  5.35 |    0.04 | 2.0142 | 0.0610 |     - |  16,896 B |\n|    Select_Distinct_Count | 22.873 \u03bcs | 0.0362 \u03bcs | 0.0283 \u03bcs |  4.91 |    0.02 | 0.0305 |      - |     - |     352 B |\n| Select_Distinct_Skip_Any | 23.640 \u03bcs | 0.0558 \u03bcs | 0.0495 \u03bcs |  5.08 |    0.02 | 0.0305 |      - |     - |     408 B |\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">FirstDifferent<\/h3>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:11px\"><code lang=\"markdown\" class=\"language-markdown\">|                   Method |         Mean |     Error |    StdDev |    Ratio | RatioSD |  Gen 0 |  Gen 1 | Gen 2 | Allocated |\n|------------------------- |-------------:|----------:|----------:|---------:|--------:|-------:|-------:|------:|----------:|\n|                  Foreach |     20.65 ns |  0.114 ns |  0.101 ns |     1.00 |    0.00 | 0.0038 |      - |     - |      32 B |\n|            GroupBy_Count | 23,784.68 ns | 60.390 ns | 56.489 ns | 1,151.68 |    6.82 | 2.0142 | 0.0610 |     - |  16,984 B |\n|    Select_Distinct_Count | 22,816.42 ns |  3.934 ns |  3.285 ns | 1,104.56 |    5.44 | 0.0305 |      - |     - |     352 B |\n| Select_Distinct_Skip_Any |    183.27 ns |  2.127 ns |  1.990 ns |     8.87 |    0.12 | 0.0486 |      - |     - |     408 B |\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">MiddleDifferent<\/h3>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:11px\"><code lang=\"markdown\" class=\"language-markdown\">|                   Method |      Mean |     Error |    StdDev | Ratio | RatioSD |  Gen 0 |  Gen 1 | Gen 2 | Allocated |\n|------------------------- |----------:|----------:|----------:|------:|--------:|-------:|-------:|------:|----------:|\n|                  Foreach |  2.141 \u03bcs | 0.0098 \u03bcs | 0.0091 \u03bcs |  1.00 |    0.00 | 0.0038 |      - |     - |      32 B |\n|            GroupBy_Count | 24.941 \u03bcs | 0.1059 \u03bcs | 0.0991 \u03bcs | 11.65 |    0.05 | 2.0142 | 0.0610 |     - |  16,984 B |\n|    Select_Distinct_Count | 22.839 \u03bcs | 0.0146 \u03bcs | 0.0114 \u03bcs | 10.66 |    0.05 | 0.0305 |      - |     - |     352 B |\n| Select_Distinct_Skip_Any | 11.906 \u03bcs | 0.0330 \u03bcs | 0.0308 \u03bcs |  5.56 |    0.03 | 0.0458 |      - |     - |     408 B |<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">LastDifferent<\/h3>\n\n\n\n<pre class=\"wp-block-code\" style=\"font-size:11px\"><code lang=\"markdown\" class=\"language-markdown\">|                   Method |      Mean |     Error |    StdDev | Ratio | RatioSD |  Gen 0 |  Gen 1 | Gen 2 | Allocated |\n|------------------------- |----------:|----------:|----------:|------:|--------:|-------:|-------:|------:|----------:|\n|                  Foreach |  4.081 \u03bcs | 0.0087 \u03bcs | 0.0077 \u03bcs |  1.00 |    0.00 |      - |      - |     - |      32 B |\n|            GroupBy_Count | 24.983 \u03bcs | 0.0767 \u03bcs | 0.0680 \u03bcs |  6.12 |    0.02 | 2.0142 | 0.0610 |     - |  16,984 B |\n|    Select_Distinct_Count | 23.225 \u03bcs | 0.0366 \u03bcs | 0.0324 \u03bcs |  5.69 |    0.01 | 0.0305 |      - |     - |     352 B |\n| Select_Distinct_Skip_Any | 22.885 \u03bcs | 0.0429 \u03bcs | 0.0335 \u03bcs |  5.61 |    0.01 | 0.0305 |      - |     - |     408 B |\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>Foreach<\/code> approach is 5 times better than any other alternative that bases on <code>GroupBy()<\/code> or <code>Distinct()<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For scenarios where all items have the same parent key or &#8220;problematic&#8221; item is at the end of the collection, the performance of all LINQ solutions is similar.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, the <code>Select_Distinct_Skip_Any()<\/code> outperforms the other methods if the &#8220;problematic&#8221; item is detected sooner. It is because this method stops enumerating as soon as it spots the difference, while <code>Count()<\/code> based solutions always enumerate the entire collection.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Allocation wise, the <code>GroupBy_Count<\/code>() is most expensive. It is due to the fact that <code>GroupBy()<\/code> has to preserve all items and group them together, while the <code>Distinct()<\/code> can discard the duplicates.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Summary<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It looks like the <code>Foreach()<\/code> approach is most performant and takes the least memory to execute. In scenarios where high performance is important, I would consider writing specialized methods like it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">However, in the less critical scenarios, I would stick to options offered by LINQ as it offers more concise, easier to read, and maintain code.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For this particular scenario I would choose the:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><code>Select(x =&gt; x.ParentKey).Distinct()<\/code> over <code>GroupBy(x =&gt; x.ParentKey)<\/code>, because the former approach is much more lighweight,<\/li><li><code>Skip(1).Any()<\/code> over <code>Count() &gt; 1<\/code>, because the former approach won&#8217;t enumerate the entire collection if not required.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">The Source-code<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The source code for this exercise can be found on my github project: <a href=\"https:\/\/github.com\/Suremaker\/cs-geek\/tree\/main\/AllItemsWithSameKey \" class=\"ek-link\">https:\/\/github.com\/Suremaker\/cs-geek\/tree\/main\/AllItemsWithSameKey <\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Why C# Geek post series? I love programming and C#. I have been working with it for the last 15 years. While my roles and responsibilities have changed over this time, my passion for C# stayed at the same level, thus I decided to write a set of posts on my personal observations, experiences, and learnings. What to expect from this series?Well, I will try to cover various aspects of working with C# and .NET, where I will try to avoid opinionated posts but rather focus on sharing findings and providing comparisons. LINQ: Ensure that all items have the same parent key Recently I have encountered an interesting problem. There is a method processing in bulk the collection of items, however, it is only able to process them if all items belong to the same parent object. In order to ensure it is the case, the method in question validates the input collection using the following code: The presented code is perfectly fine and does the job. I know however that LINQ offers various extension methods allowing to achieve the same outcome, thus I got curious to explore them and find out which one would be best suitable for this particular scenario. Setting up BenchmarkDotNet In order to compare the differences, I created a new project following guidelines from BenchmarkDotNet overview page with Program class: I created a class Item with ParentKey property: Then, I created a base benchmark class that will contain all benchmark methods: &#8230; followed by a few child classes that run these benchmarks against collections of 1000 items, where: all items have the same key first item has different key last item has different key middle item has different key Validation methods Having the benchmark skeleton in place, I created the MultipleParentKeyDetector class and implemented a few alternatives of methods detecting the presence of items with different ParentKey. The GroupBy_Count() is the method used in the original code: I followed it by an equivalent that uses Distinct() method: Having it implemented, I thought that we do not really care about the exact number of different keys. The next method I wrote checks if there are any other parent keys than the first one: Finally, I implemented a reference method using foreach and that returns immediately after spotting the first difference: Running benchmarks Having validation methods implemented, I came back to BenchmarkBase and added the benchmark methods: Finally, it was possible to run them with dotnet run -c Release command. The outcome The benchmark was run on .NET 5 SDK and runtime on Windows 10: AllTheSame FirstDifferent MiddleDifferent LastDifferent The Foreach approach is 5 times better than any other alternative that bases on GroupBy() or Distinct(). For scenarios where all items have the same parent key or &#8220;problematic&#8221; item is at the end of the collection, the performance of all LINQ solutions is similar. However, the Select_Distinct_Skip_Any() outperforms the other methods if the &#8220;problematic&#8221; item is detected sooner. It is because this method stops enumerating as soon as it spots the difference, while Count() based solutions always enumerate the entire collection. Allocation wise, the GroupBy_Count() is most expensive. It is due to the fact that GroupBy() has to preserve all items and group them together, while the Distinct() can discard the duplicates. The Summary It looks like the Foreach() approach is most performant and takes the least memory to execute. In scenarios where high performance is important, I would consider writing specialized methods like it. However, in the less critical scenarios, I would stick to options offered by LINQ as it offers more concise, easier to read, and maintain code. For this particular scenario I would choose the: Select(x =&gt; x.ParentKey).Distinct() over GroupBy(x =&gt; x.ParentKey), because the former approach is much more lighweight, Skip(1).Any() over Count() &gt; 1, because the former approach won&#8217;t enumerate the entire collection if not required. The Source-code The source code for this exercise can be found on my github project: https:\/\/github.com\/Suremaker\/cs-geek\/tree\/main\/AllItemsWithSameKey<\/p>\n","protected":false},"author":1,"featured_media":828,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_editorskit_title_hidden":false,"_editorskit_reading_time":4,"_editorskit_typography_data":[],"_editorskit_blocks_typography":"","_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","footnotes":""},"categories":[24],"tags":[25,26],"class_list":["post-906","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cs-geek","tag-linq","tag-performance"],"featured_image_urls":{"full":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs.png",1200,628,false],"thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-150x150.png",150,150,true],"medium":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-300x157.png",300,157,true],"medium_large":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-768x402.png",768,402,true],"large":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-1024x536.png",960,503,true],"1536x1536":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs.png",1200,628,false],"2048x2048":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs.png",1200,628,false],"bard-slider-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-1160x611.png",1160,611,true],"bard-full-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-1160x607.png",1160,607,true],"bard-grid-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-500x263.png",500,263,true],"bard-list-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-300x300.png",300,300,true],"bard-single-navigation":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-75x75.png",75,75,true]},"post_excerpt_stackable":"<p>Why C# Geek post series? I love programming and C#. I have been working with it for the last 15 years. While my roles and responsibilities have changed over this time, my passion for C# stayed at the same level, thus I decided to write a set of posts on my personal observations, experiences, and learnings. What to expect from this series?Well, I will try to cover various aspects of working with C# and .NET, where I will try to avoid opinionated posts but rather focus on sharing findings and providing comparisons. LINQ: Ensure that all items have the same&hellip;<\/p>\n","category_list":"<a href=\"https:\/\/woitech.eu\/blog\/category\/cs-geek\/\" rel=\"category tag\">C# Geek<\/a>","author_info":{"name":"suremaker","url":"https:\/\/woitech.eu\/blog\/author\/suremaker\/"},"comments_num":"0 comments","featured_image_urls_v2":{"full":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs.png",1200,628,false],"thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-150x150.png",150,150,true],"medium":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-300x157.png",300,157,true],"medium_large":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-768x402.png",768,402,true],"large":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-1024x536.png",960,503,true],"1536x1536":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs.png",1200,628,false],"2048x2048":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs.png",1200,628,false],"bard-slider-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-1160x611.png",1160,611,true],"bard-full-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-1160x607.png",1160,607,true],"bard-grid-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-500x263.png",500,263,true],"bard-list-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-300x300.png",300,300,true],"bard-single-navigation":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-75x75.png",75,75,true]},"post_excerpt_stackable_v2":"<p>Why C# Geek post series? I love programming and C#. I have been working with it for the last 15 years. While my roles and responsibilities have changed over this time, my passion for C# stayed at the same level, thus I decided to write a set of posts on my personal observations, experiences, and learnings. What to expect from this series?Well, I will try to cover various aspects of working with C# and .NET, where I will try to avoid opinionated posts but rather focus on sharing findings and providing comparisons. LINQ: Ensure that all items have the same&hellip;<\/p>\n","category_list_v2":"<a href=\"https:\/\/woitech.eu\/blog\/category\/cs-geek\/\" rel=\"category tag\">C# Geek<\/a>","author_info_v2":{"name":"suremaker","url":"https:\/\/woitech.eu\/blog\/author\/suremaker\/"},"comments_num_v2":"0 comments","cc_featured_image_caption":{"caption_text":"","source_text":"","source_url":""},"uagb_featured_image_src":{"full":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs.png",1200,628,false],"thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-150x150.png",150,150,true],"medium":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-300x157.png",300,157,true],"medium_large":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-768x402.png",768,402,true],"large":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-1024x536.png",960,503,true],"1536x1536":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs.png",1200,628,false],"2048x2048":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs.png",1200,628,false],"bard-slider-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-1160x611.png",1160,611,true],"bard-full-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-1160x607.png",1160,607,true],"bard-grid-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-500x263.png",500,263,true],"bard-list-thumbnail":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-300x300.png",300,300,true],"bard-single-navigation":["https:\/\/woitech.eu\/blog\/wp-content\/uploads\/2021\/07\/cs-75x75.png",75,75,true]},"uagb_author_info":{"display_name":"suremaker","author_link":"https:\/\/woitech.eu\/blog\/author\/suremaker\/"},"uagb_comment_info":0,"uagb_excerpt":"Why C# Geek post series? I love programming and C#. I have been working with it for the last 15 years. While my roles and responsibilities have changed over this time, my passion for C# stayed at the same level, thus I decided to write a set of posts on my personal observations, experiences, and&hellip;","_links":{"self":[{"href":"https:\/\/woitech.eu\/blog\/wp-json\/wp\/v2\/posts\/906","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/woitech.eu\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/woitech.eu\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/woitech.eu\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/woitech.eu\/blog\/wp-json\/wp\/v2\/comments?post=906"}],"version-history":[{"count":22,"href":"https:\/\/woitech.eu\/blog\/wp-json\/wp\/v2\/posts\/906\/revisions"}],"predecessor-version":[{"id":929,"href":"https:\/\/woitech.eu\/blog\/wp-json\/wp\/v2\/posts\/906\/revisions\/929"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/woitech.eu\/blog\/wp-json\/wp\/v2\/media\/828"}],"wp:attachment":[{"href":"https:\/\/woitech.eu\/blog\/wp-json\/wp\/v2\/media?parent=906"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/woitech.eu\/blog\/wp-json\/wp\/v2\/categories?post=906"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/woitech.eu\/blog\/wp-json\/wp\/v2\/tags?post=906"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}