<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blog | Giorgi Dalakishvili | Personal Website</title><link>https://www.giorgi.dev/blog/</link><atom:link href="https://www.giorgi.dev/blog/index.xml" rel="self" type="application/rss+xml"/><description>Blog</description><generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-us</language><copyright>© Giorgi Dalakishvili 2019 - 2026</copyright><lastBuildDate>Tue, 10 Mar 2026 13:00:00 +0400</lastBuildDate><image><url>img/map[gravatar:%!s(bool=false) shape:circle]</url><title>Blog</title><link>https://www.giorgi.dev/blog/</link></image><item><title>DuckDB.NET 1.5.0: Simplified Scalar Functions, Table Functions, and NULL Handling</title><link>https://www.giorgi.dev/database/duckdb-net-1-5-udf-improvements/</link><pubDate>Tue, 10 Mar 2026 13:00:00 +0400</pubDate><guid>https://www.giorgi.dev/database/duckdb-net-1-5-udf-improvements/</guid><description>&lt;p>In the
&lt;a href="https://www.giorgi.dev/database/duckdb-net-1-5-performance/">previous post&lt;/a>, I covered the performance improvements in DuckDB.NET 1.5.0. This post covers the API side: a new high-level registration API for scalar and table user-defined functions, named parameter support for table functions, and explicit NULL handling for scalar UDFs.&lt;/p>
&lt;h2 id="scalar-functions-before-and-after">Scalar Functions: Before and After&lt;/h2>
&lt;p>DuckDB.NET has supported scalar UDFs since version 1.0. The existing API gives you full control over vectors and row iteration - but even simple functions require boilerplate.&lt;/p>
&lt;p>Here&amp;rsquo;s an &lt;code>is_prime&lt;/code> function using the low-level API:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterScalarFunction&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>, &lt;span style="color:#66d9ef">bool&lt;/span>&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;is_prime&amp;#34;&lt;/span>, (readers, writer, rowCount) =&amp;gt;
{
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">ulong&lt;/span> index = &lt;span style="color:#ae81ff">0&lt;/span>; index &amp;lt; rowCount; index++)
{
&lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = readers[&lt;span style="color:#ae81ff">0&lt;/span>].GetValue&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt;(index);
&lt;span style="color:#66d9ef">var&lt;/span> prime = &lt;span style="color:#66d9ef">true&lt;/span>;
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i = &lt;span style="color:#ae81ff">2&lt;/span>; i &amp;lt;= Math.Sqrt(&lt;span style="color:#66d9ef">value&lt;/span>); i++)
{
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">value&lt;/span> % i == &lt;span style="color:#ae81ff">0&lt;/span>)
{
prime = &lt;span style="color:#66d9ef">false&lt;/span>;
&lt;span style="color:#66d9ef">break&lt;/span>;
}
}
writer.WriteValue(prime, index);
}
});
&lt;/code>&lt;/pre>&lt;/div>&lt;p>You have to manually iterate over rows, read values from input vectors by index, and write results to the output vector. This works, but it&amp;rsquo;s a lot of ceremony for a function that just checks if a number is prime.&lt;/p>
&lt;h3 id="high-level-scalar-functions">High-Level Scalar Functions&lt;/h3>
&lt;p>The new simplified API lets you register a scalar function with a plain &lt;code>Func&amp;lt;&amp;gt;&lt;/code> delegate. The framework handles row iteration and vector access automatically:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterScalarFunction&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>, &lt;span style="color:#66d9ef">bool&lt;/span>&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;is_prime&amp;#34;&lt;/span>, IsPrime);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>That&amp;rsquo;s it. The library wraps your function, iterates over each row in the chunk, reads the input value, calls your function, and writes the result.&lt;/p>
&lt;p>The API supports zero to four parameters, plus variable arguments:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#75715e">// Zero parameters
&lt;/span>&lt;span style="color:#75715e">&lt;/span>connection.RegisterScalarFunction(&lt;span style="color:#e6db74">&amp;#34;the_answer&amp;#34;&lt;/span>, () =&amp;gt; &lt;span style="color:#ae81ff">4&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>);
&lt;span style="color:#75715e">// Two parameters
&lt;/span>&lt;span style="color:#75715e">&lt;/span>connection.RegisterScalarFunction&amp;lt;&lt;span style="color:#66d9ef">long&lt;/span>, &lt;span style="color:#66d9ef">long&lt;/span>, &lt;span style="color:#66d9ef">long&lt;/span>&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;add&amp;#34;&lt;/span>, (a, b) =&amp;gt; a + b);
&lt;span style="color:#75715e">// Three parameters
&lt;/span>&lt;span style="color:#75715e">&lt;/span>connection.RegisterScalarFunction&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>, &lt;span style="color:#66d9ef">int&lt;/span>, &lt;span style="color:#66d9ef">int&lt;/span>, &lt;span style="color:#66d9ef">int&lt;/span>&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;clamp&amp;#34;&lt;/span>,
(&lt;span style="color:#66d9ef">value&lt;/span>, min, max) =&amp;gt; Math.Clamp(&lt;span style="color:#66d9ef">value&lt;/span>, min, max));
&lt;span style="color:#75715e">// Variable arguments
&lt;/span>&lt;span style="color:#75715e">&lt;/span>connection.RegisterScalarFunction(&lt;span style="color:#e6db74">&amp;#34;sum_all&amp;#34;&lt;/span>, (&lt;span style="color:#66d9ef">long&lt;/span>[] args) =&amp;gt; args.Sum());
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Variable argument functions receive all inputs as an array. DuckDB allows calling them with any number of arguments:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span> sum_all(&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>); &lt;span style="color:#75715e">-- 6
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> sum_all(&lt;span style="color:#ae81ff">10&lt;/span>); &lt;span style="color:#75715e">-- 10
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> sum_all(); &lt;span style="color:#75715e">-- 0
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="dynamic-type-support">Dynamic Type Support&lt;/h3>
&lt;p>The simplified API also supports &lt;code>object&lt;/code> as an input type, which maps to DuckDB&amp;rsquo;s &lt;code>ANY&lt;/code> type. This lets you write functions that accept any column type:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterScalarFunction&amp;lt;&lt;span style="color:#66d9ef">object&lt;/span>, &lt;span style="color:#66d9ef">string&lt;/span>&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;net_type_name&amp;#34;&lt;/span>,
&lt;span style="color:#66d9ef">value&lt;/span> =&amp;gt; &lt;span style="color:#66d9ef">value&lt;/span>.GetType().Name);
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span> net_type_name(&lt;span style="color:#ae81ff">42&lt;/span>); &lt;span style="color:#75715e">-- &amp;#39;Int32&amp;#39;
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> net_type_name(&lt;span style="color:#ae81ff">42&lt;/span>::BIGINT); &lt;span style="color:#75715e">-- &amp;#39;Int64&amp;#39;
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> net_type_name(&lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">hello&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>); &lt;span style="color:#75715e">-- &amp;#39;String&amp;#39;
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> net_type_name(&lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">2024-01-01&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>::DATE); &lt;span style="color:#75715e">-- &amp;#39;DateOnly&amp;#39;
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can combine &lt;code>object&lt;/code> with typed parameters. Here&amp;rsquo;s a .NET format function that accepts any formattable value:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterScalarFunction(&lt;span style="color:#e6db74">&amp;#34;format_net&amp;#34;&lt;/span>,
(&lt;span style="color:#66d9ef">object&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>, &lt;span style="color:#66d9ef">string&lt;/span> format) =&amp;gt; &lt;span style="color:#66d9ef">value&lt;/span> &lt;span style="color:#66d9ef">is&lt;/span> IFormattable f
? f.ToString(format, CultureInfo.InvariantCulture)
: &lt;span style="color:#66d9ef">value&lt;/span>.ToString());
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span> format_net(&lt;span style="color:#ae81ff">255&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">X&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>); &lt;span style="color:#75715e">-- &amp;#39;FF&amp;#39;
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> format_net(&lt;span style="color:#ae81ff">0&lt;/span>.&lt;span style="color:#ae81ff">15&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">P&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>); &lt;span style="color:#75715e">-- &amp;#39;15.00 %&amp;#39;
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> format_net(&lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">2024-11-06&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>::DATE, &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">yyyy/MM/dd&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>); &lt;span style="color:#75715e">-- &amp;#39;2024/11/06&amp;#39;
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="null-handling-for-scalar-functions">NULL Handling for Scalar Functions&lt;/h2>
&lt;p>By default, DuckDB short-circuits NULLs automatically: if any input to a scalar function is NULL, the result is NULL and your function is never called. This is the correct behavior for most functions, but sometimes you need to handle NULLs explicitly - for example, to implement COALESCE-like logic or to return a default value.&lt;/p>
&lt;p>With the high-level API, the opt-in is the type signature itself. Use a nullable parameter type and DuckDB.NET automatically detects it at registration time and configures the function to receive NULLs:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterScalarFunction&amp;lt;&lt;span style="color:#66d9ef">int?&lt;/span>, &lt;span style="color:#66d9ef">string&lt;/span>&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;describe_val&amp;#34;&lt;/span>,
x =&amp;gt; x.HasValue ? x.Value.ToString() : &lt;span style="color:#e6db74">&amp;#34;nothing&amp;#34;&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span> describe_val(&lt;span style="color:#ae81ff">42&lt;/span>); &lt;span style="color:#75715e">-- &amp;#39;42&amp;#39;
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> describe_val(&lt;span style="color:#66d9ef">NULL&lt;/span>::INT); &lt;span style="color:#75715e">-- &amp;#39;nothing&amp;#39;
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This works for reference types too - &lt;code>string?&lt;/code> (with nullable annotations enabled) opts into null handling, while &lt;code>string&lt;/code> keeps the default NULL propagation:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterScalarFunction&amp;lt;&lt;span style="color:#66d9ef">string?&lt;/span>, &lt;span style="color:#66d9ef">string&lt;/span>&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;echo_or_default&amp;#34;&lt;/span>,
s =&amp;gt; s ?? &lt;span style="color:#e6db74">&amp;#34;was_null&amp;#34;&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span> echo_or_default(&lt;span style="color:#66d9ef">NULL&lt;/span>::VARCHAR); &lt;span style="color:#75715e">-- &amp;#39;was_null&amp;#39;
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> echo_or_default(&lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">hello&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>); &lt;span style="color:#75715e">-- &amp;#39;hello&amp;#39;
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can mix nullable and non-nullable parameters. If any parameter is nullable, the function receives NULLs - but non-nullable parameters will throw a clear error if they get one:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterScalarFunction&amp;lt;&lt;span style="color:#66d9ef">int?&lt;/span>, &lt;span style="color:#66d9ef">int&lt;/span>, &lt;span style="color:#66d9ef">string&lt;/span>&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;coalesce_add&amp;#34;&lt;/span>,
(a, b) =&amp;gt; a.HasValue ? (a.Value + b).ToString() : b.ToString());
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span> coalesce_add(&lt;span style="color:#66d9ef">NULL&lt;/span>::INT, &lt;span style="color:#ae81ff">5&lt;/span>); &lt;span style="color:#75715e">-- &amp;#39;5&amp;#39;
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> coalesce_add(&lt;span style="color:#ae81ff">10&lt;/span>, &lt;span style="color:#ae81ff">5&lt;/span>); &lt;span style="color:#75715e">-- &amp;#39;15&amp;#39;
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>NULL handling also works with the low-level vector API. In scalar function callbacks, &lt;code>GetValue&amp;lt;T&amp;gt;&lt;/code> returns &lt;code>null&lt;/code> for NULL rows when &lt;code>T&lt;/code> is a nullable type (&lt;code>int?&lt;/code>, &lt;code>string&lt;/code>, etc.) instead of throwing, so you can write:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterScalarFunction&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">string&lt;/span>&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;echo_nullable&amp;#34;&lt;/span>, (readers, writer, rowCount) =&amp;gt;
{
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">ulong&lt;/span> i = &lt;span style="color:#ae81ff">0&lt;/span>; i &amp;lt; rowCount; i++)
{
&lt;span style="color:#66d9ef">var&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = readers[&lt;span style="color:#ae81ff">0&lt;/span>].GetValue&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>&amp;gt;(i);
writer.WriteValue(&lt;span style="color:#66d9ef">value&lt;/span> ?? &lt;span style="color:#e6db74">&amp;#34;was_null&amp;#34;&lt;/span>, i);
}
}, &lt;span style="color:#66d9ef">new&lt;/span>() { HandlesNulls = &lt;span style="color:#66d9ef">true&lt;/span> });
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="table-functions-before-and-after">Table Functions: Before and After&lt;/h2>
&lt;p>The low-level table function API requires you to define columns, return a &lt;code>TableFunction&lt;/code> with data, and provide a mapper callback that writes each row to output vectors:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterTableFunction&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt;(&lt;span style="color:#e6db74">&amp;#34;employees&amp;#34;&lt;/span>, parameters =&amp;gt;
{
&lt;span style="color:#66d9ef">var&lt;/span> count = parameters[&lt;span style="color:#ae81ff">0&lt;/span>].GetValue&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt;();
&lt;span style="color:#66d9ef">var&lt;/span> employees = Enumerable.Range(&lt;span style="color:#ae81ff">1&lt;/span>, count)
.Select(i =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> Employee(i, &lt;span style="color:#e6db74">$&amp;#34;Employee{i}&amp;#34;&lt;/span>, &lt;span style="color:#ae81ff">5&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span> + i * &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>));
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> TableFunction(
&lt;span style="color:#66d9ef">new&lt;/span> List&amp;lt;ColumnInfo&amp;gt; { &lt;span style="color:#66d9ef">new&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;id&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">typeof&lt;/span>(&lt;span style="color:#66d9ef">int&lt;/span>)), &lt;span style="color:#66d9ef">new&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;name&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">typeof&lt;/span>(&lt;span style="color:#66d9ef">string&lt;/span>)) },
employees);
},
(item, writers, rowIndex) =&amp;gt;
{
&lt;span style="color:#66d9ef">var&lt;/span> employee = (Employee)item!;
writers[&lt;span style="color:#ae81ff">0&lt;/span>].WriteValue(employee.Id, rowIndex);
writers[&lt;span style="color:#ae81ff">1&lt;/span>].WriteValue(employee.Name, rowIndex);
});
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The column definitions and mapper are separate, so they can easily get out of sync - add a column to the schema but forget the mapper, or reorder them differently.&lt;/p>
&lt;h3 id="high-level-table-functions">High-Level Table Functions&lt;/h3>
&lt;p>The new API uses a projection expression to define both the columns and the mapper in one place:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterTableFunction(&lt;span style="color:#e6db74">&amp;#34;employees&amp;#34;&lt;/span>,
(&lt;span style="color:#66d9ef">int&lt;/span> count) =&amp;gt; GetEmployees(count),
e =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> { e.Id, e.Name });
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The first argument is the function name. The second is a data function that receives the SQL parameters and returns an &lt;code>IEnumerable&amp;lt;T&amp;gt;&lt;/code>. The third is a projection expression that defines which properties to expose as columns - column names and types are extracted automatically from the expression.&lt;/p>
&lt;p>The projection supports anonymous types, object initializers, computed columns, and single properties:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#75715e">// Computed columns
&lt;/span>&lt;span style="color:#75715e">&lt;/span>connection.RegisterTableFunction(&lt;span style="color:#e6db74">&amp;#34;ext_computed&amp;#34;&lt;/span>,
(&lt;span style="color:#66d9ef">int&lt;/span> count) =&amp;gt; GetEmployees(count),
e =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> { FullName = &lt;span style="color:#e6db74">&amp;#34;Dr. &amp;#34;&lt;/span> + e.Name, DoubleSalary = e.Salary * &lt;span style="color:#ae81ff">2&lt;/span> });
&lt;span style="color:#75715e">// Object initializer
&lt;/span>&lt;span style="color:#75715e">&lt;/span>connection.RegisterTableFunction(&lt;span style="color:#e6db74">&amp;#34;ext_init&amp;#34;&lt;/span>,
(&lt;span style="color:#66d9ef">int&lt;/span> count) =&amp;gt; GetEmployees(count),
e =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> EmployeeDto { Id = e.Id, Name = e.Name });
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Like the scalar API, table functions support zero to four parameters:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#75715e">// No parameters
&lt;/span>&lt;span style="color:#75715e">&lt;/span>connection.RegisterTableFunction(&lt;span style="color:#e6db74">&amp;#34;all_employees&amp;#34;&lt;/span>,
() =&amp;gt; employees.AsEnumerable(),
e =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> { e.Id, e.Name });
&lt;span style="color:#75715e">// Four parameters
&lt;/span>&lt;span style="color:#75715e">&lt;/span>connection.RegisterTableFunction(&lt;span style="color:#e6db74">&amp;#34;ext_four&amp;#34;&lt;/span>,
(&lt;span style="color:#66d9ef">int&lt;/span> start, &lt;span style="color:#66d9ef">int&lt;/span> count, &lt;span style="color:#66d9ef">string&lt;/span> prefix, &lt;span style="color:#66d9ef">double&lt;/span> multiplier) =&amp;gt;
Enumerable.Range(start, count).Select(i =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> Employee(i, &lt;span style="color:#e6db74">$&amp;#34;{prefix}{i}&amp;#34;&lt;/span>, i * multiplier)),
e =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> { e.Id, e.Name, e.Salary });
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Async data sources work too - just call &lt;code>ToBlockingEnumerable()&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterTableFunction(&lt;span style="color:#e6db74">&amp;#34;ext_async&amp;#34;&lt;/span>,
(&lt;span style="color:#66d9ef">int&lt;/span> count) =&amp;gt; FetchEmployeesAsync(count).ToBlockingEnumerable(),
e =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> { e.Id, e.Name });
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="named-parameters-for-table-functions">Named Parameters for Table Functions&lt;/h2>
&lt;p>DuckDB supports named parameters for table functions. DuckDB.NET 1.5.0 exposes this with the &lt;code>[Named]&lt;/code> attribute:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterTableFunction(&lt;span style="color:#e6db74">&amp;#34;employees&amp;#34;&lt;/span>,
(&lt;span style="color:#66d9ef">int&lt;/span> count, [Named] &lt;span style="color:#66d9ef">string?&lt;/span> prefix) =&amp;gt;
GetEmployees(count).Select(e =&amp;gt; e with { Name = (prefix ?? &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>) + e.Name }),
e =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> { e.Id, e.Name });
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In SQL, positional parameters come first, and named parameters use the &lt;code>=&lt;/code> syntax:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">FROM&lt;/span> employees(&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#66d9ef">prefix&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">Dr. &lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>);
&lt;span style="color:#75715e">-- (1, &amp;#39;Dr. Employee1&amp;#39;), (2, &amp;#39;Dr. Employee2&amp;#39;), (3, &amp;#39;Dr. Employee3&amp;#39;)
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#75715e">-- Named parameters are optional - omit them and they&amp;#39;re NULL
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">FROM&lt;/span> employees(&lt;span style="color:#ae81ff">3&lt;/span>);
&lt;span style="color:#75715e">-- (1, &amp;#39;Employee1&amp;#39;), (2, &amp;#39;Employee2&amp;#39;), (3, &amp;#39;Employee3&amp;#39;)
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can have multiple named parameters:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterTableFunction(&lt;span style="color:#e6db74">&amp;#34;employees&amp;#34;&lt;/span>,
(&lt;span style="color:#66d9ef">int&lt;/span> count, [Named] &lt;span style="color:#66d9ef">string?&lt;/span> prefix, [Named] &lt;span style="color:#66d9ef">double?&lt;/span> multiplier) =&amp;gt;
GetEmployees(count).Select(e =&amp;gt; e with
{
Name = (prefix ?? &lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>) + e.Name,
Salary = e.Salary * (multiplier ?? &lt;span style="color:#ae81ff">1&lt;/span>)
}),
e =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> { e.Id, e.Name, e.Salary });
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#75715e">-- Provide both
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">FROM&lt;/span> employees(&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#66d9ef">prefix&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">X&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>, multiplier &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>.&lt;span style="color:#ae81ff">0&lt;/span>);
&lt;span style="color:#75715e">-- Provide only one
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">FROM&lt;/span> employees(&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#66d9ef">prefix&lt;/span> &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">Y&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>By default, the SQL parameter name matches the C# parameter name. You can override it with a custom name:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">connection.RegisterTableFunction(&lt;span style="color:#e6db74">&amp;#34;employees&amp;#34;&lt;/span>,
(&lt;span style="color:#66d9ef">int&lt;/span> count, [Named(&lt;span style="color:#e6db74">&amp;#34;max_rows&amp;#34;&lt;/span>)] &lt;span style="color:#66d9ef">int?&lt;/span> limit) =&amp;gt; GetEmployees(limit ?? count),
e =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> { e.Id, e.Name });
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">SELECT&lt;/span> &lt;span style="color:#f92672">*&lt;/span> &lt;span style="color:#66d9ef">FROM&lt;/span> employees(&lt;span style="color:#ae81ff">10&lt;/span>, max_rows &lt;span style="color:#f92672">=&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Named parameters should typically be nullable types (&lt;code>string?&lt;/code>, &lt;code>int?&lt;/code>) since they&amp;rsquo;re optional in SQL. If a named parameter is non-nullable and the caller omits it, DuckDB.NET throws a clear error:&lt;/p>
&lt;pre>&lt;code>Table function 'employees' named parameter 'limit' is NULL, but parameter type 'Int32' is non-nullable.
&lt;/code>&lt;/pre>&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>DuckDB.NET 1.5.0 adds four improvements to user-defined functions:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>High-level scalar functions&lt;/strong>: Register with a plain &lt;code>Func&amp;lt;&amp;gt;&lt;/code> instead of manually iterating vectors. Supports 0–4 parameters, variable arguments, and dynamic &lt;code>object&lt;/code> type.&lt;/li>
&lt;li>&lt;strong>High-level table functions&lt;/strong>: Define columns and mapping with a single projection expression. Supports 0–4 parameters, computed columns, and async data sources.&lt;/li>
&lt;li>&lt;strong>NULL handling for scalar functions&lt;/strong>: Use nullable parameter types (&lt;code>int?&lt;/code>, &lt;code>string?&lt;/code>) and DuckDB.NET automatically configures the function to receive NULLs instead of short-circuiting.&lt;/li>
&lt;li>&lt;strong>Named parameters for table functions&lt;/strong>: The &lt;code>[Named]&lt;/code> attribute maps C# parameters to DuckDB&amp;rsquo;s &lt;code>=&lt;/code> syntax for optional, named arguments.&lt;/li>
&lt;/ul>
&lt;p>The low-level vector-based APIs remain available for cases where you need direct control over chunk processing.&lt;/p></description></item><item><title>DuckDB.NET 1.5.0 Performance: Up to 40% Faster Writes and 22% Fewer Allocations</title><link>https://www.giorgi.dev/database/duckdb-net-1-5-performance/</link><pubDate>Tue, 03 Mar 2026 05:00:00 +0400</pubDate><guid>https://www.giorgi.dev/database/duckdb-net-1-5-performance/</guid><description>&lt;h2 id="whats-new-in-duckdbnet-150">What&amp;rsquo;s New in DuckDB.NET 1.5.0&lt;/h2>
&lt;p>
&lt;a href="https://github.com/Giorgi/DuckDB.NET" target="_blank" rel="noopener">DuckDB.NET&lt;/a> 1.5.0 focuses on performance. The codebase has been optimized across multiple layers - from the low-level native interop (
&lt;a href="https://github.com/Giorgi/DuckDB.NET/commit/370ca9070710784258cde85e43a88144f8200b6f" target="_blank" rel="noopener">LibraryImport migration&lt;/a>,
&lt;a href="https://github.com/Giorgi/DuckDB.NET/commit/377c3a1c76e2117015c0b29b4896b4535ac15cce" target="_blank" rel="noopener">SuppressGCTransition&lt;/a>) to the ADO.NET provider (reader reuse, appender boxing elimination) and type converters (decimal rewrite, BigNum conversion). The results show up across every major code path: reading, writing, and type conversion.&lt;/p>
&lt;p>Note that the current pre-release (1.5.0-alpha) still uses the DuckDB 1.4.4 native library under the hood - the performance gains come entirely from improvements on the .NET side. The stable DuckDB.NET 1.5.0 release will ship with DuckDB 1.5.0 once it becomes available.&lt;/p>
&lt;h2 id="what-changed">What Changed&lt;/h2>
&lt;h3 id="libraryimport-migration">LibraryImport Migration&lt;/h3>
&lt;p>All P/Invoke declarations have been migrated from &lt;code>[DllImport]&lt;/code> to the source-generated &lt;code>[LibraryImport]&lt;/code>. The runtime no longer needs to generate marshalling stubs at JIT time - the source generator produces them at compile time, eliminating stub overhead on every native call. As part of this migration, string returns now use custom marshallers (&lt;code>DuckDBOwnedStringMarshaller&lt;/code> and &lt;code>DuckDBCallerOwnedStringMarshaller&lt;/code>) that correctly and transparently handle ownership semantics - whether DuckDB or the caller is responsible for freeing the memory.&lt;/p>
&lt;h3 id="suppressgctransition-on-fast-native-calls">SuppressGCTransition on Fast Native Calls&lt;/h3>
&lt;p>Many DuckDB C API calls are trivially fast - retrieving a vector data pointer, checking validity, getting chunk size. These calls complete in nanoseconds, but the .NET runtime&amp;rsquo;s GC transition (cooperative → preemptive → cooperative) adds measurable overhead on each call. Adding &lt;code>[SuppressGCTransition]&lt;/code> to these methods skips the transition entirely. This is only safe for native functions that execute in under a microsecond, perform no blocking syscalls or I/O, don&amp;rsquo;t call back into the runtime, don&amp;rsquo;t throw exceptions, and don&amp;rsquo;t manipulate locks. The attribute was applied to every DuckDB C API method that meets these criteria - primarily vector data and validity pointer access, chunk size queries, and similar lightweight operations.&lt;/p>
&lt;h3 id="aggressiveinlining-on-hot-path-methods">AggressiveInlining on Hot-Path Methods&lt;/h3>
&lt;p>&lt;code>[MethodImpl(MethodImplOptions.AggressiveInlining)]&lt;/code> was added to frequently called methods in the reader and writer paths - including &lt;code>IsValid()&lt;/code>, &lt;code>GetFieldData&amp;lt;T&amp;gt;()&lt;/code>, &lt;code>AppendValueInternal&amp;lt;T&amp;gt;()&lt;/code>, and type conversion helpers. For example, &lt;code>IsValid()&lt;/code> and &lt;code>GetFieldData&amp;lt;T&amp;gt;()&lt;/code> are called for every column of every row. At 100,000 rows and 20 columns, that&amp;rsquo;s 2 million calls per query. Inlining these small methods directly into the read/write loops removes call overhead.&lt;/p>
&lt;h3 id="appender-boxing-elimination">Appender Boxing Elimination&lt;/h3>
&lt;p>In 1.4.4, all &lt;code>AppendValue()&lt;/code> overloads funneled into a single generic &lt;code>AppendValueInternal&amp;lt;T&amp;gt;(T? value)&lt;/code> without a &lt;code>struct&lt;/code> constraint. When &lt;code>T&lt;/code> is a value type, the nullable wrapper was boxed on every call. In 1.5.0, this is split into &lt;code>struct&lt;/code>-constrained and &lt;code>class&lt;/code>-constrained overloads - the struct path unwraps &lt;code>Nullable&amp;lt;T&amp;gt;&lt;/code> directly via &lt;code>HasValue&lt;/code>/&lt;code>Value&lt;/code>, eliminating the boxing. With 8 columns per row, that&amp;rsquo;s 6 fewer heap allocations per row for a typical mixed-type schema.&lt;/p>
&lt;h3 id="decimal-conversion-rewrite">Decimal Conversion Rewrite&lt;/h3>
&lt;p>The decimal reader was rewritten for all four internal storage paths:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>SmallInt/Integer/BigInt paths&lt;/strong>: Replaced &lt;code>decimal.Divide(raw, powerOfTen)&lt;/code> with the direct constructor &lt;code>new decimal(Math.Abs(raw), 0, 0, raw &amp;lt; 0, scale)&lt;/code>. This constructs the decimal from its binary components without any arithmetic.&lt;/li>
&lt;li>&lt;strong>HugeInt path&lt;/strong>: Uses &lt;code>BigInteger.DivRem&lt;/code> with pre-computed &lt;code>BigInteger[]&lt;/code> powers of ten instead of repeated intermediate BigInteger arithmetic.&lt;/li>
&lt;li>&lt;strong>Pre-computed lookup tables&lt;/strong>: Static &lt;code>decimal[]&lt;/code> and &lt;code>BigInteger[]&lt;/code> arrays for powers of ten (scales 0–28 and 0–38 respectively) are computed once at startup and reused for all conversions.&lt;/li>
&lt;/ul>
&lt;h3 id="vector-reader-reuse-across-chunks">Vector Reader Reuse Across Chunks&lt;/h3>
&lt;p>DuckDB returns data in chunks of up to 2,048 rows. In 1.4.4, new &lt;code>VectorDataReader&lt;/code> instances were allocated for each chunk. In 1.5.0, readers implement &lt;code>Reset(IntPtr vector)&lt;/code> which updates data and validity pointers in place, reusing the same reader objects across all chunks in a result set. Composite readers (struct, list, map, decimal) override &lt;code>Reset&lt;/code> to also update their nested child readers.&lt;/p>
&lt;h3 id="bignum-on-conversion">BIGNUM O(n) Conversion&lt;/h3>
&lt;p>The &lt;code>BIGNUM&lt;/code> (previously &lt;code>VarInt&lt;/code>) to &lt;code>BigInteger&lt;/code> conversion was rewritten from an O(n²) digit-by-digit algorithm to a direct O(n) construction using &lt;code>BigInteger&lt;/code>'s byte-span constructor. For positive values, the raw bytes are passed directly. For negative values, a byte complement is needed - small payloads (≤128 bytes) use &lt;code>stackalloc&lt;/code> for this, larger ones rent from &lt;code>ArrayPool&amp;lt;byte&amp;gt;&lt;/code>. The result: &lt;strong>93% faster&lt;/strong> reads and &lt;strong>98-99% fewer allocations&lt;/strong> at 10K–100K rows.&lt;/p>
&lt;h2 id="benchmarks">Benchmarks&lt;/h2>
&lt;p>All benchmarks compare &lt;strong>DuckDB.NET.Data.Full 1.4.4&lt;/strong> (stable, from NuGet.org) against &lt;strong>1.5.0-alpha.35&lt;/strong> (pre-release, from GitHub) using
&lt;a href="https://github.com/dotnet/BenchmarkDotNet" target="_blank" rel="noopener">BenchmarkDotNet&lt;/a>. Each version is compiled and run independently using &lt;code>WithMsBuildArguments&lt;/code> to swap the package version at build time, so both versions run against the same benchmark code with no shared state.&lt;/p>
&lt;h3 id="reader-17-22-faster">Reader: 17-22% Faster&lt;/h3>
&lt;p>The reader benchmark creates a table with a mix of column types (INT, VARCHAR, DOUBLE, BOOLEAN, DECIMAL, BIGINT, TIMESTAMP) and reads 100,000 rows using typed getters (&lt;code>GetInt32&lt;/code>, &lt;code>GetString&lt;/code>, &lt;code>GetDecimal&lt;/code>, etc.).&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Method&lt;/th>
&lt;th>Job&lt;/th>
&lt;th>ColumnCount&lt;/th>
&lt;th align="right">Mean&lt;/th>
&lt;th align="right">Ratio&lt;/th>
&lt;th align="right">Allocated&lt;/th>
&lt;th align="right">Alloc Ratio&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>ReadAllColumns&lt;/td>
&lt;td>alpha-1.5.0&lt;/td>
&lt;td>5&lt;/td>
&lt;td align="right">20.45 ms&lt;/td>
&lt;td align="right">-22%&lt;/td>
&lt;td align="right">3.82 MB&lt;/td>
&lt;td align="right">-1%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ReadAllColumns&lt;/td>
&lt;td>stable-1.4.4&lt;/td>
&lt;td>5&lt;/td>
&lt;td align="right">26.12 ms&lt;/td>
&lt;td align="right">baseline&lt;/td>
&lt;td align="right">3.85 MB&lt;/td>
&lt;td align="right">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ReadAllColumns&lt;/td>
&lt;td>alpha-1.5.0&lt;/td>
&lt;td>10&lt;/td>
&lt;td align="right">44.15 ms&lt;/td>
&lt;td align="right">-17%&lt;/td>
&lt;td align="right">7.63 MB&lt;/td>
&lt;td align="right">-1%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ReadAllColumns&lt;/td>
&lt;td>stable-1.4.4&lt;/td>
&lt;td>10&lt;/td>
&lt;td align="right">53.60 ms&lt;/td>
&lt;td align="right">baseline&lt;/td>
&lt;td align="right">7.68 MB&lt;/td>
&lt;td align="right">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ReadAllColumns&lt;/td>
&lt;td>alpha-1.5.0&lt;/td>
&lt;td>20&lt;/td>
&lt;td align="right">89.56 ms&lt;/td>
&lt;td align="right">-17%&lt;/td>
&lt;td align="right">11.45 MB&lt;/td>
&lt;td align="right">-1%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>ReadAllColumns&lt;/td>
&lt;td>stable-1.4.4&lt;/td>
&lt;td>20&lt;/td>
&lt;td align="right">107.50 ms&lt;/td>
&lt;td align="right">baseline&lt;/td>
&lt;td align="right">11.56 MB&lt;/td>
&lt;td align="right">&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>Allocations are nearly identical because both versions allocate the same &lt;code>string&lt;/code> objects for VARCHAR columns. The speedup comes from &lt;code>LibraryImport&lt;/code>, &lt;code>SuppressGCTransition&lt;/code>, and &lt;code>AggressiveInlining&lt;/code> working together on the hot read path.&lt;/p>
&lt;h3 id="appender-20-40-faster-22-less-memory">Appender: 20-40% Faster, 22% Less Memory&lt;/h3>
&lt;p>The appender benchmark creates rows with 8 columns (INT, VARCHAR, DOUBLE, BOOLEAN, DECIMAL, TIMESTAMP, BIGINT, VARCHAR) using the &lt;code>CreateRow().AppendValue(...).EndRow()&lt;/code> API.&lt;/p>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Method&lt;/th>
&lt;th>Job&lt;/th>
&lt;th>RowCount&lt;/th>
&lt;th align="right">Mean&lt;/th>
&lt;th align="right">Ratio&lt;/th>
&lt;th align="right">Allocated&lt;/th>
&lt;th align="right">Alloc Ratio&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>AppendRows&lt;/td>
&lt;td>alpha-1.5.0&lt;/td>
&lt;td>10,000&lt;/td>
&lt;td align="right">19.58 ms&lt;/td>
&lt;td align="right">-20%&lt;/td>
&lt;td align="right">8.85 MB&lt;/td>
&lt;td align="right">-22%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AppendRows&lt;/td>
&lt;td>stable-1.4.4&lt;/td>
&lt;td>10,000&lt;/td>
&lt;td align="right">24.41 ms&lt;/td>
&lt;td align="right">baseline&lt;/td>
&lt;td align="right">11.36 MB&lt;/td>
&lt;td align="right">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AppendRows&lt;/td>
&lt;td>alpha-1.5.0&lt;/td>
&lt;td>100,000&lt;/td>
&lt;td align="right">86.90 ms&lt;/td>
&lt;td align="right">-41%&lt;/td>
&lt;td align="right">89.20 MB&lt;/td>
&lt;td align="right">-22%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AppendRows&lt;/td>
&lt;td>stable-1.4.4&lt;/td>
&lt;td>100,000&lt;/td>
&lt;td align="right">147.46 ms&lt;/td>
&lt;td align="right">baseline&lt;/td>
&lt;td align="right">114.38 MB&lt;/td>
&lt;td align="right">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td>&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;td align="right">&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AppendRows&lt;/td>
&lt;td>alpha-1.5.0&lt;/td>
&lt;td>1,000,000&lt;/td>
&lt;td align="right">803.55 ms&lt;/td>
&lt;td align="right">-40%&lt;/td>
&lt;td align="right">899.61 MB&lt;/td>
&lt;td align="right">-22%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>AppendRows&lt;/td>
&lt;td>stable-1.4.4&lt;/td>
&lt;td>1,000,000&lt;/td>
&lt;td align="right">1,349.50 ms&lt;/td>
&lt;td align="right">baseline&lt;/td>
&lt;td align="right">1151.38 MB&lt;/td>
&lt;td align="right">&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>The consistent 22% allocation reduction is the boxing elimination at work. At 1 million rows with 8 columns, that&amp;rsquo;s millions of heap allocations removed. The 40% speed gain comes from boxing elimination, reduced GC pressure from fewer allocations, and &lt;code>AggressiveInlining&lt;/code> on the write path.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th>Area&lt;/th>
&lt;th>Speedup&lt;/th>
&lt;th>Memory Reduction&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td>Reader (mixed types)&lt;/td>
&lt;td>17-22%&lt;/td>
&lt;td>~1%&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td>Appender (8 columns)&lt;/td>
&lt;td>20-40%&lt;/td>
&lt;td>22%&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;p>These improvements require no code changes - just update the DuckDB.NET.Data package to 1.5.0 and the optimizations apply automatically.&lt;/p>
&lt;p>In the next post, I&amp;rsquo;ll cover the API improvements in 1.5.0 - including a simplified API for scalar and table user-defined functions.&lt;/p></description></item><item><title>PostgreSQL range types and Entity Framework Core</title><link>https://www.giorgi.dev/database/postgresql-range-types-entity-framework/</link><pubDate>Thu, 21 Dec 2023 01:01:02 +0400</pubDate><guid>https://www.giorgi.dev/database/postgresql-range-types-entity-framework/</guid><description>&lt;blockquote>
&lt;p>This blog post is part of
&lt;a href="https://www.csadvent.christmas/" target="_blank" rel="noopener">The C# Advent Calendar&lt;/a>, a series of 50 posts about C#. Be sure to check out the rest of the blog posts in the calendar!&lt;/p>
&lt;/blockquote>
&lt;h2 id="ranges-in-relational-databases">Ranges in Relational Databases&lt;/h2>
&lt;p>When modeling scheduling applications, such as an application for managing calendar events or hotel bookings, it is needed to store the start date(time) and end date(time) of the events. It is also common to store price history or market data with an associated validity period. Finally, if you are building a system where users can define price based on the order quantity (If you buy less than five items, the unit price is $100, between 5 and 10 unit price is $90, etc), you need to store the starting and ending quantities.&lt;/p>
&lt;p>The typical solution for such scenarios is to store the range in two separate columns (such as &lt;code>StartDate&lt;/code> and &lt;code>EndDate&lt;/code>) and use a query like this to lookup for the events that occur at a particular time: &lt;code>SELECT * FROM events WHERE @lookupTime BETWEEN StartDate AND EndDate&lt;/code>. While this works in simple cases, there is no easy way to
&lt;a href="https://stackoverflow.com/questions/12035747/date-range-overlapping-check-constraint" target="_blank" rel="noopener">make sure that no two events overlap&lt;/a>, and more advanced scenarios can quickly become complex to support.&lt;/p>
&lt;p>In this article, I will show how to use
&lt;a href="https://www.postgresql.org/docs/current/rangetypes.html" target="_blank" rel="noopener">PostgreSQL range types&lt;/a> to represent ranges, how it simplifies querying and can guarantee non-overlapping periods in the database, and how to use them with Entity Framework Core.&lt;/p>
&lt;h2 id="ranges-in-postgresql">Ranges in PostgreSQL&lt;/h2>
&lt;p>Range types in PostgreSQL represent a range of values for their underlying type. Postgres comes with several built-in range types: &lt;code>int4range&lt;/code> and &lt;code>int8range&lt;/code> represent ranges of &lt;code>int&lt;/code> and &lt;code>bigint&lt;/code>, &lt;code>numrange&lt;/code> - ranges of numerics, &lt;code>tstzrange&lt;/code>, &lt;code>daterange&lt;/code>, and &lt;code>tsrange&lt;/code> represent ranges of &lt;code>timestamptz&lt;/code>, &lt;code>date&lt;/code>, and &lt;code>time&lt;/code> types. You can use these types just like any other:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">CREATE&lt;/span> &lt;span style="color:#66d9ef">TABLE&lt;/span> &lt;span style="color:#66d9ef">IF&lt;/span> &lt;span style="color:#66d9ef">NOT&lt;/span> &lt;span style="color:#66d9ef">EXISTS&lt;/span> &lt;span style="color:#66d9ef">public&lt;/span>.&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Meetings&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
(
&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Id&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> integer &lt;span style="color:#66d9ef">NOT&lt;/span> &lt;span style="color:#66d9ef">NULL&lt;/span> &lt;span style="color:#66d9ef">GENERATED&lt;/span> &lt;span style="color:#66d9ef">BY&lt;/span> &lt;span style="color:#66d9ef">DEFAULT&lt;/span> &lt;span style="color:#66d9ef">AS&lt;/span> &lt;span style="color:#66d9ef">IDENTITY&lt;/span> ( &lt;span style="color:#66d9ef">INCREMENT&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#66d9ef">START&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#66d9ef">MINVALUE&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span> &lt;span style="color:#66d9ef">MAXVALUE&lt;/span> &lt;span style="color:#ae81ff">2147483647&lt;/span> &lt;span style="color:#66d9ef">CACHE&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span> ),
&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> character varying(&lt;span style="color:#ae81ff">20&lt;/span>) &lt;span style="color:#66d9ef">NOT&lt;/span> &lt;span style="color:#66d9ef">NULL&lt;/span>,
&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Title&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> character varying(&lt;span style="color:#ae81ff">50&lt;/span>) &lt;span style="color:#66d9ef">NOT&lt;/span> &lt;span style="color:#66d9ef">NULL&lt;/span>,
&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> tstzrange &lt;span style="color:#66d9ef">NOT&lt;/span> &lt;span style="color:#66d9ef">NULL&lt;/span>,
&lt;span style="color:#66d9ef">CONSTRAINT&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">PK_Meetings&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">PRIMARY&lt;/span> &lt;span style="color:#66d9ef">KEY&lt;/span> (&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Id&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)
)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this example, the &lt;code>Time&lt;/code> column represents the start time and end time of the corresponding meeting. Next, let&amp;rsquo;s insert a couple of records:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">INSERT&lt;/span> &lt;span style="color:#66d9ef">INTO&lt;/span> &lt;span style="color:#66d9ef">public&lt;/span>.&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Meetings&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Title&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)
&lt;span style="color:#66d9ef">VALUES&lt;/span> (&lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">Winterfell&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">PostgreSQL Demo&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">[2023-12-23 11:30, 2023-12-23 12:30)&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>);
&lt;span style="color:#66d9ef">INSERT&lt;/span> &lt;span style="color:#66d9ef">INTO&lt;/span> &lt;span style="color:#66d9ef">public&lt;/span>.&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Meetings&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Title&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)
&lt;span style="color:#66d9ef">VALUES&lt;/span> (&lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">Jobria&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">Npgsql Demo&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">[2023-12-23 13:00, 2023-12-23 13:30]&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>);
&lt;span style="color:#66d9ef">INSERT&lt;/span> &lt;span style="color:#66d9ef">INTO&lt;/span> &lt;span style="color:#66d9ef">public&lt;/span>.&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Meetings&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Title&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)
&lt;span style="color:#66d9ef">VALUES&lt;/span> (&lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">Casterly Rock&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">Upgrade PostgreSQL&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">[2023-12-23 14:00, 2023-12-23 15:30]&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Range values are created by specifying the start and end bounds, and they follow mathematical notation for interval: A square bracket means inclusive bound, and parentheses means exclusive bound.&lt;/p>
&lt;h3 id="range-operators-in-postgresql">Range Operators in PostgreSQL&lt;/h3>
&lt;p>The real power of PostgreSQL ranges comes from
&lt;a href="https://www.postgresql.org/docs/14/functions-range.html" target="_blank" rel="noopener">range operators&lt;/a>. The most commonly used operators are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>&amp;amp;&amp;amp;&lt;/code> - Overlap operator, tests if two ranges overlap.&lt;/li>
&lt;li>&lt;code>@&amp;gt;&lt;/code> and &lt;code>&amp;lt;@&lt;/code> - Containment operators test if the first range contains the second one or vice versa.&lt;/li>
&lt;li>&lt;code>-|-&lt;/code> - Checks if two ranges are adjacent.&lt;/li>
&lt;li>&lt;code>*&lt;/code> - Computes intersection of two ranges.&lt;/li>
&lt;/ul>
&lt;p>Let&amp;rsquo;s see some real-life examples:&lt;/p>
&lt;ul>
&lt;li>Find all meetings that occur in the specified time range:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">Select&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Title&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">from&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Meetings&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">where&lt;/span>
&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;lt;&lt;/span>&lt;span style="color:#f92672">@&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">[2023-12-23 13:00, 2023-12-23 15:00]&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>::tstzrange
&lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Title&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span>
&lt;span style="color:#f92672">|&lt;/span>&lt;span style="color:#75715e">------------|---------------|-----------------------------------------------------------|
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Jobria&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Npgsql Demo&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">[&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 13:00:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">,&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 13:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">]&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span>
&lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#f92672">|&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Find all meetings that overlap with the specified time range:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">Select&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Title&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">from&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Meetings&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">where&lt;/span>
&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;amp;&lt;/span>&lt;span style="color:#f92672">&amp;amp;&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">[2023-12-23 13:00, 2023-12-23 15:00]&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>::tstzrange
&lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Title&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span>
&lt;span style="color:#f92672">|&lt;/span>&lt;span style="color:#75715e">-----------------|----------------------|-----------------------------------------------------------|
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Jobria&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Npgsql Demo&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">[&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 13:00:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">,&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 13:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">]&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span>
&lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Casterly Rock&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Upgrade PostgreSQL&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">[&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 14:00:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">,&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 15:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">]&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span>
&lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#f92672">|&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Find all busy times in the specified time range:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">Select&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Title&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>,
&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">*&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">[2023-12-23 13:00, 2023-12-23 15:00]&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>::tstzrange &lt;span style="color:#66d9ef">as&lt;/span> BusyTime
&lt;span style="color:#66d9ef">from&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Meetings&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">where&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">&amp;amp;&lt;/span>&lt;span style="color:#f92672">&amp;amp;&lt;/span> &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">[2023-12-23 13:00, 2023-12-23 15:00]&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>::tstzrange
&lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Title&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">BusyTime&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span>
&lt;span style="color:#f92672">|&lt;/span>&lt;span style="color:#75715e">-----------------|----------------------|-----------------------------------------------------------|-----------------------------------------------------------|
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Jobria&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Npgsql Demo&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">[&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 13:00:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">,&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 13:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">]&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">[&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 13:00:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">,&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 13:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">]&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span>
&lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Casterly Rock&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Upgrade PostgreSQL&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">[&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 14:00:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">,&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 15:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">]&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">[&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 14:00:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">,&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 15:00:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&amp;#34;&lt;/span>&lt;span style="color:#e6db74">]&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">|&lt;/span>
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="ranges-exclusion-constraints">Ranges Exclusion Constraints&lt;/h3>
&lt;p>Finally, let&amp;rsquo;s make sure that no two events are happening at the same time in the same room. First, you need to load
&lt;a href="https://www.postgresql.org/docs/current/btree-gist.html" target="_blank" rel="noopener">btree_gist&lt;/a> extension by running the following command: &lt;code>CREATE EXTENSION btree_gist;&lt;/code>. Next, create an
&lt;a href="https://www.postgresql.org/docs/current/sql-createtable.html#SQL-CREATETABLE-EXCLUDE" target="_blank" rel="noopener">Exclusion Constraint&lt;/a> that guarantees that there are no overlapping periods for any given room:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">Alter&lt;/span> &lt;span style="color:#66d9ef">Table&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Meetings&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;span style="color:#66d9ef">Add&lt;/span> &lt;span style="color:#66d9ef">Constraint&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">NoOverlap&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
EXCLUDE &lt;span style="color:#66d9ef">USING&lt;/span> gist (&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">WITH&lt;/span> &lt;span style="color:#f92672">=&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#66d9ef">WITH&lt;/span> &lt;span style="color:#f92672">&amp;amp;&lt;/span>&lt;span style="color:#f92672">&amp;amp;&lt;/span>)
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This &lt;code>Exclude&lt;/code> constraint checks that no two rows with the same &lt;code>RoomName&lt;/code> have &lt;code>Time&lt;/code> values that overlap. With this constraint in place, if you try to insert an overlapping record, the insert will fail:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">&lt;span style="color:#66d9ef">INSERT&lt;/span> &lt;span style="color:#66d9ef">INTO&lt;/span> &lt;span style="color:#66d9ef">public&lt;/span>.&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Meetings&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Title&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)
&lt;span style="color:#66d9ef">VALUES&lt;/span> (&lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">Winterfell&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">Overlapping Event&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>, &lt;span style="color:#e6db74">&amp;#39;&lt;/span>&lt;span style="color:#e6db74">[2023-12-23 12:00, 2023-12-23 12:30]&lt;/span>&lt;span style="color:#e6db74">&amp;#39;&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sql" data-lang="sql">ERROR: &lt;span style="color:#66d9ef">Key&lt;/span> (&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)&lt;span style="color:#f92672">=&lt;/span>(Winterfell, [&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 12:00:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>,&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 12:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>])
conflicts &lt;span style="color:#66d9ef">with&lt;/span> &lt;span style="color:#66d9ef">existing&lt;/span> &lt;span style="color:#66d9ef">key&lt;/span> (&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)&lt;span style="color:#f92672">=&lt;/span>(Winterfell, [&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 11:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>,&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 12:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)).
conflicting &lt;span style="color:#66d9ef">key&lt;/span> value violates exclusion &lt;span style="color:#66d9ef">constraint&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">NoOverlap&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
ERROR: conflicting &lt;span style="color:#66d9ef">key&lt;/span> value violates exclusion &lt;span style="color:#66d9ef">constraint&lt;/span> &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">NoOverlap&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>
&lt;span style="color:#66d9ef">SQL&lt;/span> &lt;span style="color:#66d9ef">state&lt;/span>: &lt;span style="color:#ae81ff">23&lt;/span>P01
Detail: &lt;span style="color:#66d9ef">Key&lt;/span> (&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)&lt;span style="color:#f92672">=&lt;/span>(Winterfell, [&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 12:00:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>,&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 12:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>])
conflicts &lt;span style="color:#66d9ef">with&lt;/span> &lt;span style="color:#66d9ef">existing&lt;/span> &lt;span style="color:#66d9ef">key&lt;/span> (&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">RoomName&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">Time&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)&lt;span style="color:#f92672">=&lt;/span>(Winterfell, [&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 11:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>,&lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">2023-12-23 12:30:00+04&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span>)).
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now that we have covered how range types work in PostgreSQL, let&amp;rsquo;s see how to use them from Entity Framework Core.&lt;/p>
&lt;h2 id="entity-framework-core-and-postgresql-range-types">Entity Framework Core and PostgreSQL Range Types.&lt;/h2>
&lt;p>Entity Framework Core provider for PostgreSQL supports
&lt;a href="https://www.npgsql.org/efcore/mapping/range.html" target="_blank" rel="noopener">mapping ranges and performing operations&lt;/a> on ranges in LINQ queries. To represent the PostgreSQL range in C# I will use &lt;code>NpgsqlRange&amp;lt;T&amp;gt;&lt;/code> type provided by Npgsql:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">MeetingRoom&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Id { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Name { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; };
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> MaxCapacity { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Meeting&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Id { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> Title { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> MeetingRoom Room { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> NpgsqlRange&amp;lt;DateTime&amp;gt; Time { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The exclusion constraint can&amp;rsquo;t be specified using the Entity Framework &lt;code>ModelBuilder&lt;/code> api, so I&amp;rsquo;ll use
&lt;a href="https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/managing?tabs=dotnet-core-cli#arbitrary-changes-via-raw-sql" target="_blank" rel="noopener">raw SQL&lt;/a> inside a migration to create it:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">partial&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ExcludeConstraint&lt;/span> : Migration
{
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(&lt;span style="color:#e6db74">@&amp;#34;Alter Table &amp;#34;&amp;#34;Meetings&amp;#34;&amp;#34;
&lt;/span>&lt;span style="color:#e6db74"> Add Constraint &amp;#34;&amp;#34;NoOverlap&amp;#34;&amp;#34;
&lt;/span>&lt;span style="color:#e6db74"> EXCLUDE USING gist (&amp;#34;&amp;#34;RoomId&amp;#34;&amp;#34; WITH =, &amp;#34;&amp;#34;Time&amp;#34;&amp;#34; WITH &amp;amp;&amp;amp;)&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next, let&amp;rsquo;s insert some rooms and events and see how to perform queries on ranges with Entity Framework Core.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">demoContext.Meetings.Add(&lt;span style="color:#66d9ef">new&lt;/span> Meeting
{
Title = &lt;span style="color:#e6db74">&amp;#34;PostgreSQL Demo&amp;#34;&lt;/span>,
Room = winterfellMeetingRoom,
Time = &lt;span style="color:#66d9ef">new&lt;/span> NpgsqlRange&amp;lt;DateTime&amp;gt;(&lt;span style="color:#66d9ef">new&lt;/span> DateTime(&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, DateTimeKind.Utc),
&lt;span style="color:#66d9ef">new&lt;/span> DateTime(&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, DateTimeKind.Utc))
});
demoContext.Meetings.Add(&lt;span style="color:#66d9ef">new&lt;/span> Meeting
{
Title = &lt;span style="color:#e6db74">&amp;#34;Npgsql Demo&amp;#34;&lt;/span>,
Room = jobriaMeetingRoom,
Time = &lt;span style="color:#66d9ef">new&lt;/span> NpgsqlRange&amp;lt;DateTime&amp;gt;(&lt;span style="color:#66d9ef">new&lt;/span> DateTime(&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, DateTimeKind.Utc),
&lt;span style="color:#66d9ef">new&lt;/span> DateTime(&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, DateTimeKind.Utc))
});
demoContext.Meetings.Add(&lt;span style="color:#66d9ef">new&lt;/span> Meeting
{
Title = &lt;span style="color:#e6db74">&amp;#34;Upgrade PostgreSQL&amp;#34;&lt;/span>,
Room = casterlyRockMeetingRoom,
Time = &lt;span style="color:#66d9ef">new&lt;/span> NpgsqlRange&amp;lt;DateTime&amp;gt;(&lt;span style="color:#66d9ef">new&lt;/span> DateTime(&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, DateTimeKind.Utc),
&lt;span style="color:#66d9ef">new&lt;/span> DateTime(&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>, &lt;span style="color:#ae81ff">3&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, DateTimeKind.Utc))
});
&lt;span style="color:#66d9ef">await&lt;/span> demoContext.SaveChangesAsync();
&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see inserting range values is quite straightforward with Entity Framework Core. Performing operations on ranges is simple as well:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">var&lt;/span> targetRange = &lt;span style="color:#66d9ef">new&lt;/span> NpgsqlRange&amp;lt;DateTime&amp;gt;(&lt;span style="color:#66d9ef">new&lt;/span> DateTime(&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, DateTimeKind.Utc),
&lt;span style="color:#66d9ef">new&lt;/span> DateTime(&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, DateTimeKind.Utc));
&lt;span style="color:#75715e">//Find all meetings that occur in the specified time range
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> meetings = &lt;span style="color:#66d9ef">await&lt;/span> demoContext.Meetings.Where(m =&amp;gt; m.Time.ContainedBy(targetRange)).ToListAsync();
&lt;span style="color:#75715e">//Find all meetings that overlap with the specified time range
&lt;/span>&lt;span style="color:#75715e">&lt;/span>meetings = &lt;span style="color:#66d9ef">await&lt;/span> demoContext.Meetings.Where(m =&amp;gt; m.Time.Overlaps(targetRange)).ToListAsync();
&lt;span style="color:#75715e">//Find all busy times in the specified time range
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> meetingsWithBusyTimes = &lt;span style="color:#66d9ef">await&lt;/span> demoContext.Meetings.Where(m =&amp;gt; m.Time.Overlaps(targetRange))
.Select(m =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span>
{
Meeting = m,
BusyTime = m.Time.Intersect(targetRange)
}).ToListAsync();
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Entity Framework Core and Npgsql translate these operations to corresponding PostgreSQL range operations so they get evaluated on the database server.&lt;/p>
&lt;p>Finally, if you try to insert a new meeting that overlaps with an existing one, Entity Framework Core will throw &lt;code>DbUpdateException&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">try&lt;/span>
{
demoContext.Meetings.Add(&lt;span style="color:#66d9ef">new&lt;/span> Meeting
{
Title = &lt;span style="color:#e6db74">&amp;#34;Conflicting meeting&amp;#34;&lt;/span>,
Room = casterlyRockMeetingRoom,
Time = &lt;span style="color:#66d9ef">new&lt;/span> NpgsqlRange&amp;lt;DateTime&amp;gt;(&lt;span style="color:#66d9ef">new&lt;/span> DateTime(&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, DateTimeKind.Utc),
&lt;span style="color:#66d9ef">new&lt;/span> DateTime(&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>, &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">3&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">6&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#ae81ff">0&lt;/span>, DateTimeKind.Utc))
});
&lt;span style="color:#66d9ef">await&lt;/span> demoContext.SaveChangesAsync();
}
&lt;span style="color:#66d9ef">catch&lt;/span> (DbUpdateException ex)
{
&lt;span style="color:#66d9ef">var&lt;/span> npgsqlException = ex.GetBaseException() &lt;span style="color:#66d9ef">as&lt;/span> NpgsqlException;
&lt;span style="color:#66d9ef">if&lt;/span> (npgsqlException?.SqlState == PostgresErrorCodes.ExclusionViolation)
{
Console.WriteLine(&lt;span style="color:#e6db74">&amp;#34;This meeting overlaps with another meeting in the same room&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>In this article I showed how PostgreSQL range types can simplify representing and performing operations on ranges. Apart from range types PostgreSQL has many other
interesting features not found in other relational databases and I recommend every developer to check it out.&lt;/p>
&lt;p>The sample code from this post is available on GitHub:
&lt;a href="https://github.com/Giorgi/PostgresRangeTypes" target="_blank" rel="noopener">https://github.com/Giorgi/PostgresRangeTypes&lt;/a>&lt;/p></description></item><item><title>How Cloudflare Broke My Build and How I Fixed It</title><link>https://www.giorgi.dev/miscellaneous/how-cloudflare-broke-my-build-and-how-i-fixed-it/</link><pubDate>Mon, 15 Aug 2022 12:00:52 +0400</pubDate><guid>https://www.giorgi.dev/miscellaneous/how-cloudflare-broke-my-build-and-how-i-fixed-it/</guid><description>&lt;h2 id="mysterious-build-failure">Mysterious Build Failure&lt;/h2>
&lt;p>About a month ago, my open-source project
&lt;a href="https://github.com/Giorgi/EntityFramework.Exceptions" target="_blank" rel="noopener">EntityFramework.Exceptions&lt;/a> failed to build on AppVeyor, the continuous integration service that I have been using for several years. I hadn&amp;rsquo;t made any changes that could have caused the build to fail, so I set out to investigate the cause of the failure.&lt;/p>
&lt;p>It turned out that the build failed because it failed to upload source code coverage data to
&lt;a href="https://coveralls.io/github/Giorgi/EntityFramework.Exceptions" target="_blank" rel="noopener">coveralls.io&lt;/a>. The error message was very minimal:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-html" data-lang="html">BadRequest - &amp;lt;&lt;span style="color:#f92672">html&lt;/span>&amp;gt;
&amp;lt;&lt;span style="color:#f92672">head&lt;/span>&amp;gt;&amp;lt;&lt;span style="color:#f92672">title&lt;/span>&amp;gt;400 Bad Request&amp;lt;/&lt;span style="color:#f92672">title&lt;/span>&amp;gt;&amp;lt;/&lt;span style="color:#f92672">head&lt;/span>&amp;gt;
&amp;lt;&lt;span style="color:#f92672">body&lt;/span>&amp;gt;
&amp;lt;&lt;span style="color:#f92672">center&lt;/span>&amp;gt;&amp;lt;&lt;span style="color:#f92672">h1&lt;/span>&amp;gt;400 Bad Request&amp;lt;/&lt;span style="color:#f92672">h1&lt;/span>&amp;gt;&amp;lt;/&lt;span style="color:#f92672">center&lt;/span>&amp;gt;
&amp;lt;&lt;span style="color:#f92672">hr&lt;/span>&amp;gt;&amp;lt;&lt;span style="color:#f92672">center&lt;/span>&amp;gt;nginx&amp;lt;/&lt;span style="color:#f92672">center&lt;/span>&amp;gt;
&amp;lt;/&lt;span style="color:#f92672">body&lt;/span>&amp;gt;
&amp;lt;/&lt;span style="color:#f92672">html&lt;/span>&amp;gt;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>I checked
&lt;a href="https://status.coveralls.io/history" target="_blank" rel="noopener">coveralls status page&lt;/a>, but there wasn&amp;rsquo;t any ongoing incident, so I reached out to coveralls support. Coveralls support responded that they had recently migrated their services to different servers and were now using Cloudflare as a service provider but these changes should have been transparent to the end users. To ensure this wasn&amp;rsquo;t a DNS issue, I added &lt;code>ping -n 1 coveralls.io&lt;/code> to the &lt;code>appVeyor.yml&lt;/code> file. The resolved IP address was correct, so the issue wasn&amp;rsquo;t related to DNS.&lt;/p>
&lt;p>Coveralls support tried to check the cause of the &lt;code>Bad Request&lt;/code> error in their logs but surprisingly my attempts to upload the coverage data didn&amp;rsquo;t show up. This meant that the &lt;code>Bad Request&lt;/code> didn&amp;rsquo;t originate from coveralls.io and was coming from Cloudflare. Why was Cloudflare suddenly rejecting my attempts to upload coverage that was working flawlessly for the last couple of years?&lt;/p>
&lt;h2 id="finding-the-root-cause-of-the-issue">Finding the Root Cause of the Issue&lt;/h2>
&lt;p>Now that I knew that the failure was Cloudflare&amp;rsquo;s fault, I asked the coveralls.io team to get in touch with Cloudflare support to find out the cause of the error response. I downloaded
&lt;a href="https://github.com/csMACnz/coveralls.net" target="_blank" rel="noopener">coveralls.net&lt;/a> (Coveralls uploader for .Net Code coverage) to my local machine, attempted to submit the coverage data, and captured the request with
&lt;a href="https://www.telerik.com/fiddler" target="_blank" rel="noopener">Fiddler&lt;/a>. I saved the HTTP request as a &lt;code>har&lt;/code> file and sent it to coveralls.io support, hoping Cloudflare would help solve the issue. Unfortunately, Cloudflare was not helpful at all.&lt;/p>
&lt;p>My next step, as suggested by James from coveralls.io, was to submit the coverage file with curl and see if that worked. I ran the following command: &lt;code>curl https://coveralls.io/api/v1/jobs --form &amp;quot;json_file=@uploadedCoverage.json&amp;quot;&lt;/code> and to my surprise, the coverage file was successfully received by coveralls.io. Now that I was able to submit coverage data with curl, all I had to do, was to compare the HTTP requests issued by curl and coveralls.net to find the difference between them that was causing the failure.&lt;/p>
&lt;p>The HTTP request looked like this (request bodies omitted):&lt;/p>
&lt;p>&lt;em>coveralls.net HTTP request:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-http" data-lang="http">&lt;span style="color:#a6e22e">POST&lt;/span> https://coveralls.io/api/v1/jobs &lt;span style="color:#66d9ef">HTTP&lt;/span>&lt;span style="color:#f92672">/&lt;/span>&lt;span style="color:#ae81ff">1.1&lt;/span>
Host&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#ae81ff">coveralls.io&lt;/span>
Content-Type&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#ae81ff">multipart/form-data; boundary=&amp;#34;e5d8bb1a-4ab2-4bb8-9d8b-152c5b704cfa&amp;#34;&lt;/span>
Content-Length&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#ae81ff">3093&lt;/span>
--e5d8bb1a-4ab2-4bb8-9d8b-152c5b704cfa
Content-Type: text/plain; charset=utf-8
Content-Disposition: form-data; name=json_file; filename=coverage.json; filename*=utf-8&amp;#39;&amp;#39;coverage.json
&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;em>curl HTTP request:&lt;/em>&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-http" data-lang="http">&lt;span style="color:#a6e22e">POST&lt;/span> https://coveralls.io/api/v1/jobs &lt;span style="color:#66d9ef">HTTP&lt;/span>&lt;span style="color:#f92672">/&lt;/span>&lt;span style="color:#ae81ff">1.1&lt;/span>
Host&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#ae81ff">coveralls.io&lt;/span>
User-Agent&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#ae81ff">curl/7.83.1&lt;/span>
Accept&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#ae81ff">*/*&lt;/span>
Content-Length&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#ae81ff">3080&lt;/span>
Content-Type&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#ae81ff">multipart/form-data; boundary=------------------------c8eebbf0920b1cc0&lt;/span>
--------------------------c8eebbf0920b1cc0
Content-Disposition: form-data; name=&amp;#34;json_file&amp;#34;; filename=&amp;#34;uploadedCoverage.json&amp;#34;
Content-Type: application/octet-stream
&lt;/code>&lt;/pre>&lt;/div>&lt;p>There are several differences between these requests, but can you guess what was causing Cloudflare to fail? It turned out that Cloudflare returned &lt;code>Bad Request&lt;/code> because of double quotes around the &lt;code>boundary&lt;/code> directive value in the &lt;code>Content-Type&lt;/code> header!&lt;/p>
&lt;p>Content-Type &lt;code>boundary&lt;/code> with double quotes is perfectly valid, so why does Cloudflare treat it as an error? Unfortunately, I don&amp;rsquo;t have an answer to this question. The
&lt;a href="https://github.com/fdintino/nginx-upload-module/pull/130" target="_blank" rel="noopener">pull request to handle quotes&lt;/a> was merged more than two years ago, so either Cloudflare uses an older version of
&lt;a href="https://github.com/fdintino/nginx-upload-module" target="_blank" rel="noopener">nginx upload module&lt;/a>, or Cloudflare uses another module that cannot handle double quotes around the &lt;code>boundary&lt;/code> directive.&lt;/p>
&lt;h2 id="fixing-coveralls-uploader">Fixing Coveralls Uploader&lt;/h2>
&lt;p>Now that I knew what was causing the failure, it was easy to fix the coveralls.net:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">var&lt;/span> boundary = formData.Headers.ContentType?.Parameters.FirstOrDefault(o =&amp;gt; o.Name == &lt;span style="color:#e6db74">&amp;#34;boundary&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">if&lt;/span> (boundary != &lt;span style="color:#66d9ef">null&lt;/span>)
{
boundary.Value = boundary.Value?.Replace(&lt;span style="color:#e6db74">&amp;#34;\&amp;#34;&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">string&lt;/span>.Empty);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>I submitted a
&lt;a href="https://github.com/csMACnz/coveralls.net/pull/111" target="_blank" rel="noopener">pull request&lt;/a> to the
&lt;a href="https://github.com/csMACnz/coveralls.net" target="_blank" rel="noopener">coveralls.net&lt;/a> repo, and as soon as it was merged, my build was green again!&lt;/p></description></item><item><title>Introducing GraphQLinq - Strongly Typed GraphQL Queries with LINQ to GraphQL.</title><link>https://www.giorgi.dev/dotnet/introducing-graphqlinq-strongly-typed-graphql-queries-with-linq-to-graphql/</link><pubDate>Tue, 27 Apr 2021 10:10:19 +0400</pubDate><guid>https://www.giorgi.dev/dotnet/introducing-graphqlinq-strongly-typed-graphql-queries-with-linq-to-graphql/</guid><description>&lt;p>Consuming a GraphQL api in C# is straightforward with either using &lt;code>HttpClient&lt;/code> directly or using a client library such as
&lt;a href="https://github.com/graphql-dotnet/graphql-client" target="_blank" rel="noopener">GraphQL.Client&lt;/a> but both suffer from the same problems: The GraphQL queries do not go through any compile-time checking, and any mistake in the query isn’t discovered until you execute the query at runtime. For example, to query
&lt;a href="https://api.spacex.land/graphql/" target="_blank" rel="noopener">SpaceX GraphQL API&lt;/a> for all missions where the manufacturer is &lt;code>Orbital ATK&lt;/code> you need to run the following query:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-js" data-lang="js">{
&lt;span style="color:#a6e22e">missions&lt;/span> (&lt;span style="color:#a6e22e">find&lt;/span>&lt;span style="color:#f92672">:&lt;/span> {&lt;span style="color:#a6e22e">manufacturer&lt;/span>&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#e6db74">&amp;#34;Orbital ATK&amp;#34;&lt;/span>}) {
&lt;span style="color:#a6e22e">id&lt;/span>
&lt;span style="color:#a6e22e">name&lt;/span>
&lt;span style="color:#a6e22e">description&lt;/span>
&lt;span style="color:#a6e22e">twitter&lt;/span>
&lt;span style="color:#a6e22e">website&lt;/span>
&lt;span style="color:#a6e22e">wikipedia&lt;/span>
&lt;span style="color:#a6e22e">manufacturers&lt;/span>
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>.Net solved the same problem for SQL with LINQ: LINQ queries are first-class language objects and are strongly typed. This way, there are no hardcoded queries in the project, and the LINQ queries are type-checked by the compiler.&lt;/p>
&lt;p>In this article, I will show how
&lt;a href="https://github.com/Giorgi/GraphQLinq/" target="_blank" rel="noopener">GraphQLinq - a LINQ to GraphQL library&lt;/a> solves this problem.&lt;/p>
&lt;h2 id="getting-started-with-graphqlinq">Getting Started with GraphQLinq&lt;/h2>
&lt;p>GraphQLinq consists of two parts: &lt;code>GraphQLinq.Scaffolding&lt;/code> for generating strongly typed classes from the GrapQL endpoint and client library, &lt;code>GraphQLinq.Client&lt;/code>, for writing and executing LINQ to GraphQL queries.&lt;/p>
&lt;p>&lt;code>GraphQLinq.Scaffolding&lt;/code> is a .NET tool, so run the following command to install it:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sh" data-lang="sh">dotnet tool install --global --version 1.0.0-beta GraphQLinq.Scaffolding
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This will install the tool globally and make it available for all projects.&lt;/p>
&lt;p>Next, navigate to your project folder and run the &lt;code>graphql-scaffold&lt;/code> tool to scaffold the client-side code:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sh" data-lang="sh">graphqlinq-scaffold https://api.spacex.land/graphql -o SpaceX -n SpaceX
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This will generate the code in the SpaceX folder (specified by the &lt;code>o&lt;/code> option) and use &lt;code>SpaceX&lt;/code> as a namespace too.&lt;/p>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/GraphQLinq/Scaffolding.gif" data-caption="Scaffolding GraphQL client with GraphQLinq.Scaffolding">
&lt;img src="https://www.giorgi.dev/img/GraphQLinq/Scaffolding.gif" alt="" >
&lt;/a>
&lt;figcaption>
Scaffolding GraphQL client with GraphQLinq.Scaffolding
&lt;/figcaption>
&lt;/figure>
&lt;p>Now, install the &lt;code>GraphQLinq.Client&lt;/code> package in your project to write some LINQ to GraphQL queries:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-sh" data-lang="sh">dotnet add package GraphQLinq.Client --version 1.0.0-beta
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="running-graphql-queries-with-linq">Running GraphQL Queries with LINQ&lt;/h2>
&lt;p>The scaffolding tool generates &lt;code>QueryContext&lt;/code> class that serves as an entry point for writing and running LINQ queries. For example, you can write the above graphQL query like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">var&lt;/span> spaceXContext = &lt;span style="color:#66d9ef">new&lt;/span> QueryContext();
&lt;span style="color:#66d9ef">var&lt;/span> missionsQuery = spaceXContext.Missions(&lt;span style="color:#66d9ef">new&lt;/span> MissionsFind { Manufacturer = &lt;span style="color:#e6db74">&amp;#34;Orbital ATK&amp;#34;&lt;/span> }, &lt;span style="color:#66d9ef">null&lt;/span>, &lt;span style="color:#66d9ef">null&lt;/span>)
.Include(mission =&amp;gt; mission.Manufacturers);
&lt;span style="color:#66d9ef">var&lt;/span> missions = missionsQuery.ToList();
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This LINQ query will generate a graphQL query that includes all primitive properties of the &lt;code>Mission&lt;/code> class and also includes the list of manufacturers in the query.&lt;/p>
&lt;p>Here are a couple of more LINQ to GraphQL queries:&lt;/p>
&lt;h3 id="query-all-primitive-properties-of-a-type">Query all Primitive Properties of a Type&lt;/h3>
&lt;p>Quering for all primitive properties is very straightforward:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">var&lt;/span> spaceXContext = &lt;span style="color:#66d9ef">new&lt;/span> QueryContext();
&lt;span style="color:#66d9ef">var&lt;/span> company = spaceXContext.Company().ToItem();
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="query-specific-properties">Query Specific Properties&lt;/h3>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">var&lt;/span> companySummary = spaceXContext.Company().Select(c =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> CompanySummary
{
Ceo = c.Ceo,
Name = c.Name,
Headquarters = c.Headquarters
}).ToItem();
&lt;span style="color:#75715e">/// Alternatively, use an anonymous object
&lt;/span>&lt;span style="color:#75715e">&lt;/span>
&lt;span style="color:#66d9ef">var&lt;/span> companySummaryAnonymous = spaceXContext.Company().Select(c =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> { c.Ceo, c.Name, c.Headquarters }).ToItem();
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="include-navigation-properties">Include Navigation Properties&lt;/h3>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">var&lt;/span> companyWithHeadquartersAndLinks = spaceXContext.Company()
.Include(info =&amp;gt; info.Headquarters)
.Include(info =&amp;gt; info.Links).ToItem();
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="view-generated-query">View Generated Query&lt;/h3>
&lt;p>To view the generated GraphQL query, call &lt;code>ToString&lt;/code> on the LINQ query itself. You will get the GraphQL query together with the variables bound to the query.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">var&lt;/span> missionsQuery = spaceXContext.Missions(&lt;span style="color:#66d9ef">new&lt;/span> MissionsFind { Manufacturer = &lt;span style="color:#e6db74">&amp;#34;Orbital ATK&amp;#34;&lt;/span> }, &lt;span style="color:#66d9ef">null&lt;/span>, &lt;span style="color:#66d9ef">null&lt;/span>)
.Include(mission =&amp;gt; mission.Manufacturers);
&lt;span style="color:#66d9ef">var&lt;/span> query = missionsQuery.Query;
&lt;span style="color:#66d9ef">var&lt;/span> fullQuery = missionsQuery.ToString();
&lt;/code>&lt;/pre>&lt;/div>&lt;p>After executing the above code snippet &lt;code>query&lt;/code> will be equal to:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-js" data-lang="js">&lt;span style="color:#a6e22e">query&lt;/span> (&lt;span style="color:#a6e22e">$find&lt;/span>&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#a6e22e">MissionsFind&lt;/span>) { &lt;span style="color:#a6e22e">result&lt;/span>&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#a6e22e">missions&lt;/span> (&lt;span style="color:#a6e22e">find&lt;/span>&lt;span style="color:#f92672">:&lt;/span> &lt;span style="color:#a6e22e">$find&lt;/span>) {
&lt;span style="color:#a6e22e">description&lt;/span>
&lt;span style="color:#a6e22e">id&lt;/span>
&lt;span style="color:#a6e22e">name&lt;/span>
&lt;span style="color:#a6e22e">twitter&lt;/span>
&lt;span style="color:#a6e22e">website&lt;/span>
&lt;span style="color:#a6e22e">wikipedia&lt;/span>
&lt;span style="color:#a6e22e">manufacturers&lt;/span>
}}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>and &lt;code>fullQuery&lt;/code> will be:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-js" data-lang="js">{&lt;span style="color:#e6db74">&amp;#34;query&amp;#34;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>&lt;span style="color:#e6db74">&amp;#34;query ($find: MissionsFind) { result: missions (find: $find) {
&lt;/span>&lt;span style="color:#e6db74"> description
&lt;/span>&lt;span style="color:#e6db74"> id
&lt;/span>&lt;span style="color:#e6db74"> name
&lt;/span>&lt;span style="color:#e6db74"> twitter
&lt;/span>&lt;span style="color:#e6db74"> website
&lt;/span>&lt;span style="color:#e6db74"> wikipedia
&lt;/span>&lt;span style="color:#e6db74"> manufacturers
&lt;/span>&lt;span style="color:#e6db74"> }}&amp;#34;&lt;/span>,&lt;span style="color:#e6db74">&amp;#34;variables&amp;#34;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>{&lt;span style="color:#e6db74">&amp;#34;find&amp;#34;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>{&lt;span style="color:#e6db74">&amp;#34;manufacturer&amp;#34;&lt;/span>&lt;span style="color:#f92672">:&lt;/span>&lt;span style="color:#e6db74">&amp;#34;Orbital ATK&amp;#34;&lt;/span>}}}
&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="roadmap">Roadmap&lt;/h2>
&lt;p>The library is in the early stages and does not support all possible GraphQL schemas and queries. Check
&lt;a href="https://github.com/Giorgi/GraphQLinq/issues" target="_blank" rel="noopener">open issues&lt;/a> for a list of proposed features and known issues.&lt;/p>
&lt;p>If you find the library useful, don’t forget to ⭐ the
&lt;a href="https://github.com/Giorgi/GraphQLinq" target="_blank" rel="noopener">GitHub repository&lt;/a>. If you have any questions or suggestions, you are welcome to submit an issue or send a PR.&lt;/p></description></item><item><title>Updating LINQPad.QueryPlanVisualizer for LINQPad6 And Entity Framework Core</title><link>https://www.giorgi.dev/miscellaneous/updating-linqpad-queryplanvisualizer-for-linqpad6-and-entity-framework-core/</link><pubDate>Mon, 01 Mar 2021 15:15:19 +0400</pubDate><guid>https://www.giorgi.dev/miscellaneous/updating-linqpad-queryplanvisualizer-for-linqpad6-and-entity-framework-core/</guid><description>&lt;p>
&lt;a href="https://www.giorgi.dev/miscellaneous/introducing-linqpad-queryplanvisualizer/">LINQPad.QueryPlanVisualizer&lt;/a> is a custom visualizer for LINQPad that shows a database query plan inside LINQPad. It also shows missing indexes for the query that you can create directly from LINQPad.&lt;/p>
&lt;p>Since then, a new major version, LINQPad 6, was released that targets .NET Core 3 and .NET 5 and uses Entity Framework Core (as well as LINQ-to-SQL) for running Linq queries.&lt;/p>
&lt;p>To support these changes, I have just released a new version of
&lt;a href="https://github.com/Giorgi/LINQPad.QueryPlanVisualizer/" target="_blank" rel="noopener">LINQPad.QueryPlanVisualizer&lt;/a> that is compatible with LINQPad 6, supports Entity Framework Core, and, last but not least, adds support for viewing PostgreSQL query plans in LINQPad. It also can share SQL Server query plan to Brent Ozar&amp;rsquo;s
&lt;a href="https://www.brentozar.com/pastetheplan/" target="_blank" rel="noopener">Paste The Plan&lt;/a> and share PostgreSQL query plan
&lt;a href="https://explain.dalibo.com/" target="_blank" rel="noopener">explain.dalibo.com&lt;/a>.&lt;/p>
&lt;h2 id="using-linqpadqueryplanvisualizer">Using LINQPad.QueryPlanVisualizer&lt;/h2>
&lt;p>To use
&lt;a href="https://github.com/Giorgi/LINQPad.QueryPlanVisualizer/" target="_blank" rel="noopener">LINQPad.QueryPlanVisualizer&lt;/a> in your queries, either reference it from
&lt;a href="https://www.nuget.org/packages/LINQPadQueryPlanVisualizer/" target="_blank" rel="noopener">NuGet&lt;/a> or download the
&lt;a href="https://github.com/Giorgi/QueryPlanVisualizer/releases/latest" target="_blank" rel="noopener">latest release&lt;/a> from GitHub and copy &lt;code>QueryPlanVisualizer.LinqPad6.dll&lt;/code> to &lt;strong>My Documents\LINQPad Plugins\NetCore3&lt;/strong> folder.&lt;/p>
&lt;p>To view the query plan call &lt;code>DumpPlan&lt;/code> method on any &lt;code>IQueryable&lt;/code> instance:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">Posts.OrderBy(post =&amp;gt; post.PostDate.Year &amp;gt; &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>).OrderByDescending(post =&amp;gt; post.BlogId).DumpPlan();
&lt;/code>&lt;/pre>&lt;/div>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/linqpad6/Query-Plan.png" data-caption="LINQPad showing SQL Server Query Execution Plan">
&lt;img src="https://www.giorgi.dev/img/linqpad6/Query-Plan.png" alt="" >
&lt;/a>
&lt;figcaption>
LINQPad showing SQL Server Query Execution Plan
&lt;/figcaption>
&lt;/figure>
&lt;p>If you run the same query on an PostgreSQL database, you will see a similar output:&lt;/p>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/linqpad6/Query-Plan-Postgres.png" data-caption="LINQPad showing PostgreSQL Query Execution Plan">
&lt;img src="https://www.giorgi.dev/img/linqpad6/Query-Plan-Postgres.png" alt="" >
&lt;/a>
&lt;figcaption>
LINQPad showing PostgreSQL Query Execution Plan
&lt;/figcaption>
&lt;/figure>
&lt;h2 id="internals-of-linqpadqueryplanvisualizer">Internals of LINQPad.QueryPlanVisualizer&lt;/h2>
&lt;p>As the visualizer supports multiple ORMs (EF Core and LINQ-to-SQL) and different databases (SQL Server and PostgreSQL), the project uses
&lt;a href="https://refactoring.guru/design-patterns/bridge" target="_blank" rel="noopener">Bridge Pattern&lt;/a> to avoid creating separate classes for all possible combinations. The main class hierarchies are &lt;code>OrmHelper&lt;/code>, which is responsible for extracting &lt;code>DbCommand&lt;/code> from &lt;code>IQueryable&lt;/code> for a specific ORM; &lt;code>DatabaseProvider&lt;/code> is responsible for getting query plan for a specific database; and &lt;code>PlanProcessor&lt;/code> that converts raw plan to Html. The &lt;code>OrmHelper&lt;/code> also contains a factory method for creating an instance for the ORM that the current query uses:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> OrmHelper Create&amp;lt;T&amp;gt;(IQueryable&amp;lt;T&amp;gt; queryable, &lt;span style="color:#66d9ef">object&lt;/span> dataContext)
{
&lt;span style="color:#66d9ef">if&lt;/span> (dataContext &lt;span style="color:#66d9ef">is&lt;/span> DbContext dbContext)
{
&lt;span style="color:#66d9ef">var&lt;/span> efCoreHelper = &lt;span style="color:#66d9ef">new&lt;/span> EFCoreHelper(dbContext.Database.ProviderName);
efCoreHelper.Initialize(queryable);
&lt;span style="color:#66d9ef">return&lt;/span> efCoreHelper;
}
&lt;span style="color:#66d9ef">var&lt;/span> queryType = queryable.GetType();
&lt;span style="color:#66d9ef">var&lt;/span> dataQueryType = queryType.Assembly.GetType(&lt;span style="color:#e6db74">&amp;#34;System.Data.Linq.DataQuery`1&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> tableQueryType = queryType.Assembly.GetType(&lt;span style="color:#e6db74">&amp;#34;System.Data.Linq.Table`1&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> queryGenericType = queryType.GetGenericTypeDefinition();
&lt;span style="color:#66d9ef">if&lt;/span> (queryGenericType == dataQueryType || queryGenericType == tableQueryType)
{
&lt;span style="color:#66d9ef">var&lt;/span> contextField = queryType.GetField(&lt;span style="color:#e6db74">&amp;#34;context&amp;#34;&lt;/span>, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField);
&lt;span style="color:#66d9ef">var&lt;/span> context = contextField?.GetValue(queryable);
&lt;span style="color:#66d9ef">if&lt;/span> (context != &lt;span style="color:#66d9ef">null&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> linqToSqlHelper = &lt;span style="color:#66d9ef">new&lt;/span> LinqToSqlHelper(context);
linqToSqlHelper.Initialize(queryable);
&lt;span style="color:#66d9ef">return&lt;/span> linqToSqlHelper;
}
}
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">null&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>dataContext&lt;/code> object is the data context that LIQNPad exposes via &lt;code>Util.CurrentDataContext&lt;/code>.&lt;/p>
&lt;p>The constructor of &lt;code>EFCoreHelper&lt;/code> sets &lt;code>DatabaseProvider&lt;/code> and &lt;code>PlanProcessor&lt;/code> based on the target database:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> EFCoreHelper(&lt;span style="color:#66d9ef">string&lt;/span> provider) : &lt;span style="color:#66d9ef">base&lt;/span>(CreateParameters(provider))
{
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> (DatabaseProvider provider, PlanProcessor planConvertor) CreateParameters(&lt;span style="color:#66d9ef">string&lt;/span> provider)
{
&lt;span style="color:#66d9ef">return&lt;/span> provider &lt;span style="color:#66d9ef">switch&lt;/span>
{
&lt;span style="color:#e6db74">&amp;#34;Microsoft.EntityFrameworkCore.SqlServer&amp;#34;&lt;/span> =&amp;gt; (&lt;span style="color:#66d9ef">new&lt;/span> SqlServerDatabaseProvider(), &lt;span style="color:#66d9ef">new&lt;/span> SqlServerPlanProcessor()),
&lt;span style="color:#e6db74">&amp;#34;Npgsql.EntityFrameworkCore.PostgreSQL&amp;#34;&lt;/span> =&amp;gt; (&lt;span style="color:#66d9ef">new&lt;/span> PostgresDatabaseProvider(), &lt;span style="color:#66d9ef">new&lt;/span> PostgresPlanProcessor()),
_ =&amp;gt; (&lt;span style="color:#66d9ef">null&lt;/span>, &lt;span style="color:#66d9ef">null&lt;/span>)
};
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>LinqToSqlHelper&lt;/code> always passes &lt;code>SqlServerDatabaseProvider&lt;/code> and &lt;code>SqlServerPlanProcessor&lt;/code> to the base class as it only supports SQL Server.&lt;/p>
&lt;h2 id="extracting-and-formatting-query-plan-from-sql-server">Extracting and Formatting Query Plan from SQL Server&lt;/h2>
&lt;p>To extract query plan for SQL Server, the &lt;code>SqlServerDatabaseProvider&lt;/code> class executes &lt;code>SET STATISTICS XML ON&lt;/code> command before running the actual query and captures the plan returned by the database:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> ExtractPlanInternal(DbCommand command)
{
&lt;span style="color:#66d9ef">using&lt;/span> var setStatisticsCommand = command.Connection.CreateCommand();
setStatisticsCommand.CommandText = &lt;span style="color:#e6db74">&amp;#34;SET STATISTICS XML ON&amp;#34;&lt;/span>;
setStatisticsCommand.ExecuteNonQuery();
&lt;span style="color:#66d9ef">using&lt;/span> var reader = command.ExecuteReader();
&lt;span style="color:#66d9ef">while&lt;/span> (reader.NextResult())
{
&lt;span style="color:#66d9ef">if&lt;/span> (reader.GetName(&lt;span style="color:#ae81ff">0&lt;/span>) == &lt;span style="color:#e6db74">&amp;#34;Microsoft SQL Server 2005 XML Showplan&amp;#34;&lt;/span>)
{
reader.Read();
&lt;span style="color:#66d9ef">return&lt;/span> reader.GetString(&lt;span style="color:#ae81ff">0&lt;/span>);
}
}
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">null&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next, &lt;code>SqlServerPlanProcessor&lt;/code> uses
&lt;a href="https://github.com/JustinPealing/html-query-plan" target="_blank" rel="noopener">html-query-plan&lt;/a> to display the plan as Html.&lt;/p>
&lt;h2 id="extracting-query-plan-from-postgresql">Extracting Query Plan from PostgreSQL&lt;/h2>
&lt;p>For PostgreSQL, the &lt;code>PostgresPlanProcessor&lt;/code> class prepends &lt;code>EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS)&lt;/code> to the query text to get the plan from a database:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> ExtractPlanInternal(DbCommand command)
{
command.CommandText = &lt;span style="color:#e6db74">&amp;#34;EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS) &amp;#34;&lt;/span> + command.CommandText;
&lt;span style="color:#66d9ef">using&lt;/span> var reader = command.ExecuteReader();
&lt;span style="color:#66d9ef">var&lt;/span> plan = &lt;span style="color:#66d9ef">string&lt;/span>.Join(Environment.NewLine, reader.Cast&amp;lt;IDataRecord&amp;gt;().Select(r =&amp;gt; r.GetString(&lt;span style="color:#ae81ff">0&lt;/span>)));
&lt;span style="color:#66d9ef">return&lt;/span> plan;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The PostgreSQL query plan is then displayed directly as plain text.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>LINQPad is a handy tool for prototyping and testing Linq queries.
&lt;a href="https://github.com/Giorgi/LINQPad.QueryPlanVisualizer/" target="_blank" rel="noopener">LINQPad.QueryPlanVisualizer&lt;/a> takes it further by showing query plan and adding other helpful features. If you have questions or suggestions please leave a comment, create an issue and star the repository.&lt;/p></description></item><item><title>EntityFramework.Exceptions 3.1.1 - Support for Entity Framework Core 3 and Improved API</title><link>https://www.giorgi.dev/entity-framework/refactoring-entityframework-exceptions/</link><pubDate>Thu, 12 Mar 2020 07:35:16 +0000</pubDate><guid>https://www.giorgi.dev/entity-framework/refactoring-entityframework-exceptions/</guid><description>&lt;p>
&lt;a href="https://www.giorgi.dev/entity-framework/introducing-entityframework-exceptions/">In the previous article&lt;/a> I introduced EntityFramework.Exceptions, a library which simplifies handling exceptions in Entity Framework Core but the library had one important limitation: In order to
use it you had to inherit your custom DbContext from &lt;code>ExceptionProcessorContextBase&lt;/code> class. This means that if you wanted to use some other base class for your DbContext you were out of luck. The
latest version of the library solves this issue by replacing one of the internal services used by entity framework core with a custom implementation and also adds support for Entity Framework Core 3.1.1&lt;/p>
&lt;p>The service that needs to be replaced is
&lt;a href="https://github.com/dotnet/efcore/blob/master/src/EFCore/ChangeTracking/Internal/IStateManager.cs" target="_blank" rel="noopener">IStateManager&lt;/a> and is used by the ChangeTracker. The custom
implementation of &lt;code>IStateManager&lt;/code> interface inherits from the built in
&lt;a href="https://github.com/dotnet/efcore/blob/master/src/EFCore/ChangeTracking/Internal/StateManager.cs" target="_blank" rel="noopener">StateManager&lt;/a> class and overrides &lt;code>SaveChanges&lt;/code> and &lt;code>SaveChangesAsync&lt;/code> methods. Let&amp;rsquo;s see how it works:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">abstract&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ExceptionProcessorStateManager&lt;/span>&amp;lt;T&amp;gt; : StateManager &lt;span style="color:#66d9ef">where&lt;/span> T : DbException
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> Dictionary&amp;lt;DatabaseError, Func&amp;lt;DbUpdateException, Exception&amp;gt;&amp;gt; ExceptionMapping = &lt;span style="color:#66d9ef">new&lt;/span> Dictionary&amp;lt;DatabaseError, Func&amp;lt;DbUpdateException, Exception&amp;gt;&amp;gt;
{
{DatabaseError.MaxLength, exception =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> MaxLengthExceededException(&lt;span style="color:#e6db74">&amp;#34;Maximum length exceeded&amp;#34;&lt;/span>, exception.InnerException) },
{DatabaseError.UniqueConstraint, exception =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> UniqueConstraintException(&lt;span style="color:#e6db74">&amp;#34;Unique constraint violation&amp;#34;&lt;/span>, exception.InnerException) },
{DatabaseError.CannotInsertNull, exception =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> CannotInsertNullException(&lt;span style="color:#e6db74">&amp;#34;Cannot insert null&amp;#34;&lt;/span>, exception.InnerException) },
{DatabaseError.NumericOverflow, exception =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> NumericOverflowException(&lt;span style="color:#e6db74">&amp;#34;Numeric overflow&amp;#34;&lt;/span>, exception.InnerException) },
{DatabaseError.ReferenceConstraint, exception =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> ReferenceConstraintException(&lt;span style="color:#e6db74">&amp;#34;Reference constraint violation&amp;#34;&lt;/span>, exception.InnerException) }
};
&lt;span style="color:#66d9ef">protected&lt;/span> ExceptionProcessorStateManager(StateManagerDependencies dependencies) : &lt;span style="color:#66d9ef">base&lt;/span>(dependencies)
{
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> SaveChanges(&lt;span style="color:#66d9ef">bool&lt;/span> acceptAllChangesOnSuccess)
{
&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">base&lt;/span>.SaveChanges(acceptAllChangesOnSuccess);
}
&lt;span style="color:#66d9ef">catch&lt;/span> (DbUpdateException originalException)
{
&lt;span style="color:#66d9ef">var&lt;/span> exception = GetException(originalException);
&lt;span style="color:#66d9ef">if&lt;/span> (exception != &lt;span style="color:#66d9ef">null&lt;/span>)
{
&lt;span style="color:#66d9ef">throw&lt;/span> exception;
}
&lt;span style="color:#66d9ef">throw&lt;/span>;
}
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; SaveChangesAsync(&lt;span style="color:#66d9ef">bool&lt;/span> acceptAllChangesOnSuccess, CancellationToken cancellationToken = &lt;span style="color:#66d9ef">new&lt;/span> CancellationToken())
{
&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#66d9ef">var&lt;/span> result = &lt;span style="color:#66d9ef">await&lt;/span> &lt;span style="color:#66d9ef">base&lt;/span>.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
&lt;span style="color:#66d9ef">return&lt;/span> result;
}
&lt;span style="color:#66d9ef">catch&lt;/span> (DbUpdateException originalException)
{
&lt;span style="color:#66d9ef">var&lt;/span> exception = GetException(originalException);
&lt;span style="color:#66d9ef">if&lt;/span> (exception != &lt;span style="color:#66d9ef">null&lt;/span>)
{
&lt;span style="color:#66d9ef">throw&lt;/span> exception;
}
&lt;span style="color:#66d9ef">throw&lt;/span>;
}
}
&lt;span style="color:#66d9ef">private&lt;/span> Exception GetException(DbUpdateException ex)
{
&lt;span style="color:#66d9ef">if&lt;/span> (ex.GetBaseException() &lt;span style="color:#66d9ef">is&lt;/span> T dbException &amp;amp;&amp;amp; GetDatabaseError(dbException) &lt;span style="color:#66d9ef">is&lt;/span> DatabaseError error &amp;amp;&amp;amp; ExceptionMapping.TryGetValue(error, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">var&lt;/span> ctor))
{
&lt;span style="color:#66d9ef">return&lt;/span> ctor(ex);
}
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">null&lt;/span>;
}
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">abstract&lt;/span> DatabaseError? GetDatabaseError(T dbException);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The abstract &lt;code>ExceptionProcessorStateManager&lt;/code> class catches any database exception thrown during &lt;code>SaveChanges&lt;/code> call and tries to translate it into one of the supported exception instances. If it succeeds it throws the new exception and if it doesn&amp;rsquo;t it simply rethrows the original exception. The &lt;code>GetDatabaseError&lt;/code> is overriden in database specific projects and returns &lt;code>DatabaseError&lt;/code> based on the
specific &lt;code>DbException&lt;/code> that was thrown:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">SqlServerExceptionProcessorStateManager&lt;/span>: ExceptionProcessorStateManager&amp;lt;SqlException&amp;gt;
{
&lt;span style="color:#66d9ef">public&lt;/span> SqlServerExceptionProcessorStateManager(StateManagerDependencies dependencies) : &lt;span style="color:#66d9ef">base&lt;/span>(dependencies)
{
}
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> ReferenceConstraint = &lt;span style="color:#ae81ff">5&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span>&lt;span style="color:#ae81ff">7&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> CannotInsertNull = &lt;span style="color:#ae81ff">5&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> CannotInsertDuplicateKeyUniqueIndex = &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">6&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> CannotInsertDuplicateKeyUniqueConstraint = &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">6&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">7&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> ArithmeticOverflow = &lt;span style="color:#ae81ff">8&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> StringOrBinaryDataWouldBeTruncated = &lt;span style="color:#ae81ff">8&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>;
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> DatabaseError? GetDatabaseError(SqlException dbException)
{
&lt;span style="color:#66d9ef">switch&lt;/span> (dbException.Number)
{
&lt;span style="color:#66d9ef">case&lt;/span> ReferenceConstraint:
&lt;span style="color:#66d9ef">return&lt;/span> DatabaseError.ReferenceConstraint;
&lt;span style="color:#66d9ef">case&lt;/span> CannotInsertNull:
&lt;span style="color:#66d9ef">return&lt;/span> DatabaseError.CannotInsertNull;
&lt;span style="color:#66d9ef">case&lt;/span> CannotInsertDuplicateKeyUniqueIndex:
&lt;span style="color:#66d9ef">case&lt;/span> CannotInsertDuplicateKeyUniqueConstraint:
&lt;span style="color:#66d9ef">return&lt;/span> DatabaseError.UniqueConstraint;
&lt;span style="color:#66d9ef">case&lt;/span> ArithmeticOverflow:
&lt;span style="color:#66d9ef">return&lt;/span> DatabaseError.NumericOverflow;
&lt;span style="color:#66d9ef">case&lt;/span> StringOrBinaryDataWouldBeTruncated:
&lt;span style="color:#66d9ef">return&lt;/span> DatabaseError.MaxLength;
&lt;span style="color:#66d9ef">default&lt;/span>:
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">null&lt;/span>;
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In order to actually replace &lt;code>IStateManager&lt;/code> with the custom implementation you need to install either
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.SqlServer" target="_blank" rel="noopener">SQL Server&lt;/a>,
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.PostgreSQL" target="_blank" rel="noopener">PostgreSQL&lt;/a> or
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.MySQL" target="_blank" rel="noopener">MySQL&lt;/a> nuget package and call &lt;code>UseExceptionProcessor&lt;/code>
method of the &lt;code>ExceptionProcessorExtensions&lt;/code> from the database specific package:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">DemoContext&lt;/span> : DbContext, IDemoContext
{
&lt;span style="color:#66d9ef">public&lt;/span> DbSet&amp;lt;Product&amp;gt; Products { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> DbSet&amp;lt;ProductSale&amp;gt; ProductSale { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> OnModelCreating(ModelBuilder builder)
{
builder.Entity&amp;lt;Product&amp;gt;().Property(b =&amp;gt; b.Price).HasColumnType(&lt;span style="color:#e6db74">&amp;#34;decimal(5,2)&amp;#34;&lt;/span>).IsRequired();
builder.Entity&amp;lt;Product&amp;gt;().Property(b =&amp;gt; b.Name).IsRequired().HasMaxLength(&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>);
builder.Entity&amp;lt;Product&amp;gt;().HasIndex(u =&amp;gt; u.Name).IsUnique();
}
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(&lt;span style="color:#e6db74">@&amp;#34;Data Source=(localdb)\ProjectsV13;Initial Catalog=Test;Integrated Security=True;Connect Timeout=30;&amp;#34;&lt;/span>)
.UseExceptionProcessor();
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>UseExceptionProcessor&lt;/code> method is very simple and all it does is a call to
&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontextoptionsbuilder.replaceservice?view=efcore-3.1" target="_blank" rel="noopener">DbContextOptionsBuilder.ReplaceService&amp;lt;TService,TImplementation&amp;gt;&lt;/a> method:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ExceptionProcessorExtensions&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> DbContextOptionsBuilder UseExceptionProcessor(&lt;span style="color:#66d9ef">this&lt;/span> DbContextOptionsBuilder self)
{
self.ReplaceService&amp;lt;IStateManager, SqlServerExceptionProcessorStateManager&amp;gt;();
&lt;span style="color:#66d9ef">return&lt;/span> self;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> DbContextOptionsBuilder&amp;lt;TContext&amp;gt; UseExceptionProcessor&amp;lt;TContext&amp;gt;(&lt;span style="color:#66d9ef">this&lt;/span> DbContextOptionsBuilder&amp;lt;TContext&amp;gt; self) &lt;span style="color:#66d9ef">where&lt;/span> TContext : DbContext
{
self.ReplaceService&amp;lt;IStateManager, SqlServerExceptionProcessorStateManager&amp;gt;();
&lt;span style="color:#66d9ef">return&lt;/span> self;
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once you have done it you will start getting database specific exceptions instead of &lt;code>DbUpdateException&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> demoContext = &lt;span style="color:#66d9ef">new&lt;/span> DemoContext())
{
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>
});
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>
});
&lt;span style="color:#66d9ef">try&lt;/span>
{
demoContext.SaveChanges();
}
&lt;span style="color:#66d9ef">catch&lt;/span> (UniqueConstraintException e)
{
&lt;span style="color:#75715e">//Unique index was violated. Show corresponding error message to user.
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The full source code of the library is available on GitHub:
&lt;a href="https://github.com/Giorgi/EntityFramework.Exceptions" target="_blank" rel="noopener">EntityFramework.Exceptions&lt;/a> If you have questions or suggestions feel free to leave a comment, create an issue and star the repository.&lt;/p></description></item><item><title>Introducing EntityFramework.Exceptions</title><link>https://www.giorgi.dev/entity-framework/introducing-entityframework-exceptions/</link><pubDate>Tue, 11 Dec 2018 07:35:16 +0000</pubDate><guid>https://www.giorgi.dev/entity-framework/introducing-entityframework-exceptions/</guid><description>&lt;p>When using Entity Framework Core for data access all database exceptions are wrapped in &lt;code>DbUpdateException&lt;/code>. If you need to know whether the exception was caused by a unique constraint, value being too long or value missing for a required column you need to dig into the concrete &lt;code>DbException&lt;/code> subclass instance and check the error number to determine the exact cause.&lt;/p>
&lt;p>&lt;a href="https://github.com/Giorgi/EntityFramework.Exceptions" rel="noopener" target="_blank">EntityFramework.Exceptions&lt;/a> simplifies this by handling all the database specific details and throwing different exceptions for different cases. All you have to do is&lt;/p>
&lt;p>inherit your &lt;code>DbContext&lt;/code> from &lt;code>ExceptionProcessorContext&lt;/code> and handle the exception(s) (such as &lt;code>UniqueConstraintException&lt;/code>, &lt;code>CannotInsertNullException&lt;/code>, &lt;code>MaxLengthExceededException&lt;/code>, &lt;code>NumericOverflowException&lt;/code>) you need.&lt;/p>
&lt;h3 id="the-problem-with-entity-framework-exceptions">The Problem with Entity Framework Exceptions&lt;/h3>
&lt;p>Let’s say we have a &lt;code>Product&lt;/code> table which has Name column with a unique index and Price column. Entity Framework context will look like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">DemoContext&lt;/span> : DbContext
{
&lt;span style="color:#66d9ef">public&lt;/span> DbSet&amp;lt;Product&amp;gt; Products { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> OnModelCreating(ModelBuilder builder)
{
builder.Entity&amp;lt;Product&amp;gt;().Property(b =&amp;gt; b.Price).HasColumnType(&lt;span style="color:#e6db74">&amp;#34;decimal(5,2)&amp;#34;&lt;/span>).IsRequired();
builder.Entity&amp;lt;Product&amp;gt;().Property(b =&amp;gt; b.Name).IsRequired().HasMaxLength(&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>);
builder.Entity&amp;lt;Product&amp;gt;().HasIndex(u =&amp;gt; u.Name).IsUnique();
}
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(&lt;span style="color:#e6db74">@&amp;#34;Data Source=localhost;Initial Catalog=Test;Integrated Security=True;Connect Timeout=30;&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>If we try to insert two records with the same name we will get &lt;code>DbUpdateException&lt;/code>. As we have already mentioned &lt;code>DbUpdateException&lt;/code> is thrown every time when saving changes to the database fails. In order to check that the exception was caused by the unique index we need to get the specific database &lt;code>DbException&lt;/code> subclass instance and check the error number. In case of SQL Server we can do it like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> demoContext = &lt;span style="color:#66d9ef">new&lt;/span> DemoContext())
{
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>
});
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>
});
&lt;span style="color:#66d9ef">try&lt;/span>
{
demoContext.SaveChanges();
}
&lt;span style="color:#66d9ef">catch&lt;/span> (DbUpdateException e)
{
&lt;span style="color:#66d9ef">var&lt;/span> sqlException = e.GetBaseException() &lt;span style="color:#66d9ef">as&lt;/span> SqlException;
&lt;span style="color:#75715e">//2601 is error number of unique index violation
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> (sqlException != &lt;span style="color:#66d9ef">null&lt;/span> &amp;amp;&amp;amp; sqlException.Number == &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">6&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>)
{
&lt;span style="color:#75715e">//Unique index was violated. Show corresponding error message to user.
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>While this works it has several disadvantages. First of all it is repetitive to write this code every time we try to save changes to database. Secondly, there are other database errors to handle such as trying to insert null in non-null column or trying to insert longer value than the column allows. Finally, the error numbers are different for different database servers.&lt;/p>
&lt;p>To avoid these issues I have created a library
&lt;a href="https://github.com/Giorgi/EntityFramework.Exceptions" target="_blank" rel="noopener">EntityFramework.Exceptions&lt;/a> which handles database specific errors and throws different exceptions for different database errors.&lt;/p>
&lt;h3 id="entityframeworkexceptions-8211-easy-way-to-handle-exceptions">EntityFramework.Exceptions – Easy Way to Handle Exceptions&lt;/h3>
&lt;p>With EntityFramework.Exceptions we can rewrite the above code like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> demoContext = &lt;span style="color:#66d9ef">new&lt;/span> DemoContext())
{
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>
});
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>
});
&lt;span style="color:#66d9ef">try&lt;/span>
{
demoContext.SaveChanges();
}
&lt;span style="color:#66d9ef">catch&lt;/span> (UniqueConstraintException e)
{
&lt;span style="color:#75715e">//Unique index was violated. Show corresponding error message to user.
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see we no longer have to deal with database specific exception and error numbers. Instead &lt;code>UniqueConstraintException&lt;/code> is thrown when either a unique index or unique constraint is violated. As a result our code is cleaner, shorter and easier to understand. What’s more the library provides other exceptions such as &lt;code>CannotInsertNullException&lt;/code>, &lt;code>MaxLengthExceededException&lt;/code>, &lt;code>NumericOverflowException&lt;/code>. For example if we try insert a product with Name longer than 15 characters we will get &lt;code>MaxLengthExceededException&lt;/code> exception:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> demoContext = &lt;span style="color:#66d9ef">new&lt;/span> DemoContext())
{
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp Change 3 Colors&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>
});
&lt;span style="color:#66d9ef">try&lt;/span>
{
demoContext.SaveChanges();
}
&lt;span style="color:#66d9ef">catch&lt;/span> (MaxLengthExceededException e)
{
&lt;span style="color:#75715e">//Max length of Name column exceeded. Show corresponding error message to user.
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Apart from this EntityFrameworkCore.Exceptions supports other database servers such as PostgreSQL and MySQL so if you ever switch to different database server your exception handling code will stay the same.&lt;/p>
&lt;p>To get started with &lt;code>EntityFramework.Exceptions&lt;/code> all you need to do is install either
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.SqlServer" target="_blank" rel="noopener">SQL Server&lt;/a>,
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.PostgreSQL" target="_blank" rel="noopener">PostgreSQL&lt;/a> or
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.MySQL" target="_blank" rel="noopener">MySQL&lt;/a> nuget package and inherit your DbContext from ExceptionProcessorContext:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">DemoContext&lt;/span> : ExceptionProcessorContext
{
&lt;span style="color:#66d9ef">public&lt;/span> DbSet&amp;lt;Product&amp;gt; Products { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="how-does-entityframeworkexceptions-work-">How Does EntityFramework.Exceptions Work ?&lt;/h3>
&lt;p>The &lt;a href="https://github.com/Giorgi/EntityFramework.Exceptions" rel="noopener" target="_blank">implementation&lt;/a> is pretty straightforward. There is an &lt;code>ExceptionProcessorContextBase&lt;/code> class in &lt;code>EntityFramework.Exceptions.Common&lt;/code> project which inherits from &lt;code>DbContext&lt;/code>, overrides &lt;code>SaveChanges&lt;/code> and handles any exception that occurs. It gets the database specific exception instance and asks derived classes to tell which exception it should throw for the &lt;code>DbException&lt;/code> that occurred. For more details please check the
&lt;a href="https://github.com/Giorgi/EntityFramework.Exceptions" target="_blank" rel="noopener">GitHub repository.&lt;/a> If you find the library useful don’t forget to &lt;em>Star&lt;/em> the repository. If you have any questions or suggestions you are welcome to submit an issue or send a PR.&lt;/p></description></item><item><title>Introducing LINQPad.QueryPlanVisualizer</title><link>https://www.giorgi.dev/miscellaneous/introducing-linqpad-queryplanvisualizer/</link><pubDate>Tue, 15 Mar 2016 07:00:58 +0000</pubDate><guid>https://www.giorgi.dev/miscellaneous/introducing-linqpad-queryplanvisualizer/</guid><description>&lt;p>If you are a heavy LINQPad user you have probably wished to be able to see query execution plan details inside LINQPad. Currently the only way to view query execution plan is to switch to SQL tab, click Analyze SQL button and open the query in SQL Server Management Studio. I got tired of clicking all these buttons and decided to
&lt;a href="http://www.linqpad.net/customvisualizers.aspx" target="_blank" rel="noopener">write custom visualizer&lt;/a> which solves the issue. With
&lt;a href="https://github.com/Giorgi/LINQPad.QueryPlanVisualizer" target="_blank" rel="noopener">LINQPad.QueryPlanVisualizer&lt;/a> you can now view query execution plan and missing indexes as well as create those missing indexes without leaving LINQPad.&lt;/p>
&lt;h2 id="linqpadqueryplanvisualizer">LINQPad.QueryPlanVisualizer&lt;/h2>
&lt;p>With LINQPad.QueryPlanVisualizer you can:&lt;/p>
&lt;ul>
&lt;li>View query execution plan&lt;/li>
&lt;li>View missing indexes for query&lt;/li>
&lt;li>Create missing indexes&lt;/li>
&lt;li>Open plan in SQL Server Management Studio or other default app&lt;/li>
&lt;li>Save plan to xml file&lt;/li>
&lt;/ul>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/linqpad/Query-Plan.png" data-caption="LINQPad showing Query Execution Plan">
&lt;img src="https://www.giorgi.dev/img/linqpad/Query-Plan.png" alt="" >
&lt;/a>
&lt;figcaption>
LINQPad showing Query Execution Plan
&lt;/figcaption>
&lt;/figure>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/linqpad/Missing-Index.png" data-caption="Missing Index">
&lt;img src="https://www.giorgi.dev/img/linqpad/Missing-Index.png" alt="" >
&lt;/a>
&lt;figcaption>
Missing Index
&lt;/figcaption>
&lt;/figure>
&lt;h2 id="how-it-works">How it works&lt;/h2>
&lt;p>The visualizer executes &lt;code>SET STATISTICS XML ON&lt;/code> command before executing the actual query to retrieve the actual execution plan from database. After that it converts the xml plan to html and uses
&lt;a href="https://github.com/JustinPealing/html-query-plan" target="_blank" rel="noopener">html-query-plan&lt;/a> library to display the resulting html. Missing index details (if any) are extracted from the execution plan xml too.&lt;/p>
&lt;p>For more details and installation guide visit the project at &lt;a href="https://github.com/Giorgi/LINQPad.QueryPlanVisualizer">https://github.com/Giorgi/LINQPad.QueryPlanVisualizer&lt;/a>&lt;/p></description></item><item><title>Faking num lock, caps lock and scroll lock leds</title><link>https://www.giorgi.dev/miscellaneous/faking-num-lock-caps-lock-and-scroll-lock-leds/</link><pubDate>Thu, 10 May 2012 12:21:55 +0000</pubDate><guid>https://www.giorgi.dev/miscellaneous/faking-num-lock-caps-lock-and-scroll-lock-leds/</guid><description>&lt;h3 id="introduction">Introduction&lt;/h3>
&lt;p>Some time ago I came across a post where the author wanted to turn on and off num lock, cap lock and scroll lock led light &lt;strong>without&lt;/strong> actually toggling their state. This sounded challenging and impossible but after some googling I found a piece of code in C for achieving exactly what I was looking for. In this post I show how it works and how to port it to C#&lt;/p>
&lt;h3 id="toggling-keyboard-indicators-from-c">Toggling Keyboard Indicators from C#&lt;/h3>
&lt;p>The C version is available at &lt;a href="http://www.rohitab.com/discuss/topic/32092-toggle-led-lights/page__p__10051697#entry10051697" title="Toggle Led Lights" target="_blank">rohitab.com&lt;/a> As you can see from the code toggling the keyboard indicators consists of three steps:&lt;/p>
&lt;ul>
&lt;li>Define MS-DOS device name for the target keyboard.&lt;/li>
&lt;li>Get handle of the target keyboard.&lt;/li>
&lt;li>Send command to the handle for changing keyboard indicators.&lt;/li>
&lt;/ul>
&lt;p>The first two steps are the pretty straightforward and easy to interop from C#. In order to define an MS-DOS name we need to call
&lt;a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa363904%28v=vs.85%29.aspx" title="DefineDosDevice" target="_blank" rel="noopener">DefineDosDevice method&lt;/a> pass the target keyboard path, desired MS-DOS device name and &lt;code>DDD_RAW_TARGET_PATH flag&lt;/code>. The C# code looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#a6e22e">[DllImport(&amp;#34;kernel32.dll&amp;#34;)]&lt;/span>
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">extern&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> DefineDosDevice(&lt;span style="color:#66d9ef">uint&lt;/span> dwFlags, &lt;span style="color:#66d9ef">string&lt;/span> lpDeviceName, &lt;span style="color:#66d9ef">string&lt;/span> lpTargetPath);
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> DddRawTargetPath = &lt;span style="color:#ae81ff">0&lt;/span>x00000001;
&lt;/code>&lt;/pre>&lt;/div>&lt;p>We can call it like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">DefineDosDevice(DddRawTargetPath, &lt;span style="color:#e6db74">&amp;#34;keyboard&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;\\Device\\KeyboardClass0&amp;#34;&lt;/span>);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once we have defined Dos device name we can now use CreateFile to get handle to it. After that we use DeviceIoControl method to send
&lt;a href="http://msdn.microsoft.com/en-us/library/windows/hardware/ff542067%28v=vs.85%29.aspx" title="IOCTL_KEYBOARD_SET_INDICATORS control code" target="_blank" rel="noopener">IOCTL_KEYBOARD_SET_INDICATORS control code&lt;/a> which indicates that we want to set keyboard indicator status. IOCTL_KEYBOARD_SET_INDICATORS control code is defined in Ntddkbd.h using CTL_CODE macro. In C# we can define it like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">uint&lt;/span> FileAnyAccess = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">uint&lt;/span> MethodBuffered = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">uint&lt;/span> FileDeviceKeyboard = &lt;span style="color:#ae81ff">0&lt;/span>x0000000b;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">uint&lt;/span> IOCTL_KEYBOARD_SET_INDICATORS =
ControlCode(FileDeviceKeyboard, &lt;span style="color:#ae81ff">0&lt;/span>x0002, MethodBuffered, FileAnyAccess);
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">uint&lt;/span> ControlCode(&lt;span style="color:#66d9ef">uint&lt;/span> deviceType, &lt;span style="color:#66d9ef">uint&lt;/span> function, &lt;span style="color:#66d9ef">uint&lt;/span> method, &lt;span style="color:#66d9ef">uint&lt;/span> access)
{
&lt;span style="color:#66d9ef">return&lt;/span> ((deviceType) &amp;lt;&amp;lt; &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">6&lt;/span>) | ((access) &amp;lt;&amp;lt; &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span>) | ((function) &amp;lt;&amp;lt; &lt;span style="color:#ae81ff">2&lt;/span>) | (method);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The information about which indicators should be on and which should be off is specified by a &lt;a href="http://msdn.microsoft.com/en-us/library/windows/hardware/ff542331%28v=vs.85%29.aspx" title="KEYBOARD_INDICATOR_PARAMETERS" target="_blank">KEYBOARD_INDICATOR_PARAMETERS&lt;/a> structure.&lt;/p>
&lt;p>The final code looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">uint&lt;/span> IOCTL_KEYBOARD_SET_INDICATORS = ControlCode(FileDeviceKeyboard, &lt;span style="color:#ae81ff">0&lt;/span>x0002, MethodBuffered, FileAnyAccess);
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">uint&lt;/span> FileAnyAccess = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">uint&lt;/span> MethodBuffered = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">uint&lt;/span> FileDeviceKeyboard = &lt;span style="color:#ae81ff">0&lt;/span>x0000000b;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> DddRawTargetPath = &lt;span style="color:#ae81ff">0&lt;/span>x00000001;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> ToggleLights(Locks locks)
{
DefineDosDevice(DddRawTargetPath, &lt;span style="color:#e6db74">&amp;#34;keyboard&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;\\Device\\KeyboardClass0&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> indicators = &lt;span style="color:#66d9ef">new&lt;/span> KeyboardIndicatorParameters();
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> hKeybd = CreateFile(&lt;span style="color:#e6db74">&amp;#34;\\\\.\\keyboard&amp;#34;&lt;/span>, FileAccess.Write, &lt;span style="color:#ae81ff">0&lt;/span>, IntPtr.Zero, FileMode.Open, &lt;span style="color:#ae81ff">0&lt;/span>, IntPtr.Zero))
{
indicators.UnitId = &lt;span style="color:#ae81ff">0&lt;/span>;
indicators.LedFlags = locks;
&lt;span style="color:#66d9ef">var&lt;/span> size = Marshal.SizeOf(&lt;span style="color:#66d9ef">typeof&lt;/span>(KeyboardIndicatorParameters));
&lt;span style="color:#66d9ef">uint&lt;/span> bytesReturned;
DeviceIoControl(hKeybd, IOCTL_KEYBOARD_SET_INDICATORS, &lt;span style="color:#66d9ef">ref&lt;/span> indicators, (&lt;span style="color:#66d9ef">uint&lt;/span>)size,
IntPtr.Zero, &lt;span style="color:#ae81ff">0&lt;/span>, &lt;span style="color:#66d9ef">out&lt;/span> bytesReturned, IntPtr.Zero);
}
}
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">uint&lt;/span> ControlCode(&lt;span style="color:#66d9ef">uint&lt;/span> deviceType, &lt;span style="color:#66d9ef">uint&lt;/span> function, &lt;span style="color:#66d9ef">uint&lt;/span> method, &lt;span style="color:#66d9ef">uint&lt;/span> access)
{
&lt;span style="color:#66d9ef">return&lt;/span> ((deviceType) &amp;lt;&amp;lt; &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">6&lt;/span>) | ((access) &amp;lt;&amp;lt; &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span>) | ((function) &amp;lt;&amp;lt; &lt;span style="color:#ae81ff">2&lt;/span>) | (method);
}
&lt;span style="color:#66d9ef">struct&lt;/span> &lt;span style="color:#a6e22e">KeyboardIndicatorParameters&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">ushort&lt;/span> UnitId;
&lt;span style="color:#66d9ef">public&lt;/span> Locks LedFlags;
}
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e">[Flags]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">enum&lt;/span> Locks : &lt;span style="color:#66d9ef">ushort&lt;/span>
{
None = &lt;span style="color:#ae81ff">0&lt;/span>,
KeyboardScrollLockOn = &lt;span style="color:#ae81ff">1&lt;/span>,
KeyboardNumLockOn = &lt;span style="color:#ae81ff">2&lt;/span>,
KeyboardCapsLockOn = &lt;span style="color:#ae81ff">4&lt;/span>
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>Even though toggling keyboard indicators may not be very useful porting it to C# was interesting and fun. The only real application that I was able to find is an &lt;a href="https://addons.miranda-im.org/details.php?action=viewfile&amp;#038;id=2071" title="Keyboard Notify Ext" target="_blank">addon for Miranda IM&lt;/a> which uses it as a notification mechanism.&lt;/p></description></item><item><title>Building Expression Evaluator with Expression Trees in C# – Improving the Api</title><link>https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp/</link><pubDate>Wed, 18 Apr 2012 09:03:09 +0000</pubDate><guid>https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp/</guid><description>&lt;p>This post is part of a series about building a simple mathematical expression evaluator. For previous posts and full source code see
&lt;a href="https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-table-of-contents/" title="Building Expression Evaluator with Expression Trees in C# – Table of Contents">Table of Contents&lt;/a>.&lt;/p>
&lt;h3 id="introduction">Introduction&lt;/h3>
&lt;p>
&lt;a href="https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-3/" title="Building Expression Evaluator with Expression Trees in C# – Part 3">In previous part&lt;/a> we added support for variables but it has a big disadvantage: values of the variables are bound by position and not by name. This means that you should pass values for the variables in the same order as they appear in the expression. For example the following test will fail:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Can_Process_Multiple_Variables()
{
&lt;span style="color:#66d9ef">var&lt;/span> a = &lt;span style="color:#ae81ff">6&lt;/span>;
&lt;span style="color:#66d9ef">var&lt;/span> b = &lt;span style="color:#ae81ff">4.32&lt;/span>m;
&lt;span style="color:#66d9ef">var&lt;/span> c = &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">4.15&lt;/span>m;
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;(c+b)*a&amp;#34;&lt;/span>, a, b, c), Is.EqualTo((c + b) * a));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>On the other hand binding by name has following advantages: If you change the expression you do not have to worry about keeping order of the variables you pass in sync. So in this post we are going to change expression evaluator to bind parameters by name. We will also add a new overload for better performance when we need to evaluate same expression with different parameter values.&lt;/p>
&lt;h3 id="binding-variables-by-name">Binding Variables by Name&lt;/h3>
&lt;p>The easiest way to pass variables with their name is to pass a dictionary of variables with variable name acting as a key. While this will work it is not the most elegant and readable solution. A better solution will be to pass an anonymous object with properties pointing to the variable values. So our first task is to make the following test pass:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Can_Process_Multiple_Variables()
{
&lt;span style="color:#66d9ef">var&lt;/span> a = &lt;span style="color:#ae81ff">6&lt;/span>;
&lt;span style="color:#66d9ef">var&lt;/span> b = &lt;span style="color:#ae81ff">4.32&lt;/span>m;
&lt;span style="color:#66d9ef">var&lt;/span> c = &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">4.15&lt;/span>m;
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;(c+b)*a&amp;#34;&lt;/span>, &lt;span style="color:#66d9ef">new&lt;/span> { a, b, c }), Is.EqualTo((c + b) * a));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="passing-parameters-with-anonymous-objects">Passing Parameters with Anonymous Objects&lt;/h3>
&lt;p>The first thing we need to do when an object containing variables is passed is to enumerate its properties and build a dictionary of variable name and value. Once we have the dictionary we pass it to the private Enumerate method which we implemented in previous post. The code is pretty straightforward:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression, &lt;span style="color:#66d9ef">object&lt;/span> argument = &lt;span style="color:#66d9ef">null&lt;/span>)
{
&lt;span style="color:#66d9ef">if&lt;/span> (argument == &lt;span style="color:#66d9ef">null&lt;/span>)
{
&lt;span style="color:#66d9ef">return&lt;/span> Evaluate(expression, &lt;span style="color:#66d9ef">new&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;());
}
&lt;span style="color:#66d9ef">var&lt;/span> argumentType = argument.GetType();
&lt;span style="color:#66d9ef">var&lt;/span> properties = argumentType.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(p =&amp;gt; p.CanRead &amp;amp;&amp;amp; IsNumeric(p.PropertyType));
&lt;span style="color:#66d9ef">var&lt;/span> arguments = properties.ToDictionary(property =&amp;gt; property.Name,
property =&amp;gt; Convert.ToDecimal(property.GetValue(argument, &lt;span style="color:#66d9ef">null&lt;/span>)));
&lt;span style="color:#66d9ef">return&lt;/span> Evaluate(expression, arguments);
}
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> IsNumeric(Type type)
{
&lt;span style="color:#66d9ef">switch&lt;/span> (Type.GetTypeCode(type))
{
&lt;span style="color:#66d9ef">case&lt;/span> TypeCode.SByte:
&lt;span style="color:#66d9ef">case&lt;/span> TypeCode.Byte:
&lt;span style="color:#66d9ef">case&lt;/span> TypeCode.Int16:
&lt;span style="color:#66d9ef">case&lt;/span> TypeCode.UInt16:
&lt;span style="color:#66d9ef">case&lt;/span> TypeCode.Int32:
&lt;span style="color:#66d9ef">case&lt;/span> TypeCode.UInt32:
&lt;span style="color:#66d9ef">case&lt;/span> TypeCode.Int64:
&lt;span style="color:#66d9ef">case&lt;/span> TypeCode.UInt64:
&lt;span style="color:#66d9ef">case&lt;/span> TypeCode.Single:
&lt;span style="color:#66d9ef">case&lt;/span> TypeCode.Double:
&lt;span style="color:#66d9ef">case&lt;/span> TypeCode.Decimal:
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
}
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">false&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The Enumerate method has not changed much. The most important change is that it needs to reorder the variables in the same order as they appear in the expression.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression, Dictionary&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt; arguments)
{
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">string&lt;/span>.IsNullOrWhiteSpace(expression))
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (arguments == &lt;span style="color:#66d9ef">null&lt;/span>)
{
arguments = &lt;span style="color:#66d9ef">new&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;();
}
&lt;span style="color:#66d9ef">var&lt;/span> arrayParameter = Expression.Parameter(&lt;span style="color:#66d9ef">typeof&lt;/span>(&lt;span style="color:#66d9ef">decimal&lt;/span>[]), &lt;span style="color:#e6db74">&amp;#34;args&amp;#34;&lt;/span>);
parameters.Clear();
operatorStack.Clear();
expressionStack.Clear();
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> reader = &lt;span style="color:#66d9ef">new&lt;/span> StringReader(expression))
{
&lt;span style="color:#66d9ef">int&lt;/span> peek;
&lt;span style="color:#66d9ef">while&lt;/span> ((peek = reader.Peek()) &amp;gt; -&lt;span style="color:#ae81ff">1&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> next = (&lt;span style="color:#66d9ef">char&lt;/span>)peek;
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span>.IsDigit(next))
{
expressionStack.Push(ReadOperand(reader));
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span>.IsLetter(next))
{
expressionStack.Push(ReadParameter(reader, arrayParameter));
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (Operation.IsDefined(next))
{
&lt;span style="color:#66d9ef">var&lt;/span> currentOperation = ReadOperation(reader);
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span> &amp;amp;&amp;amp; operatorStack.Peek() != &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span> &amp;amp;&amp;amp;
currentOperation.Precedence &amp;lt;= ((Operation)operatorStack.Peek()).Precedence);
operatorStack.Push(next);
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next == &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>)
{
reader.Read();
operatorStack.Push(&lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>);
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next == &lt;span style="color:#e6db74">&amp;#39;)&amp;#39;&lt;/span>)
{
reader.Read();
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span> &amp;amp;&amp;amp; operatorStack.Peek() != &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>);
operatorStack.Pop();
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next == &lt;span style="color:#e6db74">&amp;#39; &amp;#39;&lt;/span>)
{
reader.Read();
}
&lt;span style="color:#66d9ef">else&lt;/span>
{
&lt;span style="color:#66d9ef">throw&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> ArgumentException(&lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Encountered invalid character {0}&amp;#34;&lt;/span>, next),
&lt;span style="color:#e6db74">&amp;#34;expression&amp;#34;&lt;/span>);
}
}
}
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>[], &lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(expressionStack.Pop(), arrayParameter);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">var&lt;/span> values = parameters.Select(parameter =&amp;gt; arguments[parameter]).ToArray();
&lt;span style="color:#66d9ef">return&lt;/span> compiled(values);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is all we need to add support for binding arguments by name to our expression evaluator. We will now add another overload for passing parameters to the Evaluate method.&lt;/p>
&lt;h3 id="passing-parameters-with-named-arguments">Passing Parameters with Named Arguments&lt;/h3>
&lt;p>Passing arguments by anonymous objects works but we can do better: We can use named arguments together with dynamic keyword to allow specifying arguments in the following way:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#a6e22e">[Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Can_Pass_Named_Variables()
{
&lt;span style="color:#66d9ef">dynamic&lt;/span> dynamicEngine = &lt;span style="color:#66d9ef">new&lt;/span> ExpressionEvaluator();
&lt;span style="color:#66d9ef">var&lt;/span> a = &lt;span style="color:#ae81ff">6&lt;/span>;
&lt;span style="color:#66d9ef">var&lt;/span> b = &lt;span style="color:#ae81ff">4.5&lt;/span>m;
&lt;span style="color:#66d9ef">var&lt;/span> c = &lt;span style="color:#ae81ff">2.6&lt;/span>m;
Assert.That(dynamicEngine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;(c+b)*a&amp;#34;&lt;/span>, a: &lt;span style="color:#ae81ff">6&lt;/span>, b: &lt;span style="color:#ae81ff">4.5&lt;/span>, c: &lt;span style="color:#ae81ff">2.6&lt;/span>),
Is.EqualTo((c + b) * a));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>As it is based on features introduced in .Net 4 this overload will only be available for users using .Net 4 or upper.&lt;/p>
&lt;p>To implement this feature we need to inherit ExpressionEvaluator class from &lt;a href="http://msdn.microsoft.com/en-us/library/dd487439.aspx" title="DynamicObject Class" target="_blank">DynamicObject&lt;/a> and override &lt;a href="http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.tryinvokemember.aspx" title="DynamicObject.TryInvokeMember Method" target="_blank">TryInvokeMember&lt;/a> method. This method will get invoked whenever a method is called which should be resolved at runtime. Parameter names as well as values are passed to this method. This allows us to build a dictionary of arguments and pass them to the Enumerate method showed in this post. This is how we do it:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> TryInvokeMember(InvokeMemberBinder binder, &lt;span style="color:#66d9ef">object&lt;/span>[] args, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">object&lt;/span> result)
{
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#e6db74">&amp;#34;Evaluate&amp;#34;&lt;/span> != binder.Name)
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">base&lt;/span>.TryInvokeMember(binder, args, &lt;span style="color:#66d9ef">out&lt;/span> result);
}
&lt;span style="color:#66d9ef">if&lt;/span> (!(args[&lt;span style="color:#ae81ff">0&lt;/span>] &lt;span style="color:#66d9ef">is&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span>))
{
&lt;span style="color:#66d9ef">throw&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> ArgumentException(&lt;span style="color:#e6db74">&amp;#34;No expression specified for parsing&amp;#34;&lt;/span>);
}
&lt;span style="color:#75715e">//args will contain expression and arguments,
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">//ArgumentNames will contain only argument names
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> (args.Length != binder.CallInfo.ArgumentNames.Count + &lt;span style="color:#ae81ff">1&lt;/span>)
{
&lt;span style="color:#66d9ef">throw&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> ArgumentException(&lt;span style="color:#e6db74">&amp;#34;Argument names missing.&amp;#34;&lt;/span>);
}
&lt;span style="color:#66d9ef">var&lt;/span> arguments = &lt;span style="color:#66d9ef">new&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">string&lt;/span>, &lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;();
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i = &lt;span style="color:#ae81ff">0&lt;/span>; i &amp;lt; binder.CallInfo.ArgumentNames.Count; i++)
{
&lt;span style="color:#66d9ef">if&lt;/span> (IsNumeric(args[i + &lt;span style="color:#ae81ff">1&lt;/span>].GetType()))
{
arguments.Add(binder.CallInfo.ArgumentNames[i], Convert.ToDecimal(args[i + &lt;span style="color:#ae81ff">1&lt;/span>]));
}
}
result = Evaluate((&lt;span style="color:#66d9ef">string&lt;/span>)args[&lt;span style="color:#ae81ff">0&lt;/span>], arguments);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now we can specify parameter names in a more elegant way without having to construct an anonymous object.&lt;/p>
&lt;h3 id="improving-expression-evaluator-performance">Improving Expression Evaluator Performance&lt;/h3>
&lt;p>Let’s say you have an expression which you want to evaluate multiple times but with different parameters. Currently the Evaluate method will parse and compile the expression tree for every evaluation even thought the expression is same every time. As the compilation of expression trees is an expensive operation it will be better if the library allowed to compile the expression only once and execute it multiple times with different values of variables.&lt;/p>
&lt;p>To support this scenario we will add a new method Compile which takes an expression and returns a delegate which you can execute multiple times. Here is an example of what we want to do:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#a6e22e">[Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Can_Invoke_Expression_Multiple_Times()
{
&lt;span style="color:#66d9ef">var&lt;/span> a = &lt;span style="color:#ae81ff">6&lt;/span>m;
&lt;span style="color:#66d9ef">var&lt;/span> b = &lt;span style="color:#ae81ff">3.9&lt;/span>m;
&lt;span style="color:#66d9ef">var&lt;/span> c = &lt;span style="color:#ae81ff">4.9&lt;/span>m;
&lt;span style="color:#66d9ef">var&lt;/span> compiled = engine.Compile(&lt;span style="color:#e6db74">&amp;#34;(a+b)/(a+c)&amp;#34;&lt;/span>);
Assert.That(compiled(&lt;span style="color:#66d9ef">new&lt;/span> { a, b, c }), Is.EqualTo((a + b) / (a + c)));
a = &lt;span style="color:#ae81ff">5.4&lt;/span>m;
b = -&lt;span style="color:#ae81ff">2.4&lt;/span>m;
c = &lt;span style="color:#ae81ff">7.5&lt;/span>m;
Assert.That(compiled(&lt;span style="color:#66d9ef">new&lt;/span> { a, b, c }), Is.EqualTo((a + b) / (a + c)));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The Compile method is the same as the Evaluate method with the only difference being that it returns the compiled delegate instead of invoking it. This means we can extract a new method and use it from both methods. I will not show the full source code here as it is does not add any new parsing logic and the code is available at
&lt;a href="https://github.com/Giorgi/Math-Expression-Evaluator" target="_blank" rel="noopener">github&lt;/a>.&lt;/p>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>In this post we improved expression evaluator and added support for binding variables by name. We also added a new overload for specifying variables as named arguments. Finally we added a new method returning compiled delegate to avoid multiple compilations.&lt;/p>
&lt;p>In the next post we will add support for unary negation. Do not miss it&lt;/p></description></item><item><title>Using Reactive Extensions for Streaming Data from Database</title><link>https://www.giorgi.dev/reactive-extensions/using-reactive-extensions-for-streaming-data-from-database/</link><pubDate>Mon, 02 Apr 2012 22:04:03 +0000</pubDate><guid>https://www.giorgi.dev/reactive-extensions/using-reactive-extensions-for-streaming-data-from-database/</guid><description>&lt;p>You have probably heard about &lt;a title="Reactive Extensions" target="_blank" href="http://msdn.microsoft.com/en-us/data/gg577609">Reactive Extensions&lt;/a>, a library from Microsoft that greatly simplifies working with asynchronous data streams and allows to query them with Linq operators. There are many different scenarios where using rx results in a much more simple and flexible code. This post demonstrates how to use reactive extensions for loading data from database asynchronously in chunks.&lt;/p>
&lt;h3 id="the-problem">The Problem&lt;/h3>
&lt;p>Recently I had to load data from sqlite database in grid view but the query was taking long time as there were hundreds of thousands of rows and the query was doing like %search term% filtering. The customer wanted to run the query in background and load data in chunks as they became available similar to sql server management studio. This way the grid would show first 200 available rows in the beginning and then when the next 200 rows were loaded they would be added to the grid and so on until the query completes. Here are some screenshots that demonstrate the desired functionality:&lt;/p>
&lt;p>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/reactive-extensions-streaming/Searching.png" >
&lt;img src="https://www.giorgi.dev/img/reactive-extensions-streaming/Searching.png" alt="" >
&lt;/a>
&lt;/figure>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/reactive-extensions-streaming/Loaded%20200%20rows.png" >
&lt;img src="https://www.giorgi.dev/img/reactive-extensions-streaming/Loaded%20200%20rows.png" alt="" >
&lt;/a>
&lt;/figure>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/reactive-extensions-streaming/Loaded%20400%20rows.png" >
&lt;img src="https://www.giorgi.dev/img/reactive-extensions-streaming/Loaded%20400%20rows.png" alt="" >
&lt;/a>
&lt;/figure>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/reactive-extensions-streaming/Loaded%20600%20rows.png" >
&lt;img src="https://www.giorgi.dev/img/reactive-extensions-streaming/Loaded%20600%20rows.png" alt="" >
&lt;/a>
&lt;/figure>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/reactive-extensions-streaming/Finished%20searching.png" >
&lt;img src="https://www.giorgi.dev/img/reactive-extensions-streaming/Finished%20searching.png" alt="" >
&lt;/a>
&lt;/figure>
&lt;/p>
&lt;h3 id="solution-without-reactive-extensions">Solution without Reactive Extensions&lt;/h3>
&lt;p>The most straightforward way to solve the problem is to call the method that reads the data in background thread and send the data to main thread when there are enough rows. Here is how to do it in WinForms application using backgroundworker:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">internal&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">CompanyDal&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> IEnumerable&amp;lt;Company&amp;gt; GetCompanies(&lt;span style="color:#66d9ef">string&lt;/span> title)
{
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> connection = &lt;span style="color:#66d9ef">new&lt;/span> SQLiteConnection(connectionString))
{
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> cmd = connection.CreateCommand())
{
cmd.CommandText =
&lt;span style="color:#e6db74">&amp;#34;select company, category, state, phone from maindata where company like @name&amp;#34;&lt;/span>;
cmd.Parameters.AddWithValue(&lt;span style="color:#e6db74">&amp;#34;@name&amp;#34;&lt;/span>, String.Format(&lt;span style="color:#e6db74">&amp;#34;%{0}%&amp;#34;&lt;/span>, title));
connection.Open();
&lt;span style="color:#66d9ef">var&lt;/span> reader = cmd.ExecuteReader();
&lt;span style="color:#66d9ef">using&lt;/span> (reader)
{
&lt;span style="color:#66d9ef">while&lt;/span> (reader.Read())
{
&lt;span style="color:#66d9ef">var&lt;/span> company = &lt;span style="color:#66d9ef">new&lt;/span> Company
{
Title = reader.GetString(&lt;span style="color:#ae81ff">0&lt;/span>),
Category = reader.GetString(&lt;span style="color:#ae81ff">1&lt;/span>),
State = reader.GetString(&lt;span style="color:#ae81ff">2&lt;/span>),
Phone = reader.GetString(&lt;span style="color:#ae81ff">3&lt;/span>)
};
&lt;span style="color:#66d9ef">yield&lt;/span> &lt;span style="color:#66d9ef">return&lt;/span> company;
}
}
}
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> WorkerDoWork(&lt;span style="color:#66d9ef">object&lt;/span> sender, DoWorkEventArgs e)
{
&lt;span style="color:#66d9ef">var&lt;/span> data = &lt;span style="color:#66d9ef">new&lt;/span> List&amp;lt;Company&amp;gt;();
&lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> company &lt;span style="color:#66d9ef">in&lt;/span> companyDal.GetCompanies(e.Argument.ToString()))
{
data.Add(company);
&lt;span style="color:#66d9ef">if&lt;/span> (data.Count % BufferSize == &lt;span style="color:#ae81ff">0&lt;/span>)
{
worker.ReportProgress(&lt;span style="color:#ae81ff">0&lt;/span>, data.ToArray());
data.Clear();
}
}
worker.ReportProgress(&lt;span style="color:#ae81ff">0&lt;/span>, data.ToArray());
}
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> WorkerProgressChanged(&lt;span style="color:#66d9ef">object&lt;/span> sender, ProgressChangedEventArgs e)
{
&lt;span style="color:#66d9ef">var&lt;/span> data = e.UserState &lt;span style="color:#66d9ef">as&lt;/span> IEnumerable&amp;lt;Company&amp;gt;;
&lt;span style="color:#66d9ef">if&lt;/span> (data != &lt;span style="color:#66d9ef">null&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> index = Math.Max(companyGridView.FirstDisplayedScrollingRowIndex, &lt;span style="color:#ae81ff">0&lt;/span>);
companies.AddRange(data);
companyBindingSource.ResetBindings(&lt;span style="color:#66d9ef">false&lt;/span>);
progressLabel.Text = &lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Loaded {0} rows&amp;#34;&lt;/span>, companies.Count);
companyGridView.FirstDisplayedScrollingRowIndex = index;
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The solution works but it can easily become complicated if you want to have more control when the data is loaded or want to be able to stop loading data. For example if you want to load next chunk every 2 seconds or load data when user clicks load button then most part of the code will be dealing with thread synchronization and it will be difficult to understand what is the code really doing. Let’s see how reactive extensions can help.&lt;/p>
&lt;h3 id="reactive-extensions-8211-simplifying-the-solution">Reactive Extensions – Simplifying the Solution&lt;/h3>
&lt;p>To solve the problem with rx first need to convert the source collection which in our case is matching companies to observable stream. We can do it with &lt;a href="http://msdn.microsoft.com/en-us/library/hh212140%28v=vs.103%29.aspx" title="ToObservable" target="_blank">ToObservable method.&lt;/a> We want the query to run on background thread so we pass a thread pool scheduler as a parameter. Now we need to tell the stream that we want to get data in chunks. We do it by calling &lt;a href="http://msdn.microsoft.com/en-us/library/hh212130%28v=vs.103%29.aspx" title="Observable.Buffer method" target="_blank">Observable.Buffer method&lt;/a> and pass desired chunk size. As we want the observer to provide notifications on the gui thread we call ObserveOn method and pass SynchronizationContext.Current. Finally we subscribe to the observable to actually process the data:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> SearchButtonClick(&lt;span style="color:#66d9ef">object&lt;/span> sender, EventArgs e)
{
companies.Clear();
companyBindingSource.ResetBindings(&lt;span style="color:#66d9ef">false&lt;/span>);
stopButton.Enabled = &lt;span style="color:#66d9ef">true&lt;/span>;
searchButton.Enabled = &lt;span style="color:#66d9ef">false&lt;/span>;
progressLabel.Text = &lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Searching for {0} ...&amp;#34;&lt;/span>, searchBox.Text);
companyDal.GetCompanies(searchBox.Text).ToObservable(Scheduler.ThreadPool).
Buffer(BufferSize).ObserveOn(SynchronizationContext.Current).
Subscribe(loadedData =&amp;gt;
{
&lt;span style="color:#66d9ef">var&lt;/span> index = Math.Max(companyGridView.FirstDisplayedScrollingRowIndex, &lt;span style="color:#ae81ff">0&lt;/span>);
companies.AddRange(loadedData);
companyBindingSource.ResetBindings(&lt;span style="color:#66d9ef">false&lt;/span>);
progressLabel.Text = &lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Loaded {0} rows&amp;#34;&lt;/span>, companies.Count);
companyGridView.FirstDisplayedScrollingRowIndex = index;
},
exception =&amp;gt; { progressLabel.Text = exception.Message; },
() =&amp;gt; { progressLabel.Text = &lt;span style="color:#e6db74">&amp;#34;Finished loading data&amp;#34;&lt;/span>; });
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>We now have basic solution implemented with just a few lines of codes. The important points are that there is practically no threading code, no need to implement partial loading manually and as we will see the above code is easy to extend.&lt;/p>
&lt;h3 id="extending-reactive-extensions-solution">Extending Reactive Extensions Solution&lt;/h3>
&lt;p>First of all we want to toggle search and stop button state when the search is completed. We could do that in onError and onCompleted callbacks but that would result in duplicated code. Instead we can use
&lt;a href="http://msdn.microsoft.com/en-us/library/hh212133%28v=vs.103%29.aspx" title="Observable.Finally method" target="_blank" rel="noopener">Observable.Finally method&lt;/a> which executes in both cases. As we want the Finally callback to execute on the gui thread too we append it to ObserveOn method call.&lt;/p>
&lt;p>Secondly we want the query to stop when stop button is clicked. The most straightforward way is to store Subscribe method return value in a field and dispose subscription when stop button is clicked but as you have probably guessed there is ‘reactive’ way to do the same. In the form constructor we create an observable from the stop button click event by using
&lt;a href="http://msdn.microsoft.com/en-us/library/hh229705%28v=vs.103%29.aspx" title="Observable.FromEventPattern method" target="_blank" rel="noopener">Observable.FromEventPattern method.&lt;/a> We then combine it with our data stream by TakeUntil method which tells the data stream to return records until the click event observable produces a value:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> MainForm()
{
InitializeComponent();
companyBindingSource.DataSource = companies;
stopClicked = Observable.FromEventPattern&amp;lt;EventArgs&amp;gt;(stopButton, &lt;span style="color:#e6db74">&amp;#34;Click&amp;#34;&lt;/span>);
}
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> SearchButtonClick(&lt;span style="color:#66d9ef">object&lt;/span> sender, EventArgs e)
{
companies.Clear();
companyBindingSource.ResetBindings(&lt;span style="color:#66d9ef">false&lt;/span>);
stopButton.Enabled = &lt;span style="color:#66d9ef">true&lt;/span>;
searchButton.Enabled = &lt;span style="color:#66d9ef">false&lt;/span>;
progressLabel.Text = &lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Searching for {0} ...&amp;#34;&lt;/span>, searchBox.Text);
companyDal.GetCompanies(searchBox.Text).ToObservable(Scheduler.ThreadPool).
Buffer(BufferSize).TakeUntil(stopClicked).
ObserveOn(SynchronizationContext.Current).
Finally(() =&amp;gt;
{
searchButton.Enabled = &lt;span style="color:#66d9ef">true&lt;/span>;
stopButton.Enabled = &lt;span style="color:#66d9ef">false&lt;/span>;
}).
Subscribe(loadedData =&amp;gt;
{
&lt;span style="color:#66d9ef">var&lt;/span> index = Math.Max(companyGridView.FirstDisplayedScrollingRowIndex, &lt;span style="color:#ae81ff">0&lt;/span>);
companies.AddRange(loadedData);
companyBindingSource.ResetBindings(&lt;span style="color:#66d9ef">false&lt;/span>);
progressLabel.Text = &lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Loaded {0} rows&amp;#34;&lt;/span>, companies.Count);
companyGridView.FirstDisplayedScrollingRowIndex = index;
},
exception =&amp;gt; { progressLabel.Text = exception.Message; },
() =&amp;gt; { progressLabel.Text = &lt;span style="color:#e6db74">&amp;#34;Finished loading data&amp;#34;&lt;/span>; });
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Finally let’s have a look at how easily we can change buffering logic. The buffer method has many overloads so if we want to buffer data based on time spans we can simply use the overload that takes a timespan instance as a parameter. We can also combine buffer size with timespan so that buffer is created when any of the conditions are met. To show a little but more useful example imagine that we want to load next chunk of data when user clicks load button. We can achieve it by using buffer overload which takes another observable indicating when to create next buffer. As with stop button we first create an observable from load button click event and then we combine our observables like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> SearchButtonClick(&lt;span style="color:#66d9ef">object&lt;/span> sender, EventArgs e)
{
companies.Clear();
companyBindingSource.ResetBindings(&lt;span style="color:#66d9ef">false&lt;/span>);
searchButton.Enabled = &lt;span style="color:#66d9ef">false&lt;/span>;
loadButton.Enabled = stopButton.Enabled = &lt;span style="color:#66d9ef">true&lt;/span>;
progressLabel.Text = &lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Searching for {0} ...&amp;#34;&lt;/span>, searchBox.Text);
companyDal.GetCompanies(searchBox.Text).ToObservable(Scheduler.ThreadPool).
Buffer(() =&amp;gt; loadClicked).TakeUntil(stopClicked).
ObserveOn(SynchronizationContext.Current).
Finally(() =&amp;gt;
{
searchButton.Enabled = &lt;span style="color:#66d9ef">true&lt;/span>;
loadButton.Enabled = stopButton.Enabled = &lt;span style="color:#66d9ef">false&lt;/span>;
}).
Subscribe(loadedData =&amp;gt;
{
&lt;span style="color:#66d9ef">var&lt;/span> index = Math.Max(companyGridView.FirstDisplayedScrollingRowIndex, &lt;span style="color:#ae81ff">0&lt;/span>);
companies.AddRange(loadedData);
companyBindingSource.ResetBindings(&lt;span style="color:#66d9ef">false&lt;/span>);
progressLabel.Text = &lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Loaded {0} rows&amp;#34;&lt;/span>, companies.Count);
companyGridView.FirstDisplayedScrollingRowIndex = index;
},
exception =&amp;gt; { progressLabel.Text = exception.Message; },
() =&amp;gt; { progressLabel.Text = &lt;span style="color:#e6db74">&amp;#34;Finished loading data&amp;#34;&lt;/span>; });
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>As we can see reactive extensions enables us to solve complex problems very easily. It makes working with asynchronous streams simple and allow us to concentrate on business logic instead of managing threads manually. For more information about rx and its internals I highly recommend &lt;a href="http://channel9.msdn.com/shows/Going+Deep/E2E-Erik-Meijer-and-Wes-Dyer-Reactive-Framework-Rx-Under-the-Hood-1-of-2/" title="E2E: Erik Meijer and Wes Dyer - Reactive Framework (Rx) Under the Hood 1 of 2" target="_blank">Channel 9&lt;/a>&lt;a href="http://channel9.msdn.com/shows/Going+Deep/E2E-Erik-Meijer-and-Wes-Dyer-Reactive-Framework-Rx-Under-the-Hood-2-of-2/" title="E2E: Erik Meijer and Wes Dyer - Reactive Framework (Rx) Under the Hood 2 of 2" target="_blank">videos.&lt;/a>&lt;/p>
&lt;p>Have you used rx to solve real life problems? Did you it help you implement complex solutions in a concise way? Share your experience and thoughts about it in comments.&lt;/p></description></item><item><title>Building Expression Evaluator with Expression Trees in C# – Part 3</title><link>https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-3/</link><pubDate>Wed, 21 Mar 2012 08:14:52 +0000</pubDate><guid>https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-3/</guid><description>&lt;h3 id="introduction">Introduction&lt;/h3>
&lt;p>
&lt;a href="https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-2/" title="Building Expression Evaluator with Expression Trees in C# – Part 2">In part two of this series&lt;/a> we built an expression evaluator capable of parsing expressions with parentheses. In this part we are going to add support for expression with variables.&lt;/p>
&lt;h3 id="adding-support-for-single-variable-to-expression-evaluator">Adding support for single variable to expression evaluator&lt;/h3>
&lt;p>The simplest case is to make the following test pass:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#a6e22e">[Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Can_Process_Simple_Variables()
{
&lt;span style="color:#66d9ef">decimal&lt;/span> a = &lt;span style="color:#ae81ff">2.6&lt;/span>m;
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;a&amp;#34;&lt;/span>, a), Is.EqualTo(a));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>First of all we need to add a second parameter to Evaluate method. To keep the existing tests green we will also specify a default value for it. Running the test causes Evaluate method to throw an exception as it does not recognize variables. To avoid exception during parsing the expression when we encounter a letter we will read the variable name and as variables are similar to numeric operands store them on expression stack. To represent the variables in expression tree we are going to use &lt;a title="Expression.Parameter Method" href="http://msdn.microsoft.com/en-us/library/bb355908.aspx" target="_blank">Expression.Parameter Method&lt;/a> which is a factory method for creating instances of ParameterExpression class. The method for parsing variables looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> ParameterExpression ReadParameter(TextReader reader)
{
&lt;span style="color:#66d9ef">var&lt;/span> operand = &lt;span style="color:#66d9ef">string&lt;/span>.Empty;
&lt;span style="color:#66d9ef">int&lt;/span> peek;
&lt;span style="color:#66d9ef">while&lt;/span> ((peek = reader.Peek()) &amp;gt; -&lt;span style="color:#ae81ff">1&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> next = (&lt;span style="color:#66d9ef">char&lt;/span>)peek;
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span>.IsLetter(next))
{
reader.Read();
operand += next;
}
&lt;span style="color:#66d9ef">else&lt;/span>
{
&lt;span style="color:#66d9ef">break&lt;/span>;
}
}
&lt;span style="color:#66d9ef">var&lt;/span> parameter = Expression.Parameter(&lt;span style="color:#66d9ef">typeof&lt;/span>(&lt;span style="color:#66d9ef">decimal&lt;/span>), operand);
&lt;span style="color:#66d9ef">return&lt;/span> parameter;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This change solves the issue of parsing variables but running the test causes an InvalidOperationException as we are not passing any parameters to Expression.Lambda method. To fix this issue we need to keep track of all variables that we encounter while parsing the expression and pass them to Expression.Lambda method. As we need to pass the value of the parameter to the compiled expression tree we also have to change type of the delegate.&lt;/p>
&lt;p>The Evaluate method now looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression, &lt;span style="color:#66d9ef">decimal&lt;/span> variable1 = &lt;span style="color:#ae81ff">0&lt;/span>)
{
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">string&lt;/span>.IsNullOrWhiteSpace(expression))
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
}
operatorStack.Clear();
expressionStack.Clear();
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> reader = &lt;span style="color:#66d9ef">new&lt;/span> StringReader(expression))
{
&lt;span style="color:#66d9ef">int&lt;/span> peek;
&lt;span style="color:#66d9ef">while&lt;/span> ((peek = reader.Peek()) &amp;gt; -&lt;span style="color:#ae81ff">1&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> next = (&lt;span style="color:#66d9ef">char&lt;/span>)peek;
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span>.IsDigit(next))
{
expressionStack.Push(ReadOperand(reader));
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span>.IsLetter(next))
{
&lt;span style="color:#66d9ef">var&lt;/span> parameterExpression = ReadParameter(reader);
parameters.Add(parameterExpression);
expressionStack.Push(parameterExpression);
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (Operation.IsDefined(next))
{
&lt;span style="color:#66d9ef">var&lt;/span> currentOperation = ReadOperation(reader);
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span> &amp;amp;&amp;amp; operatorStack.Peek() != &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span> &amp;amp;&amp;amp;
currentOperation.Precedence &amp;lt;= ((Operation)operatorStack.Peek()).Precedence);
operatorStack.Push(next);
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next == &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>)
{
reader.Read();
operatorStack.Push(&lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>);
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next == &lt;span style="color:#e6db74">&amp;#39;)&amp;#39;&lt;/span>)
{
reader.Read();
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span> &amp;amp;&amp;amp; operatorStack.Peek() != &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>);
operatorStack.Pop();
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next != &lt;span style="color:#e6db74">&amp;#39; &amp;#39;&lt;/span>)
{
&lt;span style="color:#66d9ef">throw&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> ArgumentException(&lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Encountered invalid character {0}&amp;#34;&lt;/span>, next), &lt;span style="color:#e6db74">&amp;#34;expression&amp;#34;&lt;/span>);
}
}
}
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>, &lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(expressionStack.Pop(), parameters).Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled(variable1);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The new test now passes but we have successfully broken previous test when we do not pass any variable. To make all tests pass again we will need to build the delegate based on number of parameters and pass corresponding variable if there is one. The simplest solution is to build the appropriate delegate based on number of parameters like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">if&lt;/span> (parameters.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> compiled = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>, &lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(expressionStack.Pop(), parameters).Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled(variable1);
}
&lt;span style="color:#66d9ef">else&lt;/span>
{
&lt;span style="color:#66d9ef">var&lt;/span> compiled = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(expressionStack.Pop()).Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This works but it only supports one parameter and this approach cannot be used when we do not know how many variables will be in the expression. Also, the Func delegate supports only &lt;a href="http://msdn.microsoft.com/en-us/library/dd402862.aspx" title="Func delegate with 16 parameters" target="_blank">16 parameters&lt;/a> and it would be very ugly to use it.&lt;/p>
&lt;h3 id="adding-support-for-multiple-variables">Adding support for multiple variables&lt;/h3>
&lt;p>A better solution is to build and expression tree that accepts an array of variables. This way when there is no parameter we will simply pass an empty array and when there are several parameters we will simply pass them. The delegate signature will be the same in all situations so we will not need any if else statements to handle different scenarios.&lt;/p>
&lt;p>To help us with the implementation of new approach we are going to add these tests to our test suite:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#a6e22e">[Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Can_Process_Simple_Variables()
{
&lt;span style="color:#66d9ef">decimal&lt;/span> a = &lt;span style="color:#ae81ff">2.6&lt;/span>m;
&lt;span style="color:#66d9ef">decimal&lt;/span> b = &lt;span style="color:#ae81ff">5.7&lt;/span>m;
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;a&amp;#34;&lt;/span>, a), Is.EqualTo(a));
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;a+a&amp;#34;&lt;/span>, a), Is.EqualTo(a + a));
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;a+b&amp;#34;&lt;/span>, a, b), Is.EqualTo(a + b));
}
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e">[Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Can_Process_Multiple_Variables()
{
&lt;span style="color:#66d9ef">var&lt;/span> a = &lt;span style="color:#ae81ff">6&lt;/span>;
&lt;span style="color:#66d9ef">var&lt;/span> b = &lt;span style="color:#ae81ff">4.32&lt;/span>m;
&lt;span style="color:#66d9ef">var&lt;/span> c = &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">4.15&lt;/span>m;
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;(((9-a/2)*2-b)/2-a-1)/(2+c/(2+4))&amp;#34;&lt;/span>, a, b, c),
Is.EqualTo((((&lt;span style="color:#ae81ff">9&lt;/span> - a / &lt;span style="color:#ae81ff">2&lt;/span>) * &lt;span style="color:#ae81ff">2&lt;/span> - b) / &lt;span style="color:#ae81ff">2&lt;/span> - a - &lt;span style="color:#ae81ff">1&lt;/span>) / (&lt;span style="color:#ae81ff">2&lt;/span> + c / (&lt;span style="color:#ae81ff">2&lt;/span> + &lt;span style="color:#ae81ff">4&lt;/span>))));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The expression tree that we are going to build will have one array parameter. To access individual members of this array in the expression we will use &lt;a href="http://msdn.microsoft.com/en-us/library/bb345296.aspx" title="Expression.ArrayIndex Method" target="_blank">Expression.ArrayIndex Method&lt;/a> We will also change the Evaluate method to accept variable number of parameters by params keyword. Let’s look at the modified code:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression, &lt;span style="color:#66d9ef">params&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span>[] arguments)
{
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">string&lt;/span>.IsNullOrWhiteSpace(expression))
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
}
operatorStack.Clear();
expressionStack.Clear();
&lt;span style="color:#66d9ef">var&lt;/span> arrayParameter = Expression.Parameter(&lt;span style="color:#66d9ef">typeof&lt;/span>(&lt;span style="color:#66d9ef">decimal&lt;/span>[]), &lt;span style="color:#e6db74">&amp;#34;args&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> reader = &lt;span style="color:#66d9ef">new&lt;/span> StringReader(expression))
{
&lt;span style="color:#66d9ef">int&lt;/span> peek;
&lt;span style="color:#66d9ef">while&lt;/span> ((peek = reader.Peek()) &amp;gt; -&lt;span style="color:#ae81ff">1&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> next = (&lt;span style="color:#66d9ef">char&lt;/span>)peek;
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span>.IsDigit(next))
{
expressionStack.Push(ReadOperand(reader));
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span>.IsLetter(next))
{
&lt;span style="color:#66d9ef">var&lt;/span> parameterExpression = ReadParameter(reader, arrayParameter);
expressionStack.Push(parameterExpression);
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (Operation.IsDefined(next))
{
&lt;span style="color:#66d9ef">var&lt;/span> currentOperation = ReadOperation(reader);
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span> &amp;amp;&amp;amp; operatorStack.Peek() != &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span> &amp;amp;&amp;amp;
currentOperation.Precedence &amp;lt;= ((Operation)operatorStack.Peek()).Precedence);
operatorStack.Push(next);
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next == &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>)
{
reader.Read();
operatorStack.Push(&lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>);
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next == &lt;span style="color:#e6db74">&amp;#39;)&amp;#39;&lt;/span>)
{
reader.Read();
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span> &amp;amp;&amp;amp; operatorStack.Peek() != &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>);
operatorStack.Pop();
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next != &lt;span style="color:#e6db74">&amp;#39; &amp;#39;&lt;/span>)
{
&lt;span style="color:#66d9ef">throw&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> ArgumentException(&lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Encountered invalid character {0}&amp;#34;&lt;/span>, next),
&lt;span style="color:#e6db74">&amp;#34;expression&amp;#34;&lt;/span>);
}
}
}
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>[], &lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(expressionStack.Pop(), arrayParameter).Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled(arguments);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>You will notice that there are several changes from the previous version: There is only one parameter in the expression tree we are building, we are passing this parameter to the ReadParameter method and we are generating the same type of delegate for all cases.&lt;/p>
&lt;p>The only remaining change is need in ReadParameter method so that it returns correct item from the parameter array when we encounter a variable. We also need to make sure that we return the same item when the same variable is used again the expression. For this we will need to keep track of the variables and return corresponding item from the ReadParameter method. Here is how it works:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> Expression ReadParameter(TextReader reader, Expression arrayParameter)
{
&lt;span style="color:#66d9ef">var&lt;/span> parameter = &lt;span style="color:#66d9ef">string&lt;/span>.Empty;
&lt;span style="color:#66d9ef">int&lt;/span> peek;
&lt;span style="color:#66d9ef">while&lt;/span> ((peek = reader.Peek()) &amp;gt; -&lt;span style="color:#ae81ff">1&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> next = (&lt;span style="color:#66d9ef">char&lt;/span>)peek;
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span>.IsLetter(next))
{
reader.Read();
parameter += next;
}
&lt;span style="color:#66d9ef">else&lt;/span>
{
&lt;span style="color:#66d9ef">break&lt;/span>;
}
}
&lt;span style="color:#66d9ef">if&lt;/span> (!parameters.Contains(parameter))
{
parameters.Add(parameter);
}
&lt;span style="color:#66d9ef">return&lt;/span> Expression.ArrayIndex(arrayParameter, Expression.Constant(parameters.IndexOf(parameter)));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see whenever we encounter a variable we add it to parameter collection if it is not already present and then build an array index expression using corresponding index.&lt;/p>
&lt;p>Using &lt;a href="http://msdn.microsoft.com/en-us/library/bb397975(v=vs.90).aspx" title="Expression Tree Visualizer">expression tree visualizer&lt;/a> we can see how the expression tree that we build using the expression evaluator for last test case looks like:&lt;/p>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/expression-evaluator/ExpressionTree.png" >
&lt;img src="https://www.giorgi.dev/img/expression-evaluator/ExpressionTree.png" alt="" >
&lt;/a>
&lt;/figure>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>Our new implementation of expression evaluator now supports expressions that contain numbers as well as variables. In the next post we will add several overloads to make passing parameters easier and add a new overload for better performance when we need to evaluate same expression with different parameter values. Full source code will be made available on github in couple of days.&lt;/p></description></item><item><title>Building Expression Evaluator with Expression Trees in C# – Part 2</title><link>https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-2/</link><pubDate>Tue, 28 Feb 2012 07:41:10 +0000</pubDate><guid>https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-2/</guid><description>&lt;h3 id="introduction">Introduction&lt;/h3>
&lt;p>
&lt;a href="https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-1/" title="In previous post">In previous post&lt;/a> I showed how to build a simple expression evaluator using expression trees. Although it works fine it has some drawbacks:&lt;/p>
&lt;ul>
&lt;li>It does not support parentheses.&lt;/li>
&lt;li>It does not support variables.&lt;/li>
&lt;li>In some cases the result can be wrong as the parser is not using left to right parsing.&lt;/li>
&lt;li>It is compiling delegates for every expression which results in slower execution.&lt;/li>
&lt;/ul>
&lt;p>To solve these issues we will use a different algorithm and deal with all points except variable support.&lt;/p>
&lt;h3 id="implementing-expression-evaluator-using-shunting-yard-algorithm">Implementing expression evaluator using shunting yard algorithm&lt;/h3>
&lt;p>The algorithm that we are going to implement is called &lt;a href="https://en.wikipedia.org/wiki/Shunting-yard_algorithm" target="_blank">Shunting yard algorithm&lt;/a> and works like this:&lt;/p>
&lt;ol>
&lt;li>If the next token is number add it to operand stack.&lt;/li>
&lt;li>If it is an operation token and operator stack is empty push it to operator stack.&lt;/li>
&lt;li>If it is an operation token and operator stack is not empty and current operation has higher precedence push it to operator stack. If not pop the operator from stack and evaluate it with arguments from the operand stack and jump to step 2.&lt;/li>
&lt;li>When we reach the end evaluate all operations on the stack&lt;/li>
&lt;/ol>
&lt;p>Translating this into code is pretty straightforward and results in following expression evaluator:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">string&lt;/span>.IsNullOrWhiteSpace(expression))
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
}
&lt;span style="color:#66d9ef">var&lt;/span> expressionStack = &lt;span style="color:#66d9ef">new&lt;/span> Stack&amp;lt;Expression&amp;gt;();
&lt;span style="color:#66d9ef">var&lt;/span> operatorStack = &lt;span style="color:#66d9ef">new&lt;/span> Stack&amp;lt;&lt;span style="color:#66d9ef">char&lt;/span>&amp;gt;();
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> reader = &lt;span style="color:#66d9ef">new&lt;/span> StringReader(expression))
{
&lt;span style="color:#66d9ef">int&lt;/span> peek;
&lt;span style="color:#66d9ef">while&lt;/span> ((peek = reader.Peek()) &amp;gt; -&lt;span style="color:#ae81ff">1&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> next = (&lt;span style="color:#66d9ef">char&lt;/span>)peek;
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span>.IsDigit(next))
{
expressionStack.Push(ReadOperand(reader));
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (Operation.IsDefined(next))
{
&lt;span style="color:#66d9ef">var&lt;/span> currentOperation = ReadOperation(reader);
&lt;span style="color:#66d9ef">while&lt;/span> (&lt;span style="color:#66d9ef">true&lt;/span>)
{
&lt;span style="color:#66d9ef">if&lt;/span> (operatorStack.Count == &lt;span style="color:#ae81ff">0&lt;/span>)
{
operatorStack.Push(next);
&lt;span style="color:#66d9ef">break&lt;/span>;
}
&lt;span style="color:#66d9ef">var&lt;/span> lastOperition = operatorStack.Peek();
&lt;span style="color:#66d9ef">if&lt;/span> (currentOperation.Precedence &amp;gt; ((Operation)lastOperition).Precedence)
{
operatorStack.Push(next);
&lt;span style="color:#66d9ef">break&lt;/span>;
}
&lt;span style="color:#66d9ef">var&lt;/span> right = expressionStack.Pop();
&lt;span style="color:#66d9ef">var&lt;/span> left = expressionStack.Pop();
expressionStack.Push(((Operation)operatorStack.Pop()).Apply(left, right));
}
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next != &lt;span style="color:#e6db74">&amp;#39; &amp;#39;&lt;/span>)
{
&lt;span style="color:#66d9ef">throw&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> ArgumentException(&lt;span style="color:#e6db74">&amp;#34;Invalid character encountered&amp;#34;&lt;/span>, &lt;span style="color:#e6db74">&amp;#34;expression&amp;#34;&lt;/span>);
}
}
}
&lt;span style="color:#66d9ef">while&lt;/span> (operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> right = expressionStack.Pop();
&lt;span style="color:#66d9ef">var&lt;/span> left = expressionStack.Pop();
expressionStack.Push(((Operation)operatorStack.Pop()).Apply(left, right));
}
&lt;span style="color:#66d9ef">var&lt;/span> compiled = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(expressionStack.Pop()).Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">internal&lt;/span> &lt;span style="color:#66d9ef">sealed&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Operation&lt;/span>
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> precedence;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> name;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> Func&amp;lt;Expression, Expression, Expression&amp;gt; operation;
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> Operation Addition = &lt;span style="color:#66d9ef">new&lt;/span> Operation(&lt;span style="color:#ae81ff">1&lt;/span>, Expression.Add, &lt;span style="color:#e6db74">&amp;#34;Addition&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> Operation Subtraction = &lt;span style="color:#66d9ef">new&lt;/span> Operation(&lt;span style="color:#ae81ff">1&lt;/span>, Expression.Subtract, &lt;span style="color:#e6db74">&amp;#34;Subtraction&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> Operation Multiplication = &lt;span style="color:#66d9ef">new&lt;/span> Operation(&lt;span style="color:#ae81ff">2&lt;/span>, Expression.Multiply, &lt;span style="color:#e6db74">&amp;#34;Multiplication&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> Operation Division = &lt;span style="color:#66d9ef">new&lt;/span> Operation(&lt;span style="color:#ae81ff">2&lt;/span>, Expression.Divide, &lt;span style="color:#e6db74">&amp;#34;Division&amp;#34;&lt;/span>);
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">char&lt;/span>, Operation&amp;gt; Operations = &lt;span style="color:#66d9ef">new&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">char&lt;/span>, Operation&amp;gt;
{
{ &lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>, Addition },
{ &lt;span style="color:#e6db74">&amp;#39;-&amp;#39;&lt;/span>, Subtraction },
{ &lt;span style="color:#e6db74">&amp;#39;*&amp;#39;&lt;/span>, Multiplication},
{ &lt;span style="color:#e6db74">&amp;#39;/&amp;#39;&lt;/span>, Division }
};
&lt;span style="color:#66d9ef">private&lt;/span> Operation(&lt;span style="color:#66d9ef">int&lt;/span> precedence, Func&amp;lt;Expression, Expression, Expression&amp;gt; operation, &lt;span style="color:#66d9ef">string&lt;/span> name)
{
&lt;span style="color:#66d9ef">this&lt;/span>.precedence = precedence;
&lt;span style="color:#66d9ef">this&lt;/span>.operation = operation;
&lt;span style="color:#66d9ef">this&lt;/span>.name = name;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> Precedence
{
&lt;span style="color:#66d9ef">get&lt;/span> { &lt;span style="color:#66d9ef">return&lt;/span> precedence; }
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">explicit&lt;/span> &lt;span style="color:#66d9ef">operator&lt;/span> Operation(&lt;span style="color:#66d9ef">char&lt;/span> operation)
{
Operation result;
&lt;span style="color:#66d9ef">if&lt;/span> (Operations.TryGetValue(operation, &lt;span style="color:#66d9ef">out&lt;/span> result))
{
&lt;span style="color:#66d9ef">return&lt;/span> result;
}
&lt;span style="color:#66d9ef">else&lt;/span>
{
&lt;span style="color:#66d9ef">throw&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> InvalidCastException();
}
}
&lt;span style="color:#66d9ef">public&lt;/span> Expression Apply(Expression left, Expression right)
{
&lt;span style="color:#66d9ef">return&lt;/span> operation(left, right);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> IsDefined(&lt;span style="color:#66d9ef">char&lt;/span> operation)
{
&lt;span style="color:#66d9ef">return&lt;/span> Operations.ContainsKey(operation);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>This code passes all the tests we have in previous post and solves issues three and four. Let’s refactor it and extract common code in a separate method:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> EvaluateWhile(Func&amp;lt;&lt;span style="color:#66d9ef">bool&lt;/span>&amp;gt; condition)
{
&lt;span style="color:#66d9ef">while&lt;/span> (condition())
{
&lt;span style="color:#66d9ef">var&lt;/span> right = expressionStack.Pop();
&lt;span style="color:#66d9ef">var&lt;/span> left = expressionStack.Pop();
expressionStack.Push(((Operation)operatorStack.Pop()).Apply(left, right));
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The evaluate method now looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">string&lt;/span>.IsNullOrWhiteSpace(expression))
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
}
operatorStack.Clear();
expressionStack.Clear();
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> reader = &lt;span style="color:#66d9ef">new&lt;/span> StringReader(expression))
{
&lt;span style="color:#66d9ef">int&lt;/span> peek;
&lt;span style="color:#66d9ef">while&lt;/span> ((peek = reader.Peek()) &amp;gt; -&lt;span style="color:#ae81ff">1&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> next = (&lt;span style="color:#66d9ef">char&lt;/span>)peek;
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span>.IsDigit(next))
{
expressionStack.Push(ReadOperand(reader));
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (Operation.IsDefined(next))
{
&lt;span style="color:#66d9ef">var&lt;/span> currentOperation = ReadOperation(reader);
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span> &amp;amp;&amp;amp;
currentOperation.Precedence &amp;lt;= ((Operation)operatorStack.Peek()).Precedence);
operatorStack.Push(next);
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next != &lt;span style="color:#e6db74">&amp;#39; &amp;#39;&lt;/span>)
{
&lt;span style="color:#66d9ef">throw&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> ArgumentException(&lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Encountered invalid character {0}&amp;#34;&lt;/span>, next), &lt;span style="color:#e6db74">&amp;#34;expression&amp;#34;&lt;/span>);
}
}
}
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(expressionStack.Pop()).Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>At this point we are ready to add support for parentheses. Our goal is to make the following test pass:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#a6e22e">[Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Supports_Parentheses()
{
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;2*(5+3)&amp;#34;&lt;/span>), Is.EqualTo(&lt;span style="color:#ae81ff">2&lt;/span> * (&lt;span style="color:#ae81ff">5&lt;/span> + &lt;span style="color:#ae81ff">3&lt;/span>)));
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;(5+3)*2&amp;#34;&lt;/span>), Is.EqualTo((&lt;span style="color:#ae81ff">5&lt;/span> + &lt;span style="color:#ae81ff">3&lt;/span>) * &lt;span style="color:#ae81ff">2&lt;/span>));
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;(5+3)*5-2&amp;#34;&lt;/span>), Is.EqualTo((&lt;span style="color:#ae81ff">5&lt;/span> + &lt;span style="color:#ae81ff">3&lt;/span>) * &lt;span style="color:#ae81ff">5&lt;/span> - &lt;span style="color:#ae81ff">2&lt;/span>));
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;(5+3)*(5-2)&amp;#34;&lt;/span>), Is.EqualTo((&lt;span style="color:#ae81ff">5&lt;/span> + &lt;span style="color:#ae81ff">3&lt;/span>) * (&lt;span style="color:#ae81ff">5&lt;/span> - &lt;span style="color:#ae81ff">2&lt;/span>)));
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;((5+3)*3-(8-2)/2)/2&amp;#34;&lt;/span>), Is.EqualTo(((&lt;span style="color:#ae81ff">5&lt;/span> + &lt;span style="color:#ae81ff">3&lt;/span>) * &lt;span style="color:#ae81ff">3&lt;/span> - (&lt;span style="color:#ae81ff">8&lt;/span> - &lt;span style="color:#ae81ff">2&lt;/span>) / &lt;span style="color:#ae81ff">2&lt;/span>) / &lt;span style="color:#ae81ff">2&lt;/span>m));
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;(4*(3+5)-4-8/2-(6-4)/2)*((2+4)*4-(8-5)/3)-5&amp;#34;&lt;/span>),
Is.EqualTo((&lt;span style="color:#ae81ff">4&lt;/span> * (&lt;span style="color:#ae81ff">3&lt;/span> + &lt;span style="color:#ae81ff">5&lt;/span>) - &lt;span style="color:#ae81ff">4&lt;/span> - &lt;span style="color:#ae81ff">8&lt;/span> / &lt;span style="color:#ae81ff">2&lt;/span> - (&lt;span style="color:#ae81ff">6&lt;/span> - &lt;span style="color:#ae81ff">4&lt;/span>) / &lt;span style="color:#ae81ff">2&lt;/span>) * ((&lt;span style="color:#ae81ff">2&lt;/span> + &lt;span style="color:#ae81ff">4&lt;/span>) * &lt;span style="color:#ae81ff">4&lt;/span> - (&lt;span style="color:#ae81ff">8&lt;/span> - &lt;span style="color:#ae81ff">5&lt;/span>) / &lt;span style="color:#ae81ff">3&lt;/span>) - &lt;span style="color:#ae81ff">5&lt;/span>));
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;(((9-6/2)*2-4)/2-6-1)/(2+24/(2+4))&amp;#34;&lt;/span>),
Is.EqualTo((((&lt;span style="color:#ae81ff">9&lt;/span> - &lt;span style="color:#ae81ff">6&lt;/span> / &lt;span style="color:#ae81ff">2&lt;/span>) * &lt;span style="color:#ae81ff">2&lt;/span> - &lt;span style="color:#ae81ff">4&lt;/span>) / &lt;span style="color:#ae81ff">2&lt;/span>m - &lt;span style="color:#ae81ff">6&lt;/span> - &lt;span style="color:#ae81ff">1&lt;/span>) / (&lt;span style="color:#ae81ff">2&lt;/span> + &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span> / (&lt;span style="color:#ae81ff">2&lt;/span> + &lt;span style="color:#ae81ff">4&lt;/span>))));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>We only need to add the following changes:&lt;/p>
&lt;ul>
&lt;li>If the token is left parentheses push it to operator stack.&lt;/li>
&lt;li>If the token is right parentheses evaluate all operators in operator stack until we reach left parentheses&lt;/li>
&lt;/ul>
&lt;p>The first point is really straightforward and for the second one we can use the EvaluateWhile method we introduced above. After adding this code our method looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">string&lt;/span>.IsNullOrWhiteSpace(expression))
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>;
}
operatorStack.Clear();
expressionStack.Clear();
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> reader = &lt;span style="color:#66d9ef">new&lt;/span> StringReader(expression))
{
&lt;span style="color:#66d9ef">int&lt;/span> peek;
&lt;span style="color:#66d9ef">while&lt;/span> ((peek = reader.Peek()) &amp;gt; -&lt;span style="color:#ae81ff">1&lt;/span>)
{
&lt;span style="color:#66d9ef">var&lt;/span> next = (&lt;span style="color:#66d9ef">char&lt;/span>)peek;
&lt;span style="color:#66d9ef">if&lt;/span> (&lt;span style="color:#66d9ef">char&lt;/span>.IsDigit(next))
{
expressionStack.Push(ReadOperand(reader));
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (Operation.IsDefined(next))
{
&lt;span style="color:#66d9ef">var&lt;/span> currentOperation = ReadOperation(reader);
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span> &amp;amp;&amp;amp; operatorStack.Peek() != &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span> &amp;amp;&amp;amp;
currentOperation.Precedence &amp;lt;= ((Operation)operatorStack.Peek()).Precedence);
operatorStack.Push(next);
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next == &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>)
{
reader.Read();
operatorStack.Push(&lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>);
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next == &lt;span style="color:#e6db74">&amp;#39;)&amp;#39;&lt;/span>)
{
reader.Read();
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span> &amp;amp;&amp;amp; operatorStack.Peek() != &lt;span style="color:#e6db74">&amp;#39;(&amp;#39;&lt;/span>);
operatorStack.Pop();
&lt;span style="color:#66d9ef">continue&lt;/span>;
}
&lt;span style="color:#66d9ef">if&lt;/span> (next != &lt;span style="color:#e6db74">&amp;#39; &amp;#39;&lt;/span>)
{
&lt;span style="color:#66d9ef">throw&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> ArgumentException(&lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Encountered invalid character {0}&amp;#34;&lt;/span>, next), &lt;span style="color:#e6db74">&amp;#34;expression&amp;#34;&lt;/span>);
}
}
}
EvaluateWhile(() =&amp;gt; operatorStack.Count &amp;gt; &lt;span style="color:#ae81ff">0&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(expressionStack.Pop()).Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>Modifying our expression evaluator using shunting yard algorithm was pretty easy and has several advantages compared to previous implementation. In the next post we will add support for parsing expressions with variables.&lt;/p></description></item><item><title>Building Expression Evaluator with Expression Trees in C# – Part 1</title><link>https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-1/</link><pubDate>Thu, 23 Feb 2012 13:00:15 +0000</pubDate><guid>https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-1/</guid><description>&lt;h3 id="introduction">Introduction&lt;/h3>
&lt;p>This is first part of a series about writing expression evaluator in C#. Even though there are several of them available already I decided to write my own as this one is based on expression trees and is very simple. The evaluator supports numeric expressions as well as expression with parameters.&lt;/p>
&lt;p>In this part we will build a simple expression evaluator which will support expressions like this: “2+2″, “2.7+3.2″, “1.7+2.9+14.24+6.58″, “84+15+4-4*3*9+24+4-54/3-5-7+47″, “25/3+1.34*2.56+1.49+2.36/1.48″, etc. It will however not be able to parse expression with parentheses or with variables. These features will be implemented in the coming posts.&lt;/p>
&lt;h3 id="getting-started">Getting Started&lt;/h3>
&lt;p>As the evaluator is based on expression trees it you should have a basic understanding of how they work. I will not cover them here as there are many &lt;a href="http://msdn.microsoft.com/en-us/library/bb397951%28VS.90%29.aspx" target="_blank">good&lt;/a> &lt;a href="http://blogs.msdn.com/b/charlie/archive/2008/01/31/expression-tree-basics.aspx" target="_blank">articles&lt;/a> available out there.&lt;/p>
&lt;p>The first thing we need to do is to make the following to tests pass:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#a6e22e">[TestFixture]&lt;/span>
&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ExpressionEvaluatorTests&lt;/span>
{
&lt;span style="color:#66d9ef">private&lt;/span> BasicExpressionEvaluator evaluator;
&lt;span style="color:#66d9ef">private&lt;/span> Random generator;
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e"> [TestFixtureSetUp]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> SetUp()
{
evaluator = &lt;span style="color:#66d9ef">new&lt;/span> BasicExpressionEvaluator();
generator = &lt;span style="color:#66d9ef">new&lt;/span> Random();
}
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e"> [Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Empty_String_Is_Zero()
{
Assert.That(evaluator.Evaluate(&lt;span style="color:#e6db74">&amp;#34; &amp;#34;&lt;/span>), Is.EqualTo(&lt;span style="color:#ae81ff">0&lt;/span>));
}
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e"> [Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Decimal_Is_Treated_As_Decimal()
{
&lt;span style="color:#66d9ef">var&lt;/span> left = generator.Next(&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> right = generator.Next(&lt;span style="color:#ae81ff">1&lt;/span>, &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>);
&lt;span style="color:#66d9ef">decimal&lt;/span> input = left + right / &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>m;
Assert.That(evaluator.Evaluate(input.ToString()), Is.EqualTo(input));
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The tests are very simple and the necessary code to make them pass is quite straightforward too:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">BasicExpressionEvaluator&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">decimal&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">decimal&lt;/span>.TryParse(expression, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>;
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Let’s add support for adding to decimal numbers. Here is the test we need to pass:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#a6e22e">[Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Can_Add_Two_Decimal_Numbers()
{
Assert.That(evaluator.Evaluate(&lt;span style="color:#e6db74">&amp;#34;2.7+3.2&amp;#34;&lt;/span>), Is.EqualTo(&lt;span style="color:#ae81ff">2.7&lt;/span>m + &lt;span style="color:#ae81ff">3.2&lt;/span>m));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Implementation is again quite easy. All we have to do it parse the operands and construct corresponding Expression by calling Expression.Add Method.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>);
Expression sum = Expression.Add(
Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(parts[&lt;span style="color:#ae81ff">0&lt;/span>])),
Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(parts[&lt;span style="color:#ae81ff">1&lt;/span>])));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(sum);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">decimal&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">decimal&lt;/span>.TryParse(expression, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>We can also extend it and implement adding multiple numbers:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>);
Expression sum = Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(parts[&lt;span style="color:#ae81ff">0&lt;/span>]));
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i = &lt;span style="color:#ae81ff">1&lt;/span>; i &amp;lt; parts.Length; i++)
{
sum = Expression.Add(sum, Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(parts[i])));
}
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(sum);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">decimal&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">decimal&lt;/span>.TryParse(expression, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>We can take advantage of Enumerable.Aggregate method and refactor the above snippet like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> sum = parts.Aggregate(Expression.Constant(&lt;span style="color:#ae81ff">0&lt;/span>m) &lt;span style="color:#66d9ef">as&lt;/span> Expression,
(current, next) =&amp;gt;
Expression.Add(current, Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(next))));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(sum);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">decimal&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">decimal&lt;/span>.TryParse(expression, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next step is to support subtraction. First we will process expressions like “5-2″ and “15.2-2.3-4.8-0.58″. As with additions we will split the string and subtract the terms:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> sum = parts.Aggregate(Expression.Constant(&lt;span style="color:#ae81ff">0&lt;/span>m) &lt;span style="color:#66d9ef">as&lt;/span> Expression,
(current, next) =&amp;gt; Expression.Add(current, Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(next))));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(sum);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;-&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;-&amp;#39;&lt;/span>);
Expression sum = Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(parts[&lt;span style="color:#ae81ff">0&lt;/span>]));
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i = &lt;span style="color:#ae81ff">1&lt;/span>; i &amp;lt; parts.Length; i++)
{
sum = Expression.Subtract(sum, Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(parts[i])));
}
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(sum);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">decimal&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">decimal&lt;/span>.TryParse(expression, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>We can refactor it again by using the Aggregate method again. However with subtraction we cannot start subtracting from zero as we did with addition. Instead we have to start subtracting from the first term:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> sum = parts.Aggregate(Expression.Constant(&lt;span style="color:#ae81ff">0&lt;/span>m) &lt;span style="color:#66d9ef">as&lt;/span> Expression,
(current, next) =&amp;gt; Expression.Add(current, Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(next))));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(sum);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;-&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;-&amp;#39;&lt;/span>);
Expression sum = Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(parts[&lt;span style="color:#ae81ff">0&lt;/span>]));
sum = parts.Skip(&lt;span style="color:#ae81ff">1&lt;/span>).Aggregate(sum, (current, next) =&amp;gt;
Expression.Subtract(current, Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(next))));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(sum);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">decimal&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">decimal&lt;/span>.TryParse(expression, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, let’s mix additions and subtractions in one expression. We want the following test to pass:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#a6e22e">[Test]&lt;/span>
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Can_Add_And_Subtract_Multiple_Numbers()
{
Assert.That(evaluator.Evaluate(&lt;span style="color:#e6db74">&amp;#34;15+8-4-2+7&amp;#34;&lt;/span>),
Is.EqualTo(&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span> + &lt;span style="color:#ae81ff">8&lt;/span> - &lt;span style="color:#ae81ff">4&lt;/span> - &lt;span style="color:#ae81ff">2&lt;/span> + &lt;span style="color:#ae81ff">7&lt;/span>));
Assert.That(evaluator.Evaluate(&lt;span style="color:#e6db74">&amp;#34;17.89-2.47+7.16&amp;#34;&lt;/span>),
Is.EqualTo(&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">7.89&lt;/span>m - &lt;span style="color:#ae81ff">2.47&lt;/span>m + &lt;span style="color:#ae81ff">7.16&lt;/span>m));
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Currently the test fails with FormatException being thrown as we are trying to parse strings. To avoid the exception and pass the test instead of parsing the terms we will recursively evaluate the term and parse the result. This is how it looks:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>);
&lt;span style="color:#66d9ef">var&lt;/span> sum = parts.Aggregate(Expression.Constant(&lt;span style="color:#ae81ff">0&lt;/span>m) &lt;span style="color:#66d9ef">as&lt;/span> Expression,
(current, next) =&amp;gt;
Expression.Add(current,
Expression.Constant(Evaluate(next))));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(sum);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;-&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;-&amp;#39;&lt;/span>);
Expression sum = Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(parts[&lt;span style="color:#ae81ff">0&lt;/span>]));
sum = parts.Skip(&lt;span style="color:#ae81ff">1&lt;/span>).Aggregate(sum, (current, next) =&amp;gt;
Expression.Subtract(current, Expression.Constant(&lt;span style="color:#66d9ef">decimal&lt;/span>.Parse(next))));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(sum);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">decimal&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">decimal&lt;/span>.TryParse(expression, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notice that the only change is in the part of the code which processes additions.&lt;/p>
&lt;h3 id="adding-multiplication-and-division">Adding multiplication and division&lt;/h3>
&lt;p>Now we are ready to add support for multiplication and division. Evaluating expressions which contain only multiplication or division is easy so I will jump to expression which contain all operations. As we did with addition and subtraction we need to recursively evaluate the operands before adding or subtracting them. Also, as multiplication and division have higher priority than addition and subtraction we first have to split the input string by ‘+’ and ‘-‘ and than split the result by ‘*’ and ‘/’. This way we will first perform higher priority operations first and than move up the stack to perform operations with less priority. For example splitting 2+5*3 by ‘+’ will result in 2 and 5*3 which we will split by ‘*’ and as a result calculate 5*3 before performing addition. With this in mind this is how the corresponding test looks like:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>);
Expression result = Expression.Constant(Evaluate(parts[&lt;span style="color:#ae81ff">0&lt;/span>]));
result = parts.Skip(&lt;span style="color:#ae81ff">1&lt;/span>).Aggregate(result, (current, next) =&amp;gt; Expression.Add(current, Expression.Constant(Evaluate(next))));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(result);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;-&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;-&amp;#39;&lt;/span>);
Expression result = Expression.Constant(Evaluate(parts[&lt;span style="color:#ae81ff">0&lt;/span>]));
result = parts.Skip(&lt;span style="color:#ae81ff">1&lt;/span>).Aggregate(result, (current, next) =&amp;gt; Expression.Subtract(current, Expression.Constant(Evaluate(next))));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(result);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;*&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;*&amp;#39;&lt;/span>);
Expression result = Expression.Constant(Evaluate(parts[&lt;span style="color:#ae81ff">0&lt;/span>]));
result = parts.Skip(&lt;span style="color:#ae81ff">1&lt;/span>).Aggregate(result, (current, next) =&amp;gt; Expression.Multiply(current, Expression.Constant(Evaluate(next))));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(result);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(&lt;span style="color:#e6db74">&amp;#39;/&amp;#39;&lt;/span>))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(&lt;span style="color:#e6db74">&amp;#39;/&amp;#39;&lt;/span>);
Expression result = Expression.Constant(Evaluate(parts[&lt;span style="color:#ae81ff">0&lt;/span>]));
result = parts.Skip(&lt;span style="color:#ae81ff">1&lt;/span>).Aggregate(result, (current, next) =&amp;gt; Expression.Divide(current, Expression.Constant(Evaluate(next))));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(result);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
&lt;span style="color:#66d9ef">decimal&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">decimal&lt;/span>.TryParse(expression, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can notice that all if block have very similar code: The first term is evaluated and other terms are aggregated based on the current operation. We can take advantage of this and refactor the above code in one small snippet. First, we will introduce a dictionary which maps operation to corresponding expression tree building method. Using it we can replace all if statements with one foreach loop:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">char&lt;/span>, Func&amp;lt;Expression, Expression, Expression&amp;gt;&amp;gt; operations =
&lt;span style="color:#66d9ef">new&lt;/span> Dictionary&amp;lt;&lt;span style="color:#66d9ef">char&lt;/span>, Func&amp;lt;Expression, Expression, Expression&amp;gt;&amp;gt;
{
{ &lt;span style="color:#e6db74">&amp;#39;+&amp;#39;&lt;/span>, (current, next) =&amp;gt; Expression.Add(current, next) },
{ &lt;span style="color:#e6db74">&amp;#39;-&amp;#39;&lt;/span>, (current, next) =&amp;gt; Expression.Subtract(current, next) },
{ &lt;span style="color:#e6db74">&amp;#39;*&amp;#39;&lt;/span>, (current, next) =&amp;gt; Expression.Multiply(current, next) },
{ &lt;span style="color:#e6db74">&amp;#39;/&amp;#39;&lt;/span>, (current, next) =&amp;gt; Expression.Divide(current, next) }
};
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">decimal&lt;/span> Evaluate(&lt;span style="color:#66d9ef">string&lt;/span> expression)
{
&lt;span style="color:#66d9ef">foreach&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> operation &lt;span style="color:#66d9ef">in&lt;/span> operations)
{
&lt;span style="color:#66d9ef">if&lt;/span> (expression.Contains(operation.Key))
{
&lt;span style="color:#66d9ef">var&lt;/span> parts = expression.Split(operation.Key);
Expression result = Expression.Constant(Evaluate(parts[&lt;span style="color:#ae81ff">0&lt;/span>]));
result = parts.Skip(&lt;span style="color:#ae81ff">1&lt;/span>).Aggregate(result,
(current, next) =&amp;gt;
operation.Value(current, Expression.Constant(Evaluate(next))));
&lt;span style="color:#66d9ef">var&lt;/span> lambda = Expression.Lambda&amp;lt;Func&amp;lt;&lt;span style="color:#66d9ef">decimal&lt;/span>&amp;gt;&amp;gt;(result);
&lt;span style="color:#66d9ef">var&lt;/span> compiled = lambda.Compile();
&lt;span style="color:#66d9ef">return&lt;/span> compiled();
}
}
&lt;span style="color:#66d9ef">decimal&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span> = &lt;span style="color:#ae81ff">0&lt;/span>;
&lt;span style="color:#66d9ef">decimal&lt;/span>.TryParse(expression, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>);
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The above function finds the first operation in the given expression and starts recursively evaluating the terms. This in turn will find other operations and evaluate them again recursively until all the operations are performed.&lt;/p>
&lt;p>Using the above method you can evaluate any expression as long as it does not contain variables or parentheses. This is also confirmed by the following test results:&lt;/p>
&lt;figure>
&lt;a data-fancybox="" href="https://www.giorgi.dev/img/expression-evaluator/SimpleEvaluator.png" >
&lt;img src="https://www.giorgi.dev/img/expression-evaluator/SimpleEvaluator.png" alt="" >
&lt;/a>
&lt;/figure>
&lt;h3 id="conclusion">Conclusion&lt;/h3>
&lt;p>As you can see we can build a very simple expression evaluator very easily using very little lines of code. In the following posts we will add support for expression with parentheses and expressions containing variables. Stay tuned!&lt;/p></description></item><item><title>Building Expression Evaluator with Expression Trees in C# – Table of Contents</title><link>https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-table-of-contents/</link><pubDate>Tue, 21 Feb 2012 17:43:45 +0000</pubDate><guid>https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-table-of-contents/</guid><description>&lt;p>This is table of contents for Building Expression Evaluator with Expression Trees in C# series. We are going to build a simple mathematical expression evaluator in C# using expression trees. The library supports simple expressions such as &lt;code>2.5+5.9&lt;/code>, &lt;code>17.89-2.47+7.16&lt;/code>, &lt;code>5/2/2+1.5*3+4.58&lt;/code>, expressions with parentheses &lt;code>(((9-6/2)*2-4)/2-6-1)/(2+24/(2+4))&lt;/code> and expressions with variables:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">var&lt;/span> a = &lt;span style="color:#ae81ff">6&lt;/span>;
&lt;span style="color:#66d9ef">var&lt;/span> b = &lt;span style="color:#ae81ff">4.32&lt;/span>m;
&lt;span style="color:#66d9ef">var&lt;/span> c = &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">4.15&lt;/span>m;
Assert.That(engine.Evaluate(&lt;span style="color:#e6db74">&amp;#34;(((9-a/2)*2-b)/2-a-1)/(2+c/(2+4))&amp;#34;&lt;/span>, a, b, c),
Is.EqualTo((((&lt;span style="color:#ae81ff">9&lt;/span> - a / &lt;span style="color:#ae81ff">2&lt;/span>) * &lt;span style="color:#ae81ff">2&lt;/span> - b) / &lt;span style="color:#ae81ff">2&lt;/span> - a - &lt;span style="color:#ae81ff">1&lt;/span>) / (&lt;span style="color:#ae81ff">2&lt;/span> + c / (&lt;span style="color:#ae81ff">2&lt;/span> + &lt;span style="color:#ae81ff">4&lt;/span>))));
&lt;/code>&lt;/pre>&lt;/div>&lt;p>At the end of the series full source code &lt;del datetime="2012-04-17T21:09:51+00:00">will be available at github&lt;/del> and the library will be published to NuGet.&lt;/p>
&lt;p>Source code is available at github:
&lt;a href="https://github.com/Giorgi/Math-Expression-Evaluator" title="Math-Expression-Evaluator" target="_blank" rel="noopener">Math-Expression-Evaluator&lt;/a>&lt;/p>
&lt;h3 id="table-of-contents">Table of Contents&lt;/h3>
&lt;ol>
&lt;li>
&lt;a href="https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-1/" title="Building Expression Evaluator with Expression Trees in C# – A basic implementation">Building Expression Evaluator with Expression Trees in C# – A basic implementation.&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-2/" title="Building Expression Evaluator with Expression Trees in C# – Adding support for parentheses">Building Expression Evaluator with Expression Trees in C# – Adding support for parentheses.&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp-part-3/" title="Building Expression Evaluator with Expression Trees in C# – Adding support for variables">Building Expression Evaluator with Expression Trees in C# – Adding support for variables.&lt;/a>&lt;/li>
&lt;li>
&lt;a href="https://www.giorgi.dev/net-framework/building-expression-evaluator-with-expression-trees-in-csharp/" title="Building Expression Evaluator with Expression Trees in C# – Improving the Api">Building Expression Evaluator with Expression Trees in C# – Improving the Api&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>Dynamic ViewState in ASP.Net WebForms – Part 2</title><link>https://www.giorgi.dev/asp-net/dynamic-viewstate-in-asp-net-webforms-part-2/</link><pubDate>Tue, 31 Jan 2012 08:26:35 +0000</pubDate><guid>https://www.giorgi.dev/asp-net/dynamic-viewstate-in-asp-net-webforms-part-2/</guid><description>&lt;p>&lt;a title="Dynamic ViewState in ASP.Net WebForms" href="https://www.giorgi.dev/asp-net/dynamic-viewstate-in-asp-net-webforms/" target="_blank">In previous post&lt;/a> I showed an approach combining DynamicObject with ViewState for accessing ViewState members using dynamic properties instead of string indexes. However that approach had some disadvantages:&lt;/p>
&lt;ul>
&lt;li>It is tied some a concrete base page class.&lt;/li>
&lt;li>It does not have all the properties/methods of StateBag class&lt;/li>
&lt;li>Mixing ViewState and ViewBag can be confusing for new developers.&lt;/li>
&lt;/ul>
&lt;p>In order to overcome these issues I made several changes to the &lt;a href="https://github.com/Giorgi/Dynamic-ViewState-in-ASP.Net-WebForms/blob/master/ViewStateEx/DynamicViewState.cs" title="DynamicViewState class" target="_blank">DynamicViewState class.&lt;/a>&lt;/p>
&lt;p>First of all I changed the constructor to accept a StateBag instance instead of Page class so that the DynamicViewState class does not need to be nested inside base page class. Secondly I added all the interfaces which are implemented by StateBag. This makes it fully compatible with StateBag class so we can simply rename the new property in base page class from ViewBag to ViewState. This way you can leave the old code that uses ViewState and use the new approach for all new code. Here is a sample snippet:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">partial&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">SamplePage&lt;/span> : BasePage
{
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Page_Load(&lt;span style="color:#66d9ef">object&lt;/span> sender, EventArgs e)
{
&lt;span style="color:#66d9ef">if&lt;/span> (IsPostBack)
{
ViewState.loadCount++;
loadLabel.Text = &lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Load count: {0}&amp;#34;&lt;/span>, ViewState.loadCount);
}
&lt;span style="color:#66d9ef">else&lt;/span>
{
ViewState[&lt;span style="color:#e6db74">&amp;#34;loadCount&amp;#34;&lt;/span>] = &lt;span style="color:#ae81ff">0&lt;/span>;
loadLabel.Text = &lt;span style="color:#e6db74">&amp;#34;Load count: 0&amp;#34;&lt;/span>;
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="getting-started-with-dynamicviewstate">Getting started with DynamicViewState&lt;/h3>
&lt;p>If you would like to use DynamicViewState in your code the simplest way for getting it is using Nuget package available at
&lt;a href="https://nuget.org/packages/DynamicViewState" title="Dynamic ViewState Nuget package" target="_blank" rel="noopener">Dynamic ViewState Nuget package&lt;/a>. Once you add the package to your project simply change the base class of your pages to BasePage and that is all you need. Source code is available at
&lt;a href="https://github.com/Giorgi/Dynamic-ViewState-in-ASP.Net-WebForms" title="Dynamic ViewState source code" target="_blank" rel="noopener">github&lt;/a>.&lt;/p>
&lt;h3 id="performance-benchmarks">Performance Benchmarks&lt;/h3>
&lt;p>In a comment to previous post Jalpesh Vadgama asked about performance impact. I ran benchmark to compare dynamic implementation with ordinary ViewState usage. As expected dynamic implementation is slower but the difference is quite small:&lt;/p>
&lt;ul>
&lt;li>Average milliseconds with dynamic: 0.0003&lt;/li>
&lt;li>Average milliseconds without dynamic: 0.00016&lt;/li>
&lt;/ul>
&lt;p>As you can see dynamic access is twice slower compared to ordinary ViewState usage.&lt;/p></description></item><item><title>Dynamic ViewState in ASP.Net WebForms</title><link>https://www.giorgi.dev/asp-net/dynamic-viewstate-in-asp-net-webforms/</link><pubDate>Tue, 17 Jan 2012 09:19:01 +0000</pubDate><guid>https://www.giorgi.dev/asp-net/dynamic-viewstate-in-asp-net-webforms/</guid><description>&lt;p>If you have worked with ASP.Net MVC 3 you are probably aware of ViewBag property which allows you to pass data from Controller to View by using dynamic properties instead of hard coding strings. While the properties still are not strongly typed and compiler does not check them it can provide an advantage. For example you can avoid casting as it returns dynamic objects. This post will show how to use the same approach in ASP.Net Web Forms for ViewState property giving us a dynamic viewstate class.&lt;/p>
&lt;p>In order to access the properties which do not exist at compile time the class has to inherit from
&lt;a href="http://msdn.microsoft.com/en-us/library/system.dynamic.dynamicobject.aspx" target="_blank" rel="noopener">DynamicObject&lt;/a>. This will allow us to override methods which are called when we get or set dynamic properties. In these methods we will use the ViewState of the current page to store and extract values.&lt;/p>
&lt;p>To start with we need let’s create the DynamicViewState class and base page class which will contain the ViewBag property:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">DynamicViewState&lt;/span> : DynamicObject
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> TrySetMember(SetMemberBinder binder, &lt;span style="color:#66d9ef">object&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>)
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">base&lt;/span>.TrySetMember(binder, &lt;span style="color:#66d9ef">value&lt;/span>);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> TryGetMember(GetMemberBinder binder, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">object&lt;/span> result)
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">base&lt;/span>.TryGetMember(binder, &lt;span style="color:#66d9ef">out&lt;/span> result);
}
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">BasePage&lt;/span> : Page
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> &lt;span style="color:#66d9ef">dynamic&lt;/span> viewBag;
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">dynamic&lt;/span> ViewBag
{
&lt;span style="color:#66d9ef">get&lt;/span> { &lt;span style="color:#66d9ef">return&lt;/span> viewBag; }
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The TryGetMember and TrySetMember methods are invokes whenever we get or set properties of ViewBag instance. This means that we should have access to ViewState of the current page so let’s pass the page to the constructor of DynamicViewState:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">DynamicViewState&lt;/span> : DynamicObject
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> BasePage basePage;
&lt;span style="color:#66d9ef">public&lt;/span> DynamicViewState(BasePage basePage)
{
&lt;span style="color:#66d9ef">this&lt;/span>.basePage = basePage;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> TrySetMember(SetMemberBinder binder, &lt;span style="color:#66d9ef">object&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>)
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">base&lt;/span>.TrySetMember(binder, &lt;span style="color:#66d9ef">value&lt;/span>);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> TryGetMember(GetMemberBinder binder, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">object&lt;/span> result)
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">base&lt;/span>.TryGetMember(binder, &lt;span style="color:#66d9ef">out&lt;/span> result);
}
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">BasePage&lt;/span> : Page
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> &lt;span style="color:#66d9ef">dynamic&lt;/span> viewBag;
&lt;span style="color:#66d9ef">public&lt;/span> BasePage()
{
viewBag = &lt;span style="color:#66d9ef">new&lt;/span> DynamicViewState(&lt;span style="color:#66d9ef">this&lt;/span>);
}
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">dynamic&lt;/span> ViewBag
{
&lt;span style="color:#66d9ef">get&lt;/span> { &lt;span style="color:#66d9ef">return&lt;/span> viewBag; }
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>We now have the page but as ViewState is protected property we cannot access it from our methods. We could add methods for storing and retrieving items from the ViewState but that would allow all other classes to manipulate the page’s ViewState. Instead a better option is to make the DynamicViewState class nested. This way we will be able to access the ViewState property without exposing it to the outside world. The implementation of the DynamicViewState now looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">BasePage&lt;/span> : Page
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">DynamicViewState&lt;/span> : DynamicObject
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> BasePage basePage;
&lt;span style="color:#66d9ef">public&lt;/span> DynamicViewState(BasePage basePage)
{
&lt;span style="color:#66d9ef">this&lt;/span>.basePage = basePage;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> TrySetMember(SetMemberBinder binder, &lt;span style="color:#66d9ef">object&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>)
{
basePage.ViewState[binder.Name] = &lt;span style="color:#66d9ef">value&lt;/span>;
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> TryGetMember(GetMemberBinder binder, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">object&lt;/span> result)
{
result = basePage.ViewState[binder.Name];
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
}
}
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> &lt;span style="color:#66d9ef">dynamic&lt;/span> viewBag;
&lt;span style="color:#66d9ef">public&lt;/span> BasePage()
{
viewBag = &lt;span style="color:#66d9ef">new&lt;/span> DynamicViewState(&lt;span style="color:#66d9ef">this&lt;/span>);
}
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">dynamic&lt;/span> ViewBag
{
&lt;span style="color:#66d9ef">get&lt;/span> { &lt;span style="color:#66d9ef">return&lt;/span> viewBag; }
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Sample usage looks like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">partial&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">SamplePage&lt;/span> : BasePage
{
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Page_Load(&lt;span style="color:#66d9ef">object&lt;/span> sender, EventArgs e)
{
&lt;span style="color:#66d9ef">if&lt;/span> (IsPostBack)
{
ViewBag.loadCount++;
loadLabel.Text = &lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;Load count: {0}&amp;#34;&lt;/span>, ViewState[&lt;span style="color:#e6db74">&amp;#34;loadCount&amp;#34;&lt;/span>]);
}
&lt;span style="color:#66d9ef">else&lt;/span>
{
ViewBag.loadCount = &lt;span style="color:#ae81ff">0&lt;/span>;
loadLabel.Text = &lt;span style="color:#e6db74">&amp;#34;Load count: 0&amp;#34;&lt;/span>;
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see we can mix ViewBag and ViewState together without any issues as ViewBag is simply a façade over ViewState.&lt;/p>
&lt;p>If we also override TrySetIndex and TryGetIndex methods we can access ViewBag properties by using indexes exactly in the same way as ViewState. This is how final implementation look like:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">BasePage&lt;/span> : Page
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">DynamicViewState&lt;/span> : DynamicObject
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> BasePage basePage;
&lt;span style="color:#66d9ef">public&lt;/span> DynamicViewState(BasePage basePage)
{
&lt;span style="color:#66d9ef">this&lt;/span>.basePage = basePage;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> TrySetMember(SetMemberBinder binder, &lt;span style="color:#66d9ef">object&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>)
{
basePage.ViewState[binder.Name] = &lt;span style="color:#66d9ef">value&lt;/span>;
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> TryGetMember(GetMemberBinder binder, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">object&lt;/span> result)
{
result = basePage.ViewState[binder.Name];
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> TrySetIndex(SetIndexBinder binder, &lt;span style="color:#66d9ef">object&lt;/span>[] indexes, &lt;span style="color:#66d9ef">object&lt;/span> &lt;span style="color:#66d9ef">value&lt;/span>)
{
&lt;span style="color:#66d9ef">if&lt;/span> (indexes.Length == &lt;span style="color:#ae81ff">1&lt;/span>)
{
basePage.ViewState[indexes[&lt;span style="color:#ae81ff">0&lt;/span>].ToString()] = &lt;span style="color:#66d9ef">value&lt;/span>;
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
}
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">base&lt;/span>.TrySetIndex(binder, indexes, &lt;span style="color:#66d9ef">value&lt;/span>);
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> TryGetIndex(GetIndexBinder binder, &lt;span style="color:#66d9ef">object&lt;/span>[] indexes, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">object&lt;/span> result)
{
&lt;span style="color:#66d9ef">if&lt;/span> (indexes.Length == &lt;span style="color:#ae81ff">1&lt;/span>)
{
result = basePage.ViewState[indexes[&lt;span style="color:#ae81ff">0&lt;/span>].ToString()];
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">true&lt;/span>;
}
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">base&lt;/span>.TryGetIndex(binder, indexes, &lt;span style="color:#66d9ef">out&lt;/span> result);
}
}
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> &lt;span style="color:#66d9ef">dynamic&lt;/span> viewBag;
&lt;span style="color:#66d9ef">public&lt;/span> BasePage()
{
viewBag = &lt;span style="color:#66d9ef">new&lt;/span> DynamicViewState(&lt;span style="color:#66d9ef">this&lt;/span>);
}
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">dynamic&lt;/span> ViewBag
{
&lt;span style="color:#66d9ef">get&lt;/span> { &lt;span style="color:#66d9ef">return&lt;/span> viewBag; }
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Apart from dynamic viewstate the above approach can be also used for providing similar implementations for Session, Cache and similar objects.&lt;/p>
&lt;p>Full source code with a demo application is available at &lt;a href="https://github.com/Giorgi/Dynamic-ViewState-in-ASP.Net-WebForms" title="Dynamic ViewState in ASP.Net WebForms" target="_blank">Dynamic ViewState in ASP.Net WebForms&lt;/a>&lt;/p></description></item><item><title>AppDomain.AssemblyResolve Event Tips</title><link>https://www.giorgi.dev/net-framework/assemblyresolve-event-tips/</link><pubDate>Wed, 04 Jan 2012 09:21:51 +0000</pubDate><guid>https://www.giorgi.dev/net-framework/assemblyresolve-event-tips/</guid><description>&lt;p>AppDomain.AssemblyResolve event can be used to load referenced assemblies at runtime. This can be useful in multiple scenarios: you can use it to load a library from a different location or load a library based on bitness or load a library which is embedded in the executable file. In all cases there are some tips and tricks which can help you to easily implement the event handler and avoid exceptions.&lt;/p>
&lt;h3 id="tip-1-use-assemblyname-class-to-parse-the-name-of-the-assembly">Tip 1: Use AssemblyName class to parse the name of the assembly&lt;/h3>
&lt;p>When the AssemblyResolve event is fired you can use the Name property of ResolveEventArgs class to find the name of the requested assembly. However, it will also include version, culture and public key token of the assembly. Instead of manipulating the string to extract the name part it is much more easier to construct a new instance of &lt;a title="AssemblyName" href="http://msdn.microsoft.com/en-us/library/system.reflection.assemblyname.aspx" target="_blank">AssemblyName&lt;/a> class and use the Name property which returns only the name of the library.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> Assembly ResolveAssembly(&lt;span style="color:#66d9ef">object&lt;/span> sender, ResolveEventArgs args)
{
&lt;span style="color:#75715e">//args.Name looks like this:
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">//MyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#75715e">//Don&amp;#39;t parse it yourself
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> name = args.Name.Split(&lt;span style="color:#e6db74">&amp;#39;,&amp;#39;&lt;/span>)[&lt;span style="color:#ae81ff">0&lt;/span>];
&lt;span style="color:#75715e">//Use the AssemblyName class to get the name
&lt;/span>&lt;span style="color:#75715e">&lt;/span> name = &lt;span style="color:#66d9ef">new&lt;/span> AssemblyName(args.Name).Name;
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="tip-2-assemblyresolve-firing-multiple-times">Tip 2: AssemblyResolve firing multiple times.&lt;/h3>
&lt;p>AssemblyResolve event can fire multiple times for the same assembly. Instead of loading it again you should return previously loaded assembly. To keep track of the assemblies you have loaded you can use a dictionary or you can get the loaded assemblies by calling &lt;a title="GetAssemblies" href="http://msdn.microsoft.com/en-us/library/system.appdomain.getassemblies.aspx" target="_blank">AppDomain.GetAssemblies&lt;/a> method. Make sure that you use appropriate locking mechanism to prevent race conditions.&lt;/p>
&lt;h3 id="tip-3-assemblyresolve-and-filenotfoundexception">Tip 3: AssemblyResolve and FileNotFoundException.&lt;/h3>
&lt;p>Even if you are loading the assembly in the AssemblyResolve event handler you might still get a FileNotFoundException if you are not careful. For example the following code will generate the exception (mainclass is defined in the library we are loading) :&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
&lt;span style="color:#66d9ef">var&lt;/span> mainClass = &lt;span style="color:#66d9ef">new&lt;/span> MainClass();
mainClass.Print();
}
&lt;span style="color:#66d9ef">static&lt;/span> Assembly ResolveAssembly(&lt;span style="color:#66d9ef">object&lt;/span> sender, ResolveEventArgs args)
{
&lt;span style="color:#66d9ef">return&lt;/span> Assembly.LoadFile(&lt;span style="color:#e6db74">@&amp;#34;path to the library&amp;#34;&lt;/span>);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The exception is occurring because jit tries to resolve all assemblies as soon as it hits the Main method. At that point the AssemblyResolve event does not have any subscribers so the event is not raised at all. On way to solve the problem is to move the code which needs the assembly to a separate method:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
{
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
}
&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Print()
{
&lt;span style="color:#66d9ef">var&lt;/span> mainClass = &lt;span style="color:#66d9ef">new&lt;/span> MainClass();
mainClass.Print();
}
&lt;span style="color:#66d9ef">static&lt;/span> Assembly ResolveAssembly(&lt;span style="color:#66d9ef">object&lt;/span> sender, ResolveEventArgs args)
{
&lt;span style="color:#66d9ef">return&lt;/span> Assembly.LoadFile(&lt;span style="color:#e6db74">@&amp;#34;path to the library&amp;#34;&lt;/span>);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Another solution is to subscribe to the event before the Main method is hit. For example, you can change the Program class to a static class and put the assignment in static constructor.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">Program&lt;/span>
{
    &lt;span style="color:#66d9ef">static&lt;/span> Program()
    {
        AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
    }
    &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> Main(&lt;span style="color:#66d9ef">string&lt;/span>[] args)
    {
        &lt;span style="color:#66d9ef">var&lt;/span> mainClass = &lt;span style="color:#66d9ef">new&lt;/span> MainClass();
     mainClass.Print();
    }
    &lt;span style="color:#66d9ef">static&lt;/span> Assembly ResolveAssembly(&lt;span style="color:#66d9ef">object&lt;/span> sender, ResolveEventArgs args)
    {
        &lt;span style="color:#66d9ef">return&lt;/span> Assembly.LoadFile(&lt;span style="color:#e6db74">@&amp;#34;path to the library&amp;#34;&lt;/span>);
    }
}
&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>Silverlight slideshow with remote control and windows taskbar support</title><link>https://www.giorgi.dev/silverlight/silverlight-slideshow-with-remote-control-and-windows-taskbar-support/</link><pubDate>Wed, 28 Dec 2011 10:04:11 +0000</pubDate><guid>https://www.giorgi.dev/silverlight/silverlight-slideshow-with-remote-control-and-windows-taskbar-support/</guid><description>&lt;p>Source code for this article is available at &lt;a href="https://github.com/Giorgi/Silverlight-Remote-Control" title="Silverlight Remote Control Source Code" target="_blank">Silverlight Remote Control Source Code&lt;/a>&lt;/p>
&lt;p>
&lt;a href="http://code.msdn.microsoft.com/nesl" target="_blank" rel="noopener">Native Extensions For Microsoft Silverlight&lt;/a> is a collection of COM automation based runtime libraries and Silverlight libraries which allow elevated trusted out of browser applications to utilize windows platform feature. For example you can build applications which use Sensor API, Speech API, react to remote control, integrate with windows 7 taskbar or listen to windows messages sent to the host window. In this post I will build a simple slideshow application to demonstrate some of the capabilities of the library.&lt;/p>
&lt;p>The application allows users to choose images from their computer and control the slideshow with remote control as well as with taskbar buttons. Building the slideshow itself is very easy so I will briefly explain how it works: When the user selects pictures a timer is started. When the timer tick event is fired it fades out current image, switches to the next image and fades it in. The user is also able to stop the slideshow and manually switch between the images. The buttons can be also triggered with remote control and taskbar buttons.&lt;/p>
&lt;h3 id="responding-to-remote-control-buttons">Responding to remote control buttons&lt;/h3>
&lt;p>According to &lt;a title="Using Remote Control Input Outside of Windows Media Center" href="http://msdn.microsoft.com/en-us/library/bb417079.aspx" target="_blank">remote control documentation&lt;/a> to respond to remote control you need to listen to various windows messages. For example, let’s implement pause/resume when APPCOMMAND_MEDIA_PAUSE or APPCOMMAND_MEDIA_PLAY commands are received. To interpret these commands we need to catch WM_APPCOMMAND message.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> MainPage()
{
InitializeComponent();
WindowMessageInterceptor.Current.WindowMessage += MessageReceived;
WindowMessageInterceptor.Current.AddMessageIntercept(NativeMethods.WM_APPCOMMAND);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you have probably guessed MessageReceived is a method which will be called when the host window receives WM_APPCOMMAND message. The handler will also receive message details such as wParam and lParam. To get the command which triggered the message we need to process lParam by calling GET_APPCOMMAND_LPARAM Macro. The macro is defined in Winuser.h as&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#75715e">#&lt;/span>&lt;span style="color:#75715e">define GET_APPCOMMAND_LPARAM(lParam) ((short)(HIWORD(lParam) &amp;amp; ~FAPPCOMMAND_MASK))&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>HIWORD is another macro defined in Windef.h as&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cpp" data-lang="cpp">&lt;span style="color:#75715e">#&lt;/span>&lt;span style="color:#75715e">define HIWORD(l) ((WORD)((((DWORD_PTR)(l)) &amp;gt;&amp;gt; 16) &amp;amp; 0xffff))&lt;/span>&lt;span style="color:#75715e">
&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The corresponding code in C# looks like this&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> GET_APPCOMMAND_LPARAM(&lt;span style="color:#66d9ef">int&lt;/span> lParam)
{
&lt;span style="color:#66d9ef">return&lt;/span> (&lt;span style="color:#66d9ef">short&lt;/span>)(HIWORD(lParam) &amp;amp; ~FAPPCOMMAND_MASK);
}
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> HIWORD(&lt;span style="color:#66d9ef">int&lt;/span> lParam)
{
&lt;span style="color:#66d9ef">return&lt;/span> ((lParam &amp;gt;&amp;gt; &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">6&lt;/span>) &amp;amp; &lt;span style="color:#ae81ff">0&lt;/span>xffff);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>We are now ready to implement the MessageReceived handler:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> MessageReceived(&lt;span style="color:#66d9ef">object&lt;/span> sender, WindowMessageEventArgs e)
{
&lt;span style="color:#66d9ef">if&lt;/span> (e.Message == NativeMethods.WM_APPCOMMAND)
{
&lt;span style="color:#66d9ef">int&lt;/span> cmd = NativeMethods.GET_APPCOMMAND_LPARAM(e.lParam);
&lt;span style="color:#66d9ef">switch&lt;/span> (cmd)
{
&lt;span style="color:#66d9ef">case&lt;/span> NativeMethods.APPCOMMAND_MEDIA_PLAY:
ResumeSlideShow();
&lt;span style="color:#66d9ef">break&lt;/span>;
&lt;span style="color:#66d9ef">case&lt;/span> NativeMethods.APPCOMMAND_MEDIA_PAUSE:
PauseSlideShow();
&lt;span style="color:#66d9ef">break&lt;/span>;
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Let’s add support for navigating between the images using left and right buttons on the controller. These buttons don’t generate WM_APPCOMMAND message. Instead they trigger WM_KEYDOWN message which Silverlight supports out of the box. This means that to respond to these buttons we need to simply respond to &lt;a title="KeyDown Event" href="http://msdn.microsoft.com/en-us/library/system.windows.uielement.keydown%28VS.95%29.aspx" target="_blank">KeyDown&lt;/a> event:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> SlideShowKeyDown(&lt;span style="color:#66d9ef">object&lt;/span> sender, KeyEventArgs e)
{
&lt;span style="color:#66d9ef">if&lt;/span> (e.Key == Key.Left)
{
PreviousImage();
}
&lt;span style="color:#66d9ef">if&lt;/span> (e.Key == Key.Right)
{
NextImage();
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="adding-support-for-taskbar-buttons">Adding support for taskbar buttons&lt;/h3>
&lt;p>The first step in adding support for taskbar buttons is to add the actual buttons. First we create the buttons and than instruct the taskbar to show them:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> CreateTaskbarButtons()
{
&lt;span style="color:#66d9ef">for&lt;/span> (&lt;span style="color:#66d9ef">int&lt;/span> i = &lt;span style="color:#ae81ff">0&lt;/span>; i &amp;lt; &lt;span style="color:#ae81ff">4&lt;/span>; i++)
{
&lt;span style="color:#66d9ef">var&lt;/span> thumbbarButton = TaskbarButton.Current.CreateThumbbarButton((&lt;span style="color:#66d9ef">uint&lt;/span>)(i + &lt;span style="color:#ae81ff">1&lt;/span>));
thumbbarButton.Flags = THUMBBUTTONFLAGS.THBF_ENABLED;
thumbbarButton.ImageDataType = ButtonImageDataType.PNG;
&lt;span style="color:#66d9ef">var&lt;/span> resourceInfo = Application.GetResourceStream(&lt;span style="color:#66d9ef">new&lt;/span> Uri(&lt;span style="color:#66d9ef">string&lt;/span>.Format(&lt;span style="color:#e6db74">&amp;#34;images/{0}.png&amp;#34;&lt;/span>, i), UriKind.Relative));
&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> br = &lt;span style="color:#66d9ef">new&lt;/span> BinaryReader(resourceInfo.Stream))
{
thumbbarButton.Image = br.ReadBytes((&lt;span style="color:#66d9ef">int&lt;/span>)resourceInfo.Stream.Length);
}
taskbarButtons.Add(thumbbarButton);
}
taskbarButtons[&lt;span style="color:#ae81ff">0&lt;/span>].Tooltip = &lt;span style="color:#e6db74">&amp;#34;First Image&amp;#34;&lt;/span>;
taskbarButtons[&lt;span style="color:#ae81ff">1&lt;/span>].Tooltip = &lt;span style="color:#e6db74">&amp;#34;Previous Image&amp;#34;&lt;/span>;
taskbarButtons[&lt;span style="color:#ae81ff">2&lt;/span>].Tooltip = &lt;span style="color:#e6db74">&amp;#34;Next Image&amp;#34;&lt;/span>;
taskbarButtons[&lt;span style="color:#ae81ff">3&lt;/span>].Tooltip = &lt;span style="color:#e6db74">&amp;#34;Last Image&amp;#34;&lt;/span>;
TaskbarButton.Current.ShowThumbbarButtons();
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>You have probably notices that the code snippet is creating buttons but there are no event handlers to respond when they are clicked. Instead we need to use WindowMessageInterceptor class again and subscribe to CommandMessage event. The event is raised in response to WM_COMMAND message which is generated when a taskbar button is clicked. Apart from subscribing to event we need to call AddCommandMessageIntercept method and specify in which control notification code we are interested it. This way the event will be raised only for that particular notification code. In case of taskbar buttons the notification code is THBN_CLICKED.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">WindowMessageInterceptor.Current.CommandMessage += CommandMessage;
WindowMessageInterceptor.Current.AddCommandMessageIntercept(NativeMethods.THBN_CLICKED);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>When the event handler is called it will receive an instance of CommandMessageEventArgs class specifying notification code and control identifier. The control identifier corresponds to the ButtonID we passed to CreateThumbbarButton method when we created the buttons. This way we can identify which button caused the event to fire.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> CommandMessage(&lt;span style="color:#66d9ef">object&lt;/span> sender, CommandMessageEventArgs e)
{
&lt;span style="color:#66d9ef">if&lt;/span> (e.NotifyCode != NativeMethods.THBN_CLICKED)
{
&lt;span style="color:#66d9ef">return&lt;/span>;
}
&lt;span style="color:#66d9ef">switch&lt;/span> (e.ControlID)
{
&lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>:
FirstImage();
&lt;span style="color:#66d9ef">break&lt;/span>;
&lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>:
PreviousImage();
&lt;span style="color:#66d9ef">break&lt;/span>;
&lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#ae81ff">3&lt;/span>:
NextImage();
&lt;span style="color:#66d9ef">break&lt;/span>;
&lt;span style="color:#66d9ef">case&lt;/span> &lt;span style="color:#ae81ff">4&lt;/span>:
LastImage();
&lt;span style="color:#66d9ef">break&lt;/span>;
}
}
&lt;/code>&lt;/pre>&lt;/div></description></item><item><title>How to Get Elevated Process Path in .Net</title><link>https://www.giorgi.dev/net-framework/how-to-get-elevated-process-path-in-net/</link><pubDate>Thu, 22 Dec 2011 08:46:23 +0000</pubDate><guid>https://www.giorgi.dev/net-framework/how-to-get-elevated-process-path-in-net/</guid><description>&lt;p>As you know retrieving process path in .Net is quite straightforward. For example if you have process instance &lt;code>someProcess&lt;/code> you can get the process path by accessing &lt;code>someProcess.MainModule.FileName&lt;/code> However for elevated processes the previous code snippet throws Win32Exception.&lt;/p>
&lt;p>As you have probably guessed the reason is related to the bugs I mentioned in my
&lt;a href="net/access-denied-process-bugs/" title="previous post">previous post.&lt;/a> In this case too the Process class is using &lt;code>PROCESS_QUERY_INFORMATION&lt;/code> access right to open the desired process. The difference from the previous case is that in order to get the main module and process path the code really needs &lt;code>PROCESS_QUERY_INFORMATION&lt;/code> so we cannot simply change it with &lt;code>PROCESS_QUERY_LIMITED_INFORMATION&lt;/code>.&lt;/p>
&lt;p>Fortunately Microsoft has introduced a new function for getting process path &lt;a title="QueryFullProcessImageName Function" href="http://msdn.microsoft.com/en-us/library/ms684919%28VS.85%29.aspx" target="_blank">QueryFullProcessImageName&lt;/a> which does work with &lt;code>PROCESS_QUERY_LIMITED_INFORMATION&lt;/code> access right. To call the function we need to open the process ourselves and pass the handle.&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> GetExecutablePath(Process Process)
{
&lt;span style="color:#75715e">//If running on Vista or later use the new function
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> (Environment.OSVersion.Version.Major &amp;gt;= &lt;span style="color:#ae81ff">6&lt;/span>)
{
&lt;span style="color:#66d9ef">return&lt;/span> GetExecutablePathAboveVista(Process.Id);
}
&lt;span style="color:#66d9ef">return&lt;/span> Process.MainModule.FileName;
}
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span> GetExecutablePathAboveVista(&lt;span style="color:#66d9ef">int&lt;/span> ProcessId)
{
&lt;span style="color:#66d9ef">var&lt;/span> buffer = &lt;span style="color:#66d9ef">new&lt;/span> StringBuilder(&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span>);
IntPtr hprocess = OpenProcess(ProcessAccessFlags.PROCESS_QUERY_LIMITED_INFORMATION,
&lt;span style="color:#66d9ef">false&lt;/span>, ProcessId);
&lt;span style="color:#66d9ef">if&lt;/span> (hprocess != IntPtr.Zero)
{
&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#66d9ef">int&lt;/span> size = buffer.Capacity;
&lt;span style="color:#66d9ef">if&lt;/span> (QueryFullProcessImageName(hprocess, &lt;span style="color:#ae81ff">0&lt;/span>, buffer, &lt;span style="color:#66d9ef">out&lt;/span> size))
{
&lt;span style="color:#66d9ef">return&lt;/span> buffer.ToString();
}
}
&lt;span style="color:#66d9ef">finally&lt;/span>
{
CloseHandle(hprocess);
}
}
&lt;span style="color:#66d9ef">throw&lt;/span> &lt;span style="color:#66d9ef">new&lt;/span> Win32Exception(Marshal.GetLastWin32Error());
}
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e">[DllImport(&amp;#34;kernel32.dll&amp;#34;)]&lt;/span>
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">extern&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> QueryFullProcessImageName(IntPtr hprocess, &lt;span style="color:#66d9ef">int&lt;/span> dwFlags,
StringBuilder lpExeName, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> size);
&lt;span style="color:#a6e22e">[DllImport(&amp;#34;kernel32.dll&amp;#34;)]&lt;/span>
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">extern&lt;/span> IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess,
&lt;span style="color:#66d9ef">bool&lt;/span> bInheritHandle, &lt;span style="color:#66d9ef">int&lt;/span> dwProcessId);
&lt;span style="color:#a6e22e">
&lt;/span>&lt;span style="color:#a6e22e">[DllImport(&amp;#34;kernel32.dll&amp;#34;, SetLastError = true)]&lt;/span>
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">extern&lt;/span> &lt;span style="color:#66d9ef">bool&lt;/span> CloseHandle(IntPtr hHandle);
&lt;/code>&lt;/pre>&lt;/div>&lt;p>You can also turn the above code snippet into an extension method too. If you want Microsoft to add a new property for easily getting the process path vote for this suggestion at the connect website: &lt;a href="https://connect.microsoft.com/VisualStudio/feedback/details/595717/add-path-property-to-process-class-so-that-it-works-with-elavated-processes-too" target="_blank">Add Path property to Process class so that it works with elavated processes too.&lt;/a>&lt;/p></description></item><item><title>Bugs in System.Diagnostics.Process Class</title><link>https://www.giorgi.dev/net/access-denied-process-bugs/</link><pubDate>Tue, 20 Dec 2011 09:00:48 +0000</pubDate><guid>https://www.giorgi.dev/net/access-denied-process-bugs/</guid><description>&lt;p>While working on an application which makes use of &lt;a href="http://msdn.microsoft.com/en-us/library/system.diagnostics.process.aspx" target="_blank">Process&lt;/a> class I found several bugs which occur in special conditions. More specifically accessing &lt;code>StartTime&lt;/code> or &lt;code>HasExited&lt;/code> properties causes a &lt;code>Win32Exception&lt;/code> with &lt;code>NativeErrorCode&lt;/code> equal to five. This indicates that the exception is caused by not having enough rights to retrieve the required information.&lt;/p>
&lt;p>The exception occurs under the following conditions:&lt;/p>
&lt;ul>
&lt;li>The source application should not be elevated
&lt;ul>
&lt;li>The process instance should represent an elevated process&lt;/li>
&lt;li>The instance should not be a result of Process.Start call&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>The snippet below demonstrates these points:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#75715e">//The following code will not throw an exception unless explorer is running elevated
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">var&lt;/span> explorer = Process.GetProcessesByName(&lt;span style="color:#e6db74">&amp;#34;explorer&amp;#34;&lt;/span>).First();
Console.WriteLine(explorer.StartTime);
Console.WriteLine(explorer.HasExited);
&lt;span style="color:#66d9ef">var&lt;/span> startInfo = &lt;span style="color:#66d9ef">new&lt;/span> ProcessStartInfo(&lt;span style="color:#e6db74">&amp;#34;notepad&amp;#34;&lt;/span>) { Verb = &lt;span style="color:#e6db74">&amp;#34;runas&amp;#34;&lt;/span> };
&lt;span style="color:#66d9ef">var&lt;/span> notepad = Process.Start(startInfo);
&lt;span style="color:#75715e">//Even though notepad is an elevated process no exception will be thrown
&lt;/span>&lt;span style="color:#75715e">&lt;/span>Console.WriteLine(notepad.StartTime);
Console.WriteLine(notepad.HasExited);
&lt;span style="color:#66d9ef">var&lt;/span> notepad2 = Process.GetProcessById(notepad.Id);
&lt;span style="color:#75715e">//Exception is thrown.
&lt;/span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">try&lt;/span>
{
Console.WriteLine(notepad2.StartTime);
}
&lt;span style="color:#66d9ef">catch&lt;/span>(Win32Exception e)
{
Console.WriteLine(e.ToString());
}
&lt;span style="color:#66d9ef">try&lt;/span>
{
Console.WriteLine(notepad2.HasExited);
}
&lt;span style="color:#66d9ef">catch&lt;/span>(Win32Exception e)
{
Console.WriteLine(e.ToString());
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>By looking at the stacktrace we get when accessing &lt;code>StartTime&lt;/code> property we can see that Process.StartTime calls Process.GetProcessTimes which calls Process.GetProcessHandle which in turn calls ProcessManager.OpenProcess. This is where the exception is occurring. Using Reflector we can see that ProcessManager.OpenProcess invokes native function &lt;a title="OpenProcess" href="http://msdn.microsoft.com/en-us/library/ms684320(VS.85).aspx" target="_blank">OpenProcess&lt;/a> and passes the desired access. In our case the access that is passed is equal to 0x400 which corresponds to &lt;code>PROCESS_QUERY_INFORMATION&lt;/code> access right. However, starting from Vista there is a new access right called &lt;code>PROCESS_QUERY_LIMITED_INFORMATION&lt;/code> which is enough for calling &lt;a title="GetProcessTime" href="http://msdn.microsoft.com/en-us/library/ms683223(VS.85).aspx" target="_blank">GetProcessTimes&lt;/a> function. Let’s see what we get when we call GetProcessTimes with the new access right:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">var&lt;/span> startinfo = &lt;span style="color:#66d9ef">new&lt;/span> ProcessStartInfo(&lt;span style="color:#e6db74">&amp;#34;notepad&amp;#34;&lt;/span>) { Verb = &lt;span style="color:#e6db74">&amp;#34;runas&amp;#34;&lt;/span> };
&lt;span style="color:#66d9ef">var&lt;/span> process = Process.Start(startinfo);
&lt;span style="color:#66d9ef">var&lt;/span> process2 = Process.GetProcessById(process.Id);
&lt;span style="color:#66d9ef">long&lt;/span> create, exit, kernel, user;
&lt;span style="color:#66d9ef">var&lt;/span> handle = NativeMethods.OpenProcess(&lt;span style="color:#ae81ff">0&lt;/span>x1000, &lt;span style="color:#66d9ef">false&lt;/span>, process2.Id);
NativeMethods.GetProcessTimes(handle, &lt;span style="color:#66d9ef">out&lt;/span> create, &lt;span style="color:#66d9ef">out&lt;/span> exit, &lt;span style="color:#66d9ef">out&lt;/span> kernel, &lt;span style="color:#66d9ef">out&lt;/span> user);
NativeMethods.CloseHandle(handle);
Console.WriteLine(DateTime.FromFileTime(create));
&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you run this snippet you will see that process start time is printed and no exception occurs. This solves the problem with StartTime property.&lt;/p>
&lt;p>Printing the stacktrace of &lt;code>HasExited&lt;/code> property shows that the exception occurs at exactly the same place. In this case the requested access is 0x100400 which is combination of &lt;code>PROCESS_QUERY_INFORMATION&lt;/code> and &lt;code>SYNCHRONIZE&lt;/code>. As you have probably guessed it is again enough to use &lt;code>PROCESS_QUERY_LIMITED_INFORMATION&lt;/code> instead of &lt;code>PROCESS_QUERY_INFORMATION&lt;/code>. I will not show the full code here as it is quite straightforward.&lt;/p>
&lt;p>So, if you receive access denied exception when querying &lt;code>StartTime&lt;/code> or &lt;code>HasExited&lt;/code> make sure that the target process is not an elevated process. If it is you will have to manually retrieve the information you need.&lt;/p></description></item></channel></rss>