<?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: Gunnar Peipman</title>
    <description>The latest articles on DEV Community by Gunnar Peipman (@gpeipman).</description>
    <link>https://dev.to/gpeipman</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%2Fuser%2Fprofile_image%2F322318%2Fc7c44172-c8dc-4e59-b92a-6cf483935328.png</url>
      <title>DEV Community: Gunnar Peipman</title>
      <link>https://dev.to/gpeipman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gpeipman"/>
    <language>en</language>
    <item>
      <title>Using data from MSSQL linked server with EF Core</title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Thu, 27 Feb 2020 22:39:43 +0000</pubDate>
      <link>https://dev.to/gpeipman/using-data-from-mssql-linked-server-with-ef-core-1345</link>
      <guid>https://dev.to/gpeipman/using-data-from-mssql-linked-server-with-ef-core-1345</guid>
      <description>&lt;p&gt;My previous blog post &lt;a href="https://gunnarpeipman.com/mssql-mysql-linked-server/"&gt;Querying MySQL from SQL Server using linked server&lt;/a&gt; introduced how to link MySQL database to MSSQL and how to write queries that gather data from local and linked server. This blog post demonstrates how to get data mixed from those sources to Entity Framework Core.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using data from MSSQL linked servers
&lt;/h3&gt;

&lt;p&gt;Linked servers are not directly supported by Entity Framework Core at LINQ level. There are few options how to handle data in linked servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Custom SQL&lt;/strong&gt; – we can write custom SQL queries for DbContext. On positive side we have full control over all SQL we want to run. On negative side we have custom SQL and we have to go around DbSets and create all kind of nasty hacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom views&lt;/strong&gt; – we can hide querying from linked servers to views and have DbSets like we usually do. It needs more work on database side but we don’t have to hack DbContext.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post I don’t stop on custom SQL approach as this is same as running custom SQL queries through DbContext and it’s well covered in many other public sources. Also I don’t prefer this approach as too much database gets into application code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NB!&lt;/strong&gt; This blog post doesn’t cover data modification in linked server. It seems like simple topic but there are actually many complexities to get over. In my practice data from linked servers is only for displaying it as otherwise there are good chances that direct changes to data will conflict with business rules set by other systems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Sample customer tables
&lt;/h3&gt;

&lt;p&gt;All the following code is based on my blog post &lt;a href="https://gunnarpeipman.com/mssql-mysql-linked-server/"&gt;Querying MySQL from SQL Server using linked server&lt;/a&gt;. I had two customers databases – one on MSSQL and other on MySQL. MySQL database was linked to MSSQL. The following screenshot shows customers table in both databases. Some fields are matching and some fields are different.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lXgsKlfi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mysql-mssql-linked-server-customers.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lXgsKlfi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mysql-mssql-linked-server-customers.png" alt="Customers tables in MySQL and MSSQL" title="Customers tables in MySQL and MSSQL"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find guidance about setting up linked server from my blog post &lt;a href="https://gunnarpeipman.com/mssql-mysql-linked-server/"&gt;Querying MySQL from SQL Server using linked server&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating view with data from local and linked server
&lt;/h3&gt;

&lt;p&gt;Using SQL Server Management Studio (SSMS) it’s easy to write a query to mix data from both customer tables shown above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;crm_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;credit_rating&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;CreditRating&lt;/span&gt;
&lt;span class="n"&gt;FROM&lt;/span&gt; 
    &lt;span class="n"&gt;Customers&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
    &lt;span class="n"&gt;LEFT&lt;/span&gt; &lt;span class="n"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;MYSQLCRM&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="n"&gt;crm_c&lt;/span&gt; &lt;span class="n"&gt;ON&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crm_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssn&lt;/span&gt;
&lt;span class="n"&gt;ORDER&lt;/span&gt; &lt;span class="n"&gt;BY&lt;/span&gt;
    &lt;span class="n"&gt;crm_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;credit_rating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We take all fields from customers table on MSSQL and ask credit rating field from customers table on MySQL. It works like charm if link between databases is set up and configured appropriately.&lt;/p&gt;

&lt;p&gt;For EF Core we want to have DbSet so we don’t need to mess with custom SQL in our code. The closer we will stay to DbSet pattern the better it is. Happily EF Core doesn’t care if data is coming from table or view. Let’s take the SQL query shown above and save it as view to MSSQL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;VIEW&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;VW_CUSTOMERS&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;AS&lt;/span&gt;
    &lt;span class="n"&gt;SELECT&lt;/span&gt;        
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ssn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BillingAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;crm_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;credit_rating&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;CreditRating&lt;/span&gt;
    &lt;span class="n"&gt;FROM&lt;/span&gt;
        &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
        &lt;span class="n"&gt;LEFT&lt;/span&gt; &lt;span class="n"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;MYSQLCRM&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="n"&gt;crm_c&lt;/span&gt; &lt;span class="n"&gt;ON&lt;/span&gt;
            &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ssn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crm_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssn&lt;/span&gt;
&lt;span class="n"&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can open it in SSMS and see table with results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IrZUvr_m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mysql-mssql-linked-server-query-results.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IrZUvr_m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mysql-mssql-linked-server-query-results.png" alt="Mixed results from view" title="Mixed results from view"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s one thing left to do – we have to make sure that deletes to VW_CUSTOMERS view doesn’t affect linked database. For this we can use INSTEAD OF DELETE trigger. This trigger is run when rows are deleted from view and it replaces default delete behavior.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;VW_CUSTOMERS_DELETE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
   &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;].[&lt;/span&gt;&lt;span class="n"&gt;VW_CUSTOMERS&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
   &lt;span class="k"&gt;INSTEAD&lt;/span&gt; &lt;span class="k"&gt;OF&lt;/span&gt; &lt;span class="k"&gt;DELETE&lt;/span&gt;
&lt;span class="k"&gt;AS&lt;/span&gt; 
&lt;span class="k"&gt;BEGIN&lt;/span&gt;

    &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;NOCOUNT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;

    &lt;span class="k"&gt;DELETE&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Customers&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;deleted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;END&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What happens without this trigger?&lt;/strong&gt; Without this trigger MSSQL detects the situation it cannot handle – modification to multiple tables. The result is error and cancelled delete operation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Using linked server table from DbContext
&lt;/h3&gt;

&lt;p&gt;To demonstrate using data from linked server with EF Core I created simple customer class based on VW_CUSTOMERS view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;SalesCustomer&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;FirstName&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;LastName&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;Ssn&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;BillingAddress&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;int&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;CreditRating&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We also need DbContext class to communicate with database. It looks almost like any other DbContext but there’s one trick – CreditRating property is marked as read-only. Of course, to make DbContext use VW_CUSTOMERS view instead of looking for SalesCustomer table we have to call ToTable() method on SalesCustomer entity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;SalesDbContext&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;SalesDbContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbContextOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SalesDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&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="n"&gt;options&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;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnModelCreating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ModelBuilder&lt;/span&gt; &lt;span class="n"&gt;modelBuilder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;modelBuilder&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;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SalesCustomer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;ToTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"VW_CUSTOMERS"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;modelBuilder&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;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SalesCustomer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Property&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreditRating&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ValueGeneratedOnAddOrUpdate&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="nf"&gt;OnModelCreating&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modelBuilder&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;SalesCustomer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Customers&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We can create more properties based on fields in linked tables but we have to mark them all as read-only to avoid conflicting changes to databases owned by other systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;Although MSSQL linked servers are not something we are using everyday and EF Core doesn’t have out-of-box tooling for it, we can still work out views and triggers to make mixed data from local and linked server visible as a table for EF Core. We marked customer entity properties that come from linked server as read-only to avoid making changes to linked table. This is usual requirement if linked table belongs to some other system that has its own business rules. We left all complex stuff to database and on EF Core we are using views as tables.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/ef-core-mssql-linked-server/"&gt;Using data from MSSQL linked server with EF Core&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>efcore</category>
      <category>sqlserver</category>
      <category>mssql</category>
      <category>mysql</category>
    </item>
    <item>
      <title>Querying MySQL from SQL Server using linked server</title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Thu, 06 Feb 2020 04:40:56 +0000</pubDate>
      <link>https://dev.to/gpeipman/querying-mysql-from-sql-server-using-linked-server-2no0</link>
      <guid>https://dev.to/gpeipman/querying-mysql-from-sql-server-using-linked-server-2no0</guid>
      <description>&lt;p&gt;SQL Server has interesting feature called Linked Servers. It’s about linking other databases to SQL Server and using their data like it’s local. There are many powerful open-source systems written on PHP and they are mostly using MySQL as database. This blog post shows how to link MySQL database to SQL Server and how to use linked server in SQL queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is linked server?
&lt;/h3&gt;

&lt;p&gt;Linked server in MSSQL is some other database server connected to given one, making it possible to query and manipulate data in other databases. By example, we can link some MySQL database to MSSQL and use it almost like any other database on MSSQL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EeGA3HSM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-linked-server.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EeGA3HSM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-linked-server.png" alt="Architure of SQL Server linked servers" title="Architure of SQL Server linked servers"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Image is taken from MSSQL 2019 documentation page&lt;/em&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/linked-servers/linked-servers-database-engine?view=sql-server-ver15"&gt;&lt;em&gt;Linked Servers (Database Engine)&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although communication with linked servers goes through OLE DB providers, there is also OLE DB provider for ODBC and we can use it if our external database doesn’t have OLE DB provider.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NB!&lt;/strong&gt; Linked server is available for whole SQL Server instance. It means that all SQL Server databases can use linked server to retrieve data.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Linking MySQL to SQL Server
&lt;/h3&gt;

&lt;p&gt;Adding linked server and configuring connection setting is not always easy and straightforward.&lt;/p&gt;

&lt;p&gt;To get MySQL linked to SQL Server I needed to create ODBC DSN for MySQL (I named it as MySQLCrm). Before going to next steps, make sure that ODBC data source works.&lt;/p&gt;

&lt;p&gt;Follow these steps to link MySQL to SQL Server:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run SQL Server Management Studio (SSMS)&lt;/li&gt;
&lt;li&gt;Connect to your server&lt;/li&gt;
&lt;li&gt;Expand Server Objects node from tree at left&lt;/li&gt;
&lt;li&gt;Right-click on Linked Servers&lt;/li&gt;
&lt;li&gt;Select New Linked Server…&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should see the following dialog (or bit different but the idea remains the same).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vrCo1wEe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-mysql-linked-server.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vrCo1wEe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-mysql-linked-server.png" alt="MySQL linked server settings" title="MySQL linked server settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NB!&lt;/strong&gt; Pay extra attention to what you insert to this dialog. With this set of data I made link work. I tried different values and if something is one millimeter wrong then connection fails. It’s damn sensitive dialog.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Connection string to MySQL database should be like shown here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DRIVER=(MySQL ODBC 8.0 Unicode Driver); SERVER=localhost; PORT=3306; DATABASE=crmlinked; USER=crmuser; PASSWORD=crm_user_password; OPTION=3;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also pay attention to OPTION=3 – without this I got only errors back when connecting to linked server.&lt;/p&gt;

&lt;p&gt;Try to save by clicking OK and see if you can browse to linked server. If you get errors then right-click on server and select properties. Keeping the dialog open, move to Server Options page. Set RPC and RPC Out settings to True.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UdI5HzWP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-mysql-linked-server-options.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UdI5HzWP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-mysql-linked-server-options.png" alt="MySQL linked server options" title="MySQL linked server options"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m still not very sure what are these options doing but some of those who had issues with link to MySQL made it work after setting RPC-s to true.&lt;/p&gt;

&lt;p&gt;To make querying actually work, we need one more little change that affects &lt;strong&gt;whole&lt;/strong&gt; OLE DB provider and therefore all connections using it. Open Providers node under Linked Servers, right-click on MSDASQL (this is OLE DB provider for ODBC data sources) and select properties.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-mysql-linked-server-msdasql-options.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Bt9ti9MA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-mysql-linked-server-msdasql-options_thumb.png" alt="Configuring OLE DB data-source for ODBC" title="Configuring OLE DB data-source for ODBC"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check the box before Level Zero only and click OK to save changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Querying data from linked server
&lt;/h3&gt;

&lt;p&gt;Querying linked databases is actually easy. Here’s the customers table from crmlinked database in MySQL. This database is linked to my SQL Server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5GEmGI2s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mysql-linked-server-customers-table.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5GEmGI2s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mysql-linked-server-customers-table.png" alt="Customers table in MySQL database" title="Customers table in MySQL database"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Syntax for querying linked server is a little bit different from what we usually write on SQL Server. We need to use four-part names: server.database.schema.table. As there’s no schemas on MySQL and connection string specifies database name, then we can leave these out like shown here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;MYSQLCRM&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="n"&gt;customers&lt;/span&gt;

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



&lt;p&gt;Running this query from SSMS gives the following output. It’s the same data that is in MySQL customers table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6IFruWbn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mysql-linked-server-customers-list.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6IFruWbn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mysql-linked-server-customers-list.png" alt="Query results from linked MySQL server" title="Query results from linked MySQL server"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, we can also write more complex queries. Everything that ODBC can handle is okay.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mixing data from local and linked server
&lt;/h3&gt;

&lt;p&gt;Tables from linked server are not totally isolated from local database tables and views. We can also mix data from local and linked server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jIAe9YZa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-linked-server-customers-table.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jIAe9YZa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-linked-server-customers-table.png" alt="Customers table on SQL Server" title="Customers table on SQL Server"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To demonstrate mixed query over local and linked tables let’s write simple query to get all customers from local table and their credit ratings from linked table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;crm_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;credit_rating&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;CreditRating&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; 
    &lt;span class="n"&gt;Customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
    &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;MYSQLCRM&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="n"&gt;crm_c&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;
        &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crm_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssn&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="n"&gt;crm_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;credit_rating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Running this query gives us the following output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eRoq_7bZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-mysql-linked-server-mixed-query-results.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eRoq_7bZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/02/mssql-mysql-linked-server-mixed-query-results.png" alt="Results of mixed query over local and linked server data" title="Results of mixed query over local and linked server data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As Mark is not present in MySQL database (suppose he is new customer in e-shop and sales department doesn’t have him yet in their CRM system) then he doesn’t have credit rating available. Credit ratings for John and Mary are coming from MySQL in current case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using OPENQUERY() to execute query in linked server
&lt;/h3&gt;

&lt;p&gt;The examples above make all data processing on SQL Server. It can be very unoptimal if there’s a lot of data in linked server’s tables. We may want – or usually want – to process some data in linked server before SQL Server starts local processing. For this we have OPENQUERY().&lt;/p&gt;

&lt;p&gt;Here’s the example of using OPENQUERY() function in mixed query. We have to specify linked server name and SQL query to run in linked server when calling OPENQUERY(). The query in red is executed in MySQL server and results are read to SQL Server for further processing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;crm_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;credit_rating&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;CreditRating&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; 
    &lt;span class="n"&gt;Customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
    &lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;OPENQUERY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MYSQLCRM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'
            SELECT 
                c.credit_rating
            FROM 
                customers p
                left join loyalty_points lp on
                    c.customer_id = lp.customer_id
            WHERE
                lp.points &amp;gt; 1000
        '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;crm_c&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;
        &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;crm_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ssn&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt;
    &lt;span class="n"&gt;crm_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;credit_rating&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FirstName&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;OPENQUERY() is great way to optimize and speed up mixed queries by running more complex queries over linked server data in linked server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;Linked servers is powerful feature of SQL Server making it easy for us to use data from external servers. There are two ways to write queries using data from linked servers – direct queries that do all processing on SQL Server and OPENQUERY() that lets us do some processing in remote server. Linked server is integration and therefore using it needs extra care. Planning and performance measuring are must-be activities when planning to use linked server.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/mssql-mysql-linked-server/"&gt;Querying MySQL from SQL Server using linked server&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mssql</category>
      <category>sqlserver</category>
      <category>mysql</category>
    </item>
    <item>
      <title>Turn EF Core DbContext to real unit of work that hosts repository instances</title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Mon, 03 Feb 2020 05:00:30 +0000</pubDate>
      <link>https://dev.to/gpeipman/turn-ef-core-dbcontext-to-real-unit-of-work-hosting-repository-instances-4cjg</link>
      <guid>https://dev.to/gpeipman/turn-ef-core-dbcontext-to-real-unit-of-work-hosting-repository-instances-4cjg</guid>
      <description>&lt;p&gt;My last bold statement was that we &lt;a href="https://gunnarpeipman.com/ef-core-repository-unit-of-work/"&gt;don’t need custom unit of work and repository classes with Entity Framework Core&lt;/a&gt;. One issue remained unsolved and it was querying part of repositories (yeah, those custom querying methods). After some experiments to get querying interface of repositories to DbContext I worked out something that I kind of like. I managed to make DbContext to work like classic unit of work that hosts repository instances. Here’s my experiment and the solution I worked out.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NB!&lt;/strong&gt; My goal here is to stay strictly with Entity Framework Core or some other mapper that supports LINQ queries. Replacing ORM is usually extreme case and during my 20+ years in software development I have seen only few cases when it was done. If you prefer NHibernate over EF Core then check out my post &lt;a href="https://gunnarpeipman.com/aspnet-core-nhibernate/"&gt;NHibernate on ASP.NET Core&lt;/a&gt; to see how imitate DbContext with NHibernate.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  “Repositorifying” DbContext
&lt;/h3&gt;

&lt;p&gt;Making DbContext similar to typical unit of work that contains repository instances is not easy. One weakness of repository pattern is that it can be easily abused as a querying methods store. I have seen repositories that are thousands of lines long thanks to querying methods. But hey, they are still easy to use in unit tests as they implement interfaces we can mock. DbContext is different beast. It can easily be injected to repositories and other classes but it’s not easy to inject querying part of repositories there.&lt;/p&gt;

&lt;p&gt;It would be nice to have something like shown here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;SalesDbContext&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;SalesDbContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbContextOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SalesDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&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="n"&gt;options&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;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Customers&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="c1"&gt;// Other DbSets&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;DoSomething&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;debtors&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="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ListDebtors&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDays&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;14&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;newCustomers&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="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ListNewRegistrations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&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;There are some obstacles on our way that are hard – if not impossible – to remove with simple tricks. Let’s briefly explore our options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DbSet is not for inheriting&lt;/strong&gt; – internals of instantiating DbSet are complex and luckily these complexities are hidden from us. Still these complexities are good enough reason for product group to not support extending of DbSet. Let’s agree with them and abandon the idea of using digital violence to get our will.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extension methods for DbSet&lt;/strong&gt; – we can build extension methods for given DbSet and host these methods in some static class in same namespace. It’s the easy way out but it comes with price – we can’t easily mock extension methods. We have to use type isolation and there is limited number of tools with this feature. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Querying methods in DbContext&lt;/strong&gt; – another easy way out but again there’s price to pay. Piling all querying methods to DbContext will bloat this class. It can get even worse if querying methods call each other. I wouldn’t go with this approach.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Injecting querying interfaces to DbContext class&lt;/strong&gt; – creates chicken-egg situation because DbContext needs querying classes to be injected and query classes need DbContext to be injected. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of approaches mentioned above only to are worth to experiment: extension methods for DBSet and querying classes that are injected to DbContext.&lt;/p&gt;

&lt;h3&gt;
  
  
  Extension methods for DbSet
&lt;/h3&gt;

&lt;p&gt;Extension methods are easiest ones to implement. They work until we stay with pure LINQ. In case of multiple database types and custom SQL queries this approach doesn’t work. Let’s write extension methods for DbSet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SalesDbContextCustomersExtensions&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;static&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ListDebtors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Balance&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;0&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;static&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ListNewRegistrations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;fromDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Created&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;fromDate&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;We have separate class with extension methods and we don’t bloat DbContext with queries. Still these extension methods are easy to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;void&lt;/span&gt; &lt;span class="nf"&gt;DoSomething&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;debtors&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="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ListDebtors&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDays&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;14&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;newCustomers&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="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ListNewRegistrations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&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;Those who want to isolate direct access to DbSets can’t use extension methods as marking DbSets protected restricts external access to them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to go with extension methods then here few more ideas what you can do in data layer: &lt;a href="https://gunnarpeipman.com/ef-core-query-specification/"&gt;Implementing Query Specification pattern in Entity Framework Core&lt;/a&gt; and &lt;a href="https://gunnarpeipman.com/ef-core-readable-fluent-queries/"&gt;Readable fluent queries with Entity Framework Core&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Injecting querying classes to DbContext
&lt;/h3&gt;

&lt;p&gt;If extension methods are not an option then we have to introduce querying classes that are injected to DbContext. Like mentioned above there’s a chicken and egg problem, but it’s not hard to solve. Let’s start with intrface and class for querying customers. We use interface because we probably want to use fake version of querying class when writing unit tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;interface&lt;/span&gt; &lt;span class="nc"&gt;ICustomerQueries&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetDbContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SalesDbContext&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ListDebtors&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ListNewRegistrations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date&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;CustomerQueries&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ICustomerQueries&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;SalesDbContext&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="k"&gt;virtual&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ListDebtors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Balance&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;0&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;virtual&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ListNewRegistrations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Created&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;date&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;void&lt;/span&gt; &lt;span class="nf"&gt;SetDbContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SalesDbContext&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;dbContext&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;Notice two things. SetDbContext() method – we have to assign DbContext when instance of ICustomerQueries is injected to DbContext. Otherwise there’s no connection between these two classes. Customer queries class has virtual methods. We can keep there pure LINQ methods and extend this class if we need let’s say SqlServerCustomerQueries class that overrides some methods to use SQL Server specific SQL commands.&lt;/p&gt;

&lt;p&gt;Here’s our DbContext after changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;SalesDbContext&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;SalesDbContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbContextOptions&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SalesDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                          &lt;span class="n"&gt;ICustomerQueries&lt;/span&gt; &lt;span class="n"&gt;customerQueries&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="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;CustomerQueries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;customerQueries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;customerQueries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetDbContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&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;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Customers&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="c1"&gt;// Other DbSets&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ICustomerQueries&lt;/span&gt; &lt;span class="n"&gt;CustomerQueries&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;private&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="c1"&gt;// Other queries&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In assigns itself to DbContext property of all injected querying classes. It’s easy but still something we have to remember to do when introducing new quering class for some DbSet.  Our previous dummy demo method looks now just a little bit different than before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;void&lt;/span&gt; &lt;span class="nf"&gt;DoSomething&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;debtors&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="n"&gt;CustomerQueries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ListDebtors&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDays&lt;/span&gt;&lt;span class="p"&gt;(-&lt;/span&gt;&lt;span class="m"&gt;14&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;newCustomers&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="n"&gt;CustomerQueries&lt;/span&gt;
                                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ListNewRegistrations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&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;For ASP.NET Core we have to add querying interface and class to dependency injection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddScoped&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICustomerQueries&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CustomerQueries&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

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



&lt;p&gt;Now we have a bit inconveniet DbContext that actually acts as an unit of work with local instances of repositories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Supporting database-specific commands
&lt;/h3&gt;

&lt;p&gt;As I previously mentioned we need sometimes database-specific commands. These commands can contain SQL that is understood only by one supported database. It’s clear we cannot run these commands against other types of databases.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Database-specific commands&lt;/strong&gt; are usual in applications that have reporting. Some ideas about how to implement common reporting with Entity Framework Core are given in my posts &lt;a href="https://gunnarpeipman.com/ef-core-execute-raw-sql/"&gt;Execute raw SQL commands in Entity Framework Core&lt;/a&gt; and &lt;a href="https://gunnarpeipman.com/aspnet-core-reporting-dataset-datatable/"&gt;DataSet and DataTable based ad-hoc reporting with ASP.NET Core&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is customer queries class we created above with one little change – DbContext is now available for inheriting classes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;CustomerQueries&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ICustomerQueries&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="n"&gt;SalesDbContext&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;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&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="k"&gt;virtual&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ListDebtors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Balance&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;0&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;virtual&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ListNewRegistrations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Created&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;date&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;void&lt;/span&gt; &lt;span class="nf"&gt;SetDbContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SalesDbContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;context&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;We marked querying methods as virtual and there was a reason to do so. Suppose ListDeptors() method is tricky and we need custom SQL for this when using MySQL.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;MySqlCustomerQueries&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CustomerQueries&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;override&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ListDebtors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MySQL specific SQL"&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;Same way we can also build specialized classes for SQL Server, Oracle, Postgre etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mocking querying classes in unit tests
&lt;/h3&gt;

&lt;p&gt;Querying classes we inject using their interfaces can be easily mocked in unit tests. Suppose we have CustomersService class with kind method to zero all debts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;CustomersService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ICustomersService&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;SalesDbContext&lt;/span&gt; &lt;span class="n"&gt;_salesDbContext&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;CustomersService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SalesDbContext&lt;/span&gt; &lt;span class="n"&gt;salesDbContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_salesDbContext&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;salesDbContext&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;void&lt;/span&gt; &lt;span class="nf"&gt;ClearDebts&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;debtors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_salesDbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CustomerQueries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ListDebtors&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;foreach&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;debtor&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;debtors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;debtor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Balance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;_salesDbContext&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using xUnit and Moq we can write the following test class to check if ClearDebts() sets all debts to zero.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;CustomerServiceTests&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICustomerQueries&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_customerQueriesMock&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;SalesDbContext&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;CustomerServiceTests&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_customerQueriesMock&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;Mock&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ICustomerQueries&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;options&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;DbContextOptionsBuilder&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SalesDbContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
                                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseInMemoryDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Options&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;new&lt;/span&gt; &lt;span class="nf"&gt;SalesDbContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_customerQueriesMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Object&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="n"&gt;Fact&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;ClearDebths_should_set_debtors_balance_to_zero&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;service&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;CustomersService&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;debtors&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="n"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;Customer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&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;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Balance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;20&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;Customer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&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;"Jane Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Balance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;_customerQueriesMock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ListDebtors&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Returns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debtors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsQueryable&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ClearDebts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debtors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Balance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;debtors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Balance&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;In real world we should have more tests but for illustration this one is okay. We can create class-level mocks also for other querying classes we need in DbContext. Those who don’t like mocks can use stub classes. It’s easy because querying classes have interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  How this approach works in practice?
&lt;/h3&gt;

&lt;p&gt;After getting to point where I was kind of okay with the solution, of course I was eager to try it out on some real project. Luckily I have one application under development that is perfect candidate for test-drive. It’s very process-centric having complex service classes that make heavy querying.&lt;/p&gt;

&lt;p&gt;Some characteristics to understand the system I’m using as lab rat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;two web application projects&lt;/li&gt;
&lt;li&gt;one project with background services&lt;/li&gt;
&lt;li&gt;shared projects for business logic and data layers&lt;/li&gt;
&lt;li&gt;database with ~40 tables (3 tables with many fields)&lt;/li&gt;
&lt;li&gt;ten service classes (few of them really big)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m using DbContext in applications through interface meaning that application see only what they need to see. I can restrict direct access to DbSet properties and introduce generic Get, Save and Delete methods if needed. Querying classes form nice layer that handles all queries and I can build additional layers behind these if demands on querying happen to grow.&lt;/p&gt;

&lt;p&gt;Leaving out the inconvenience on assigning DbContext to querying classes after they are injected to DbContext, I’m okay with my solution. Also it makes unit tests smaller and cleaner. I can easily mock querying objects and I don’t have to write code to insert correct test data to DbContext to make queries return the results that tests expect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;Getting repositories querying interface to DbContext the way that we may have multiple implementations wasn’t very easy task. To be flexible we had to create querying classes, inject these to DbContext and assign DbContext to their data context property. Even if can safely extend DbSet the problem will be the same – DbSet needs to know DbContext. We created flexible query classes. There’s base class that hosts all pure LINQ-based querying methods. If we don’t have anything database specific then we can use these base classes. If we have something database-specific then we can extend database-specific querying classes from base class and override methods that need custom SQL. We have now DbContext that works as unit of wort containing set of repository instances.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/ef-core-dbcontext-repository/"&gt;Implementing repository querying interface in EF Core DbContext&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>efcore</category>
      <category>visualstudio</category>
      <category>arhictecture</category>
    </item>
    <item>
      <title>Creating subdomains to Azure DNS from ASP.NET Core</title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Sun, 02 Feb 2020 20:01:52 +0000</pubDate>
      <link>https://dev.to/gpeipman/creating-subdomains-to-azure-dns-from-asp-net-core-5gm0</link>
      <guid>https://dev.to/gpeipman/creating-subdomains-to-azure-dns-from-asp-net-core-5gm0</guid>
      <description>&lt;p&gt;Multitenant wep applications detect current tenant usually by URL checking name of first level folder or subdomain. Usually tenants are defined by subdomain as it is easier to distribute them over data center, cloud services or hosting accounts. This blog post demonstrates how to build Azure DNS service client to create DNS records for multitenant application subdomains.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;So, not everybody has domain these days?&lt;/strong&gt; Yes, right you are. Private users and starting companies doesn’t often have domain. Modern era businesses sometimes even doesn’t bother having domain. They are doing sales and marketing in social media channels and this is where their customers are.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Those who don’t have domain and those who don’t know how to make DNS entries usually need some solution by us. Our service is hosted on some domain anyway and I’m confident we know how to create subdomains and DNS records. For users who need subdomain we have to set subdomain up also in our DNS server.&lt;/p&gt;

&lt;p&gt;Creating DNS record for Azure DNS service is something we luckily can automate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure DNS terms
&lt;/h3&gt;

&lt;p&gt;Before we get started let’s define DNS and Azure DNS service related terms.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DNS zone&lt;/strong&gt; – any distinct, contiguous portion of the domain name space in the DNS for which administrative responsibility has been delegated to a single manager. By example, gunnarpeipman.com is one DNS zone in my Azure DNS service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS record set&lt;/strong&gt; – collection of records in a zone that have the same name and are the same type. Record set can contain one or more mappings between host name and IP-addresses. blah.gunnarpeipman.com can be record set because it points to two different IP-addresses. Also jekyll.gunnarpeipman.com is record set although it contains one CNAME record.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS record&lt;/strong&gt; – mapping in record set. Every record matches host name with some IP address or other host name. There are also other types of records available but we don’t need these right here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With first DNS wisdoms inside our heads it’s time to jump in make hands dirty.&lt;/p&gt;

&lt;h3&gt;
  
  
  Azure DNS service
&lt;/h3&gt;

&lt;p&gt;Azure DNS service is for managing DNS zones and records you own. It’s simple, reliable, stable and performant. The service relies on Microsoft global network of DNS servers.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Microsoft is not domain registrar!&lt;/strong&gt; Microsoft doesn’t provide domain registrar services on Azure cloud. If you need new domain then it must be registered with some ISP who provides domain registration service. Your domain will be there where you registered it. To use Azure DNS service you need to configure your domain DNS servers at the site where you registered the domain.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What we will build here is shown on the screenshot below. Suppose we are building administration application for our multitenant SaaS application. When new tenant is added by admin (or why not from self-service portal by new customer) we want to create subdomain for this new tenant automatically.&lt;/p&gt;

&lt;p&gt;Screenshot below demonstrates how it looks. We have Azure app service with host name demomultitenant.azurewebsites.net with multiple DNS records pointing to it. If our multitenant service has host name bigsaasapp.com then we have subdomains like contoso.bigsaasapp.com, northwind.bigsaasapp.com etc.&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%2Fstatic.gunnarpeipman.com%2Fwp-content%2Fuploads%2F2020%2F01%2Fazure-dns-zone-records.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%2Fstatic.gunnarpeipman.com%2Fwp-content%2Fuploads%2F2020%2F01%2Fazure-dns-zone-records.png" title="azure-dns-zone-records" alt="azure-dns-zone-records"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We don’t need subdomain and CNAME record for customers who want to use their own subdomain (like bigsaas.bigcorp.com). We don’t control those domains and this is up to customer to set everything up on their side.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;You need Azure AD service principal.&lt;/strong&gt; To make this example work you need service principal for your web application. I don’t cover these steps here, so please jump to documentation page &lt;a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-create-service-principal-portal" rel="noopener noreferrer"&gt;How to: Use the portal to create an Azure AD application and service principal that can access resources&lt;/a&gt; if you don’t know how to create service principal on Azure AD. After creating service principal you have to make sure it has permissions to Azure DNS service.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Configuration for Azure DNS
&lt;/h3&gt;

&lt;p&gt;Before everything else we need Azure DNS service configuration. For this I created simple class.&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;DnsServerSettings&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;TenantId&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;ClientId&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;ClientSecret&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;SubscriptionId&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;ResourceGroupName&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;ZoneName&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the example of configuration in appSettings.json file. ID-s here are random, of course.&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="s"&gt;"DnsServerSettings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s"&gt;"TenantId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"3f2ff00d-9465-4dca-8c2a-736ac4e94a55"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"ClientId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"2e60a18a-3760-4f09-9663-4dbe436b2bfc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"ClientSecret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"a7s9d69h3denkscxAS7sjndkASJDH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"SubscriptionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"77a81331-9341-4e4f-9c65-e6f0051b52de"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"ResourceGroupName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"WebAppHosting"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"ZoneName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"demomultitenant.azurewebsites.net"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make things more convenient I inject DNS service settings to framework-level dependency injection.&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dnsServerSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetSection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DnsServerSettings"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DnsServerSettings&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dnsServerSettings&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Single instance of settings is okay as we have one DNS service for all multitenant application subdomains.&lt;/p&gt;

&lt;h3&gt;
  
  
  DSN service client interface
&lt;/h3&gt;

&lt;p&gt;To have flexibility on coming up with better or more advanced implementation of DNS service client, I created simple interface.&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;interface&lt;/span&gt; &lt;span class="nc"&gt;IDnsServiceClient&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;HasCName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CreateCName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cName&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;This interface is to carry out two operations on Azure DNS service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;check if given CNAME record is already registered in DNS zone&lt;/li&gt;
&lt;li&gt;create new CNAME record to given DNS zone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s possible to extend this interface later if there’s need but for this post the interface shown is good enough.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NB!&lt;/strong&gt; Before going further you need the following NuGet package:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft.Azure.Management.Dns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  DNS service client
&lt;/h3&gt;

&lt;p&gt;As we already get Azure DNS configuration from framework-level dependency injection and we have thin interface to communicate with service, it’s time to build client class we can use in tenant admin application.&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;DnsServiceClient&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDnsServiceClient&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;DnsServerSettings&lt;/span&gt; &lt;span class="n"&gt;_serverSettings&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;DnsServiceClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DnsServerSettings&lt;/span&gt; &lt;span class="n"&gt;serverSettings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_serverSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serverSettings&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CreateCName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;HasCName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ServiceClientCredentials&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetCredentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;ApplicationTokenProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;LoginSilentAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TenantId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ClientSecret&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;The code above is skeleton for Azure DNS client. It does almost nothing useful yet. Still it gets Azure DNS configuration and is able to generate credentials needed to communicate with service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding CNAME record to DNS
&lt;/h3&gt;

&lt;p&gt;First let’s implement CreateCName() method of Azure DNS service client. We habe to create new record set with CNAME record and save it using DnsManagementClient() class.&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CreateCName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cName&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;credentials&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetCredentials&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;dnsClient&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;DnsManagementClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;dnsClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubscriptionId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubscriptionId&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;recordSet&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;RecordSet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;CnameRecord&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;CnameRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"demoservice.azurewebapps.net"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;TTL&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;14400&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;dnsClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RecordSets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateOrUpdateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResourceGroupName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZoneName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;cName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;RecordType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
            &lt;span class="n"&gt;recordSet&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;It was easy, although call to CreateOrUpdateAsync() may first seem a little strange.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quering DNS service for CNAME
&lt;/h3&gt;

&lt;p&gt;Querying for existing DNS record is not so easy and straightforward as adding new records. We may have tens of thousands DNS records and it would put too much load to service when it returns all records for zone with one shot. To keep load under control, it uses paging. We have to read next portion of records from service until NextPageLink property of paged result is empty or null.&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;DnsServiceClient&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDnsServiceClient&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;DnsServerSettings&lt;/span&gt; &lt;span class="n"&gt;_serverSettings&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;DnsServiceClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DnsServerSettings&lt;/span&gt; &lt;span class="n"&gt;serverSettings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_serverSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serverSettings&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CreateCName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cName&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;credentials&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetCredentials&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;dnsClient&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;DnsManagementClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dnsClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubscriptionId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubscriptionId&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;recordSet&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;RecordSet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
                &lt;span class="n"&gt;CnameRecord&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;CnameRecord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"demoservice.azurewebapps.net"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;TTL&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;14400&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;dnsClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RecordSets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateOrUpdateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResourceGroupName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZoneName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;cName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;RecordType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                &lt;span class="n"&gt;recordSet&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;HasCName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cName&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;credentials&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetCredentials&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;dnsClient&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;DnsManagementClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;dnsClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubscriptionId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SubscriptionId&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;page&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;dnsClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RecordSets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ListAllByDnsZoneAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResourceGroupName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                                &lt;span class="n"&gt;_serverSettings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ZoneName&lt;/span&gt;
                             &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;foreach&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;x&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;page&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;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Microsoft.Network/dnszones/CNAME"&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
                        &lt;span class="n"&gt;x&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="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;cName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                    &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;true&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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NextPageLink&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="n"&gt;page&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;dnsClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RecordSets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ListAllByDnsZoneNextAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NextPageLink&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&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;Now we are done with Azure DNS service client and we can inject it to dependency injection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using DNS service client in ASP.NET Core application
&lt;/h3&gt;

&lt;p&gt;Although you probably have some application service class with tenant adding workflow, I will keep things simple and illustrative. Here’s the Create() method of tenants controller in admin application that uses DNS service client through dependency injection to create new CName record with new tenant.&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TenantEditModel&lt;/span&gt; &lt;span class="n"&gt;model&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;ModelState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsValid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_dnsClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HasCName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CName&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ModelState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddModelError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"CName already exists"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;RedirectToAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;nameof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Index&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;If CNAME is already taken then admin is taken back to Create view and error for CNAME record is shown.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;When building administrative interface for multitenant applications we usually need to communicate with bunch of external services. One of these is usually some DNS server or service where we may want to add new CNAME record with subdomain for new tenant. We used Azure DNS service for our multitenant application and it wasn’t complex to check for existing CNAME records and add new ones.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/aspnet-core-azure-dns/" rel="noopener noreferrer"&gt;Creating subdomains to Azure DNS from ASP.NET Core&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com" rel="noopener noreferrer"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aspnetcore</category>
      <category>azure</category>
      <category>dotnetcore</category>
    </item>
    <item>
      <title>Building experimental hybrid Blazor WebAssembly application</title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Sat, 01 Feb 2020 06:42:48 +0000</pubDate>
      <link>https://dev.to/gpeipman/building-experimental-hybrid-blazor-webassembly-application-2o3m</link>
      <guid>https://dev.to/gpeipman/building-experimental-hybrid-blazor-webassembly-application-2o3m</guid>
      <description>&lt;p&gt;After getting done with &lt;a href="https://gunnarpeipman.com/blazor-on-desktop-webwindow-experiment/" rel="noopener noreferrer"&gt;Blazor desktop applications&lt;/a&gt; I tried to build kind of hybrid Blazor WebAssembly application that can run on desktop and in web equally. Although I failed to make it as one single application, I still got it work with a little different architecture. And what’s best – Blazor desktop applications run already now as self-contained executables. Here’s my experiment.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning!&lt;/strong&gt; &lt;a href="https://github.com/SteveSandersonMS/WebWindow" rel="noopener noreferrer"&gt;WebWindow&lt;/a&gt; is experimental technology by &lt;a href="https://blog.stevensanderson.com/" rel="noopener noreferrer"&gt;Steve Sanderson&lt;/a&gt; to host Blazor WebAssembly applications in desktop windows using local browser’s web view. Right now it’s not even sure if WebWindow makes its way to official codebase of Blazor. Play with it, but don’t go live with it yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Architecture of shared Blazor WebAssembly application
&lt;/h3&gt;

&lt;p&gt;What I tried to do was architecture like shown on the following screenshot.&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%2Fstatic.gunnarpeipman.com%2Fwp-content%2Fuploads%2F2020%2F01%2Fblazor-webassembly-app-as-shared-component.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%2Fstatic.gunnarpeipman.com%2Fwp-content%2Fuploads%2F2020%2F01%2Fblazor-webassembly-app-as-shared-component.png" title="Diagram: Blazor WebAssembly project shared between desktop and web application" alt="Diagram: Blazor WebAssembly project shared between desktop and web application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Blazor WebAssembly application is implemented as a shared library that contains user interface artifacts like pages, layouts and code-behind files. Also it is possible to host shared interfaces in this project.&lt;/p&gt;

&lt;p&gt;WebWindow and ASP.NET Core based web server projects are just for building application for target environment and configuring dependency injection to use implementations for given environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shared application solution
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://static.gunnarpeipman.com/wp-content/uploads/2020/01/blazor-webassembly-app-shared-visualstudio.png" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.gunnarpeipman.com%2Fwp-content%2Fuploads%2F2020%2F01%2Fblazor-webassembly-app-shared-visualstudio_thumb.png" title="Blazor WebAssembly app as shared component library" alt="Blazor WebAssembly app as shared component library"&gt;&lt;/a&gt;After moving all user interface files to shared project and deleting these files from applications projects I finished up with projects like shown on screenshot. wwwroot folder is necessary as there are static files needed by application. Most important one is index.html as it is different for both applications.&lt;/p&gt;

&lt;p&gt;Screenshot clearly shows how applications projects have only wwwroot folder (later more about it), Program and Startup classes and implementation of weather data service. Notice that I managed to move also App.razor to shared project. As application projects doesn’t have any user interface components then I got rid of _Imports.razor files too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NB!&lt;/strong&gt; There’s one issue – static assets in shared library are not supported by Blazor on desktop. When publishing these applications we can copy static assets from wwwroot of shared project to desktop application publishing folder. It’s hack, yes, but don’t forget we are messing with pre-alpha and beta level tooling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Differences in applications
&lt;/h3&gt;

&lt;p&gt;It’s important to see what’s the difference between desktop and web application. I tried first to make total hybrid application – one application running wherever you want, but it turned out to be task over my skills.&lt;/p&gt;

&lt;p&gt;First and most important difference is in project file definition – applications use different project SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Desktop application --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk.Razor"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- Web application --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;Project&lt;/span&gt; &lt;span class="na"&gt;Sdk=&lt;/span&gt;&lt;span class="s"&gt;"Microsoft.NET.Sdk.Web"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are Program and Startup class of desktop application.&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;Program&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;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ComponentsDesktop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"My Blazor App"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"wwwroot/index.html"&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;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;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IWeatherService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LocalDiskWeatherService&lt;/span&gt;&lt;span class="p"&gt;&amp;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;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;IComponentsApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;app&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="n"&gt;AddComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"app"&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;Let’s compare these two with ones from web application project.&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;Program&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;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;CreateHostBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Run&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;static&lt;/span&gt; &lt;span class="n"&gt;IWebAssemblyHostBuilder&lt;/span&gt; &lt;span class="nf"&gt;CreateHostBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;BlazorWebAssemblyHost&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateDefaultBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UseBlazorStartup&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;&amp;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;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;ConfigureServices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IServiceCollection&lt;/span&gt; &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddSingleton&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IWeatherService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WebWeatherService&lt;/span&gt;&lt;span class="p"&gt;&amp;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;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;IComponentsApplicationBuilder&lt;/span&gt; &lt;span class="n"&gt;app&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="n"&gt;AddComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"app"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Desktop application uses ComponentDesktop class to initialize program while web application uses Blazor WebAssembly host builder. Startup classes are similar in big part, but ConfigureServices method is different – this is where dependency injection happens.&lt;/p&gt;

&lt;p&gt;There are also small differences in index.html files. Let’s start with desktop application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&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;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;MyDesktopApp&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="nt"&gt;/&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;"css/bootstrap/bootstrap.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;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"css/site.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;app&amp;gt;&lt;/span&gt;Loading...&lt;span class="nt"&gt;&amp;lt;/app&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"framework://blazor.desktop.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&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;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is index.html for Blazor web application. Notice how stylesheets are linked from _content folder as Blazor web application supports static assets hosted in component projects. Also compare how Blazor application script is included. Blazor desktop application uses URL starting with framework:// while Blazor web application uses regular web server location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&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;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;BlazorDemoWeb&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;base&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="nt"&gt;/&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;"\_content/BlazorDemoShared/css/bootstrap/bootstrap.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;link&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"\_content/BlazorDemoShared/css/site.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;app&amp;gt;&lt;/span&gt;Loading...&lt;span class="nt"&gt;&amp;lt;/app&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"blazor-error-ui"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        An unhandled error has occurred.
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"reload"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Reload&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"dismiss"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"\_framework/blazor.webassembly.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&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;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these minor things differences end. Application and its user interface is living in shared project. In both cases application looks the same.&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%2Fstatic.gunnarpeipman.com%2Fwp-content%2Fuploads%2F2020%2F01%2Fblazor-desktop-application.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%2Fstatic.gunnarpeipman.com%2Fwp-content%2Fuploads%2F2020%2F01%2Fblazor-desktop-application.png" title="Blazor desktop application showing weather data" alt="Blazor desktop application showing weather data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s one minor difference – desktop application runs inside operating system window and web application runs in browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blazor desktop application as self-contained executable
&lt;/h3&gt;

&lt;p&gt;After getting things work I wanted to try out one final thing – what happens if I publish desktop application as &lt;a href="https://gunnarpeipman.com/dotnet-core-self-contained-executable/" rel="noopener noreferrer"&gt;self-contained executable with assembly trimming&lt;/a&gt; enabled. Assembly trimming removed all code from resulting assembly that is not executed by application.&lt;/p&gt;

&lt;p&gt;I opened Blazor desktop application folder in command prompt and executed the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dotnet publish -c Release -r win10-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true

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

&lt;/div&gt;



&lt;p&gt;Instead of publish folder with many files I got something really small.&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%2Fstatic.gunnarpeipman.com%2Fwp-content%2Fuploads%2F2020%2F01%2Fblazor-desktop-self-contained.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%2Fstatic.gunnarpeipman.com%2Fwp-content%2Fuploads%2F2020%2F01%2Fblazor-desktop-self-contained.png" title="Blazor desktop application as self-contained trimmed executable" alt="Blazor desktop application as self-contained trimmed executable"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Without assembly trimming executable will take ~70MB. Assembly trimming saved us roughly 50% in resulting assembly size.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;No WebAssembly JavaScript files for desktop!&lt;/strong&gt; After publishing and running Blazor desktop application I had one question: where are those JavaScript files that make Blazor possible? I couldn’t find these from publish folder. Well, it seems like Blazor desktop apps are able to load WebAssembly application without need for separate JavaScript files. And framework:// protocol we saw before seems to make the trick. Cool!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;Although not all things work like they should, I was still able to build kind of hybrid Blazor WebAssembly application. User interface is in shared component library and application projects are there to build and publish application for target environment or platform. Suprising moment was that assembly trimming works like charm with Blazor desktop applications and all I have to try out now is to buid a simple installer. Blazor WebWindow is in pre-alpha but already damn powerful toy to play with.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/blazor-hybrid-application-experimental/" rel="noopener noreferrer"&gt;Building experimental hybrid Blazor WebAssembly application&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com" rel="noopener noreferrer"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>blazor</category>
      <category>webassembly</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Create fake user for ASP.NET Core controller tests</title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Fri, 31 Jan 2020 05:50:42 +0000</pubDate>
      <link>https://dev.to/gpeipman/create-fake-user-for-asp-net-core-controller-tests-110</link>
      <guid>https://dev.to/gpeipman/create-fake-user-for-asp-net-core-controller-tests-110</guid>
      <description>&lt;p&gt;I think most of ASP.NET Core applications have authentication enabled. When writing unit tests for controllers we have one set of tests that need authenticated user and other set of tests that need anonymous user. Faking User property of controller is a little bit tricky. This blog post shows how to do it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Source code available!&lt;/strong&gt; Source code and sample ASP.NET Core solution for this post is available at my GitHub repository &lt;a href="https://github.com/gpeipman/AspNetCoreTests" rel="noopener noreferrer"&gt;gpeipman/AspNetCoreTests&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Testing controllers with authenticated user
&lt;/h3&gt;

&lt;p&gt;Let’s start with my favorite example of controller action that checks if user is authenticated. For anonymous users we have PublicIndex view and for authenticated users there is default Index view. Both are returned from same Index() action of HomeController.&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="n"&gt;IActionResult&lt;/span&gt; &lt;span class="nf"&gt;Index&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;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsAuthenticated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PublicIndex"&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;We can write unit test to see if Index view is returned for authenticated user. This solution is borrowed from my blog post &lt;a href="https://gunnarpeipman.com/aspnet-core-azure-ad-unit-test/" rel="noopener noreferrer"&gt;Faking Azure AD identity in ASP.NET Core unit tests&lt;/a&gt;.&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&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="n"&gt;Index_should_return_private&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;for_authenticated_user&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;logger&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;NullLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HomeController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt; 
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&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;ClaimsPrincipal&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;ClaimsIdentity&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;Claim&lt;/span&gt;&lt;span class="p"&gt;[]&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;Claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClaimTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NameIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SomeValueHere"&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;Claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClaimTypes&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;"gunnar@somecompany.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                        &lt;span class="c1"&gt;// other required and custom claims&lt;/span&gt;
                                   &lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="s"&gt;"TestAuthentication"&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;controller&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;HomeController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     
    &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&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;ControllerContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;     
    &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&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;DefaultHttpContext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&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;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ViewResult&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;viewName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;True&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;viewName&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Index"&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;This test will pass as all conditions for it to work are met.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NB! Authentication provider is important!&lt;/strong&gt; If we don’t specify authentication provider (“TestAuthentication” in our case) when building ClaimsIdentity then user is not considered as authenticated. The fact that user has name and name identifier claims available doesn’t matter.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’m using similar trick also for &lt;a href="https://gunnarpeipman.com/aspnet-core-custom-authentication/" rel="noopener noreferrer"&gt;lightweight custom authentication in ASP.NET Core&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing controllers with anonymous user
&lt;/h3&gt;

&lt;p&gt;First test idea that probably comes to our minds is simple test 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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&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;Index_should_return_public_view_for_anonymous_user&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;logger&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;NullLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HomeController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt; 
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;controller&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;HomeController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&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;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ViewResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PublicIndex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewName&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;But this test fails with NullReferenceException.&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%2Fstatic.gunnarpeipman.com%2Fwp-content%2Fuploads%2F2020%2F01%2Faspnet-core-controller-user-test-fails.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%2Fstatic.gunnarpeipman.com%2Fwp-content%2Fuploads%2F2020%2F01%2Faspnet-core-controller-user-test-fails.png" title="ASP.NET Core controller test fails" alt="ASP.NET Core controller test fails"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It happens on the line where we check if user is authenticated. But why?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;There’s always user.&lt;/strong&gt; When ASP.NET Core application is running there’s always user available but it is possible that user is not authenticated. But there’s always a user.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To make anonymous user test pass we must provide claims identity with no authentication provider.&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&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;Index_should_return_public_view_for_anonymous_user&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;logger&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;NullLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HomeController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt; 
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&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;ClaimsPrincipal&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;ClaimsIdentity&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;controller&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;HomeController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
    &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&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;ControllerContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
    &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&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;DefaultHttpContext&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&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;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ViewResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PublicIndex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewName&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;Now the test will pass.&lt;/p&gt;

&lt;h3&gt;
  
  
  Moving user matters to extension methods
&lt;/h3&gt;

&lt;p&gt;Our tests pass now but we are not yet done. We have repeated code and only two tests this far. Suppose we have few hundreds of tests one day and they all set up identity for controller like shown above. If we want to change it then it’s few hundred unit tests to change. So, it’s time to make some home cleaning.&lt;/p&gt;

&lt;p&gt;To keep identity initialization in one place I added &lt;a href="https://gunnarpeipman.com/csharp-extension-methods/" rel="noopener noreferrer"&gt;extension methods&lt;/a&gt; class for ASP.NET controllers.&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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ControllerTestExtensions&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;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;WithIdentity&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;nameIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&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;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnsureHttpContext&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;principal&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;ClaimsPrincipal&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;ClaimsIdentity&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;Claim&lt;/span&gt;&lt;span class="p"&gt;[]&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;Claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClaimTypes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NameIdentifier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nameIdentifier&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;Claim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClaimTypes&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="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="c1"&gt;// other required and custom claims&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s"&gt;"TestAuthentication"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;principal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;controller&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;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;WithAnonymousIdentity&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EnsureHttpContext&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;principal&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;ClaimsPrincipal&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;ClaimsIdentity&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;principal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;EnsureHttpContext&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&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;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&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;ControllerContext&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;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ControllerContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HttpContext&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;DefaultHttpContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;controller&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;Using these extension methods we can make controller tests shorter.&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="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&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;Index_should_return_public_view_for_anonymous_user&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;logger&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;NullLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HomeController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;controller&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;HomeController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;WithAnonymousIdentity&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;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ViewResult&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"PublicIndex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewName&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="n"&gt;Fact&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;Index_should_return_private_view_for_authenticated_user&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;logger&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;NullLogger&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HomeController&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;controller&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;HomeController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithIdentity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SomeValueHere"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"gunnar@somecompany.com"&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;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ViewResult&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;viewName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ViewName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;True&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsNullOrEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;viewName&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Index"&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;User related repeated code is gone now and our tests are clean.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;Unit testing ASP.NET Core controllers can be tricky. When web application runs then ASP.NET Core sets up many things automatically and we don’t even notice it. But if we start testing our controllers we will discover these things step by step. Getting User property of controller set up correctly was a little bit tricky but still pretty easy. Using extension methods we were able to keep User property related code in one place and avoid code duplications.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/aspnet-core-test-controller-fake-user/" rel="noopener noreferrer"&gt;Create fake user for ASP.NET Core controller tests&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com" rel="noopener noreferrer"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aspnetcore</category>
      <category>testing</category>
      <category>visualstudio</category>
    </item>
    <item>
      <title>Exploring WebWindow examples for Blazor on desktop</title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Wed, 29 Jan 2020 06:16:59 +0000</pubDate>
      <link>https://dev.to/gpeipman/exploring-webwindow-examples-for-blazor-on-desktop-33cg</link>
      <guid>https://dev.to/gpeipman/exploring-webwindow-examples-for-blazor-on-desktop-33cg</guid>
      <description>&lt;p&gt;Blazor on desktop is one of latest hot topics and &lt;a href="https://gunnarpeipman.com/focus-on-blazor-announcements/"&gt;.NET Conf: Focus on Blazor&lt;/a&gt; only added more fuel to fire. Blazor seems to come everywhere and it’s unstoppable. One of interesting desktop experiments is WebWindow by Steve Sanderson. It’s cross-platform component to make Blazor WebAssembly applications run on desktop. Let’s take a closer look at WebWindow and Blazor on desktop.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning!&lt;/strong&gt; &lt;a href="https://github.com/SteveSandersonMS/WebWindow"&gt;WebWindow&lt;/a&gt; is experimental project by &lt;a href="https://blog.stevensanderson.com/"&gt;Steve Sanderson&lt;/a&gt; – one of masterminds behind Blazor. There are no ready-made stable packages or components, no beautiful templates and other luxury like this. Get ready to make hands really dirty with raw bits and bytes!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why Blazor on desktop?
&lt;/h3&gt;

&lt;p&gt;Blazor WebAssembly needs only rendering engine with WebAssembly support to run and most of modern browsers support WebAssembly. There Blazor applications doesn’t necessarily need web server to run. Web server is just one option to deliver Blazor application so browser can download and run it.&lt;/p&gt;

&lt;p&gt;If web server is not necessary then there’s question to ask: can we somehow remove web server from equation and make those Blazor WebAssembly apps look and feel more like desktop application? Blazor on desktop is here to address this question with high chances to answer “yes”.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is WebWindow?
&lt;/h3&gt;

&lt;p&gt;WebWindow is .NET Core library that makes it possible to run HTML, JavaScript and WebAssembly applications in local browser window. It uses some existing browser or web renderer library to create a window where web application is loaded and run.&lt;/p&gt;

&lt;p&gt;WebWindow is cross-platfrom library. Current experimental implementation of WebWindow works on following platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows&lt;/strong&gt; – needs Chromium-based Edge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux&lt;/strong&gt; – uses WebKit&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mac&lt;/strong&gt; – needs Safari&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how we start Blazor on desktop on all three platforms.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;WebWindows.Blazor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;BlazorDesktopApp&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;Program&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;ComponentsDesktop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Startup&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"My Blazor App"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"wwwroot/index.html"&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;WebWindow is made available on same desktop platforms where .NET Core runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Downloading and running WebWindow examples
&lt;/h3&gt;

&lt;p&gt;Experimental WebWindow code lives in GitHub repository &lt;a href="https://github.com/SteveSandersonMS/WebWindow"&gt;SteveSandersonMS/WebWindow&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone or download &lt;a href="https://github.com/SteveSandersonMS/WebWindow"&gt;latest WebWindows version&lt;/a&gt; from master branch &lt;/li&gt;
&lt;li&gt;Run Visual Studio 2019 and update it to latest version&lt;/li&gt;
&lt;li&gt;Open WebWindow.Samples.sln file in Visual Studio&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are three projects in samples solution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HelloWorldApp&lt;/strong&gt; – simple Hello, World! demonstrating basics of WebWindow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VuewFileExplorer&lt;/strong&gt; – sample of JavaScript based local file browser&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BlazorDesktopApp&lt;/strong&gt; – running Blazor WebAssembly app on desktop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s see what’s inside those sample applications.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hello WebView
&lt;/h3&gt;

&lt;p&gt;First project to explore is Hello, world! It is not about Blazor but WebWindow component that hosts HTML and Blazor applications. This is console application that creates new WebWindow and shows local index.html file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;WebWindows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;HelloWorldApp&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&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;window&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;WebWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"My first WebWindow app"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
             &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NavigateToLocalFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wwwroot/index.html"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
             &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;WaitForExit() method of WebWindow stops execution of Main() method until windows is closed.&lt;/p&gt;

&lt;p&gt;When we run application then it opens in local web view window. The following screenshot shows Hello, World application running on Windows 10.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SXv4MWw6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/blazor-webwindow-hello-world.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SXv4MWw6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/blazor-webwindow-hello-world.png" alt="Hello, World WebWindow application" title="Hello, World WebWindow application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this example our first step is made.&lt;/p&gt;

&lt;h3&gt;
  
  
  Browsing file system of host machine
&lt;/h3&gt;

&lt;p&gt;Next WebWindow example is in project called VueFileExplorer. It makes step further with WebWindow and introduces communication between user interface shown in web view and .NET Core process hosting it. WebWindow defines OnWebMessageReceived event handler and SendMessage command. These two members of WebWindow class enable two-way communication between web view and hosting process.&lt;/p&gt;

&lt;p&gt;VueFileExplorer uses Vue.js to build two-pane window to explore file system. Left pane is for files and folders, right pane is for file content like shown on the following screenshot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vfeENVkX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/webwindow-vue-file-explorer.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vfeENVkX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/webwindow-vue-file-explorer.png" alt="Vue file explorer" title="Vue file explorer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When file or folder is clicked then JavaScript sends message to window. This is the Vue app serving user interface and communicating with host application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="na"&gt;el&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;directoryInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;fileInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;navigateTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;relativePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
             &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;external&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                 &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;navigateTo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="na"&gt;basePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;directoryInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="na"&gt;relativePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;relativePath&lt;/span&gt;
             &lt;span class="p"&gt;}));&lt;/span&gt;
         &lt;span class="p"&gt;},&lt;/span&gt;
         &lt;span class="na"&gt;showFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
             &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;external&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                 &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showFile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="na"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fullName&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;external&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;receiveMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageJson&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messageJson&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showDirectory&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
             &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;directoryInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
             &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
         &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;showFile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
             &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fileInfo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
             &lt;span class="k"&gt;break&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;external&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

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



&lt;p&gt;Here’s the fragment of code from Program class demonstrating how WebWindow messages are listened and processed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&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;window&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;WebWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;".NET Core + Vue.js file explorer"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnWebMessageReceived&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;HandleWebMessageReceived&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NavigateToLocalFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"wwwroot/index.html"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitForExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;HandleWebMessageReceived&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;message&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;window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WebWindow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;sender&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;parsedMessage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;RootElement&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parsedMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"ready"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
             &lt;span class="nf"&gt;ShowDirectoryInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Directory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetCurrentDirectory&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
             &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
         &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"navigateTo"&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;basePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parsedMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"basePath"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;GetString&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;relativePath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parsedMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"relativePath"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;GetString&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;destinationPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetFullPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Combine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;basePath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;relativePath&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                                         &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TrimEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DirectorySeparatorChar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
             &lt;span class="nf"&gt;ShowDirectoryInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;destinationPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
             &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
         &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"showFile"&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;fullName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parsedMessage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"fullName"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
             &lt;span class="nf"&gt;ShowFileContents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
             &lt;span class="k"&gt;break&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;Based on message sent from web view the response is built and sent to web view as message. Here’s how showing of file contents is implemented.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;ShowFileContents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WebWindow&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;fullName&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;fileInfo&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;FileInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nf"&gt;SendCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"showFile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Clear the old display first&lt;/span&gt;
     &lt;span class="nf"&gt;SendCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"showFile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&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="n"&gt;fileInfo&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="n"&gt;size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;fullName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ReadTextFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxChars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100000&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;SendCommand is simple static method that sends prepared command to web view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SendCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WebWindow&lt;/span&gt; &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;commandName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;commandName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;arg&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;As we can see then we can use WebWindow to host JavaScript application and make it use host system through messaging.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blazor desktop application
&lt;/h3&gt;

&lt;p&gt;The pearl of WebWindow example applications is the one that hosts Blazor WebAssembly. The example uses default Blazor WebAssembly application with famous Counter and Fetch data views. Here is the example of weather forecasts we have seen in numerous Blazor demos. Now it is running on your desktop instead of web server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KExNs5Aa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/blazor-desktop-application.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KExNs5Aa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/blazor-desktop-application.png" alt="blazor-desktop-application" title="blazor-desktop-application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where comes the data to weather forecast view if Blazor application is hosted in web window and there’s no web server running? It’s coming straight from disk. Here’s the FetchData page from Blazor desktop application project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;@page "/fetchdata"
@using System.IO
@using System.Text.Json

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Weather forecast&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;This component demonstrates fetching data from the server.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

@if (forecasts == null)
{
     &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;em&amp;gt;&lt;/span&gt;Loading...&lt;span class="nt"&gt;&amp;lt;/em&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
}
else
{
     &lt;span class="nt"&gt;&amp;lt;table&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"table"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="nt"&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
             &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                 &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Date&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                 &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Temp. (C)&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                 &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Temp. (F)&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
                 &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;Summary&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
             &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
         &lt;span class="nt"&gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;
         &lt;span class="nt"&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
             @foreach (var forecast in forecasts)
             {
                 &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                     &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;@forecast.Date.ToShortDateString()&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                     &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;@forecast.TemperatureC&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                     &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;@forecast.TemperatureF&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                     &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;@forecast.Summary&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                 &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
             }
         &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;/table&amp;gt;&lt;/span&gt;
}

@code {
     WeatherForecast[] forecasts;
     protected override async Task OnInitializedAsync()
     {
         var forecastsJson = await File.ReadAllTextAsync("wwwroot/sample-data/weather.json");
         forecasts = JsonSerializer.Deserialize&lt;span class="nt"&gt;&amp;lt;WeatherForecast&lt;/span&gt;&lt;span class="err"&gt;[]&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;(forecastsJson, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
     }
     public class WeatherForecast
     {
         public DateTime Date { get; set; }
         public int TemperatureC { get; set; }
         public string Summary { get; set; }
         public int TemperatureF =&amp;gt; 32 + (int)(TemperatureC / 0.5556);     }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Instead of using HttpClient like examples of server hosted Blazor do, here they are accessing file system to load and deserialize weather forecast data.&lt;/p&gt;

&lt;p&gt;In all other means it is the same Blazor application we get when we create new Blazor application. But this time it runs on desktop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;Blazor WebApplication (formerly known as &lt;a href="https://gunnarpeipman.com/blazor-preview/"&gt;client-side Blazor&lt;/a&gt;) is going through revolutionary times. There are many things happening around it and making Blazor run on desktop is perhaps the most interesting part. Although WebWindow is in early experimental stages and it’s not even clear if this approach will stay here long enough to get to stable version, it is still very promising. WebWindow and desktop support are in my opinion important part of Blazor-everywhere vision.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/blazor-on-desktop-webwindow-experiment/"&gt;Exploring WebWindow examples for Blazor on desktop&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>blazor</category>
      <category>webassembly</category>
      <category>visualstudio</category>
    </item>
    <item>
      <title>Start today with Surface Duo development on preview emulator and SDK </title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Mon, 27 Jan 2020 05:10:57 +0000</pubDate>
      <link>https://dev.to/gpeipman/start-today-with-surface-duo-development-on-preview-emulator-and-sdk-lmi</link>
      <guid>https://dev.to/gpeipman/start-today-with-surface-duo-development-on-preview-emulator-and-sdk-lmi</guid>
      <description>&lt;p&gt;&lt;a href="https://www.microsoft.com/en-us/surface/devices/surface-neo"&gt;Surface Neo&lt;/a&gt; and &lt;a href="https://www.microsoft.com/en-us/surface/devices/surface-duo"&gt;Surface Duo&lt;/a&gt; are new devices by Microsoft, planned to launch for holidays season this year. Surface Neo runs Windows and Surface Duo is based on Android. For Surface Duo there’s already preview tooling and SDK available by Microsoft. Here’s the introduction to Surface Duo development, tools and patterns.&lt;/p&gt;

&lt;p&gt;With Surface Neo and Surface Duo Microsoft enters a era of hybrid devices. Both new devices are targeted between mobile phones and tablets. Surface Neo is more on tablets side while Surface Duo is closer to mobile devices.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Save the date!&lt;/strong&gt; If you are more than interested in new dual-screen experiences and want to see next big announcements when they are made then &lt;a href="https://developer.microsoft.com/en-us/microsoft-365/virtual-events"&gt;Microsoft 365 Developer Day&lt;/a&gt; on 11th of February is the event for you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Unique dual-screen approach
&lt;/h3&gt;

&lt;p&gt;Instead of targeting as thin folding line between two screens as possible, Microsoft intentionally decided to have suprisingly wide line. It seems unexpected and irrational at first but when we dig deeper it’s actually damn clever approach. Why? On real device a folding line between screens is wide and narrow at same time. It’s narrow enough to be not annoying for views extended over two screens and it’s wide enough to be a separation line between master-detail views where master part of view is opened on one and details part on second screen.&lt;/p&gt;

&lt;p&gt;Dual nature in user experiences is something that makes Surface Neo and Surface Duo special. It also introduces new concepts in UI design and new UI patterns. Perhaps most challenging part of those new style UI-s is supporting one and two screen modes as users can decide on how many screens they want application to cover.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Surface Duo SDK preview
&lt;/h3&gt;

&lt;p&gt;Surface Duo comes as zip-archive containing installer file. Make sure you have latest Visual Studio 2019 installed with Xamarin.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.microsoft.com/download/details.aspx?id=100847"&gt;Download Surface Duo SDK Preview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/dual-screen/android/use-emulator?tabs=windows"&gt;Use the Surface Duo emulator&lt;/a&gt; (useful hints to make emulator work)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Installer creates folder for Surface Duo system images. Emulator can be started by running batch files in installation folder or using icon on desktop.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Hyper-V warning!&lt;/strong&gt; I ran into different troubles with emulator when Hyper-V was running on my machine. Stopping Hyper-V services was not enough – I had to uninstall Hyper-V and also Windows 10 virtualization features to get Surface Duo emulator running.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Surface Duo emulator
&lt;/h3&gt;

&lt;p&gt;As Surface Duo has foldable screen it needs specialized emulator where foldable or two-part screen is already considered at operating system level. Screenshot below shows Surface Duo emulator running with no applications open.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pa6UhBUO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/surface-duo-emulator.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pa6UhBUO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/surface-duo-emulator.png" alt="Surface Duo emulator" title="Surface Duo emulator"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Regular applications open on one screen (left or right of black folding line). They can be moved from one screen to another. If application is moved under folding line then both screens are highlighted. At this point comes difference between regular and new applications if user releases window. Regular application moves to left or right screen leaving the other one as it was before. New application enters extended mode and covers both screens.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dual screen patterns
&lt;/h3&gt;

&lt;p&gt;Before getting to code let me introduce new dual screen patterns for Surface Neo and Surface Duo. As with all new UI concepts there are at least some rules to follow to guarantee great user experience. Dual screens world is not the exception.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qyFqztTi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/dual-screen-app-patterns.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qyFqztTi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/dual-screen-app-patterns.png" alt="Dual-screen app patterns" title="Dual-screen app patterns"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extended Canvas&lt;/strong&gt; – extending the canvas allows users to take advantage of the larger screen real-estate provided by dual-screen devices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TbwcCPpX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/extended-canvas-pattern.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TbwcCPpX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/extended-canvas-pattern.png" alt="Extended Canvas pattern" title="Extended Canvas pattern"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use with map and drawing canvas apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Master-Detail&lt;/strong&gt; – separating navigation or overview from details allows users to drill deeper into content while staying grounded regarding their position in the overall list/aggregate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W8dyyQf3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/master-detail-pattern.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W8dyyQf3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/master-detail-pattern.png" alt="Master-Detail pattern" title="Master-Detail pattern"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use with apps that have lists or galleries, mail and scheduling apps, photos or image curation apps, music apps with playlists and song details, apps with strong navigation structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two Page&lt;/strong&gt; – leveraging the skeuomorphic metaphor of a book to showcase one page on each screen, so it’s more conducive to reading.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_5QctcTr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/two-page-pattern.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_5QctcTr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/two-page-pattern.png" alt="Two Page pattern" title="Two Page pattern"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use with document oriented apps, apps with content that is paginated, apps made for reading, apps with itemized canvas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dual View&lt;/strong&gt; – having multiple views of the same app in the same container, allowing comparison of similar-type content side by side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--v-iUo7Vj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/dual-view-pattern.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--v-iUo7Vj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/dual-view-pattern.png" alt="Dual View pattern" title="Dual View pattern"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use with editing tools that benefit from having before/after states side-by-side, content and context side-by-side, apps that let the user compare similar items, having two canvases with coordinated content but keeping each page separate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Companion Pane&lt;/strong&gt; – show complementary context to augment users’ tasks, usually with a primary/secondary relationship, by elevating to the surface previously buried level 2 functionalities for quicker access.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3pQnIN0u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/companion-pane-pattern.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3pQnIN0u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/companion-pane-pattern.png" alt="Companion Pane pattern" title="Companion Pane pattern"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use with productivity apps that have supplemental information that appears next to the main content, creative tools like image drawing app, music or video editor apps, gaming apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploring Surface Duo examples
&lt;/h3&gt;

&lt;p&gt;Surface Duo examples are available for Xamarin Native and Xamarin Forms. For native apps there are one project per demo. For Xamarin Forms there’s just one project demonstrating all dual screen application patterns. Those who want to get quick idea how new application works and how new UI patterns are implemented should start from Xamarin Forms applications.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NB!&lt;/strong&gt; Some sample forms need Google Maps key. You can acquire on from &lt;a href="http://console.developers.google.com"&gt;Google Developers Console&lt;/a&gt;. Maps key is needed in HTML files located in assets folders. Once you have a key, just copy and paste it where it is said in HTML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before starting application make sure emulator is running. For Visual Studio emulator is like a device it can connect to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ajLvmXKP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/surface-duo-emulator-visual-studio.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ajLvmXKP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/surface-duo-emulator-visual-studio.png" alt="Surface Duo emulator is availabe in Visual Studio" title="Surface Duo emulator is availabe in Visual Studio"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although it doesn’t have much informative name, it still works and Microsoft will come up with better name for Surface Duo emulator in future.&lt;/p&gt;

&lt;p&gt;Screenshot below shows Xamarin Forms application opened on left screen. You can click buttons to see demos of different dual-screen application patterns implemented for Surface Duo. I used the very same application to make screenshots demonstrating dual-screen patterns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hh1gp-1y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/surface-duo-xamarin-app.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hh1gp-1y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/surface-duo-xamarin-app.png" alt="Xamarin demo app for Surface Duo" title="Xamarin demo app for Surface Duo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On bottom part of left screen there’s white line on black border. We can point mouse on emulator (or finger on real device) on the white line and move application to right screen or let is span over both screens. Spanning application over both screens and moving it between two screens is supported in emulator.&lt;/p&gt;

&lt;p&gt;Here’s the screen recording of Xamarin demo application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;&lt;a href="https://static.gunnarpeipman.com/wp-content/uploads/2020/01/surface-duo-demo-fixed.mp4"&gt;Watch video&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With these examples we have implementations of all dual-screen patterns available.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploring code in Visual Studio
&lt;/h3&gt;

&lt;p&gt;Xamarin application uses Xamarin.DuoSdk NuGet package that provides access to hinge sensor and dual-screen features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ek00J43y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/xamarin-duosdk-classes.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ek00J43y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/xamarin-duosdk-classes.png" alt="xamarin-duosdk-classes" title="xamarin-duosdk-classes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;HingeSensor class is for monitoring hinge sensor. This class can tell us if device has a hinge and notify us when sensor value changed. ScreenHelper gives us information about hinge size and screen rotation. It has few helpful methods more that we can use to react to hinge sensor changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vaxWM--D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/surface-duo-demo-services.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vaxWM--D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/surface-duo-demo-services.png" alt="Hinge and layout services in Xamarin demo application" title="Hinge and layout services in Xamarin demo application"&gt;&lt;/a&gt;When sniffing around n Visual Studio project I found IHingeService and ILayoutService interfaces from “shared” Xamarin project. Their implementations – HingeService and LayoutService – are in Xamarin Android project. Both of these classes are so clean of sample application specifics that they should be part of some NuGet package in my opinion. There’s anyway Xamarin.DuoSdk NuGet package available.&lt;/p&gt;

&lt;p&gt;Pages in sample application are all implemented in “shared” project. There are components like TwoPaneView and FormsWindow that are used in pages. Both components are generic enough to have them as NuGet package.&lt;/p&gt;

&lt;p&gt;It was a little surprising for me that there are no base pages or ready-made templates for new dual-screen pages implementing corresponding patterns mentioned above. It’s possible that we will get something when preview version of Surface Neo is released.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;Surface Duo preview SDK and emulator are first real tools for developers to get started with dual-screen apps development. Although emulator doesn’t look as slick as real device, it works well enough. As a developers we can really start experimenting now and extend our new applications to Surface Neo as soon as tooling for Surface Neo is available. Documentation that is available now is not complete yet, but still there’s enough information to get an idea how things work for dual-screen experiences.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dual-screen/"&gt;Create apps for dual-screen devices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dual-screen/android/duo-dimensions"&gt;Surface Duo device dimensions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dual-screen/android/get-duo-sdk?tabs=csharp"&gt;Get the Surface Duo SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.microsoft.com/en-us/dual-screen/android/use-emulator?tabs=windows"&gt;Use the Surface Duo emulator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blogs.windows.com/windowsdeveloper/2020/01/22/announcing-dual-screen-preview-sdks-and-microsoft-365-developer-day/"&gt;Announcing dual-screen preview SDKs and Microsoft 365 Developer Day&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.microsoft.com/en-us/microsoft-365/virtual-events"&gt;Microsoft 365 Developer Day&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/surface-duo-development-preview/"&gt;Start with Surface Duo development on preview emulator and SDK today&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>surface</category>
      <category>android</category>
      <category>xamarin</category>
    </item>
    <item>
      <title>Readable fluent queries with Entity Framework Core</title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Mon, 27 Jan 2020 04:51:28 +0000</pubDate>
      <link>https://dev.to/gpeipman/readable-fluent-queries-with-entity-framework-core-548f</link>
      <guid>https://dev.to/gpeipman/readable-fluent-queries-with-entity-framework-core-548f</guid>
      <description>&lt;p&gt;After my first experiments with &lt;a href="https://gunnarpeipman.com/ef-core-query-specification/"&gt;Query Specification pattern on Entity Framework Core&lt;/a&gt; I came to interesting idea – why not using extension methods that wrap query specifications or add directly some more conditions to IQueryable the way that queries are easy to read. Here’s my experiment and thoughts of fluent readable queries.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning!&lt;/strong&gt; This blog post covers my new experiments with readable fluent queries. Don’t take it as a new pattern or final solution or something too serious. I have no idea if this approach is applicable in real codebase and what are the limits or side effects. It’s just an interesting idea to play with.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Wired query specifications
&lt;/h3&gt;

&lt;p&gt;My previous post &lt;a href="https://gunnarpeipman.com/ef-core-query-specification/"&gt;Implementing Query Specification pattern in Entity Framework Core&lt;/a&gt; introduced query specification classes and it was possible to wire these one after another. Here’s the last code example from this post.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_dataContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Specify&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;DueInvoicesSpecification&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Specify&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;CustomerInvoicesSpecification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Code itself looks okay to me. Specify() method that applies query specifications looks okay and clean. It doesn’t use actions and funcs line other Entity Framework Core extension methods but still it looks fine and it’s easy to read.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issues with wired query specifications
&lt;/h3&gt;

&lt;p&gt;One thing started suddenly annoying me when I just watched the wiring of specifications and thought if it’s okay or are there other ways to do it better. Red rectangle on the following image shows the problematic part.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8PbSF156--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2019/11/query-specifications-annoying.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8PbSF156--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2019/11/query-specifications-annoying.png" alt="Issues with wired query specifications" title="Issues with wired query specifications"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Names of query specification classes repeat the word Specification, making class names too long.&lt;/li&gt;
&lt;li&gt;Calls to Specify() method doesn’t add any literal value.&lt;/li&gt;
&lt;li&gt;Call to Specify() method with query specification class is too long and not enough informative.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can be sure that the longer the list of specifications grows the uglier the code gets until it’s not easy to follow with eyes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing readable fluent queries
&lt;/h3&gt;

&lt;p&gt;After some experiments with my previous query specification code I wrote the following extension methods for IQueryable&amp;lt;Invoice&amp;gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;InvoiceQueries&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;static&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetOverDueInvoices&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Specify&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;DueInvoicesSpecification&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;static&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;ForCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Specify&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;CustomerInvoicesSpecification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customerId&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;These methods are actually dummy wrappers for query specification classes but the their effect is pretty cool. Take a look at the following code example and compare it to one with Specify() methods above.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_dataContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOverDueInvoices&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Instead of query stuffed with technical lingo we have now query that make clear and honest confession about what they will actually do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Longer fluent queries
&lt;/h3&gt;

&lt;p&gt;Let’s wire some more specifications to query and see how code looks when using query specifications. I added SentUsing and Total property to Invoice class and created the following specifications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;SentUsingEmailSpecification&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BaseSpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;SentUsingEmailSpecification&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Criteria&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SentUsing&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;SendMethodEnum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&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;HavingTotalAtLeastSpecification&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BaseSpecification&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&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;HavingTotalAtLeastSpecification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Criteria&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Total&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;total&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;Now let’s add these new specification to sample query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_dataContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Specify&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;DueInvoicesSpecification&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Specify&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;CustomerInvoicesSpecification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Specify&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;SentUsingEmailSpecification&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Specify&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;HavingTotalAtLeastSpecification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;150&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So, readability is gone. When we look at code above it’s messy – we can’t catch what query is doing with taking quick look on it. We have to read through all lines where Specify() method is called to understand what the query is doing.&lt;/p&gt;

&lt;p&gt;Using same dirty trick I did in previous post I add two extension methods to wrap new query specifications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;static&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;SentUsingEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Specify&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;SentUsingEmailSpecification&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;static&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;HavingTotalAtLeast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IQueryable&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Specify&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;HavingTotalAtLeastSpecification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total&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;After making sample query use these extension methods we get it back on track.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;invoices&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_dataContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Invoices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetOverDueInvoices&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ForCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SentUsingEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HavingTotalAtLeast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OrderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Customer&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our query is still readable and communicates its purpose. Query specification classes made sample query ugly and hard to read but extension methods solved these issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros and cons of fluent queries
&lt;/h3&gt;

&lt;p&gt;It’s good question if we need query specifications when going with extension methods like the ones above. Maybe we can go with extension methods and abandon query specifications as we can host query conditions in extension methods instead. It makes sense but we cannot forget one thing – specification and query specification classes are more familiar to developers.&lt;/p&gt;

&lt;p&gt;There’s also another issue with extension methods that query specifications doesn’t have – moving queries through layers of application. Extension method is used where it is called. We can move extension method by using actions but I’m not sure if this is something we want to do. Query specification classes allow us to create instances and use these instances with method calls to service layer or as parameters of command classes.&lt;/p&gt;

&lt;p&gt;Although I’m not sure if extension methods like show here are good or bad they still look damn nice and readable to me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;Playing with patterns and their implementations and stepping further with all kind of random ideas may sometimes lead to something interesting. This time I started with implementation of query specification pattern and got to an idea of fluent readable queries. Although I have not much idea if this idea leads to somewhere or if it is applicable in practice it still seems promising when looking at my readable fluent queries. As it’s not yet safe to go live with my queries I will try these out in some smaller project to see where it leads me.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/ef-core-readable-fluent-queries/"&gt;Readable fluent queries with Entity Framework Core&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>efcore</category>
    </item>
    <item>
      <title>Execute raw SQL commands in Entity Framework Core</title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Sun, 26 Jan 2020 21:06:33 +0000</pubDate>
      <link>https://dev.to/gpeipman/execute-raw-sql-commands-in-entity-framework-core-3102</link>
      <guid>https://dev.to/gpeipman/execute-raw-sql-commands-in-entity-framework-core-3102</guid>
      <description>&lt;p&gt;I have never seen a real-life project where object-relational mapper generates 100% of needed SQL. There have always been those special cases when raw SQL commands are needed. In this post I will demonstrate how to run raw SQL commands in Entity Framework Commands and how to read data from database without DbSet and query types.&lt;/p&gt;

&lt;h3&gt;
  
  
  Executing raw SQL commands
&lt;/h3&gt;

&lt;p&gt;Entity Framework Core has ExecuteSqlCommand() and ExecuteSqlCommandAsync() methods to run custom SQL queries and commands. Here’s the example of running stored procedure to update balance for all customers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;UpdateBalanceForCustomers&lt;/span&gt;&lt;span class="p"&gt;()&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;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteSqlCommandAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"EXEC sp_BalanceUpdate"&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;We can also use parameters with these methods like shown here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;UpdateBalanceForCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;customerId&lt;/span&gt;&lt;span class="p"&gt;)&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;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteSqlCommandAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"EXEC sp_Customer BalanceUpdate @p1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;customerId&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;These methods are fine but how they are executed is decided by Entity Framework Core.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using ADO.NET
&lt;/h3&gt;

&lt;p&gt;We can use ADO.NET data objects to get complete control and go around all wisdom coming with Entity Framework Core. Here’s the code from first sample of this post but it is implemented using raw database objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;UpdateBalanceForCustomers&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;command&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="nf"&gt;GetDbConnection&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;CreateCommand&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CommandText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sp_BalanceUpdate"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CommandType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CommandType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StoredProcedure&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;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteNonQueryAsync&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;This code is pretty primitive. It just executes stored procedure and this is it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting data from raw SQL commands
&lt;/h3&gt;

&lt;p&gt;Sometimes we need to get data back from custom raw SQL. Good example is reporting data we are showing as a table on web page. It’s sometimes tempting to return data reader to keep things minimal but be aware – you are taking a great responsibility! Before running any other command on same connection the data reader must be closed or you end up with exception.&lt;/p&gt;

&lt;p&gt;What has best worked for me is loading data to DataTable like shown here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task&amp;lt;DataTable&amp;gt; GetAverageTemperatures(DateTime forDate)
{
    using(var command = Database.GetDbConnection().CreateCommand())
    { 
        command.CommandText = "sp_AverageTemperaturesReport";
        command.CommandType = CommandType.StoredProcedure;

        var forDateParam = command.CreateParameter();
        forDateParam.ParameterName = "@ForDate";
        forDateParam.DbType = DbType.Date;
        forDateParam.Value = forDate;
        command.Parameters.Add(forDateParam);

        using(var reader = await command.ExecuteReaderAsync())
        { 
            var table = new DataTable();
            table.Load(reader);

            return table;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using this code we can also run custom SQL commands and not only stored procedures. We are still running commands in database context but what we are doing is not monitored by Entity Framework core. For reporting DataTable is ideal – we can change the structure of data returned by SQL command and everything still works.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Common view for DataTable.&lt;/strong&gt; If you write views or procedures for reporting where field names are replaced by human readable names then you can use my &lt;a href="https://gunnarpeipman.com/asp-net-mvc-simple-view-to-display-contents-of-datatable/"&gt;common DataTable view&lt;/a&gt; to display DataTables. You can also derive your own common view for your DataTables using my code as a starting point.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;There are multiple ways to execute raw SQL commands using Entity Framework Core. This post focused on a shortcut methods like ExecuteSqlCommand() and ExecuteSqlCommandAsync() and also took raw ADO.NET to focus as it is sometimes the work horse we actually need. The code here doesn’t conflict with Entity Framework database context and it is safe to use for data reading as it doesn’t leave any data readers open.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/ef-core-execute-raw-sql/"&gt;Execute raw SQL commands in Entity Framework Core&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>efcore</category>
    </item>
    <item>
      <title>DataSet and DataTable based ad-hoc reporting with ASP.NET Core</title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Sat, 25 Jan 2020 04:21:00 +0000</pubDate>
      <link>https://dev.to/gpeipman/dataset-and-datatable-based-ad-hoc-reporting-with-asp-net-core-101c</link>
      <guid>https://dev.to/gpeipman/dataset-and-datatable-based-ad-hoc-reporting-with-asp-net-core-101c</guid>
      <description>&lt;p&gt;In one of my projects I have some ASP.NET Core views that display multiple tables with reporting data. Data comes from SQL Server views and stored procedures and these can be modified in database without deploying application to server again. I came out with very common solution in ASP.NET Core to solve this problem using raw SQL commands and shared views for DataTable and DataSet. Here’s what I did.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting DataTable from ORM
&lt;/h3&gt;

&lt;p&gt;It doesn’t matter if you use Entity Framework Core or NHibernate as they both give you access to database connection to execute direct queries. Here is the example for Entity Framework Core from my blog post &lt;a href="https://gunnarpeipman.com/ef-core-execute-raw-sql/"&gt;Execute raw SQL commands in Entity Framework Core&lt;/a&gt;. This method can be member of database context or some repository class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DataTable&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAverageTemperatures&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;forDate&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;command&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="nf"&gt;GetDbConnection&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;CreateCommand&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CommandText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"sp_AverageTemperaturesReport"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CommandType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CommandType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StoredProcedure&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;forDateParam&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateParameter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;forDateParam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParameterName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"@ForDate"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;forDateParam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DbType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DbType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;forDateParam&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;forDate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parameters&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;forDateParam&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;reader&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;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteReaderAsync&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;table&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;DataTable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;table&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It reads data from custom SQL to DataTable keeping data reader open while data is copied to DataTable. After this data reader is disposed and database connection is “free” again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building DataSet
&lt;/h3&gt;

&lt;p&gt;Those who remember early days of .NET should also remember DataSet class. It’s a container holding DataTable objects and relations between them. As I had to show multiple reports in one ASP.NET Core view I decided to go with DataSet. I ask DataTables and build DataSet of them in database context or repository method like shown here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DataSet&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetActiveCustomersReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;forDate&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;dataSet&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;DataSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Customers report"&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;table&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetTopGoldCustomersForDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Top gold customers"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;dataSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tables&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;table&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetTopSilverCustomersForDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Top silver customers"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;dataSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tables&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;table&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;table&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetTopActiveCustomersForDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;forDate&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TableName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Top active customers"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;dataSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Tables&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;table&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;dataSet&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;Using this method I get DataSet with all tables I need to show on report.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common view for DataTable
&lt;/h3&gt;

&lt;p&gt;For a moment I head back to my post &lt;a href="https://gunnarpeipman.com/asp-net-mvc-simple-view-to-display-contents-of-datatable/"&gt;Simple view to display contents of DataTable&lt;/a&gt; just to grab a common ASP.NET MVC DataTable view I proposed there. For your convenience the markup of view is here. I keep this view in shared views folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;@model System.Data.DataTable
@using System.Data;

&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;@Model.TableName&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;table&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;thead&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
            @foreach (DataColumn col in Model.Columns)
            {
                &lt;span class="nt"&gt;&amp;lt;th&amp;gt;&lt;/span&gt;@col.ColumnName&lt;span class="nt"&gt;&amp;lt;/th&amp;gt;&lt;/span&gt;
            }
        &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/thead&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;tbody&amp;gt;&lt;/span&gt;
        @foreach (DataRow row in Model.Rows)
        {
            &lt;span class="nt"&gt;&amp;lt;tr&amp;gt;&lt;/span&gt;
                @foreach (DataColumn col in Model.Columns)
                {
                    &lt;span class="nt"&gt;&amp;lt;td&amp;gt;&lt;/span&gt;@row[col.ColumnName]&lt;span class="nt"&gt;&amp;lt;/td&amp;gt;&lt;/span&gt;
                }
            &lt;span class="nt"&gt;&amp;lt;/tr&amp;gt;&lt;/span&gt;
        }
    &lt;span class="nt"&gt;&amp;lt;/tbody&amp;gt;&lt;/span&gt;
\&lt;span class="nt"&gt;&amp;lt;/table&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you need to display DataTable you can use this view from controller and provide DataTable as a model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common view for DataSet
&lt;/h3&gt;

&lt;p&gt;To display DataSet we need another common view. To keep it minimal I will re-use DataTable view shown above. This view goes also to shared views folder of ASP.NET Core application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;@model System.Data.DataSet

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;@Model.DataSetName&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

@foreach(var table in Model.Tables)
{
    &lt;span class="nt"&gt;&amp;lt;partial&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"DataTable"&lt;/span&gt; &lt;span class="na"&gt;model=&lt;/span&gt;&lt;span class="s"&gt;"table"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This view shows DataSet name and iterates through tables collection calling DataTable view for every table.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using common DataSet view
&lt;/h3&gt;

&lt;p&gt;There’s one final piece left to build – common view for DataSet. As it re-uses shared DataTable view it is small. Here it is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;@model System.Data.DataSet

&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;@Model.DataSetName&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

@foreach(var table in Model.Tables)
{
    &lt;span class="nt"&gt;&amp;lt;partial&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"DataTable"&lt;/span&gt; &lt;span class="na"&gt;model=&lt;/span&gt;&lt;span class="s"&gt;"table"&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here is the controller action that calls GetActiveCustomersReport() shown method above and uses DataSet view to show results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IActionResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;CustomersReport&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;report&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;_dbContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetActiveCustomersReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;View&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DataSet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;report&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;After applying some styling to views here’s the result – simple customers and orders report generated by SQL Server views and stored procedures and shown by commong views for DataSets and DataTables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FqurJjeh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/aspnet-core-dataset-view.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FqurJjeh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://static.gunnarpeipman.com/wp-content/uploads/2020/01/aspnet-core-dataset-view.png" alt="ASP.NET Core: DataSet based report" title="ASP.NET Core: DataSet based report"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Be warned!&lt;/strong&gt; It can be tempting to use this solution as a base for more advanced reporting and after first steps you are probably impressed how much it is possible to do with those common views. But be warned – this solution doesn’t replace any advanced reporting service or reporting components. It’s perfect when you just need to pump out some tabular reports to display data from database server but again – it is not base for advanced reporting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;If we need to show simple dynamic reports in ASP.NET Core application we can use raw SQL queries and common views for DataSet and DataTable. It’s easy ad-hoc style reporting where changes to application are not needed when queries or stored procedures are tweaked. I have used this approach successfully in practice from ASP.NET MVC times. It’s easy to extend this solution but don’t go too far – if you need advanced reporting then better go with professional reporting components or services.&lt;/p&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/aspnet-core-reporting-dataset-datatable/"&gt;DataSet and DataTable based ad-hoc reporting with ASP.NET Core&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>aspnet</category>
    </item>
    <item>
      <title>Behind the compiler: 20 examples of C# code before and after compiling</title>
      <dc:creator>Gunnar Peipman</dc:creator>
      <pubDate>Wed, 22 Jan 2020 05:00:16 +0000</pubDate>
      <link>https://dev.to/gpeipman/behind-the-compiler-20-examples-of-c-code-before-and-after-compiling-4of3</link>
      <guid>https://dev.to/gpeipman/behind-the-compiler-20-examples-of-c-code-before-and-after-compiling-4of3</guid>
      <description>&lt;p&gt;Over years I have written many blog posts about C# and .NET that demonstrate also how things work internally and what C# compiler produces from the code we write. I have called these chapters usually as “Behind the compiler”. &lt;/p&gt;

&lt;h3&gt;
  
  
  Behind the compiler posts
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-throw-expressions/"&gt;Throw expressions in C# 7.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-using-declarations/"&gt;Using-declarations in C# 8.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-expression-bodied-members/"&gt;Expression bodied members in C# 7.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-expression-bodied-members-2/"&gt;More expression-bodied members in C#&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-tuple-literals-internals/"&gt;Internals of tuple literals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-default-literal-expressions/"&gt;Default literal expressions in C# 7.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-partial-classes/"&gt;C# and Partial Classes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-non-trailing-named-arguments/"&gt;Non-trailing named arguments in C# 7.2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/il-visual-studio/"&gt;Writing IL code on Visual Studio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/il-perversions-throwing-and-catching-strings/"&gt;IL perversions: throwing and catching strings&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/var-keyword/"&gt;var keyword in C#&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-pattern-matching-switch/"&gt;Pattern matching in switch statements&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-backing-field-attribute/"&gt;Adding attribute to backing field of automated property&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-lock-monitor/"&gt;Are lock and Monitor the same in C#?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-async-main/"&gt;Deep dive to async Main&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-anonymous-types/"&gt;Anonymous types in C#&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-out-variables/"&gt;Out variables in C# 7.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-idisposable/"&gt;All about IDisposable&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/code-contracts-how-they-look-after-compiling/"&gt;Code Contracts: How they look after compiling?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gunnarpeipman.com/csharp-automatic-properties/"&gt;Automatic properties in C#&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The post &lt;a href="https://gunnarpeipman.com/behind-the-compiler/"&gt;Behind the compiler: 20 examples of C# code before and after compiling&lt;/a&gt; appeared first on &lt;a href="https://gunnarpeipman.com"&gt;Gunnar Peipman - Programming Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>visualstudio</category>
    </item>
  </channel>
</rss>
