<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Turner Software</title>
    <description>The latest articles on DEV Community by Turner Software (@turnersoftware).</description>
    <link>https://dev.to/turnersoftware</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F463%2F8df5daa2-8716-4612-a6f7-9b47c9b83d05.png</url>
      <title>DEV Community: Turner Software</title>
      <link>https://dev.to/turnersoftware</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/turnersoftware"/>
    <language>en</language>
    <item>
      <title>MiniProfiler ❤ MongoDB</title>
      <dc:creator>James Turner</dc:creator>
      <pubDate>Sat, 08 Jun 2019 14:15:36 +0000</pubDate>
      <link>https://dev.to/turnersoftware/miniprofiler-mongodb-4bc1</link>
      <guid>https://dev.to/turnersoftware/miniprofiler-mongodb-4bc1</guid>
      <description>&lt;p&gt;If you're familiar with .NET, you may have heard of an awesome project called &lt;a href="https://miniprofiler.com/" rel="noopener noreferrer"&gt;MiniProfiler&lt;/a&gt; made by the awesome folks at Stack Overflow.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/MiniProfiler" rel="noopener noreferrer"&gt;
        MiniProfiler
      &lt;/a&gt; / &lt;a href="https://github.com/MiniProfiler/dotnet" rel="noopener noreferrer"&gt;
        dotnet
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A simple but effective mini-profiler for ASP.NET (and Core) websites
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;MiniProfiler for .NET (and .NET Core)&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href="https://dotnetfoundation.org/projects" rel="nofollow noopener noreferrer"&gt;&lt;img width="100px" src="https://camo.githubusercontent.com/7667cc76714be4a94e2e9de1b55b4f2ea801190714275dfec37302b13e97aad5/68747470733a2f2f646f746e6574666f756e646174696f6e2e6f72672f696d672f6c6f676f5f6269672e737667"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Welcome to MiniProfiler for .NET, ASP.NET, ASP.NET Core, ASP.NET MVC and generally all the combinations of those words. Documentation for MiniProfiler for .NET is in &lt;code&gt;/docs&lt;/code&gt;, accessible via GitHub pages at: &lt;a href="https://miniprofiler.com/dotnet/" rel="nofollow noopener noreferrer"&gt;miniprofiler.com/dotnet&lt;/a&gt;. General information for MiniProfiler across platforms can be found at &lt;a href="https://miniprofiler.com/" rel="nofollow noopener noreferrer"&gt;miniprofiler.com&lt;/a&gt;. It is part of the &lt;a href="https://www.dotnetfoundation.org/" rel="nofollow noopener noreferrer"&gt;.NET Foundation&lt;/a&gt;, and operates under their &lt;a href="https://www.dotnetfoundation.org/code-of-conduct" rel="nofollow noopener noreferrer"&gt;code of conduct&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ci.appveyor.com/project/StackExchange/dotnet/branch/main" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/79bf11f1f449bf2ad8bbe2ecfda4cad388a3d776637ffadba7ce18f05283dd40/68747470733a2f2f63692e6170707665796f722e636f6d2f6170692f70726f6a656374732f7374617475732f73696579686675686a777735757235692f6272616e63682f6d61696e3f7376673d74727565" alt="AppVeyor Build Status"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/MiniProfiler/dotnet/workflows/Main%20Build/badge.svg"&gt;&lt;img src="https://github.com/MiniProfiler/dotnet/workflows/Main%20Build/badge.svg" alt="Actions Build"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The current major version of MiniProfiler is v4.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Handy Links&lt;/h4&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Documentation
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://miniprofiler.com/dotnet/AspDotNet" rel="nofollow noopener noreferrer"&gt;Getting started for ASP.NET (&lt;em&gt;not&lt;/em&gt; .NET Core)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://miniprofiler.com/dotnet/AspDotNetCore" rel="nofollow noopener noreferrer"&gt;Getting started for ASP.NET Core&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://miniprofiler.com/dotnet/HowTo/ProfileCode" rel="nofollow noopener noreferrer"&gt;How-To Profile Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://miniprofiler.com/dotnet/NuGet" rel="nofollow noopener noreferrer"&gt;NuGet Packages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://miniprofiler.com/dotnet/HowTo/UpgradeFromV3" rel="nofollow noopener noreferrer"&gt;How-To Upgrade From MiniProfiler V3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Samples
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/MiniProfiler/dotnet/tree/main/samples/Samples.AspNetCore3" rel="noopener noreferrer"&gt;ASP.NET Core Sample App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MiniProfiler/dotnet/tree/main/samples/Samples.Mvc5" rel="noopener noreferrer"&gt;ASP.NET MVC 5 Sample App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/MiniProfiler/dotnet/tree/main/samples/Samples.Console" rel="noopener noreferrer"&gt;Console Application&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Building&lt;/h4&gt;
&lt;/div&gt;
&lt;p&gt;To build the MiniProfiler solution in Visual Studio, you'll need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visual Studio 2019 16.3+ (or the .NET Core 3.x SDK)&lt;/li&gt;
&lt;li&gt;The &lt;a href="https://marketplace.visualstudio.com/items?itemName=MadsKristensen.WebCompiler" rel="nofollow noopener noreferrer"&gt;Web Compiler&lt;/a&gt; extension
&lt;ul&gt;
&lt;li&gt;Note: no extension is needed if building via &lt;code&gt;build.cmd&lt;/code&gt; or &lt;code&gt;build.ps1&lt;/code&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/MiniProfiler/dotnet" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;If you're unfamiliar, you may have gathered from the name it has something to do with profiling code - you wouldn't be wrong!&lt;/p&gt;

&lt;p&gt;MiniProfiler isn't designed to profile every method call - it is however designed to profile the calls you specifically want to know about. These include things like database calls, your controllers or your views. You can optionally profile any other code you want through MiniProfiler's API.&lt;/p&gt;

&lt;p&gt;MongoDB - some love it, some hate it but regardless I use it and I like it for my projects. What I don't like with MongoDB is the C# driver so as an ongoing project of mine, I have built my own wrapper for MongoDB C# driver called &lt;a href="https://github.com/TurnerSoftware/MongoFramework" rel="noopener noreferrer"&gt;MongoFramework&lt;/a&gt;. I have &lt;a href="https://dev.to/turnersoftware/mongo-what-now-34lg"&gt;written about this library previously here&lt;/a&gt; so I won't go into much detail besides saying it makes dealing with MongoDB similar to dealing with Entity Framework.&lt;/p&gt;

&lt;p&gt;MongoFramework however is how MiniProfiler and MongoDB meet. You see, MiniProfiler has official packages for profiling EF6 and EF Core but doesn't actually support profiling of MongoDB queries. As I had already written a wrapper around the official MongoDB driver, I was already in a good place to extend my own integration to support profiling.&lt;/p&gt;

&lt;p&gt;MongoFramework uses a diagnostic layer inspired by how MiniProfiler actually connects into EF Core. This diagnostic layer is per connection for MongoFramework and as an interface, can easily be swapped out for any other diagnostic tool. The diagnostic layer is invoked at every entity read/write as well as the creation of indexes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fqrtvcjwr13r3fxwsa6ge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fqrtvcjwr13r3fxwsa6ge.png" alt="MiniProfiler profiling dialog showing a MongoDB column"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9wp54kvt67kyt5792mtj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F9wp54kvt67kyt5792mtj.png" alt="MiniProfiler showing an actual MongoDB query"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;are building a project in .NET&lt;/li&gt;
&lt;li&gt;are using MongoDB for persistence via MongoFramework&lt;/li&gt;
&lt;li&gt;are using or want to use MiniProfiler&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then this might be exactly what you are looking for! Check it out on &lt;a href="https://www.nuget.org/packages/MongoFramework.Profiling.MiniProfiler/" rel="noopener noreferrer"&gt;NuGet&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/TurnerSoftware" rel="noopener noreferrer"&gt;
        TurnerSoftware
      &lt;/a&gt; / &lt;a href="https://github.com/TurnerSoftware/MongoFramework" rel="noopener noreferrer"&gt;
        MongoFramework
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An "Entity Framework"-like interface for MongoDB
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/TurnerSoftware/MongoFrameworkimages/icon.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FTurnerSoftware%2FMongoFrameworkimages%2Ficon.png" alt="Icon"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;MongoFramework&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;An "Entity Framework"-like interface for MongoDB&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/15510ae1d9fd77dba1a8cbb942b69877b0b888ad9a2b5a3573adc41d057398b0/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f5475726e6572536f6674776172652f6d6f6e676f6672616d65776f726b2f6275696c642e796d6c3f6272616e63683d6d61696e"&gt;&lt;img src="https://camo.githubusercontent.com/15510ae1d9fd77dba1a8cbb942b69877b0b888ad9a2b5a3573adc41d057398b0/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f5475726e6572536f6674776172652f6d6f6e676f6672616d65776f726b2f6275696c642e796d6c3f6272616e63683d6d61696e" alt="Build"&gt;&lt;/a&gt;
&lt;a href="https://codecov.io/gh/TurnerSoftware/MongoFramework" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/be54421930d5e43cf03cf2f8656bedad1784ae1aa8ad0d2c9316359581091b0b/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f7475726e6572736f6674776172652f6d6f6e676f6672616d65776f726b2f6d61696e2e737667" alt="Codecov"&gt;&lt;/a&gt;
&lt;a href="https://www.nuget.org/packages/MongoFramework/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/a52f860f83f3897477524216000ee13de52fc381f2e9d68a3d9ec300656b55fb/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f4d6f6e676f4672616d65776f726b2e737667" alt="NuGet"&gt;&lt;/a&gt;
&lt;a href="https://www.codacy.com/app/Turnerj/MongoFramework" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1f32ae158a44d89bde1950b486782f9c83d4a30eb5c4d6493273c857b8d2bec4/68747470733a2f2f6170692e636f646163792e636f6d2f70726f6a6563742f62616467652f47726164652f3632666133316339306266393466336438653230316239363834613761346361" alt="Codacy Badge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;MongoFramework tries to bring some of the nice features from Entity Framework into the world of MongoDB.&lt;/p&gt;
&lt;p&gt;Some of the major features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Entity mapping for collections, IDs and properties through attributes&lt;/li&gt;
&lt;li&gt;Indexing through attributes (including text and geospatial)&lt;/li&gt;
&lt;li&gt;Fluent mapping builder&lt;/li&gt;
&lt;li&gt;Entity change tracking&lt;/li&gt;
&lt;li&gt;Changeset support (allowing for queuing multiple DB updates to run at once)&lt;/li&gt;
&lt;li&gt;Diff-updates (only &lt;em&gt;changes&lt;/em&gt; to an entity to be written)&lt;/li&gt;
&lt;li&gt;Entity Buckets (clustering of small documents together, &lt;a href="https://www.mongodb.com/blog/post/building-with-patterns-the-bucket-pattern" rel="nofollow noopener noreferrer"&gt;improving index performance&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Runtime type discovery (serialize and deserialize without needing to specify every "known" type)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MongoFramework is currently built on-top of the official MongoDB C# driver.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Licensing and Support&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;MongoFramework is licensed under the MIT license. It is free to use in personal and commercial projects.&lt;/p&gt;
&lt;p&gt;There are &lt;a href="https://turnersoftware.com.au/support-plans" rel="nofollow noopener noreferrer"&gt;support plans&lt;/a&gt; available that cover all active &lt;a href="https://github.com/TurnerSoftware" rel="noopener noreferrer"&gt;Turner Software OSS projects&lt;/a&gt;
Support plans provide private email support, expert…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TurnerSoftware/MongoFramework" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>showdev</category>
      <category>csharp</category>
      <category>dotnet</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>Building a Polite Web Crawler</title>
      <dc:creator>James Turner</dc:creator>
      <pubDate>Sat, 13 Apr 2019 13:11:04 +0000</pubDate>
      <link>https://dev.to/turnersoftware/building-a-polite-web-crawler-3b8h</link>
      <guid>https://dev.to/turnersoftware/building-a-polite-web-crawler-3b8h</guid>
      <description>&lt;p&gt;Web crawling is the act of having a program or script accessing a website, capturing content and discovering any pages linked to from that content. On the surface it really is only performing HTTP requests and parsing HTML, both things that can be quite easily accomplished in a variety of languages and frameworks.&lt;/p&gt;

&lt;p&gt;Web crawling is an extremely important tool for search engines or anyone wanting to perform analysis of a website. The act of crawling a site though can consume a lot of resources for the site operator depending how the site is crawled.&lt;/p&gt;

&lt;p&gt;For example, if you crawl an 1000 page site in a few seconds, you've likely caused a not insignificant amount of server load for low-bandwidth hosting. What if you crawled a slow-loading page but your crawler didn't handle it properly, continuously re-querying the same page. What if you are just crawling pages that shouldn't be crawled. These things can lead to very upset website operators.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/kkpcRessCvNyo/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/kkpcRessCvNyo/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a previous article, &lt;a href="https://dev.to/turnersoftware/no-robots-allowed-4mnl"&gt;I wrote about the Robots.txt file&lt;/a&gt; and how that can help address these problems from the website operator's perspective. Web crawlers should (but don't have to) abide by the rules governed in that file to prevent getting blocked. In addition to the Robots.txt file, there are some other things crawlers should do to avoid being blocked.&lt;/p&gt;

&lt;p&gt;When crawling a website on a large scale, especially for commercial purposes, it is a good idea to provide a custom User Agent, allowing website operators a chance to restrict what pages can be crawled.&lt;/p&gt;

&lt;p&gt;Crawl frequency is another aspect you will want to to refine to allow you to crawl a site fast enough without being a performance burden. It is highly likely you will want to limit crawling to a handful of requests a second. It is also a good idea to track how long requests are taking and to start throttling the crawler to compensate for potential site load issues.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://i.giphy.com/media/wnAsnQtnzqHU4/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/wnAsnQtnzqHU4/giphy.gif"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Actual footage of a server catching fire because of load, totally not from a &lt;a href="https://en.wikipedia.org/wiki/Silicon_Valley_(TV_series)" rel="noopener noreferrer"&gt;TV Show&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I spend my days programming in the world of .NET and had a need for a web crawler for a project of mine. There are some popular web crawlers already out there including &lt;a href="https://github.com/sjdirect/abot/" rel="noopener noreferrer"&gt;Abot&lt;/a&gt; and &lt;a href="https://github.com/dotnetcore/DotnetSpider" rel="noopener noreferrer"&gt;DotnetSpider&lt;/a&gt; however for different reasons they didn't suit my needs.&lt;/p&gt;

&lt;p&gt;I originally did have Abot setup in my project however I have been porting my project to .NET Core and it didn't support it. The library also uses a no longer support version of a library that does parsing of Robots.txt files.&lt;/p&gt;

&lt;p&gt;With DotnetSpider, it does support .NET Core but it is designed around an entire different process of using it with message queues, model binding and built-in DB writing. These are cool features but excessive for my own needs.&lt;/p&gt;

&lt;p&gt;I wanted a simple crawler, supporting async/await, with .NET Core support thus &lt;a href="https://github.com/TurnerSoftware/InfinityCrawler" rel="noopener noreferrer"&gt;InfinityCrawler&lt;/a&gt; was born!&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/TurnerSoftware" rel="noopener noreferrer"&gt;
        TurnerSoftware
      &lt;/a&gt; / &lt;a href="https://github.com/TurnerSoftware/InfinityCrawler" rel="noopener noreferrer"&gt;
        InfinityCrawler
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A simple but powerful web crawler library for .NET
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/TurnerSoftware/InfinityCrawlerimages/icon.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FTurnerSoftware%2FInfinityCrawlerimages%2Ficon.png" alt="Icon"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Infinity Crawler&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A simple but powerful web crawler library for .NET&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/4542565c3b7efce0600d60303bfe966dc2e7084a46d49dc5bf96449c10568dfd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f5475726e6572536f6674776172652f696e66696e697479637261776c65722f4275696c64"&gt;&lt;img src="https://camo.githubusercontent.com/4542565c3b7efce0600d60303bfe966dc2e7084a46d49dc5bf96449c10568dfd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f776f726b666c6f772f7374617475732f5475726e6572536f6674776172652f696e66696e697479637261776c65722f4275696c64" alt="Build"&gt;&lt;/a&gt;
&lt;a href="https://codecov.io/gh/TurnerSoftware/infinitycrawler" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/7a15959c5118dc406343abfaee470c7a2416e1f3c649b0e5083c32bcfb49416a/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f7475726e6572736f6674776172652f696e66696e697479637261776c65722f6d61696e2e737667" alt="Codecov"&gt;&lt;/a&gt;
&lt;a href="https://www.nuget.org/packages/InfinityCrawler" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/42f002a234d5e4e1186fce2a928a6defd043d6a9762ded990e468eebd88ac554/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f496e66696e697479437261776c65722e737667" alt="NuGet"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Obeys robots.txt (crawl delay &amp;amp; allow/disallow)&lt;/li&gt;
&lt;li&gt;Obeys in-page robots rules (&lt;code&gt;X-Robots-Tag&lt;/code&gt; header and &lt;code&gt;&amp;lt;meta name="robots" /&amp;gt;&lt;/code&gt; tag)&lt;/li&gt;
&lt;li&gt;Uses sitemap.xml to seed the initial crawl of the site&lt;/li&gt;
&lt;li&gt;Built around a parallel task &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; system&lt;/li&gt;
&lt;li&gt;Swappable request and content processors, allowing greater customisation&lt;/li&gt;
&lt;li&gt;Auto-throttling (see below)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Licensing and Support&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Infinity Crawler is licensed under the MIT license. It is free to use in personal and commercial projects.&lt;/p&gt;
&lt;p&gt;There are &lt;a href="https://turnersoftware.com.au/support-plans" rel="nofollow noopener noreferrer"&gt;support plans&lt;/a&gt; available that cover all active &lt;a href="https://github.com/TurnerSoftware" rel="noopener noreferrer"&gt;Turner Software OSS projects&lt;/a&gt;.
Support plans provide private email support, expert usage advice for our projects, priority bug fixes and more.
These support plans help fund our OSS commitments to provide better software for everyone.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Polite Crawling&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;The crawler is built around fast but "polite" crawling of website
This is accomplished through a number of settings that allow adjustments…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TurnerSoftware/InfinityCrawler" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I'll be honest, I don't know why I called it InfinityCrawler - it sounded cool at the time so I just went with it.&lt;/p&gt;

&lt;p&gt;This crawler is in .NET Standard and builds upon both my &lt;a href="https://github.com/TurnerSoftware/SitemapTools" rel="noopener noreferrer"&gt;SitemapTools&lt;/a&gt; and &lt;a href="https://github.com/TurnerSoftware/RobotsExclusionTools" rel="noopener noreferrer"&gt;RobotsExclusionTools&lt;/a&gt; libraries. It uses the Sitemap library to help seed the list of URLs it should start crawling.&lt;/p&gt;

&lt;p&gt;It has built in support for crawl frequency including obeying frequency defined in the Robots.txt file. It can detect slow requests and auto throttle itself to avoid thrashing the website as well as detect when performance improves and return back to normal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;InfinityCrawler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;crawler&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Crawler&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;crawler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Crawl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;siteUri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;CrawlSettings&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;UserAgent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Your Awesome Crawler User Agent Here"&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;InfinityCrawler, while available for use in any .NET project, it still is in its early stages. I am happy with its core functionality but likely will go through a few stages of restructure as well as expanding on the testing.&lt;/p&gt;

&lt;p&gt;I am personally pretty proud of &lt;a href="https://github.com/TurnerSoftware/InfinityCrawler/blob/1cf1eebc6b2b2f204cb6cdc189ffa33e1001af16/src/InfinityCrawler/TaskHandlers/ParallelAsyncTaskHandler.cs" rel="noopener noreferrer"&gt;how I implemented the async/await part&lt;/a&gt; but would love to talk to anyone that is an expert in this area with .NET to check my implementation and give pointers on how to improve it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/VJrEwNieUbo52/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/VJrEwNieUbo52/giphy.gif"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>dotnet</category>
      <category>crawling</category>
    </item>
    <item>
      <title>Building a flat website with ASP.NET Core</title>
      <dc:creator>James Turner</dc:creator>
      <pubDate>Fri, 22 Feb 2019 01:52:49 +0000</pubDate>
      <link>https://dev.to/turnersoftware/building-a-flat-website-with-aspnet-core-1jaa</link>
      <guid>https://dev.to/turnersoftware/building-a-flat-website-with-aspnet-core-1jaa</guid>
      <description>&lt;p&gt;As of last night, I &lt;a href="https://brandvantage.co" rel="noopener noreferrer"&gt;launched the website for BrandVantage&lt;/a&gt;, my product I am building. I'm pretty proud of the site, I designed and built it myself as well as designed the logo. This all happened in just over one week since &lt;a href="https://dev.to/turnerj/i-left-my-job-today-f1a"&gt;I left my job&lt;/a&gt; too!&lt;/p&gt;

&lt;p&gt;I touched on a little what BrandVantage was about &lt;a href="https://dev.to/turnerj/comment/8mlf"&gt;in a comment&lt;/a&gt; but this, and various other posts coming soon, are to talk about development and technologies, not me trying to sell you on it. When I do launch on Product Hunt, I will probably post about that though! 🙂&lt;/p&gt;

&lt;p&gt;This new site, as well as &lt;a href="https://turnerj.com/" rel="noopener noreferrer"&gt;my personal site&lt;/a&gt; and &lt;a href="https://www.turnersoftware.com.au/" rel="noopener noreferrer"&gt;my company's website&lt;/a&gt; (both also launched not too long ago) are all built very similarly: a single-page, flat HTML with minified CSS.&lt;/p&gt;

&lt;p&gt;I like having a flat site like this as it is hosting agnostic. These sites I have built, while are ASP.NET Core projects in code, they are deployed to PHP hosting. The rest of this post explains that setup a little.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Base Project
&lt;/h2&gt;

&lt;p&gt;I'm using Visual Studio with each of these projects starting out the same, an empty ASP.NET Core website project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc69inkhrlqjrv469ijk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc69inkhrlqjrv469ijk.jpg" alt="Visual Studio Solution Structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For getting the actual project working in development, you will need to update your &lt;code&gt;Startup.cs&lt;/code&gt; to be something similar to below:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Startup&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IHostingEnvironment&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsDevelopment&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseDeveloperExceptionPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseBrowserLink&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseDefaultFiles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;DefaultFilesOptions&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;DefaultFileNames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"index.html"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseStaticFiles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You want to make sure you have &lt;code&gt;UseStaticFiles&lt;/code&gt; and set your index file to be a default otherwise it won't work as you expect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Site Root
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wxfly5yrothgg1bd1xw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wxfly5yrothgg1bd1xw.jpg" alt="Site Root"&gt;&lt;/a&gt;&lt;br&gt;
This setup really is taking it back to basics, I'm not using any special features of ASP.NET Core itself, there isn't some Razor template I am exporting to flat HTML. It really is just one &lt;code&gt;index.html&lt;/code&gt; file that I've manually built.&lt;/p&gt;

&lt;p&gt;I did look into trying to export a site from CSHTML into plain HTML but it seemed a little too complicated for a single page site. I might revisit this in the future but for now, a flat HTML page is perfect for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bundling and Minification
&lt;/h2&gt;

&lt;p&gt;My setup relies on the Nuget package &lt;a href="https://www.nuget.org/packages/BuildBundlerMinifier" rel="noopener noreferrer"&gt;BuildBundlerMinifier&lt;/a&gt; which allow the minification of CSS by just simply building the codebase as you would any other .NET codebase.&lt;/p&gt;

&lt;p&gt;It uses a JSON file which for me looks like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outputFileName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wwwroot/bundles/site.min.css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"inputFiles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"wwwroot/css/normalize.css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"wwwroot/css/layout.css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"wwwroot/css/product-hunt.css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"wwwroot/css/responsive.css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"wwwroot/css/compatibility.css"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sourceMap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outputFileName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"wwwroot/fonts/font-awesome/bundles/fontawesome.min.css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"inputFiles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"wwwroot/fonts/font-awesome/css/fontawesome.min.css"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"wwwroot/fonts/font-awesome/css/light.min.css"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The bundler/minifier does support JavaScript but I don't have any JS in my site besides Google Analytics and a Product Hunt script.&lt;/p&gt;

&lt;p&gt;You can see in my site root screenshot that there is a &lt;code&gt;bundles&lt;/code&gt; folder, that is created by this process however I have that excluded from Git.&lt;/p&gt;

&lt;p&gt;Because my site is flat HTML, I do need to reference the bundled files directly which I do as shown here:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;

    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/bundles/site.min.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"fonts/font-awesome/bundles/fontawesome.min.css"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;The reason for one being in the &lt;code&gt;head&lt;/code&gt; and one being in the &lt;code&gt;body&lt;/code&gt; is slightly increasing the perception of a faster page load.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One downside to this entire flat HTML method is that these bundled files aren't regenerated on the fly. This means I need to build again every time I want to view a CSS change. For major works, I might manually revert back to non-minified CSS just to make development easier. Ideally though, you would have it auto-generate on modification - this is something that many other tools provide but I don't (yet) have in my setup.&lt;/p&gt;

&lt;p&gt;If I were to use CSHTML, I could actually reference both and use tag helpers to define what environment I am running in. Again, something I might consider for the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continuous Delivery
&lt;/h2&gt;

&lt;p&gt;You might have noticed in that first screenshot there is a cheeky &lt;code&gt;build-pipeline.yml&lt;/code&gt; file, this is used by Azure DevOps Pipelines to allow me to automate my build and deployment setup.&lt;/p&gt;

&lt;p&gt;My file looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;self&lt;/span&gt;
&lt;span class="na"&gt;queue&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Hosted VS2017&lt;/span&gt;

&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DotNetCoreInstaller@0&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2.2.101'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DotNetCoreCLI@2&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dotnet&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;build'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ArchiveFiles@2&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Archive&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;src/Website/wwwroot'&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;rootFolderOrFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/Website/wwwroot&lt;/span&gt;
    &lt;span class="na"&gt;includeRootFolder&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;archiveType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;7z&lt;/span&gt;
    &lt;span class="na"&gt;archiveFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$(Build.ArtifactStagingDirectory)/website.7z'&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PublishBuildArtifacts@1&lt;/span&gt;
  &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Publish&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Artifact:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Web'&lt;/span&gt;
  &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ArtifactName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Web&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I'm no expert on Azure DevOps Pipelines but the above is what I have working for me. First it installs a specific version of .NET Core, just so I have a stable and consistent environment (as .NET Core is already installed). I then run a build which really is just running the minification process. The next two tasks are required for getting the data to package up the build for the deployment pipeline.&lt;/p&gt;

&lt;p&gt;At the time of making this setup, Azure didn't support a full YML config for the deployment side of the pipeline so I can't show that to you. The short version of what it does is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Waits for a new build package coming from the master branch (allowing me to use other branches for dev without fear of deploying them)&lt;/li&gt;
&lt;li&gt;Unpacks the artifact&lt;/li&gt;
&lt;li&gt;Using FTPS, deploys all the changes to my hosting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Maybe not the greatest or most productive setup but one that works pretty well for me. What I can strongly say though is how much I love automated deployments - that stuff is amazing!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>devdiary</category>
      <category>ci</category>
      <category>aspnetcore</category>
    </item>
    <item>
      <title>Sitemaps 101</title>
      <dc:creator>James Turner</dc:creator>
      <pubDate>Sun, 27 Jan 2019 04:04:19 +0000</pubDate>
      <link>https://dev.to/turnersoftware/sitemaps-101-4ch8</link>
      <guid>https://dev.to/turnersoftware/sitemaps-101-4ch8</guid>
      <description>&lt;p&gt;Our ability to search online for various things and get relevant results is quite a technical achievement, especially at the scale that search engines need to work at. They need to build large indexes of websites and content so they can process are queries and bring us to the content we are after.&lt;/p&gt;

&lt;p&gt;I &lt;a href="https://dev.to/turnerj/no-robots-allowed-4mnl"&gt;previously talked about&lt;/a&gt; the "Robots.txt" file and the relationship between web crawlers and the website operator. That is one piece of the puzzle where it helps both the web crawler and the website operator communicate about what shouldn't be indexed on a site.&lt;/p&gt;

&lt;p&gt;Another piece of the puzzle are sitemaps, a file that helps you tell web crawlers and search engines about all the pages on your site, when they were last updated and the frequency of updates. This information isn't possible by just crawling pages by links in content.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Brief History
&lt;/h2&gt;

&lt;p&gt;Maybe not so surprisingly &lt;a href="https://web.archive.org/web/20050608015054/http://googleblog.blogspot.com/2005/06/webmaster-friendly.html" rel="noopener noreferrer"&gt;we have Google to thank&lt;/a&gt; about starting the concept of sitemap files back in mid 2005.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l4HnS4Ln4OCBTDpew/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l4HnS4Ln4OCBTDpew/giphy.gif" alt="The Grinder TV Show Quote - Todd saying "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In November 2006, &lt;a href="https://googlepress.blogspot.com/2006/11/major-search-engines-unite-to-support_16.html" rel="noopener noreferrer"&gt;Yahoo and Microsoft joined Google in support of the standard&lt;/a&gt; with the schema &lt;a href="https://www.sitemaps.org/protocol.html" rel="noopener noreferrer"&gt;"Sitemap 0.9"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Not long after that, they jointly announced support for a non-standard feature on "Robots.txt" files, allowing them to point to where sitemaps for a website can be located.&lt;/p&gt;

&lt;p&gt;For example, here is &lt;a href="https://dev.to/robots.txt"&gt;dev.to's robots file&lt;/a&gt; pointing to the location of the sitemap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
# User-agent: *
# Disallow: /

Sitemap: https://thepracticaldev.s3.amazonaws.com/sitemaps/sitemap.xml.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  The Format
&lt;/h2&gt;

&lt;p&gt;There are 3 flavours of sitemaps: XML, TXT and RSS&lt;/p&gt;
&lt;h3&gt;
  
  
  XML Sitemaps
&lt;/h3&gt;

&lt;p&gt;These are most likely the only form of sitemap you will ever actually work with and what is the core format defined in the specification. That said, not all XML sitemaps are the same as there are two different types.&lt;/p&gt;
&lt;h4&gt;
  
  
  Normal Sitemap File
&lt;/h4&gt;

&lt;p&gt;You have a number of &lt;code&gt;&amp;lt;url&amp;gt;&lt;/code&gt; tags which all must have a &lt;code&gt;&amp;lt;loc&amp;gt;&lt;/code&gt; tag but optionally the &lt;code&gt;&amp;lt;lastmod&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;changefreq&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;priority&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;loc&amp;gt;&lt;/code&gt; tag is simply the absolute URL of the page on your site.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;lastmod&amp;gt;&lt;/code&gt; tag helps indicate the "freshness" of a page. While a crawler might prioritise based on this value, I wouldn't recommend constantly updating the last modified to the current date to try and game the system.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;changefreq&amp;gt;&lt;/code&gt; tag is only a guideline for crawlers, don't think setting it to "hourly" will make web crawlers instantly crawl your site more often.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;priority&amp;gt;&lt;/code&gt; tag isn't for defining how important that page is compared to other websites but how important it is for the web crawler to even crawl that page. This has a default value of "0.5" when not set.&lt;/p&gt;

&lt;p&gt;Example XML Sitemap from the specification:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;urlset&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.sitemaps.org/schemas/sitemap/0.9"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;http://www.example.com/&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2005-01-02&lt;span class="nt"&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;changefreq&amp;gt;&lt;/span&gt;monthly&lt;span class="nt"&gt;&amp;lt;/changefreq&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;priority&amp;gt;&lt;/span&gt;0.8&lt;span class="nt"&gt;&amp;lt;/priority&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;http://www.example.com/catalog?item=12&lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt;desc=vacation_hawaii&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;changefreq&amp;gt;&lt;/span&gt;weekly&lt;span class="nt"&gt;&amp;lt;/changefreq&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;http://www.example.com/catalog?item=73&lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt;desc=vacation_new_zealand&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2004-12-23&lt;span class="nt"&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;changefreq&amp;gt;&lt;/span&gt;weekly&lt;span class="nt"&gt;&amp;lt;/changefreq&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;http://www.example.com/catalog?item=74&lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt;desc=vacation_newfoundland&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2004-12-23T18:00:15+00:00&lt;span class="nt"&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;priority&amp;gt;&lt;/span&gt;0.3&lt;span class="nt"&gt;&amp;lt;/priority&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;http://www.example.com/catalog?item=83&lt;span class="ni"&gt;&amp;amp;amp;&lt;/span&gt;desc=vacation_usa&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2004-11-23&lt;span class="nt"&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/urlset&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Sitemap Index File
&lt;/h4&gt;

&lt;p&gt;According to the standard, a normal sitemap file is limited to 50,000 URLs and a maximum size of 50MB. While I don't necessarily believe that type of limitation is still enforced, it did give to the rise of the a sitemap index file.&lt;/p&gt;

&lt;p&gt;These files basically look a lot like a normal sitemap file but basically just point to other sitemaps. You have a number of &lt;code&gt;&amp;lt;sitemap&amp;gt;&lt;/code&gt; tags which contain a required &lt;code&gt;&amp;lt;loc&amp;gt;&lt;/code&gt; tag and an optional &lt;code&gt;&amp;lt;lastmod&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;Example Index sitemap from the specification:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;sitemapindex&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.sitemaps.org/schemas/sitemap/0.9"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;sitemap&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;http://www.example.com/sitemap1.xml.gz&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2004-10-01T18:23:17+00:00&lt;span class="nt"&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/sitemap&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;sitemap&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;loc&amp;gt;&lt;/span&gt;http://www.example.com/sitemap2.xml.gz&lt;span class="nt"&gt;&amp;lt;/loc&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;lastmod&amp;gt;&lt;/span&gt;2004-01-01&lt;span class="nt"&gt;&amp;lt;/lastmod&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/sitemap&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/sitemapindex&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  TXT Sitemaps
&lt;/h3&gt;

&lt;p&gt;This type of sitemap really removes a lot of the functionality that you would find in an XML sitemap like the last modified date or how frequently a page is updated.&lt;/p&gt;

&lt;p&gt;This format is just having each URL you want indexed on a new line with absolutely no other data.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://www.example.com/
http://www.example.com/catalog?item=12&amp;amp;amp;desc=vacation_hawaii
http://www.example.com/catalog?item=73&amp;amp;amp;desc=vacation_new_zealand
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  RSS Sitemaps
&lt;/h3&gt;

&lt;p&gt;While not as limited as TXT Sitemaps, RSS sitemaps have their own issues like only providing information on recent URLs.&lt;/p&gt;

&lt;p&gt;You would use the &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to define the URL you want indexed and and &lt;code&gt;&amp;lt;pubDate&amp;gt;&lt;/code&gt; to define when it was last modified.&lt;/p&gt;
&lt;h2&gt;
  
  
  The future of Sitemaps
&lt;/h2&gt;

&lt;p&gt;The main specification for sitemaps hasn't changed but there are some additional types of sitemaps being developed like &lt;a href="https://support.google.com/webmasters/answer/80471?hl=en&amp;amp;ref_topic=4581190" rel="noopener noreferrer"&gt;video sitemaps&lt;/a&gt;, &lt;a href="https://support.google.com/webmasters/answer/178636?hl=en&amp;amp;ref_topic=4581190" rel="noopener noreferrer"&gt;image sitemaps&lt;/a&gt; and special &lt;a href="https://support.google.com/webmasters/answer/9606710?hl=en&amp;amp;ref_topic=4581190" rel="noopener noreferrer"&gt;Google News sitemaps&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Google have also &lt;a href="https://webmasters.googleblog.com/2012/05/multilingual-and-multinational-site.html" rel="noopener noreferrer"&gt;announced support for multilingual sitemaps&lt;/a&gt; where one can define the language each URL.&lt;/p&gt;

&lt;p&gt;The support around these additional sitemap types isn't as widespread as the main XML sitemap though that may change in the future.&lt;/p&gt;
&lt;h2&gt;
  
  
  I wrote a thing...
&lt;/h2&gt;

&lt;p&gt;My last few articles have really come around because of my work building libraries and tools that are solving problems for me and this one is no exception.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/TurnerSoftware" rel="noopener noreferrer"&gt;
        TurnerSoftware
      &lt;/a&gt; / &lt;a href="https://github.com/TurnerSoftware/SitemapTools" rel="noopener noreferrer"&gt;
        SitemapTools
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A sitemap (sitemap.xml) querying and parsing library for .NET
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/TurnerSoftware/SitemapToolsimages/icon.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2FTurnerSoftware%2FSitemapToolsimages%2Ficon.png" alt="Icon"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Sitemap Tools&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;A sitemap (sitemap.xml) querying and parsing library for .NET&lt;/p&gt;

&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/1a06e70cdc4eb482d8b9b4562ecca549bb028b445dd5da2287ab0a0c455297ca/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f5475726e6572536f6674776172652f736974656d6170746f6f6c732f6275696c642e796d6c3f6272616e63683d6d61696e"&gt;&lt;img src="https://camo.githubusercontent.com/1a06e70cdc4eb482d8b9b4562ecca549bb028b445dd5da2287ab0a0c455297ca/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f5475726e6572536f6674776172652f736974656d6170746f6f6c732f6275696c642e796d6c3f6272616e63683d6d61696e" alt="Build"&gt;&lt;/a&gt;
&lt;a href="https://codecov.io/gh/TurnerSoftware/SitemapTools" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9001576201d00a40704d27db473e8436b4d4aa32b633d00b186efec450b56997/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f7475726e6572736f6674776172652f736974656d6170746f6f6c732f6d61737465722e737667" alt="Codecov"&gt;&lt;/a&gt;
&lt;a href="https://www.nuget.org/packages/TurnerSoftware.SitemapTools" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9e5587f2b8b0d72f0e0431b039a2bbe520132fd88d592ca2afb3a60e37c5216b/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f5475726e6572536f6674776172652e536974656d6170546f6f6c732e737667" alt="NuGet"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Key features&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Parses both XML sitemaps and &lt;a href="http://www.sitemaps.org/protocol.html#index" rel="nofollow noopener noreferrer"&gt;sitemap index files&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Handles GZ-compressed XML sitemaps&lt;/li&gt;
&lt;li&gt;Supports TXT sitemaps&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Licensing and Support&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Sitemap Tools is licensed under the MIT license. It is free to use in personal and commercial projects.&lt;/p&gt;
&lt;p&gt;There are &lt;a href="https://turnersoftware.com.au/support-plans" rel="nofollow noopener noreferrer"&gt;support plans&lt;/a&gt; available that cover all active &lt;a href="https://github.com/TurnerSoftware" rel="noopener noreferrer"&gt;Turner Software OSS projects&lt;/a&gt;.
Support plans provide private email support, expert usage advice for our projects, priority bug fixes and more.
These support plans help fund our OSS commitments to provide better software for everyone.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Notes&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Does not enforce sitemap standards &lt;a href="http://www.sitemaps.org/protocol.html" rel="nofollow noopener noreferrer"&gt;as described at sitemaps.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Does not validate the sitemaps&lt;/li&gt;
&lt;li&gt;Does not support RSS sitemaps&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Example&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-cs notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;using&lt;/span&gt; TurnerSoftware&lt;span class="pl-kos"&gt;.&lt;/span&gt;SitemapTools&lt;span class="pl-kos"&gt;;&lt;/span&gt;

&lt;span class="pl-smi"&gt;var&lt;/span&gt; &lt;span class="pl-s1"&gt;sitemapQuery&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;new&lt;/span&gt; SitemapQuery&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;
&lt;span class="pl-smi"&gt;var&lt;/span&gt; &lt;span class="pl-s1"&gt;sitemapEntries&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; sitemapQuery&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;GetAllSitemapsForDomainAsync&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-s"&gt;&lt;span class="pl-s"&gt;"&lt;/span&gt;example.org&lt;span class="pl-s"&gt;"&lt;/span&gt;&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TurnerSoftware/SitemapTools" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;p&gt;I had a need to actually parse sitemap files for a project I am working on and struggled to find any existing .NET library to do so. The latest version of my library builds upon my own &lt;a href="https://github.com/TurnerSoftware/RobotsExclusionTools" rel="noopener noreferrer"&gt;"Robots.txt" parsing library&lt;/a&gt; (for sitemap file discovery) and supports XML sitemaps (both normal and index files) as well as TXT sitemaps.&lt;/p&gt;

&lt;p&gt;This library and my "Robots.txt" parsing library actually build toward a third library which I will be writing an article about in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Information
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.sitemaps.org/" rel="noopener noreferrer"&gt;sitemaps.org&lt;/a&gt;: The official site for the format&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Sitemaps" rel="noopener noreferrer"&gt;"Sitemaps" on Wikipedia&lt;/a&gt;: Covers additional details about sitemaps and any extended functionality.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>sitemap</category>
      <category>csharp</category>
    </item>
    <item>
      <title>No Robots Allowed</title>
      <dc:creator>James Turner</dc:creator>
      <pubDate>Mon, 07 Jan 2019 01:54:36 +0000</pubDate>
      <link>https://dev.to/turnersoftware/no-robots-allowed-4mnl</link>
      <guid>https://dev.to/turnersoftware/no-robots-allowed-4mnl</guid>
      <description>&lt;p&gt;You probably have heard about web crawlers/spiders/bots etc, generally in the context of a search engine indexing a site to appear in its search results.&lt;/p&gt;

&lt;p&gt;This relationship between a search engine and a website operator is a delicate one. The website operator wants traffic to come to their site when people are searching for related phrases. The search engine wants to index the site so that it can get people to the most relevant content.&lt;/p&gt;

&lt;p&gt;Website operators however do not like it when the crawler is hitting the site so hard that it is taken down nor do they like it when pages they didn't want displayed are up in search results.&lt;/p&gt;

&lt;p&gt;A website operator has a powerful tool in their arsenal: They can just block the crawler from scanning the site at all. I mean, if their site was going down often because of being crawled too heavily or was crawling pages that they REALLY didn't want indexed, that is their only choice, right?&lt;/p&gt;

&lt;p&gt;But what if it wasn't...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3oEduY5pojz5nc1Vza/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3oEduY5pojz5nc1Vza/giphy.gif" alt="The Grinder TV Show Quote - Dean saying &amp;quot;But what if it wasn't?&amp;quot;"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Welcome to the ring, robots.txt
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="http://www.robotstxt.org/orig.html"&gt;original specification for the "robots.txt" file&lt;/a&gt; was formed in 1994 with the aim to facilitate some level of control between website operators and the web crawlers.&lt;/p&gt;

&lt;p&gt;It can look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# robots.txt for http://www.example.com/

User-agent: *
Disallow: /cyberworld/map/ # This is an infinite virtual URL space
Disallow: /tmp/ # these will soon disappear
Disallow: /foo.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It is a fairly simple format, define a user-agent (or &lt;code&gt;*&lt;/code&gt; for any) you want the following rules to apply to and add rules for disallowing particular paths. The file also supports comments after a &lt;code&gt;#&lt;/code&gt; symbol.&lt;/p&gt;

&lt;p&gt;This specification has been expanded on in later years like in the &lt;a href="http://www.robotstxt.org/norobots-rfc.txt"&gt;NoRobots RFC&lt;/a&gt; to include &lt;code&gt;Allow&lt;/code&gt; rules and multiple user-agents per block.&lt;/p&gt;

&lt;p&gt;While no official documentation on it, various web crawlers support wildcard paths, using the &lt;code&gt;$&lt;/code&gt; to match to the end of the path, support for &lt;code&gt;Crawl-delay&lt;/code&gt; and support for specifying sitemaps (via &lt;code&gt;Sitemap&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;For example, here is &lt;a href="https://dev.to/robots.txt"&gt;dev.to's robots file&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
#
# To ban all spiders from the entire site uncomment the next two lines:
# User-agent: *
# Disallow: /

Sitemap: https://thepracticaldev.s3.amazonaws.com/sitemaps/sitemap.xml.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With the lack of specific disallow rules, this indicates that web crawlers can crawl any page they find.&lt;/p&gt;

&lt;p&gt;So the next question: &lt;em&gt;Is having a "robots.txt" file a guarantee that all web crawlers will behave?&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  No (sorry)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/3o6fDxnx7hNX4PpCtW/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/3o6fDxnx7hNX4PpCtW/giphy.gif" alt="The Grinder TV Show Quote - Dean Sr. hitting the table"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While true, there is no guarantee a web crawler will actually obey the robots file, it still is in their best interest otherwise they might end up being blocked.&lt;/p&gt;

&lt;p&gt;The various big search engines will obey the rules because they &lt;em&gt;need&lt;/em&gt; to, again their job is to get relevant content. It is the random other web crawlers people are writing in applications where you need to watch out for.&lt;/p&gt;

&lt;p&gt;I am one of those people writing a web crawler and wanted to properly respect the websites I am crawling. While others have written libraries that can already do this, I wanted a better solution than what I found available.&lt;/p&gt;
&lt;h2&gt;
  
  
  My Library
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/TurnerSoftware"&gt;
        TurnerSoftware
      &lt;/a&gt; / &lt;a href="https://github.com/TurnerSoftware/RobotsExclusionTools"&gt;
        RobotsExclusionTools
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A "robots.txt" parsing and querying library in C#
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Robots Exclusion Tools&lt;/h1&gt;
&lt;p&gt;A "robots.txt" parsing and querying library in C#, closely following the &lt;a href="http://www.robotstxt.org/norobots-rfc.txt" rel="nofollow"&gt;NoRobots RFC&lt;/a&gt; and other details on &lt;a href="http://www.robotstxt.org/robotstxt.html" rel="nofollow"&gt;robotstxt.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ci.appveyor.com/project/Turnerj/robotsexclusiontools" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/cb929197e212de9045b80fcdf48e5060ddf0df66d901ef8985cd4ebddc8068b0/68747470733a2f2f696d672e736869656c64732e696f2f6170707665796f722f63692f5475726e65726a2f726f626f74736578636c7573696f6e746f6f6c732f6d61696e2e737667" alt="AppVeyor"&gt;&lt;/a&gt;
&lt;a href="https://codecov.io/gh/TurnerSoftware/RobotsExclusionTools" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/93755723324355379e375105f757b9b9d0010ce7cd48a29ea26262d4939a0cac/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f7475726e6572736f6674776172652f726f626f74736578636c7573696f6e746f6f6c732f6d61696e2e737667" alt="Codecov"&gt;&lt;/a&gt;
&lt;a href="https://www.nuget.org/packages/TurnerSoftware.RobotsExclusionTools" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/003625d4408e37d51c6b3c1c4143567bd7b449000a5e50b4a02b4ee1081ac873/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f5475726e6572536f6674776172652e526f626f74734578636c7573696f6e546f6f6c732e737667" alt="NuGet"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
Features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Load Robots by string, by URI (Async) or by streams (Async)&lt;/li&gt;
&lt;li&gt;Supports multiple user-agents and "*"&lt;/li&gt;
&lt;li&gt;Supports &lt;code&gt;Allow&lt;/code&gt; and &lt;code&gt;Disallow&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Supports &lt;code&gt;Crawl-delay&lt;/code&gt; entries&lt;/li&gt;
&lt;li&gt;Supports &lt;code&gt;Sitemap&lt;/code&gt; entries&lt;/li&gt;
&lt;li&gt;Supports wildcard paths (*) as well as must-end-with declarations ($)&lt;/li&gt;
&lt;li&gt;Built-in "robots.txt" tokenization system (allowing extension to support other custom fields)&lt;/li&gt;
&lt;li&gt;Built-in "robots.txt" validator (allowing to validate a tokenized file)&lt;/li&gt;
&lt;li&gt;Dedicated parser for the data from &lt;code&gt;&amp;lt;meta name="robots" /&amp;gt;&lt;/code&gt; tag and the &lt;code&gt;X-Robots-Tag&lt;/code&gt; header&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
NoRobots RFC Compatibility&lt;/h2&gt;
&lt;p&gt;This library attempts to stick closely to the rules defined in the RFC document, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Global/any user-agent when none is explicitly defined (Section 3.2.1 of RFC)&lt;/li&gt;
&lt;li&gt;Field names (eg. "User-agent") are character restricted (Section 3.3)&lt;/li&gt;
&lt;li&gt;Allow/disallow rules are performed by order-of-occurence (Section 3.2.2)&lt;/li&gt;
&lt;li&gt;Loading by URI applies default rules based on access to "robots.txt"…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TurnerSoftware/RobotsExclusionTools"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;With &lt;a href="https://github.com/sjdirect/nrobots"&gt;NRobots&lt;/a&gt; being "an unofficial and unsupported fork" for robots file parsing, I wrote my own from scratch targeting .NET Standard 2.0. It supports all of the previously described rules while allowing flexibility to be extended later.&lt;/p&gt;

&lt;p&gt;I wrote a custom tokenizer based on &lt;a href="https://jack-vanlightly.com/blog/2016/2/3/creating-a-simple-tokenizer-lexer-in-c"&gt;Jack Vanlightly's "Simple Tokenizer" article&lt;/a&gt; which is the core of my library. I wrote a validation layer on top of it to check the token patterns to make sure they adhere to the NoRobots RFC.&lt;/p&gt;

&lt;p&gt;I do probably have a bit of the Not-Invented-Here syndrome but I think this library is a genuine step forward for anyone needing to parse robots files in .NET.&lt;/p&gt;

&lt;p&gt;In a future post, I will go into how I use this library in two other libraries I have written.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Information
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://www.robotstxt.org/"&gt;robotstxt.org&lt;/a&gt;: The most official information for the file format can be found here&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Robots_exclusion_standard"&gt;"Robot Exclusion standard" on Wikipedia&lt;/a&gt;: Covers more of the non-standard directives like addition wildcards, crawl-delay and sitemaps.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>robots</category>
      <category>showdev</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Mongo what now?</title>
      <dc:creator>James Turner</dc:creator>
      <pubDate>Sat, 25 Aug 2018 15:17:42 +0000</pubDate>
      <link>https://dev.to/turnersoftware/mongo-what-now-34lg</link>
      <guid>https://dev.to/turnersoftware/mongo-what-now-34lg</guid>
      <description>&lt;p&gt;Have you ever had your main side project spin out a side project of its own? I don't know how often this happens with others but it is seems to be a reoccurring theme with my projects.&lt;/p&gt;

&lt;p&gt;Before I explain what this &lt;em&gt;side&lt;/em&gt; side project is, let me explain a little how I got here.&lt;/p&gt;

&lt;h1&gt;
  
  
  The original side project
&lt;/h1&gt;

&lt;p&gt;For the last number of years I've been working on building my own analytics tools to help manage and analyse brands. This itself deserves its own post at some point in the future when I eventually launch it but for now, the important part is to know a little about how development progressed with it.&lt;/p&gt;

&lt;p&gt;My original concept for this was to be a PHP/MySQL site based around a little-known framework called &lt;a href="https://www.silverstripe.org/"&gt;SilverStripe&lt;/a&gt;. Should have been easy to build as it is one of the main technology stacks I use to build websites for clients. The further I went into development though, the further it didn't feel like the right technology for the job.&lt;/p&gt;

&lt;p&gt;So what was PHP/MySQL changed to C#/MySQL - not exactly the most natural duo. Not long after that, for similar reasons as before, I changed again to C#/SQL.&lt;/p&gt;

&lt;p&gt;Because I must just enjoy rewriting code, I changed again - this time, now C#/MongoDB.&lt;/p&gt;

&lt;p&gt;These changes weren't just for kicks - it was the build up of little things along the way that made me want to do each of these changes. With the side project now using C# and MongoDB, this brings us to today.&lt;/p&gt;

&lt;h1&gt;
  
  
  C# + MongoDB = MongoDB C# Driver?
&lt;/h1&gt;

&lt;p&gt;My experience with C# and databases have primarily been around the good ol' &lt;a href="https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/linq/"&gt;Linq2SQL&lt;/a&gt; interface which for the most part has done what I've needed to without issues. That said when I was working with C#/SQL for that original side project, I came to love &lt;a href="https://docs.microsoft.com/en-us/ef/ef6/"&gt;Entity Framework&lt;/a&gt; - particularly the "Code First" experience and how you interact with collections.&lt;/p&gt;

&lt;p&gt;An example of an EF "Code First" approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyContext&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DbContext&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MyContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DbSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;People&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MyContext&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"James"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;People&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;           
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;MongoDB has a fully-featured C# driver and can probably do anything you want with the database. To me though, its downside is the API interface to it. In comparison to above, interfacing with the driver looks more like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ObjectId&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;MongoClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"mongodb://localhost:27017"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;collection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetCollection&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;collection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InsertOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"James"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now that is not an inherently bad API or anything, it just wasn't how I liked dealing with databases. What I really wanted was that Entity Framework interface but with a MongoDB backend.&lt;/p&gt;

&lt;p&gt;If you're an Entity Framework Core fan, there does seem to be people working on proper MongoDB providers for it however back when I started working on this, that was not available. In addition to that though, my &lt;em&gt;original&lt;/em&gt; side project is still stuck in .NET Framework, not Core.&lt;/p&gt;

&lt;p&gt;So what did I do? I came up with an original name for my MongoDB "Entity Framework"-like library and got to work.&lt;/p&gt;
&lt;h1&gt;
  
  
  MongoFramework
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Derivative is the new original&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It may not be the first piece of code I have published for the world to see but it is probably the one that is most important to me right now.&lt;/p&gt;

&lt;p&gt;What I wanted to build was a basic wrapper for the official MongoDB C# driver, making it feel like I was working with Entity Framework. Once I got the basics working, that is when I wanted to start being clever.&lt;/p&gt;

&lt;p&gt;That MongoDB C# driver is good but there are some things that were cumbersome out-of-the-box. For example, performing document (aka. entity) updates required you to either use this builder/filter system and specify the keys and values that you updated OR you replace the whole document. Not liking either option, I set to build change tracking and support diff-updates in MongoFramework so you the developer doesn't need to worry.&lt;/p&gt;

&lt;p&gt;Another example was how there is no "changeset" support in the official driver - the second you call &lt;code&gt;InsertOne&lt;/code&gt;, it has sent that through to the DB. After a few iterations of changeset support in MongoFramework, it can now do a single &lt;code&gt;BulkWrite&lt;/code&gt; call with all the changes in a particular &lt;code&gt;MongoDbSet&lt;/code&gt;.&lt;/p&gt;
&lt;h1&gt;
  
  
  Still a Work-In-Progress
&lt;/h1&gt;

&lt;p&gt;Like many side projects, MongoFramework is still a work-in-progress. There are many more features I want to add like GridFS, async reads and DB transaction support.&lt;/p&gt;

&lt;p&gt;Even with MongoDB providers now available in EF Core, I think MongoFramework still has its place - if not in your next project, it has a place in my heart. ❤️&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/TurnerSoftware"&gt;
        TurnerSoftware
      &lt;/a&gt; / &lt;a href="https://github.com/TurnerSoftware/MongoFramework"&gt;
        MongoFramework
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      An "Entity Framework"-like interface for MongoDB
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
MongoFramework&lt;/h1&gt;
&lt;p&gt;An "Entity Framework"-like interface for MongoDB&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ci.appveyor.com/project/Turnerj/mongoframework" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/b4250b7dac89c19a86717df931122e6696583ec0036fd4ddd5dfbbc97f71b145/68747470733a2f2f696d672e736869656c64732e696f2f6170707665796f722f63692f5475726e65726a2f6d6f6e676f6672616d65776f726b2f6d61696e2e737667" alt="AppVeyor"&gt;&lt;/a&gt;
&lt;a href="https://codecov.io/gh/TurnerSoftware/MongoFramework" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/34f21358192c5e05675361e5f7b68de9cff4cd14214c5ad53f5927eaf49036b7/68747470733a2f2f696d672e736869656c64732e696f2f636f6465636f762f632f6769746875622f7475726e6572736f6674776172652f6d6f6e676f6672616d65776f726b2f6d61696e2e737667" alt="Codecov"&gt;&lt;/a&gt;
&lt;a href="https://www.nuget.org/packages/MongoFramework/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/f024c3d6f0a7f641c6b5b9b302b25ab2c30629db65ab69e5fd8a4bd9ad077dc6/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f4d6f6e676f4672616d65776f726b2e737667" alt="NuGet"&gt;&lt;/a&gt;
&lt;a href="https://www.codacy.com/app/Turnerj/MongoFramework" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/dd256769492279cd8b52badceaba03c75cd3a823d9ecb4a1f96ecd04ead318b8/68747470733a2f2f6170692e636f646163792e636f6d2f70726f6a6563742f62616467652f47726164652f3632666133316339306266393466336438653230316239363834613761346361" alt="Codacy Badge"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
Overview&lt;/h2&gt;
&lt;p&gt;MongoFramework tries to bring some of the nice features from Entity Framework into the world of MongoDB.&lt;/p&gt;
&lt;p&gt;Some of the major features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Entity mapping for collections, IDs and properties through attributes&lt;/li&gt;
&lt;li&gt;Indexing through attributes (including text and geospatial)&lt;/li&gt;
&lt;li&gt;Entity change tracking&lt;/li&gt;
&lt;li&gt;Changeset support (allowing for queuing multiple DB updates to run at once)&lt;/li&gt;
&lt;li&gt;Diff-updates (only &lt;em&gt;changes&lt;/em&gt; to an entity to be written)&lt;/li&gt;
&lt;li&gt;Entity Buckets (clustering of small documents together, &lt;a href="https://www.mongodb.com/blog/post/building-with-patterns-the-bucket-pattern" rel="nofollow"&gt;improving index performance&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Runtime type discovery (serialize and deserialize without needing to specify every "known" type)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;MongoFramework is currently built on-top of the official MongoDB C# driver.&lt;/p&gt;
&lt;h2&gt;
Extensions&lt;/h2&gt;
&lt;p&gt;These extensions are official packages that enhance the functionality of MongoFramework, integrating it with other systems and tools.&lt;/p&gt;
&lt;h3&gt;
MongoFramework.Profiling.MiniProfiler&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://www.nuget.org/packages/MongoFramework.Profiling.MiniProfiler/" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/507ce5faef2bcfca6f8c3486f5ec78604ae6905f9b3bcb834f63b7d855c9ab9b/68747470733a2f2f696d672e736869656c64732e696f2f6e756765742f762f4d6f6e676f4672616d65776f726b2e50726f66696c696e672e4d696e6950726f66696c65722e737667" alt="NuGet"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Supports profiling database reads and writes, pushing the data into &lt;a href="https://github.com/MiniProfiler/dotnet/"&gt;MiniProfiler&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
Documentation&lt;/h2&gt;
&lt;h3&gt;
Core Entity Mapping&lt;/h3&gt;
&lt;p&gt;The core mapping of entities and their properties…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/TurnerSoftware/MongoFramework"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
      <category>showdev</category>
      <category>mongodb</category>
      <category>opensource</category>
      <category>dotnet</category>
    </item>
  </channel>
</rss>
