<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://korzh.com/blog/</id>
    <title>Korzh's Blog Blog</title>
    <updated>2022-04-19T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://korzh.com/blog/"/>
    <subtitle>Korzh's Blog Blog</subtitle>
    <icon>https://korzh.com/blog/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[Using TypeScript with WebPack in ASP.NET Core projects]]></title>
        <id>https://korzh.com/blog/typescript-webpack-aspnetcore</id>
        <link href="https://korzh.com/blog/typescript-webpack-aspnetcore"/>
        <updated>2022-04-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Motivation]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/tswebpack-cover-672556cc437724f80a90b6ab447e918b.jpg" width="1055" height="281" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="motivation">Motivation<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#motivation" class="hash-link" aria-label="Direct link to Motivation" title="Direct link to Motivation">​</a></h2>
<p>Suppose you work on ASP.NET Core web application that solves some business-related tasks. You know, a few forms where users enter their data and get some reports. Although such a project may not require any complex logic on the client, you still probably need to write some JavaScript code to make user interaction with your application more convenient and enjoyable. For example, you may need a simple prompt popup on item deletion since it's not quite right to use a separate page for that. Or, you want to do client-side validation. Or ... it really can be any other client-side task, you name it.</p>
<p>Of course, you can add a few lines of script on each page (with vanilla JavaScript or using the good old JQuery) but it is really hard to maintain all these small pieces when your project gets bigger. Moreover, some of these pieces do the same things, so you either need to replicate them in many places (bad decision) or you end up creating a small library that contains all client-side functions, structures, and classes used in your project.</p>
<p>This article describes how to create such a JS library for your ASP.NET Core project with minimal effort and in a way that will provide better support for further changes.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="solution">Solution<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#solution" class="hash-link" aria-label="Direct link to Solution" title="Direct link to Solution">​</a></h2>
<p>Long story short, we are going to put all our client-side code to separate files (with classes, functions, data structures) and then bundle them with the help of WebPack 5. The resulting script can be included right into your _Layout.cshtml (so, it will be available on all pages of your web app) or you can include it only on the pages where it's necessary.</p>
<p>Moreover, we will use TypeScript instead of pure JavaScript because, you know, static typing is good and it allows us to catch a lot of errors at compile time.</p>
<p>In addition, you can consider this article as a quick introduction to client-side development. Especially if you are a .NET developer who still tends to stay backend-only and who is afraid of all that fancy client-side stuff (as I was some time ago).</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="getting-started">Getting started<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#getting-started" class="hash-link" aria-label="Direct link to Getting started" title="Direct link to Getting started">​</a></h2>
<p>Here we will describe the steps necessary to set up the configuration for bundling a small TypeScript library with your own code. For the sake of simplification, this library will contain just one function for now.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="0-install-nodejs">0. Install Node.JS<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#0-install-nodejs" class="hash-link" aria-label="Direct link to 0. Install Node.JS" title="Direct link to 0. Install Node.JS">​</a></h3>
<p>I'm pretty much sure you already have it installed. In case you don't - please do it. We will need Node.JS version 10.13.0 (or newer) on your dev/build machine.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-clientscript-sub-folder">1. ClientScript sub-folder<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#1-clientscript-sub-folder" class="hash-link" aria-label="Direct link to 1. ClientScript sub-folder" title="Direct link to 1. ClientScript sub-folder">​</a></h3>
<p>We will put all our scripts and configuration files into a separate sub-folder ClientScript of your main project's folder. It's similar to ClientApp sub-folder used in most of the SPA (single page application) templates available for ASP.NET Core.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-add-configuration-files">2. Add configuration files<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#2-add-configuration-files" class="hash-link" aria-label="Direct link to 2. Add configuration files" title="Direct link to 2. Add configuration files">​</a></h3>
<p>We will need 3 configuration files:</p>
<ul>
<li>package.json - to define our bundle and all dependencies,</li>
<li>webpack.config.js - for WebPack configuration</li>
<li>tsconfig.json - for TypeScript settings.</li>
</ul>
<p>For now, you can just copy these files as-is. We will describe each of them later.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_Ktv7">package.json</div><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"myapp-client-bundle"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"version"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"1.0.0"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"description"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"This is client-side scripts bundle for MyApp"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"private"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"scripts"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"build"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"webpack --mode=development"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"build:prod"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"webpack --mode=production"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"devDependencies"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"ts-loader"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"^9.2.5"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"typescript"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"^4.4.3"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"webpack"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"^5.52.1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"webpack-cli"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"^4.8.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"dependencies"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<div class="language-javascript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_Ktv7">webpack.config.js</div><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-javascript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> path </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'path'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">module</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">exports</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">entry</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./src/index.ts'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">module</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">rules</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">test</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex special-escape escape" style="color:#36acaa">\.</span><span class="token regex regex-source language-regex" style="color:#36acaa">tsx</span><span class="token regex regex-source language-regex quantifier number" style="color:#36acaa">?</span><span class="token regex regex-source language-regex anchor function" style="color:#d73a49">$</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">use</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'ts-loader'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token literal-property property" style="color:#36acaa">exclude</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token regex regex-source language-regex" style="color:#36acaa">node_modules</span><span class="token regex regex-delimiter" style="color:#36acaa">/</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">resolve</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">extensions</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'.tsx'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'.ts'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'.js'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token literal-property property" style="color:#36acaa">output</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">library</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'MYAPP'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token literal-property property" style="color:#36acaa">type</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'var'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">filename</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'app-client.js'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">path</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> path</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">resolve</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">__dirname</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'../wwwroot/js'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_Ktv7">tsconfig.json</div><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"compilerOptions"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"outDir"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"./dist/"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"noImplicitAny"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"module"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"es6"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"target"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"es5"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"allowJs"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"moduleResolution"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"node"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="3-add-typescript-files">3. Add TypeScript files<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#3-add-typescript-files" class="hash-link" aria-label="Direct link to 3. Add TypeScript files" title="Direct link to 3. Add TypeScript files">​</a></h3>
<p>For the sake of simplicity, our library in this initial stage will contain only one function hello() that simply prints Hello world to the browser's console. Here are two files we need for that outstanding :) functionality:</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="hellots">hello.ts<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#hellots" class="hash-link" aria-label="Direct link to hello.ts" title="Direct link to hello.ts">​</a></h4>
<p>This file contains our function packed into the funcs namespace, so we will be able to call it as MYAPP.funcs.hello()</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_Ktv7">hello.ts</div><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">namespace</span><span class="token plain"> funcs </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">hello</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> message </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Hello world!'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token builtin">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">message</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="indexts">index.ts<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#indexts" class="hash-link" aria-label="Direct link to index.ts" title="Direct link to index.ts">​</a></h4>
<p>This is our entry point. This TypeScript file will not contain any functions or classes. It will just define which parts of our code (functions, interfaces, classes, etc) we are going to expose to the outside world. Since we have only one file with "real" functionality our index.ts will as simple as:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_Ktv7">index.ts</div><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./hello'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>and it means that we would to "reveal" all public (exported) parts inside our <code>hello.ts</code></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="4-build-your-library">4. Build your library<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#4-build-your-library" class="hash-link" aria-label="Direct link to 4. Build your library" title="Direct link to 4. Build your library">​</a></h3>
<p>That's it. We are ready to build our bundle script. To do so, open your terminal program, move to <code>ClientScript</code> folder and run the following 2 commands:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm install</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>and then</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm run build</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>The first one will install all necessary NPM libraries (the ones which are listed in the <code>dependencies</code> and <code>devDependencies</code> sections of your <code>package.json</code>). You will need to run it before the first build and then only when you add a new dependency (another NPM package) to your <code>package.json</code>.</p>
<p>The second command actually runs WebPack that compiles (or, to be more correct, "transpiles") your TypeScript files to JavaScript, then bundles all JS code into one file <code>app-client.js</code> and puts that file into <code>wwwroot/js</code> folder of your web project as defined in <code>webpack.config.js</code> configuration file.</p>
<p>According to the <code>output/library</code> section of that file, all functions or structures of your new bundle will be accessible via the <code>MYAPP</code> global variable.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="5-attach-the-final-script-to-your-app">5. Attach the final script to your app<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#5-attach-the-final-script-to-your-app" class="hash-link" aria-label="Direct link to 5. Attach the final script to your app" title="Direct link to 5. Attach the final script to your app">​</a></h3>
<p>To use our script you just need to include it on your page as any other JS file:</p>
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">src</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">/js/app-client.min.js</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>You can add that line either to the <code>_Layout.cshtml</code> (to make it available on all pages of your web application) or to the view or Razor page where it's necessary.</p>
<p>Now you can call the function(s) from our new JS library:</p>
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript"></span><span class="token script language-javascript constant" style="color:#36acaa">MYAPP</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">funcs</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">hello</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="configuring-the-scope">Configuring the scope<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#configuring-the-scope" class="hash-link" aria-label="Direct link to Configuring the scope" title="Direct link to Configuring the scope">​</a></h2>
<p>One of the great features of using TypeScript and WebPack is organizing your code in modules and then combining those modules into namespaces using WebPack's configuration and TypeScript's <code>namespace</code> structure.</p>
<p>There are several possible options.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-use-module-names-and-their-aliases">1. Use module names and their aliases<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#1-use-module-names-and-their-aliases" class="hash-link" aria-label="Direct link to 1. Use module names and their aliases" title="Direct link to 1. Use module names and their aliases">​</a></h3>
<p>You can put functions and classes in a module and export that module either "as-is" or with an alias.</p>
<p>For example, if we have the following module:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_Ktv7">dialogs.ts</div><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Dialog</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">showDialog</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">...</span><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>and we use this export declaration in our <code>index.ts</code>:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./dialogs'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>then our <code>Dialog</code> class and <code>showDialog()</code> function will be available right under <code>MYAPP</code> namespace as <code>MYAPP.Dialog</code> and <code>MYAPP.showDialog()</code>.</p>
<p>You can also specify an alias for that <code>dialogs</code> module:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> dlg </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./dialogs'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Now our class and the function will be accessible as <code>MYAPP.dlg.Dialog</code> and <code>MYAPP.dlg.showDialog()</code> correspondingly.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-using-namespace-clause">2. Using namespace clause<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#2-using-namespace-clause" class="hash-link" aria-label="Direct link to 2. Using namespace clause" title="Direct link to 2. Using namespace clause">​</a></h3>
<p>You can also use the namespace clause and then re-export the imported modules, so all functions, variables, and types that belong to the same namespace even in different modules will be merged together.</p>
<p>For example, we have the following two modules:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_Ktv7">dialogs.ts</div><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">namespace</span><span class="token plain"> ui </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Dialog1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">showDialog1</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token operator" style="color:#393A34">...</span><span class="token plain">  </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>and</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockTitle_Ktv7">widgets.ts</div><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">namespace</span><span class="token plain"> ui </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Widget1</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">renderWidget1</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">...</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>w, if we place the following two lines in our <code>index.ts</code> module:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./dialogs'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'./widgets'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>we will be able to access all those exported functions and classes under the <code>MYAPP.ui</code> namespace. For example: <code>MYAPP.ui.renderWidget1()</code>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="using-third-party-libraries">Using third-party libraries<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#using-third-party-libraries" class="hash-link" aria-label="Direct link to Using third-party libraries" title="Direct link to Using third-party libraries">​</a></h2>
<p>Maybe the most significant advantage of this setup (that may seem a little complicated for just a "hello world" function) is the possibility to use any third-party JS library from hundred of thousands available on NPM repository.</p>
<p>As an example, we slightly modify our <code>hello()</code> function so that it will take a parameter <code>name</code> and will print the phrase <code>Hello, {name}</code> to the console. Before printing, the string stored in the name variable will be capitalized with the help of <code>capitalize()</code> function from the well-known <code>lodash</code> library.</p>
<p>Here are the steps we should take to achieve this goal:</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-add-lodash-library-to-your-packagejson">1. Add lodash library to your package.json<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#1-add-lodash-library-to-your-packagejson" class="hash-link" aria-label="Direct link to 1. Add lodash library to your package.json" title="Direct link to 1. Add lodash library to your package.json">​</a></h3>
<p>Just open a terminal inside your ClientScript folder and type:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm install lodash</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>As the result of this operation, you will see something like the following in the dependencies section of your package.json file:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token property" style="color:#36acaa">"dependencies"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"lodash"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"^4.17.21"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>(the actual version number can be different).</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-import-lodash-functions-in-your-hellots-file">2. Import lodash functions in your hello.ts file<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#2-import-lodash-functions-in-your-hellots-file" class="hash-link" aria-label="Direct link to 2. Import lodash functions in your hello.ts file" title="Direct link to 2. Import lodash functions in your hello.ts file">​</a></h3>
<p>Add the following line at the beginning of hello.ts:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">as</span><span class="token plain"> _ </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'lodash'</span><span class="token punctuation" style="color:#393A34">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="3-modify-hello-function">3. Modify hello() function<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#3-modify-hello-function" class="hash-link" aria-label="Direct link to 3. Modify hello() function" title="Direct link to 3. Modify hello() function">​</a></h3>
<p>Now we can use all lodash library functions using _ global variable (this is the default way of using lodash functions since the times it was not an NPM library). So, our hello function will look the following:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">hello</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">void</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> message </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'Hello, '</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> _</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">capitalize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token builtin">console</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">log</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">message</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Additionally, we will modify the function call on our page:</p>
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript"></span><span class="token script language-javascript constant" style="color:#36acaa">MYAPP</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript property-access">funcs</span><span class="token script language-javascript punctuation" style="color:#393A34">.</span><span class="token script language-javascript method function property-access" style="color:#d73a49">hello</span><span class="token script language-javascript punctuation" style="color:#393A34">(</span><span class="token script language-javascript string" style="color:#e3116c">'sergiy'</span><span class="token script language-javascript punctuation" style="color:#393A34">)</span><span class="token script language-javascript punctuation" style="color:#393A34">;</span><span class="token script language-javascript"></span><br></span><span class="token-line" style="color:#393A34"><span class="token script language-javascript"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">script</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>When we rebuild our script (npm run build), run the app, and open the main page, we will see the following string in the console panel of our browser:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">Hello, Sergiy</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="watch-mode">Watch mode<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#watch-mode" class="hash-link" aria-label="Direct link to Watch mode" title="Direct link to Watch mode">​</a></h2>
<p>It's unnecessary to run the build command each time you change something in your script or add a new package to your project. Instead, you can use the watch mode once, and WebPack will re-build your project every time something was changed:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">npm run watch</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusions">Conclusions<a href="https://korzh.com/blog/typescript-webpack-aspnetcore#conclusions" class="hash-link" aria-label="Direct link to Conclusions" title="Direct link to Conclusions">​</a></h2>
<p>As we can see, using TypeScript with WebPack to build vanilla JS client-side scripts for your ASP.NET Core projects has a lot of benefits:</p>
<ul>
<li>Strong-typed TypeScript code with all advantages of the latest JavaScript features: classes, arrow functions, modules, scopes, and promises.</li>
<li>Better support by code editors (like Visual Studio Code) with syntax highlighting, IntelliSense, etc.</li>
<li>A possibility to use 3-party libraries with typing definitions.</li>
<li>More compact and optimized JS 5 code generated by WebPack.</li>
<li>Hot reload of the changes you make in the code editor.</li>
<li>Better debugging experience (I'm going to write a separate article about it). It's possible to debug your original TypeScript code instead of minimized JS code available for your web app.</li>
</ul>
<p>There is one more note. We used WebPack in this article since it's the most popular module bundler for now. However, I believe that we can quickly get the same results with any other bundler like Browserify, Parcel, or Rollup.</p>
<p>Please let me know (via my Twitter account @korzhs or here in the comments) if this article was valuable and informative for you.</p>
<p>Happy coding!</p>]]></content>
        <category label="ASP-NET-CORE" term="ASP-NET-CORE"/>
        <category label="JAVASCRIPT" term="JAVASCRIPT"/>
        <category label="TYPESCRIPT" term="TYPESCRIPT"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Single-file web API services with .NET 5 and ASP.NET Core]]></title>
        <id>https://korzh.com/blog/single-file-web-service-aspnetcore</id>
        <link href="https://korzh.com/blog/single-file-web-service-aspnetcore"/>
        <updated>2021-04-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Introduction]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/simple-webservice-abc11c8c7d9c3cbcb922a91416c88af6.jpg" width="1800" height="951" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="introduction">Introduction<a href="https://korzh.com/blog/single-file-web-service-aspnetcore#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction">​</a></h2>
<p>There are many ways to create a web application today. We have a lot of different platforms, frameworks, and libraries: PHP, Python, Java, NodeJS, and a dozen of others.</p>
<p>While ASP.NET (Core) was always a good choice for developing enterprise-level web applications (with many complex web pages, some Web API endpoints, static resources, etc.),
it's never been the best choice for creating a small web service with just a few endpoints to handle REST API requests in JSON or plain text formats.</p>
<p>Well, that was true until .NET 5 was released last year. With support for top-level statements and new features in C# language, .NET 5 allows us to create a solid web service in just a few minutes and with a single code file.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="single-code-file-aspnet-core-web-service">Single code file ASP.NET Core web service<a href="https://korzh.com/blog/single-file-web-service-aspnetcore#single-code-file-aspnet-core-web-service" class="hash-link" aria-label="Direct link to Single code file ASP.NET Core web service" title="Direct link to Single code file ASP.NET Core web service">​</a></h2>
<p>Below you will find an example of a very basic "echo" web service with only one endpoint that just takes the content of a POST request and returns it back in JSON format. Not quite a real-world task of course, but enough for demonstration purposes.</p>
<p>So, to create our single-code-file web service just open your terminal, create an empty folder and type the following command:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">dotnet new web</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>This command will create a simple ASP.NET Core project with two code files: <code>Program.cs</code> and <code>Startup.cs</code>.</p>
<p>Since our purpose is to get only one file, we remove <code>Startup.cs</code>. Yeah, it's that simple.</p>
<p>After that just copy/paste the following piece of code into <code>Program.cs</code> file:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">using System.IO;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">using System.Net.Mime;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">using Microsoft.AspNetCore.Builder;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">using Microsoft.AspNetCore.Hosting;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">using Microsoft.AspNetCore.Http;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">using Microsoft.Extensions.Hosting;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">Host.CreateDefaultBuilder(args)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .ConfigureWebHostDefaults(webBuilder =&gt; webBuilder</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        .Configure(app =&gt; app.Run(async context =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            if (context.Request.Path == "/api/echo" &amp;&amp; context.Request.Method == "POST") {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                //getting the content of our POST request</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                using var reader = new StreamReader(context.Request.Body);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                var content = await reader.ReadToEndAsync();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                //sending it back in the response</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                context.Response.ContentType = MediaTypeNames.Text.Plain;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                await context.Response.WriteAsync(content);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            else {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                //Return 404 for any other endpoint</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                context.Response.StatusCode = StatusCodes.Status404NotFound;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                await context.Response.WriteAsync($"WRONG ENDPOINT: {context.Request.Path.ToString()}. Use POST request to /api/echo instead");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        })))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .Build().Run();</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Let's take a closer look what happens here.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="top-level-statements">Top-level statements<a href="https://korzh.com/blog/single-file-web-service-aspnetcore#top-level-statements" class="hash-link" aria-label="Direct link to Top-level statements" title="Direct link to Top-level statements">​</a></h3>
<p>Fist we have a block of <code>using</code> statement. That's usual for any C# file. Nothing special.</p>
<p>After that, we start straight with an instruction. We call <code>CreateDefaultBuilder()</code> method of <code>Host</code> class.
If you've had experience with C# before, you may be a little bit confused by this approach. No namespaces, no class definitions.
You may even say that this piece of code has the wrong syntax and will not be compiled.</p>
<p>But it will. It's possible to do now with C# 9 and it's called <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/top-level-statements" target="_blank" rel="noopener noreferrer">top-level statements</a>.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="creating-a-host">Creating a host<a href="https://korzh.com/blog/single-file-web-service-aspnetcore#creating-a-host" class="hash-link" aria-label="Direct link to Creating a host" title="Direct link to Creating a host">​</a></h3>
<p>As we already mentioned above the only instruction we actually have in our little program is <code>Host.CreateDefaultBuilder()</code> call.
It creates a <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host" target="_blank" rel="noopener noreferrer">generic host builder</a> with default settings for logging, configuration, and Dependency Injection container.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="setting-up-the-web-application">Setting up the web application<a href="https://korzh.com/blog/single-file-web-service-aspnetcore#setting-up-the-web-application" class="hash-link" aria-label="Direct link to Setting up the web application" title="Direct link to Setting up the web application">​</a></h3>
<p>After that, the <code>ConfigureWebHostDefaults</code> method loads the configuration, set Kestrel as the webserver, enables IIS integrations, and adds some default middlewares.</p>
<p>Finally, we call <code>Configure</code> method to set up the <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/" target="_blank" rel="noopener noreferrer">middleware pipeline</a>, or, to define the only middleware that will actually process all requests to our web service (with that <code>app.Run(...)</code> call).</p>
<p>The next piece of code is self-explanatory. If it's a POST request to <code>/api/echo</code> we read the content of the request's body and just write it back to the response stream. Otherwise, we return the 404 code with some error message.</p>
<p>As I mentioned above, this is a very basic example of a microweb service, but I intentionally made it simple to show how everything works in general without going into detail.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="running-the-app">Running the app<a href="https://korzh.com/blog/single-file-web-service-aspnetcore#running-the-app" class="hash-link" aria-label="Direct link to Running the app" title="Direct link to Running the app">​</a></h2>
<p>To run this simple web service just type in the terminal (while you are in the project's directory):</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">dotnet run</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Here is what you get in response by sending a POST request to /api/echo using <a href="https://www.postman.com/" target="_blank" rel="noopener noreferrer">Postman utility</a>:</p>
<p><img decoding="async" loading="lazy" alt="POST request to /api/echo via Postman" src="https://korzh.com/blog/assets/images/postman-echo01-ebead691e5e39041afe804af4aca8eed.png" width="734" height="506" class="img_ev3q"></p>]]></content>
        <category label="ASP-NET-CORE" term="ASP-NET-CORE"/>
        <category label="WEB-SERVICE" term="WEB-SERVICE"/>
        <category label="TOP-LEVEL-STATEMENTS" term="TOP-LEVEL-STATEMENTS"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[CRUD operations in ASP.NET Core with EasyData library]]></title>
        <id>https://korzh.com/blog/crud-asp-net-core-with-easydata</id>
        <link href="https://korzh.com/blog/crud-asp-net-core-with-easydata"/>
        <updated>2021-03-31T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Implementing CRUD operations in your ASP.NET Core application can be a very tedious and time-consuming task.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/db-objects-01-f87ae84c4f7fea1001ca1eb9d8f76b85.jpg" width="1980" height="963" class="img_ev3q"></p>
<blockquote>
<p>Implementing CRUD operations in your ASP.NET Core application can be a very tedious and time-consuming task.
EasyData helps to add all necessary functionality (and even more) in a matter of minutes.</p>
</blockquote>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="problem">Problem<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#problem" class="hash-link" aria-label="Direct link to Problem" title="Direct link to Problem">​</a></h2>
<p>One of the first tasks for most business applications is to implement CRUD (Create, Read, Update, Delete) operations for the main entities the app works with.</p>
<p>Every developer faces the following problems as part of solving the task:</p>
<ul>
<li>
<p>The creation of CRUD pages and forms is very boring and time-consuming. Believe me, I’ve been there a lot of times.</p>
</li>
<li>
<p>If you do it manually, it can be very slow and error-prone (missed fields, forgotten validators, etc).</p>
</li>
<li>
<p>Of course, you can use the scaffolding tool provided by Visual Studio.
However, it’s also not a quick process either, because you need to run that tool for each model class.
As a result, you get many .cs and .cshtml files, which you'll have to edit manually if something in the default behavior or appearance doesn't suit your needs.
In the event of changes in the model classes, you'll need to update those generated controllers and pages manually
or regenerate the code and forms from scratch for each affected model class.</p>
</li>
<li>
<p>Moreover, even the built-in scaffolding doesn't provide some important, often essential functions such as paging or search.</p>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="solution-easydata-library">Solution: EasyData library<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#solution-easydata-library" class="hash-link" aria-label="Direct link to Solution: EasyData library" title="Direct link to Solution: EasyData library">​</a></h2>
<p>To solve most (if not all) of those problems we created the <a href="https://github.com/KorzhCom/EasyData" target="_blank" rel="noopener noreferrer">EasyData library</a>.
The main advantage of EasyData is that it employs a declarative approach.</p>
<p>The whole process can be split into two main steps:</p>
<ol>
<li>
<p>You “describe” what data (entities and attributes) you want to work with and
how your application should work with that data (types, constraints, relations between entities, etc).</p>
</li>
<li>
<p>Based on this information, the EasyData library establishes an API endpoint for CRUD operations and a vanilla JavaScript-based UI
that lets your users perform those operations via the API.</p>
</li>
</ol>
<p>The most wonderful thing here is that when using Entity Framework Core,
all you need for the first step (“describing” the data) is your DbContext.
Simply “feed” it to the library, and EasyData automatically extracts all the information needed to create the API endpoints and CRUD UI.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="quick-demo">Quick demo<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#quick-demo" class="hash-link" aria-label="Direct link to Quick demo" title="Direct link to Quick demo">​</a></h2>
<p>Here's a small introduction video that shows how EasyData works:</p>
<p><img decoding="async" loading="lazy" alt="EasyData quick demo" src="https://korzh.com/blog/assets/images/easydata-demo01-53f7ea9e49f80b56662188bca57e8664.gif" title="EasyData quick demo" width="800" height="445" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="getting-started">Getting Started<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#getting-started" class="hash-link" aria-label="Direct link to Getting Started" title="Direct link to Getting Started">​</a></h2>
<p>First of all, to test EasyData you can open and run one of the <a href="https://github.com/korzh/EasyData/tree/master/samples" target="_blank" rel="noopener noreferrer">sample projects</a> available in this repository.</p>
<p>Installing EasyData to your project takes the following three simple steps:</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-install-easydata-nuget-packages">1. Install EasyData NuGet packages<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#1-install-easydata-nuget-packages" class="hash-link" aria-label="Direct link to 1. Install EasyData NuGet packages" title="Direct link to 1. Install EasyData NuGet packages">​</a></h3>
<ul>
<li>EasyData.AspNetCore</li>
<li>EasyData.EntityFrameworkCore.Relational</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-add-easydata-middleware-in-startupconfigure">2. Add EasyData middleware in <code>Startup.Configure</code><a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#2-add-easydata-middleware-in-startupconfigure" class="hash-link" aria-label="Direct link to 2-add-easydata-middleware-in-startupconfigure" title="Direct link to 2-add-easydata-middleware-in-startupconfigure">​</a></h3>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">using EasyData.Services;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.    .    .    .    .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    app.UseEndpoints(endpoints =&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        endpoints.MapEasyData(options =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            options.UseDbContext&lt;AppDbContext&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        endpoints.MapRazorPages();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>In the middleware options, we also specify the type of DbContext object that will be used as the source of the metadata.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="3-set-up-a-catch-all-page-for-all-crud-operations">3. Set up a catch-all page for all CRUD operations<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#3-set-up-a-catch-all-page-for-all-crud-operations" class="hash-link" aria-label="Direct link to 3. Set up a catch-all page for all CRUD operations" title="Direct link to 3. Set up a catch-all page for all CRUD operations">​</a></h3>
<p>If you're using Razor Pages, add a new page (for example <code>EasyData.chstml</code>). If it’s MVC, you'll need a controller and a view.
This page will "catch" all URLs that begin with a certain prefix (<code>/easydata</code> by default but it's configurable). So, we use a special catch-all parameter in the route definition (<code>"/easydata/{**entity}"</code>).</p>
<p>We also add EasyData styles and the script file (<code>easydata.min.js</code>), which renders the data-management UI and handles all CRUD operations on the client-side.</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">@page "/easydata/{**entity}"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    ViewData["Title"] = "EasyData";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;link rel="stylesheet" href="https://cdn.korzh.com/ed/1.2.2/easydata.min.css" /&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">&lt;div id="EasyDataContainer"&gt;&lt;/div&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">@section Scripts {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    &lt;script src="https://cdn.korzh.com/ed/1.2.2/easydata.min.js" type="text/javascript"&gt;&lt;/script&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    &lt;script&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        window.addEventListener('load', function () {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            new easydata.crud.EasyDataViewDispatcher().run()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    &lt;/script&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>That’s it. Now you can run your web app, open the <code>/easydata</code> URL and enjoy CRUD functionality.</p>
<p>The result will be something like this:</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="list-view-screen-for-one-entity">List view screen for one entity:<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#list-view-screen-for-one-entity" class="hash-link" aria-label="Direct link to List view screen for one entity:" title="Direct link to List view screen for one entity:">​</a></h4>
<p><img decoding="async" loading="lazy" alt="List view screen for Orders entity" src="https://korzh.com/blog/assets/images/easydata-sshot01-4f12a74826605c5ba90a3542f6e84b52.jpg" width="1141" height="641" class="img_ev3q"></p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="edit-record-dialog">"Edit Record" dialog:<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#edit-record-dialog" class="hash-link" aria-label="Direct link to &quot;Edit Record&quot; dialog:" title="Direct link to &quot;Edit Record&quot; dialog:">​</a></h4>
<p><img decoding="async" loading="lazy" alt="Edit Record dialog" src="https://korzh.com/blog/assets/images/easydata-sshot02-0634ad08d4b7cc8d93adabc43380007f.jpg" width="1071" height="907" class="img_ev3q"></p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="lookup-dialog-was-opened-from-edit-record">"Lookup" dialog (was opened from "Edit Record"):<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#lookup-dialog-was-opened-from-edit-record" class="hash-link" aria-label="Direct link to &quot;Lookup&quot; dialog (was opened from &quot;Edit Record&quot;):" title="Direct link to &quot;Lookup&quot; dialog (was opened from &quot;Edit Record&quot;):">​</a></h4>
<p><img decoding="async" loading="lazy" alt="Lookup dialog" src="https://korzh.com/blog/assets/images/easydata-sshot03-c5f6f81aef755d682db8d1f70d5cf8e7.jpg" width="1244" height="911" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-it-works">How it works<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#how-it-works" class="hash-link" aria-label="Direct link to How it works" title="Direct link to How it works">​</a></h2>
<p>Let's briefly go over how all this magic works.
As we mentioned before EasyData takes care of three main things:</p>
<ul>
<li>
<p>It collects database metadata.</p>
</li>
<li>
<p>It establishes an API for the main CRUD operations.</p>
</li>
<li>
<p>It renders UI (again, based on the metadata) and processes all user interactions in that UI.</p>
</li>
</ul>
<p>Let’s explore all these parts in detail.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="metadata">Metadata<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#metadata" class="hash-link" aria-label="Direct link to Metadata" title="Direct link to Metadata">​</a></h3>
<p>Metadata is the data about your data: what entities (tables) are stored in your database,
how they're connected, what attributes (fields) they have, what  the types of attributes are
and what the constraints are with respect to the values we can store in those attributes.</p>
<p>EasyData collects metadata (in one way or another) and stores it in the instance of <code>MetaData</code> class.
This object contains the list of entities (tables), the attributes (fields) for each entity, the connections between entities,
and some additional information used in API and during UI rendering and processing.</p>
<p>To fill the MetaData object, we specify a metadata loader. In the example above, we did it with the UseDbContext call.
So, the metadata is loaded from a DbConext object. Currently (in version 1.2) this is the only metadata loader available.
In future versions, it will be possible to load metadata directly from a DbConnection object or perhaps with some other method.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="easydata-middleware">EasyData middleware<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#easydata-middleware" class="hash-link" aria-label="Direct link to EasyData middleware" title="Direct link to EasyData middleware">​</a></h3>
<p>EasyData middleware is responsible for establishing a REST API for all CRUD (and not only) operations initiated by the client side.</p>
<p>To add the middleware to your pipeline use <code>MapEasyData</code> extension function inside <code>UseEndpoints</code> configuration delegate:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">   app.UseEndpoints(endpoints =&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       endpoints.MapEasyData(options =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            options.UseDbContext&lt;AppDbContext&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>This call should be made before <code>MapMvc</code> or <code>MapRazorPages</code>.
By default, EasyData middleware is assigned to <code>/api/easydata endpoint</code>, but you can change it via the configuration function (action) passed in the parameter.</p>
<p>The only thing that's required in order to configure for <code>MapEasyData</code> is to tell it where to take the metadata.
Currently, there's just one option available: getting metadata from a DbContext.
So, that’s why we add <code>UseDbContext&lt;AppDbContext&gt;()</code> call in the example above.
Besides getting metadata, <code>UseDbContext</code> also provides our middleware with all the means for performing CRUD operations (via the DbContext object).</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="easydata-ui-root-page">EasyData UI root page<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#easydata-ui-root-page" class="hash-link" aria-label="Direct link to EasyData UI root page" title="Direct link to EasyData UI root page">​</a></h3>
<p>The third part of the EasyData setup process is the page where the CRUD user interface is rendered.
It must be a so-called "catch-all" Razor page or MVC view.
By default, this page must be opened for any path that starts with the <code>/easydata/</code> prefix.
(So, all paths like <code>/easydata/student</code> or <code>/easydata/invoice</code> must be processed by this page.)</p>
<p>NB: <code>/easydata</code> is the default prefix.
You can use another name, but in this case, it will be necessary to specify it in the options of our <code>RootDispatcherView</code> object.</p>
<p>Our catch-all page can contain any HTML element of your choice.
However, to ensure the visualization and normal operation of the CRUD UI, it must include the following 4 elements:</p>
<ul>
<li>
<p><code>&lt;link&gt;</code> element with a reference to EasyData CSS file (<code>easydata.min.cs</code>)</p>
</li>
<li>
<p>Container (empty <code>div</code> element), in which our interface will be displayed.
By default, it must have the ID <code>EasyDataContainer</code>, but this can also be configured with options.</p>
</li>
<li>
<p><code>&lt;script&gt;</code> element with a reference to <code>easydata.min.js</code>.</p>
</li>
<li>
<p>And a small script that creates and launches the <code>EasyDataViewDispatcher</code> object on page load.</p>
</li>
</ul>
<p>An example of the simplest “catch-all” page you can see in the Getting Started section above.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="wrapping-up">Wrapping up<a href="https://korzh.com/blog/crud-asp-net-core-with-easydata#wrapping-up" class="hash-link" aria-label="Direct link to Wrapping up" title="Direct link to Wrapping up">​</a></h2>
<p>Currently, EasyData can work with .NET Core 3.1 and .NET 5.
Obviously, all ASP.NET Core and Entity Framework Core versions that can work with the specified releases of .NET (Core) are supported.
However, it won't be a problem to add support for previous versions of .NET Core or even .NET Framework 4.x.
If anyone needs it, please <a href="https://github.com/KorzhCom/EasyData/issues" target="_blank" rel="noopener noreferrer">submit an issue</a> in EasyData's GitHub repository.</p>
<p>EasyData can be a good tool with which to quickly prototype a new project or create a POC (proof of concept)
when we already understand what data we'll need but don't want to spend a lot of time on the simplest operations with that data.
However, we hope that in time it will be possible to fully use this solution in the production environment.</p>
<p>So, we look forward to hearing from you with any comments or advice you may have. Of course, don't forget to ad a start for the EasyData repository on GitHub, especially if this library helped you and saved some time.</p>
<p>So, we look forward to hearing from you with any comments or advice you may have.
Of course, don't forget to add a star for the <a href="https://github.com/KorzhCom/EasyData" target="_blank" rel="noopener noreferrer">EasyData repository on GitHub</a>,
especially if this library helped you and saved some time.</p>]]></content>
        <category label="ASP-NET-CORE" term="ASP-NET-CORE"/>
        <category label="ENTITY-FRAMEWORK" term="ENTITY-FRAMEWORK"/>
        <category label="JAVASCRIPT" term="JAVASCRIPT"/>
        <category label="CRUD" term="CRUD"/>
        <category label="PAGING" term="PAGING"/>
        <category label="DATA-FILTERING" term="DATA-FILTERING"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[ASP.NET Core default project structure explained (part 2)]]></title>
        <id>https://korzh.com/blog/asp-net-core-project-structure-explained-part2</id>
        <link href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2"/>
        <updated>2020-08-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[This is the second part of the article, where we take apart the default ASP.NET Core solution template piece by piece and try to explain the purpose of each part and how exactly it works. You can consider it as a reference where you can check why a particular part was added to your project and find a link to the relevant documentation that explains it in detail.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/computer-coffee-02-3db0865179d3b5ebf4e4295b5b74d3ca.jpg" width="1920" height="1442" class="img_ev3q"></p>
<blockquote>
<p>This is the second part of the <a href="https://korzh.com/blog/asp-net-core-default-project-structure-explained-part-1">article</a>, where we take apart the default ASP.NET Core solution template piece by piece and try to explain the purpose of each part and how exactly it works. You can consider it as a reference where you can check why a particular part was added to your project and find a link to the relevant documentation that explains it in detail.</p>
</blockquote>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="startup-class">Startup class<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#startup-class" class="hash-link" aria-label="Direct link to Startup class" title="Direct link to Startup class">​</a></h2>
<p>As we already mentioned in the first article, the Startup class is the entry point for all initialization codes in your application.
Long story short, the Startup defines what your application will do and how exactly it will work.</p>
<p>By default, any Startup class in the ASP.NET Core application includes three main parts:</p>
<ul>
<li>
<p><strong>The constructor</strong> where you can define some internal variables, set up some configuration settings, or perform application-wide initializations. For example, we suggest our users set here their license keys for our EasyQuery library.</p>
</li>
<li>
<p><strong>ConfigureServices</strong> method. Here we register all necessary services in the DI (dependency injection) container and define the configuration settings for some of them. In case you are not aware of what Dependency Injection is, here is an article on our blog that explains this concept in simple words.</p>
</li>
<li>
<p><strong>Configure</strong> method. This is the stage when you set up the middleware pipeline for your project.</p>
</li>
</ul>
<p>Both <code>ConfigureServices</code> and <code>Configure</code> methods are called automatically while the application starts. You just need to define the services and the middlewares there correspondingly.</p>
<p>Now, let’s take a look at each of these sections more in detail.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="startup-class-constructor">Startup class constructor<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#startup-class-constructor" class="hash-link" aria-label="Direct link to Startup class constructor" title="Direct link to Startup class constructor">​</a></h3>
<p>The default ASP.NET Core project template contains only the following one line of code in the constructor:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        public Startup(IConfiguration configuration)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            Configuration = configuration;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>It just saves in the class property the <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-3.1" target="_blank" rel="noopener noreferrer">configuration object</a>
passed here by the DI container, so we can use it in other methods.
In the <a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part1" target="_blank" rel="noopener noreferrer">first article</a>,
we have already considered the configuration mechanism used in ASP.NET Core applications.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="configureservices-method">ConfigureServices method<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#configureservices-method" class="hash-link" aria-label="Direct link to ConfigureServices method" title="Direct link to ConfigureServices method">​</a></h3>
<p>As it was mentioned before, the <code>ConfigureServices</code> method contains the function calls that register different application services in the DI container.
These can be authentication/authorization services, MVC or Razor Pages engine, the classes for working with DB, or any other possible services.</p>
<p>The only parameter of the <code>ConfigureServices</code> method has <code>IServiceCollection</code> type that represents a list of <code>ServiceDescriptor</code> items.
Each of those items maps some type (usually an interface) to a particular implementation of this type.</p>
<p>According to the interface, we can use only usual <code>IList</code> or <code>ICollection</code> functions like <code>Add</code>, <code>AddRange</code>, <code>Remove</code>, and other methods.
However, as you have already noticed, <code>ConfigureServices</code> contains such calls as <code>services.AddDbContext</code>, <code>services.AddRazorPages</code>, and similar.
You may ask, “How is it possible?” The answer is <strong>extension methods</strong>. Each sub-system used in our project (like Entity Framework Core or MVC engine) defines an extension method (sometimes even a few) for the <code>IServiceCollection</code> interface to simplify the registration in the DI and configuration of the services available in that sub-system.</p>
<p><strong>NB</strong>: Please note that the order of the services you register in the <code>ConfigureServices</code> method is not important.
This method is executed only once upon your application start.</p>
<p>So, let's take a closer look at the default ConfigureServices’ content.</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public void ConfigureServices(IServiceCollection services)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        options.UseSqlServer(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            Configuration.GetConnectionString("DefaultConnection")));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    services.AddDefaultIdentity&lt;IdentityUser&gt;(options =&gt; options.SignIn.RequireConfirmedAccount = true)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    services.AddRazorPages();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>So, there are only three calls there: <code>AddDbContext</code>, <code>AddDefaultIdentity</code>, and <code>AddRazorPages</code>. Let’s inspect each of them separately.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="adddbcontext">AddDbContext<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#adddbcontext" class="hash-link" aria-label="Direct link to AddDbContext" title="Direct link to AddDbContext">​</a></h4>
<p>This method is provided by the <a href="https://docs.microsoft.com/en-us/ef/core/" target="_blank" rel="noopener noreferrer">Entity Framework Core</a> framework. It registers and configures our database context. The <code>Action</code>delegate passed in the parameter allows us to define the parameters of this DbContext. For example, with <code>UseSqlServer</code> call, we tell our DbContext to use a particular type of connection (<code>SqlConnection</code>) with a particular connection string. The connection string itself is taken from the configuration so it can be easily changed in Development or Production environments. You can find the default connection string in the <code>appsettings.json</code> configuration file, which we already analyzed in the <a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part1" target="_blank" rel="noopener noreferrer">first article</a>.</p>
<h5 class="anchor anchorWithStickyNavbar_LWe7" id="a-few-words-about-the-builder-pattern">A few words about the “Builder” pattern<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#a-few-words-about-the-builder-pattern" class="hash-link" aria-label="Direct link to A few words about the “Builder” pattern" title="Direct link to A few words about the “Builder” pattern">​</a></h5>
<p>You may have noticed an interesting way of passing settings for DbContext.
Instead of sending an object with options, we use a procedure (Action) with one-parameter options of the DbContextOptionsBuilder type.</p>
<p>That's because, to construct the DbContext settings properly, Entity Framework Core uses the <strong>Builder Pattern</strong>.
This pattern is helpful when you need to perform several steps to construct some complex objects and simply defining a class or structure is not enough. For example, when you need to call some methods depending on the settings in the configuration.
As it happens in our case with <code>UseSqlServer()</code>.</p>
<p>There is one more reason why we use a delegate (function) instead of a plain object.
There may be a situation when you don't need that service (DbContext) at all.
For example, your web app has some actions that do not require database access.
The DbContext object will never be created for such requests, and so, we will not need to set its options.
The good thing about a delegate is that it can be called exactly when you need to construct the necessary object.</p>
<p>I paid so much attention to this pattern because it is used quite often,
especially in the process of registering services in DI (and we will use that for the next function call as well).</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="adddefaultidentity">AddDefaultIdentity<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#adddefaultidentity" class="hash-link" aria-label="Direct link to AddDefaultIdentity" title="Direct link to AddDefaultIdentity">​</a></h4>
<p>The second function call in the ConfigureServices was added here because of the Authentication option we turned on when creating our project.
It registers a bunch of services for authentication and authorization tasks such as <code>IUserStore</code>, <code>IRoleStore</code>, <code>IPasswordHasher</code>, <code>UserManager</code>, <code>SignInManager</code>, and many others.
The are all part of the ASP.NET Core Identity sub-system.</p>
<p>This method also uses the Builder Pattern, but in a slightly different way. Instead of passing the builder in the delegate’s parameter, this function returns an IdentityBuilder object, so you can call other functions with simple chaining.</p>
<p>The only function called there is <code>AddEntityFrameworkStores</code>.
It tells ASP.NET Identity where to store all the entities required for auth tasks (users, roles, claims, etc.).
As the name of the method suggests, we will use our Entity Framework services for these tasks.
The type of parameter in this method (between <code>&lt; .. &gt;</code>) defines the <code>DbContext</code> class used to store all auth objects.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="addrazorpages">AddRazorPages<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#addrazorpages" class="hash-link" aria-label="Direct link to AddRazorPages" title="Direct link to AddRazorPages">​</a></h3>
<p>The last function call in the <code>ConfigureServices</code> adds the services necessary for Razor Pages engine.
It appears here because of the template we chose at the beginning.
If we had selected a template with Model-View-Controller approach at that point, we would have had something like <code>services.AddMvc()</code> here.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="configure-method">Configure method<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#configure-method" class="hash-link" aria-label="Direct link to Configure method" title="Direct link to Configure method">​</a></h2>
<p>The last method in the Startup class defines the middleware pipeline of our ASP.NET Core program.
If you don't know what a middleware pipeline is, you might want to learn more about it because it determines how your web application handles HTTP requests.</p>
<p><img decoding="async" loading="lazy" alt="ASP.NET Core middleware pipeline" src="https://korzh.com/blog/assets/images/aspnetcore-middleware-pipeline-87128c7f53aac03d773e86a0c4903c4c.png" width="600" height="384" class="img_ev3q"></p>
<p>Unlike the services registered in <code>ConfigureServices</code> (remember, their order is not important), the order of middlewares defined in <code>Configure</code> has crucial significance. As you can see from the picture above, the first defined middleware is called for each request while every next one in the pipeline processes only those requests that were not processed by the middlewares defined before it.</p>
<p>So at the beginning of the pipeline, we need to place the middlewares that are necessary for auxiliary tasks (like logging or authentication) and that don’t consume a lot of memory and processing time.</p>
<p>Let’s take a look at the default implementation of this method for the chosen project template:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            if (env.IsDevelopment()) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                app.UseDeveloperExceptionPage();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                app.UseDatabaseErrorPage();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            else {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                app.UseExceptionHandler("/Error");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                app.UseHsts();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            app.UseHttpsRedirection();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            app.UseStaticFiles();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            app.UseRouting();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            app.UseAuthentication();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            app.UseAuthorization();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            app.UseEndpoints(endpoints =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                endpoints.MapRazorPages();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            });</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>So, let’s go through the middlewares defined here one by one.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="exception-handling-and-security-transport">Exception handling and security transport<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#exception-handling-and-security-transport" class="hash-link" aria-label="Direct link to Exception handling and security transport" title="Direct link to Exception handling and security transport">​</a></h4>
<p>The first lines define different middlewares for Development and Production modes.
If we are in the Development mode, we define the middlewares that catch all exceptions in the pipeline
and show a special page with extra information about the error (exception message, stack trace, etc.).</p>
<p>It's possible to do because (as you can see from the image above) the processing of the request is returned to the first middleware
in the pipeline before being sent back to the client.</p>
<p>In the Production mode, we also catch all exceptions and then re-execute the request with the specified path (‘/Error’ in our case).</p>
<p>The second call executed only in Production is <code>UseHsts</code>. It adds a middleware that implements HTTP Strict Transport Security Protocol.</p>
<p>The next in the pipeline is HTTPS redirection middleware (<code>app.UseHttpsRedirection</code>).
The name is self-explanatory. It just redirects all HTTP requests to HTTPS ones.
These two middlewares (<code>app.UseHsts</code> and <code>app.UseHttpsRedirection</code> calls) are added because of the <strong>Configure for HTTPS</strong> option we turned on (actually, it was turned on by default) during the project creation.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="processing-static-files-usestaticfiles">Processing static files (UseStaticFiles)<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#processing-static-files-usestaticfiles" class="hash-link" aria-label="Direct link to Processing static files (UseStaticFiles)" title="Direct link to Processing static files (UseStaticFiles)">​</a></h4>
<p>The next middleware (added by the <code>app.UseStaticFiles()</code> call) takes care of all static files.
Simply put, if the application gets a request for some .js, .css, or an image file (.png, .jpg, etc.),
this middleware looks for a file with the requested name inside <code>wwwroot</code> folder and returns it in response (if the file was found).
If not, it sends back a response with a 404 status code (“not found”).</p>
<p>There are overloads of <code>UseStaticFiles</code> that allow us to define another root folder for static files and set some other options.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="routing-userouting-and-useendpoints">Routing (UseRouting and UseEndpoints)<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#routing-userouting-and-useendpoints" class="hash-link" aria-label="Direct link to Routing (UseRouting and UseEndpoints)" title="Direct link to Routing (UseRouting and UseEndpoints)">​</a></h4>
<p>The next pair of middlewares are, maybe, the most important ones in the pipeline, since they define the routing for all other endpoints in your web app.</p>
<p>In simple words, they match a particular request to a particular endpoint, a piece of executable code that handles the request.</p>
<p>So, how exactly does it work?</p>
<p>In the <code>Configure</code> method, we call <code>app.UseRouting()</code> to add <code>EndpointRoutingMiddleware</code> to our pipeline.</p>
<p>After that, we call <code>app.UseEndpoints()</code> to add <code>EndpointMiddleware</code> to the pipeline and define the endpoints. Each endpoint is an object that contains (as mentioned above) a delegate (so, a piece of code) that actually handles the request, plus some metadata (like an authorization policy).</p>
<p>We can use such extension methods as <code>MapGet</code>, <code>MapPost</code>, and others to add an endpoint that matches a particular request path or a path template. For example, the following pattern <code>/something/{path*}</code>, will be matched for all requests started with <code>/something/</code>.</p>
<p>Additionally, other services can define their own extension functions that are used to create necessary endpoints. For example, you can use the <code>MapRazorPages</code> function for Razor Pages or <code>MapHub</code> for SingleR. In our <a href="https://github.com/KorzhCom/EasyData" target="_blank" rel="noopener noreferrer">EasyData library</a> we defined <code>MapEasyData</code> function that adds an endpoint for the API that processes all CRUD operations.</p>
<p>When our web app gets a new request (and it’s not processed by any middleware defined before <code>UseRouting</code>), the <code>EndpointRoutingMiddleware</code> matches it to some endpoint. So, one endpoint among all defined at the application start is becoming selected at that moment.</p>
<p>Then <code>EndpointMiddleware</code> actually calls the endpoint’s delegate to handle the request.
Other middlewares that are added after <code>app.UseRouting()</code> but before <code>app.UseEnpoints()</code> can see which endpoint is selected
and can change something (e.g., to apply an authorization policy) before <code>EndpointMiddleware</code> dispatches to the selected endpoint.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="authenticationauthorization-useauthentication-useauthorization">Authentication/Authorization (UseAuthentication, UseAuthorization)<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#authenticationauthorization-useauthentication-useauthorization" class="hash-link" aria-label="Direct link to Authentication/Authorization (UseAuthentication, UseAuthorization)" title="Direct link to Authentication/Authorization (UseAuthentication, UseAuthorization)">​</a></h4>
<p>These two middlewares are added in the <code>Configure</code> because of the Authentication option we turned on when we created our project.
As already mentioned above, they already know which endpoint is selected and so can add some additional identity-related information to the request’s context (like filling its <code>User</code> property) or redirect to the “sign-in/sign up” pages if the current request is anonymous but the selected endpoint requires authorization.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="wrapping-up">Wrapping Up<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2#wrapping-up" class="hash-link" aria-label="Direct link to Wrapping Up" title="Direct link to Wrapping Up">​</a></h2>
<p>So, in this article, I tried to cover all pieces of one of the most-used ASP.NET Core application templates and figure out why they are used and how they work (from a very general point of view).</p>
<p>I hope this information will help you better understand the internal mechanism of the ASP.NET Core framework and make your code more clear and efficient.</p>
<p>Thanks for the reading, and take care!</p>]]></content>
        <category label="ASP-NET-CORE" term="ASP-NET-CORE"/>
        <category label="PROJECT-TEMPLATE" term="PROJECT-TEMPLATE"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[ASP.NET Core default project structure explained (part 1)]]></title>
        <id>https://korzh.com/blog/asp-net-core-project-structure-explained-part-1</id>
        <link href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1"/>
        <updated>2020-05-23T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Introduction]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/computer-coffee-01-a7e2ae32ba982b004b23d7282738dc5d.jpg" width="1920" height="1282" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="introduction">Introduction<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction">​</a></h2>
<p>When you start learning a programming language, one of your first exercises will probably be to write a “Hello World” application to figure out the basic concepts.
This exercise works well if you’re writing a simple console program that will only print out the greetings on the screen.</p>
<p>However, once you move onto learning to write web apps using a new framework, such as ASP.NET Core, such a simple code is not enough — partially because web apps are more complex.
And also because, right off the bat, the tools and frameworks used for writing web apps try to introduce advanced techniques and approaches for building and maintaining them once they scale.</p>
<p>It all can seem like too much for a beginner.
Here we will take apart the default ASP.NET Core solution template piece by piece, then figure out the purpose of each piece and how exactly it works.</p>
<p>Please note that this isn’t a full-scale tutorial on all the aspects of creating web apps in ASP.NET Core.
Rather, it is a short guide where you can check why a particular part is added to your project or find a piece of code and a link to the relevant documentation that explains it in detail.</p>
<p>As an example, I used a new solution that targets .NET 5.
However, most of this article's information will also be relevant for the ASP.NET Core projects that target .NET Core 3.1 or the upcoming .NET 6.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="creating-a-new-web-project">Creating a New Web Project<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#creating-a-new-web-project" class="hash-link" aria-label="Direct link to Creating a New Web Project" title="Direct link to Creating a New Web Project">​</a></h2>
<p>Let’s start with creating a new empty web app with the “Create a new project” wizard in Visual Studio.</p>
<p>Here is how this step should look:</p>
<p><img decoding="async" loading="lazy" alt="Create new project dialog in Visual Studio" src="https://korzh.com/blog/assets/images/create-new-project-dlg-f293c181a454d844f19b7dc618cab490.png" width="1277" height="841" class="img_ev3q"></p>
<p>When you press Next, the wizard will ask you to choose the names of the new solution and project and the folder to place the solution files in:</p>
<p><img decoding="async" loading="lazy" alt="Configure new project dialog in Visual Studio" src="https://korzh.com/blog/assets/images/configure-project-dlg-65dcd774eb05a670e8b3c0ec8197755c.png" width="1236" height="809" class="img_ev3q"></p>
<blockquote>
<p><strong>Tip #1</strong>: Place the project and the solution into separate folders. Choosing the same location for both is convenient only for very small applications. Once you start adding other layers to your solution (domain classes, data layers, API, etc.), having them in separate projects will be more practical.</p>
</blockquote>
<blockquote>
<p><strong>Tip #2</strong>: Use the general name of your application for the name of the solution (like “MyCoolApp”) and add the “.Web” suffix for the project name (e.g., “MyCoolApp.Web”).</p>
</blockquote>
<div id="projectCreationFinalStep"><p>Finally, you will need to choose some additional project options.</p><p><img decoding="async" loading="lazy" alt="Additional information for the new project" src="https://korzh.com/blog/assets/images/project-additional-info-a0828b7b4bd4cede3dd83121c611b712.png" width="1022" height="677" class="img_ev3q"></p><p>The most important options here are:</p><ul>
<li>
<p><strong>Target Framework</strong>. I suggest selecting the latest one (for the moment), .NET 5, but all the information in this article is relevant for .NET Core 3.1 or .NET 6 (it’s in the pre-release state now) projects as well.</p>
</li>
<li>
<p><strong>Authentication Type</strong>. In this article, we suppose this option is set to <em>Individual Accounts</em>. This value means that our new web project will contain some means for managing user accounts and provide UI for the basic authentication operations: Login, Registration, Reset Password, etc.</p>
</li>
</ul><p>Let's click on "Create" when everything is set and get our new solution.</p></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="project-structure">Project Structure<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#project-structure" class="hash-link" aria-label="Direct link to Project Structure" title="Direct link to Project Structure">​</a></h2>
<p>After finishing the steps described above, you will get a new solution with one project inside it.</p>
<p>The structure of that project will look similar (or even the same) to the following one:</p>
<p><img decoding="async" loading="lazy" alt="Default ASP.NET Core project structure" src="https://korzh.com/blog/assets/images/default-asp-net-project-structure-4205075f264d2c23cd63b1c974d70a19.png" width="305" height="301" class="img_ev3q"></p>
<p>Let’s take a closer look at each part of your new project.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-connected-services">1. Connected Services<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#1-connected-services" class="hash-link" aria-label="Direct link to 1. Connected Services" title="Direct link to 1. Connected Services">​</a></h3>
<p>This is the first node in your project’s structure and possibly the less-used one.
It is intended to automate the multiple steps necessary to connect a project to an external service (like Azure Storage or Application Insights).
You can right-click on this node and select “Add connected service…” to run a wizard that will lead you through the process.
Usually, it just adds necessary packages and gives you basic instructions on how to start using your app's service.
As I’ve already mentioned, there is a good chance you will not use this node during your project’s lifetime.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-dependencies">2. Dependencies<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#2-dependencies" class="hash-link" aria-label="Direct link to 2. Dependencies" title="Direct link to 2. Dependencies">​</a></h3>
<p>This element of your project structure contains all packets or other projects on which your project depends.</p>
<p>There are four main folders inside this node:</p>
<ul>
<li><strong>Analyzers</strong><br>
<!-- -->They help you make your code better: cleaner, error-free. Each analyzer checks that your code satisfies a list of rules incorporated in it. If any part of your code does not apply to one of the rules, you will see either a Warning or an Error while you build your project.</li>
</ul>
<p>Please note that analyzers work only at compile time and do not affect your resulting application.</p>
<ul>
<li><strong>Frameworks</strong><br>
<!-- -->This folder contains a list of frameworks your project depends on. This information is important if you <a href="https://docs.microsoft.com/en-us/dotnet/core/deploying/" target="_blank" rel="noopener noreferrer">publish your web app as a runtime-dependent</a> (as opposed to a self-contained one). In this case, all the frameworks listed here must be installed on the server where you will run your app.</li>
</ul>
<blockquote>
<p>NB: You can use <code>dotnet --info</code> console command to check the list of installed frameworks.*</p>
</blockquote>
<ul>
<li>
<p><strong>Packages</strong><br>
<!-- -->This is the main item in this node. It lists all NuGet packages you added (installed) to your project. If any of those packages depend on other packages, they will be installed automatically and listed as sub-nodes of the root-level packages. You can remove each installed package here (right-click / Remove).</p>
</li>
<li>
<p><strong>Projects</strong><br>
<!-- -->This is the list of other projects in this solution your current project depends on.
You can reference other projects using the “Add reference” command from the right-click menu.
Obviously, this node is empty now since we have only one project in the solution.</p>
</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="3-properties">3. Properties<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#3-properties" class="hash-link" aria-label="Direct link to 3. Properties" title="Direct link to 3. Properties">​</a></h3>
<p>This part contains different properties of your project that you can modify by double-clicking on this node in the Solution Explorer. Most of the properties there affect the compile- and debug-time behavior of your project.</p>
<p>The only item inside this node is a “launchSettings.json” file containing the launch profiles. Each profile defines how to run your project when you click on the “Run” button in Visual Studio.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="4-wwwroot">4. wwwroot<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#4-wwwroot" class="hash-link" aria-label="Direct link to 4. wwwroot" title="Direct link to 4. wwwroot">​</a></h3>
<p>This folder contains all the static files of your web application: CSS files, JavaScript files, images, and icons.
As you might figure out from its name, this will be the root folder of your web app.
So if you put an “image1.png” file into the “wwwroot/images/dir1” folder, it will be accessible in the browser by the “/images/dir/image1.png” address.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="5-areas">5. Areas<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#5-areas" class="hash-link" aria-label="Direct link to 5. Areas" title="Direct link to 5. Areas">​</a></h3>
<p>This folder is added because we selected the “Individual Accounts” value for the “Authentication Type” option when created the project.</p>
<p>In addition to the core authentication/authorization packages (which is called ASP.NET Core Identity), the default template also adds the “Microsoft.AspNetCore.Identity.UI” package. This is a Razor-class library that contains all forms and partial views for authentication and user management: Login, Registration, Reset Password, User Profile, and many others.
All these forms will use your layout (defined in <em>Pages/Shared/_Layout.cshtml</em>) and so will match your website’s design.</p>
<p>However, you still might want to change some of those forms (or even all of them).
In this case, you can use the Scaffold command and add the necessary files to your project. The scaffolded files will be added to the <em>Areas/Identity</em> folder.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="6-data">6. Data<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#6-data" class="hash-link" aria-label="Direct link to 6. Data" title="Direct link to 6. Data">​</a></h3>
<p>As in the previous case, this folder appears in our new project because of the “Authentication Type” option that was turned on at the beginning.</p>
<p>By default, all user-related information (users, their roles, claims, etc.) is stored in a database accessed with <a href="https://docs.microsoft.com/en-us/ef/core/" target="_blank" rel="noopener noreferrer">Entity Framework Core</a> ORM (object-relational mapping) framework. If you are not familiar with EF Core, you can find a lot of tutorials on <a href="https://docs.microsoft.com/en-us/ef/core/get-started/" target="_blank" rel="noopener noreferrer">Microsoft Docs</a>.</p>
<p>The “Data” folder contains your DB context class named “ApplicationDbContext.” This class is derived from the “IdentityDbContext” class defined in the “Microsoft.AspNetCore.Identity.EntityFrameworkCore” assembly, and it includes all DB sets (tables) necessary to store user-related information.</p>
<p>You can use this class or create another DbContext class to add your models.</p>
<p>Additionally, the “Data” folder also includes “Migrations” sub-folder with all your Entity Framework Core migrations.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="7-pages">7. Pages<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#7-pages" class="hash-link" aria-label="Direct link to 7. Pages" title="Direct link to 7. Pages">​</a></h3>
<p>This folder contains all pages (forms) of your web applications. ASP.NET Core has two default approaches for content rendering: MVC (Model View Controller) and <a href="https://docs.microsoft.com/en-us/aspnet/core/razor-pages" target="_blank" rel="noopener noreferrer">Razor Pages</a> (which is, actually, a kind of MVC, where each controller and the corresponding view are stored together). Both approaches use <a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/views/razor" target="_blank" rel="noopener noreferrer">Razor syntax</a> that you can think of as HTML + C#.</p>
<p>Each Razor page is represented by a “.cshtml” file. The code related to it is stored in a so-called “code-behind” file with the same name and a “.cshtml.cs” extension.</p>
<p>From the start, the “Pages” folder contains a few default pages, such as Index (the home page of your web app), Privacy, and Error. Additionally, you can find the main layout file (_Layout.cshtml) and some partial views in the “Pages/Shared” folder.</p>
<p><strong>NB</strong>: It’s a common practice to prefix the names of the layouts and partial views with <code>_</code> symbol.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="8-root-folder-files">8. Root Folder Files<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#8-root-folder-files" class="hash-link" aria-label="Direct link to 8. Root Folder Files" title="Direct link to 8. Root Folder Files">​</a></h3>
<p>Now, when we’ve looked over all the main folders created with the default ASP.NET Core application template, let’s take a closer look at the files stored in the project’s root folder. They are the most important part of the project.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="programcs">Program.cs<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#programcs" class="hash-link" aria-label="Direct link to Program.cs" title="Direct link to Program.cs">​</a></h4>
<p>This file defines the “Program” class with one static method, “Main,” which is the entry point of your web application.</p>
<p>The only purpose of this method is to define the <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/?view=aspnetcore-3.1#host" target="_blank" rel="noopener noreferrer">host</a> and then pass the control to the Startup class. For more information about the default host builder, look at an article about <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host" target="_blank" rel="noopener noreferrer">Generic Host</a> on Microsoft’s Docs.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="applicationsettings">application.settings<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#applicationsettings" class="hash-link" aria-label="Direct link to application.settings" title="Direct link to application.settings">​</a></h4>
<p>This is a JSON file that stores the application’s settings. The settings have a hierarchical structure and can be accessed with the Configuration object defined in the Startup class or in any other place of your program where you injected IConfiguration service. For example:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">var defaultLogLevel = Configuration.GetValue&lt;string&gt;("Logging:LogLevel:Default");</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>The different levels of hierarchy are separated by the colon (<code>:</code>) symbol.</p>
<p>By default, in addition to “application.settings”, the ASP.NET Core template also includes an “application.Development.settings” file.
The settings defined in this file are loaded only into the Development environment and are not available in production.
To learn more about the “.settings” files and configuration in ASP.NET Core projects in general, please read <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/" target="_blank" rel="noopener noreferrer">this article</a> on Microsoft’s Docs.</p>
<h4 class="anchor anchorWithStickyNavbar_LWe7" id="startupcs">Startup.cs<a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part-1#startupcs" class="hash-link" aria-label="Direct link to Startup.cs" title="Direct link to Startup.cs">​</a></h4>
<p>This is the final part of the ASP.NET Core project’s structure. The “Startup” class serves three main purposes:</p>
<ul>
<li>
<p>It performs all initialization tasks (setting application-wide constants, DB seeding, migrations, etc.).</p>
</li>
<li>
<p>It registers all services used in this project in the <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection" target="_blank" rel="noopener noreferrer">DI (dependency injection) container</a>.</p>
</li>
<li>
<p>It defines the <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection" target="_blank" rel="noopener noreferrer">middleware pipeline</a> of your web-application.</p>
</li>
</ul>
<p>This class contains a lot of code (as for a project that has just been created) from the beginning and will become even bigger when you start adding new features to your application.</p>
<p>We will take a look at it in more detail in the <a href="https://korzh.com/blog/asp-net-core-project-structure-explained-part2">next article</a>.</p>]]></content>
        <category label="ASP-NET-CORE" term="ASP-NET-CORE"/>
        <category label="JAVASCRIPT" term="JAVASCRIPT"/>
        <category label="TYPESCRIPT" term="TYPESCRIPT"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[.NET vs. Java - Which Platform Is Better for Your Software Developer Career?]]></title>
        <id>https://korzh.com/blog/dotnet-vs-java-what-development-platform-better</id>
        <link href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better"/>
        <updated>2020-05-01T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Choosing a framework is not easy when you start reflecting on your future as a software developer, especially if you are a newbie.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/dotnet-vs-java-cb43cb961977241cbbeab7efebca21f6.png" width="1768" height="995" class="img_ev3q"></p>
<p>Choosing a framework is not easy when you start reflecting on your future as a software developer, especially if you are a newbie.
Mastering any programming language takes long hours and an effort you don’t want to waste.
And it’s stressful to think that the wrong option might lead you to a job where you will have to change your knowledge base again.</p>
<p>Ok, let’s switch to the positive side.
Since you are here, your options are narrowed down to these two: Java or .NET.
That’s already a huge move forward.
Both options are already a kind of win.
Java and .NET are both well-established and are used for enterprise-level development.
That means you might land a job at a huge company with lots of resources and opportunities.</p>
<p>Let’s take a quick look at the job market.
As of April 2020, Monster.com lists more than 41k jobs for “Java Developer” and 8.5k for “.NET Developer.”
According to Glassdoor, the average salary estimation for a Java developer (irrespective of level and location) is about USD 79k and USD 76.5k for an ASP.NET/C# developer.</p>
<p>Don’t be too fast with the conclusions though.
Next, you might want to know which of the two is better for you personally and is more relevant to the future of technologies.
Let’s have a closer look at both options.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="old-school-java">Old-school Java<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#old-school-java" class="hash-link" aria-label="Direct link to Old-school Java" title="Direct link to Old-school Java">​</a></h2>
<p>At the moment, Java is one of the most popular technologies worldwide.
It often ranks first in various ratings, and this is probably the main reason developers stick to it. The technology was created in 1995.</p>
<p>It is relatively easy to learn, and there are many free training resources available.
In addition, there is an enormous community that can help a newbie through the learning process.
Java is suitable for almost every type of project: web applications, mobile apps, cloud computing, etc.</p>
<p><img decoding="async" loading="lazy" alt="Applications of Java" src="https://korzh.com/blog/assets/images/applications-of-java-214a7c120d101763a637c0dbfa7a6f5c.png" width="1200" height="600" class="img_ev3q"></p>
<p>Java is a free technology, as is the Oracle JDK (Java Development Kit), or Oracle OpenJDK, that’s used to create software in Java.
It also makes it possible to work with other programming languages, such as Kotlin, Groovy, or Scala, which would be relatively easy to learn as an addition. And as we have just pointed out, the job market is full of offers for Java developers from all over the planet. Sound good?</p>
<p>But hold on — there is a slippery slope under any ideal image. Some downsides include:</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="licesing-mess">Licesing mess<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#licesing-mess" class="hash-link" aria-label="Direct link to Licesing mess" title="Direct link to Licesing mess">​</a></h3>
<p>Maybe you think that you will start your way in Java by writing a small "Hello World" program, right? Wrong.
Today, before you actually can learn Java itself, you first need to understand licensing on this platform.
Otherwise, you may appear in a situation when you at first have to pay Oracle a couple of thousands of dollars for the licensing before delivering your work to the customer.
Therefore, first, carefully study what Oracle JDK is and how it differs from Oracle Open JDK.
And how they both differ from the community-led AdoptOpenJDK.
What is Binary Code License and how much rights you have with <a href="http://openjdk.java.net/legal/gplv2+ce.html" target="_blank" rel="noopener noreferrer">GPLv2+CPE license</a>.
Several official FAQs seem to explain the situation with licensing (one on <a href="https://www.oracle.com/technetwork/java/javase/overview/oracle-jdk-faqs.html" target="_blank" rel="noopener noreferrer">Oracle’s site</a>
and another one on <a href="https://java.com/en/download/faq/distribution.xml" target="_blank" rel="noopener noreferrer">Java.com</a>) but make it even more confusing.
In addition, you can easily find a bunch of blog posts, queries on Quora (like "Is Java still free"), and even videos on this topic.</p>
<p>The question is: do you really want to deal with all this?</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="legacy-code">Legacy code<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#legacy-code" class="hash-link" aria-label="Direct link to Legacy code" title="Direct link to Legacy code">​</a></h3>
<p>Many long-standing companies have something written in Java.
Eventually, a young and passionate Java learner might end up on a job where they are working with an obsolete code instead of some new and prominent technology.
For example, a library a company has been accumulating for 20 odd years might be too expensive and time-consuming to modernize.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="high-competition">High competition<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#high-competition" class="hash-link" aria-label="Direct link to High competition" title="Direct link to High competition">​</a></h3>
<p>In addition to the huge number of vacancies, there’s an even bigger number of candidates.
That’s because virtually everyone who studies all the ratings sees Java in the top positions — and here we go!
Another thousand newbie learners opt for Java.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="a-more-promising-option-the-net-framework">A More Promising Option: the .NET Framework<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#a-more-promising-option-the-net-framework" class="hash-link" aria-label="Direct link to A More Promising Option: the .NET Framework" title="Direct link to A More Promising Option: the .NET Framework">​</a></h2>
<p>There is another very popular free open-source developer platform that can serve you better — .NET. Here is why it may be more promising for you.</p>
<p><img decoding="async" loading="lazy" alt=".NET 5 platform overview" src="https://korzh.com/blog/assets/images/dotnet5_platform-7110e6aa8774d81f843f411b042b21ff.png" width="1837" height="979" class="img_ev3q"></p>
<p>Source: <a href="https://devblogs.microsoft.com/dotnet/introducing-net-5/" target="_blank" rel="noopener noreferrer">https://devblogs.microsoft.com/dotnet/introducing-net-5/</a></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-free-open-and-cross-platform">1. Free, open, and cross-platform<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#1-free-open-and-cross-platform" class="hash-link" aria-label="Direct link to 1. Free, open, and cross-platform" title="Direct link to 1. Free, open, and cross-platform">​</a></h3>
<p>Interestingly enough, about five to six years ago all the features mentioned in the previous section would have made Java a better option,
as .NET was then fully controlled by Microsoft and ran only on Windows.</p>
<p><strong>This situation has completely changed in the last few years.</strong></p>
<p>The tables have turned, and now Java is becoming more closed off under Oracle guidance, while the approach of Microsoft is quite the opposite.
.NET now is an <a href="https://dotnet.microsoft.com/platform/free" target="_blank" rel="noopener noreferrer">open-source platform with no licensing costs</a>.
They claim freedom for developers as one of the platform’s core values.
For the time being, Microsoft seems to practice what they preach.</p>
<p>The latest version of the .NET platform — .NET Core — is compatible with all the major operating systems: Windows, Linux, and iOS. There are basic .NET tools open for immediate use and lots of add-ons for building and editing apps, as well as third-party tools. Multiple libraries built specifically for .NET make the development process even more comfortable.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-all-application-types">2. All application types<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#2-all-application-types" class="hash-link" aria-label="Direct link to 2. All application types" title="Direct link to 2. All application types">​</a></h3>
<p>The .NET framework (and its Core edition in particular) allow you to develop almost any kind of application,
be it an enterprise-level web app, desktop or mobile software, gaming project, or even an IoT service or an AI solution.</p>
<p>With the advent of the WebAssembly technology and <a href="https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor" target="_blank" rel="noopener noreferrer">Blazor framework</a>,
.NET can be used even for building client-side applications running in a browser — web apps where both client and server-side code is written in C#, thus eliminating the need to use JavaScript.</p>
<p><img decoding="async" loading="lazy" alt=".NET 5 application models" src="https://korzh.com/blog/assets/images/dotnet-application-models-cfefc663e3eec1aa4de6517b951265a7.png" width="1402" height="511" class="img_ev3q"></p>
<p>Source: <a href="https://dotnet.microsoft.com/learn/dotnet/what-is-dotnet" target="_blank" rel="noopener noreferrer">https://dotnet.microsoft.com/learn/dotnet/what-is-dotnet</a></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="3-quick-progress-and-regular-updates">3. Quick progress and regular updates<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#3-quick-progress-and-regular-updates" class="hash-link" aria-label="Direct link to 3. Quick progress and regular updates" title="Direct link to 3. Quick progress and regular updates">​</a></h3>
<p>The .NET framework has been constantly evolving throughout its 20-year history.
But in the last four to five years, the pace of its progress has increased significantly.</p>
<p>Since the initial release of .NET Core back in 2016, we have received a major update almost once per year.</p>
<p>In November 2020 Microsoft plans to release another big update to the platform — .NET 5.
It is supposed to take the best of the previous versions and become a united platform for all the development tasks.
Moreover, at its annual conference in 2019 Microsoft introduced <a href="https://medium.com/capgemini-dynamics-365-team/future-of-net-net-5-microsoft-build-2019-from-a-net-developer-point-of-view-7a1158fb0691" target="_blank" rel="noopener noreferrer">a roadmap for the .NET platform</a>
that gives a clear understanding of what to expect and when.</p>
<p><img decoding="async" loading="lazy" alt=".NET Schedule" src="https://korzh.com/blog/assets/images/dotnet_schedule-62872ec1cbb180a61f256848c6e9f4bf.png" width="1837" height="1015" class="img_ev3q"></p>
<p>Source: <a href="https://devblogs.microsoft.com/dotnet/introducing-net-5/" target="_blank" rel="noopener noreferrer">https://devblogs.microsoft.com/dotnet/introducing-net-5/</a></p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="4-great-performance">4. Great performance<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#4-great-performance" class="hash-link" aria-label="Direct link to 4. Great performance" title="Direct link to 4. Great performance">​</a></h3>
<p>For years, the applications created either with Java or on .NET/C# had a similar level of performance.
However, Microsoft and many third-party contributers have made some significant investments in the optimization of this aspect and continues to improve.
For example, the ASP.NET Core web framework allows you to serve <a href="https://www.ageofascent.com/2019/02/04/asp-net-core-saturating-10gbe-at-7-million-requests-per-second/" target="_blank" rel="noopener noreferrer">7 million requests per second on a single server</a>.
And according to <a href="https://www.techempower.com/benchmarks/#section=data-r18&amp;hw=ph&amp;test=json" target="_blank" rel="noopener noreferrer">TechEmpower tests</a>, ASP.NET Core is way ahead of any Java web framework.</p>
<p>Here's another good demonstration of the performance that can be achieved on the .NET platform —
<a href="https://github.com/ixy-languages/ixy-languages" target="_blank" rel="noopener noreferrer">an example of a super-fast network driver written in different languages</a>.
The driver written in C# shows almost the same level of performance as C and Rust, slightly better than the Go language, and way ahead of all other high-level languages, including Java.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="5-c--modern-and-versatile">5. C# — modern and versatile<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#5-c--modern-and-versatile" class="hash-link" aria-label="Direct link to 5. C# — modern and versatile" title="Direct link to 5. C# — modern and versatile">​</a></h3>
<p>.NET was initially designed to support many programming languages.
The most widely employed among them are C# (pronounced “see sharp”) and F# (which offers a more functional approach).</p>
<p>The most popular and loved is definitely C# — a general-purpose and object-oriented programming language.
It is easy to learn and is widely used for all kinds of software development, from enterprise-level solutions to low-level IoT and gaming projects.
It makes simple many features that are overly complicated in Java.</p>
<p>Despite its long history, C# keeps up with the times.
A lot of features, such as LINQ queries, lambda expressions, asynchronous operations with async/await, and others were introduced in C# way ahead of other programming languages. Java still doesn’t have many features that were available in C# for years.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="net-limitations">.NET Limitations<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#net-limitations" class="hash-link" aria-label="Direct link to .NET Limitations" title="Direct link to .NET Limitations">​</a></h2>
<p>Just like we said about Java, there is always a downside to any ideal picture.
With .NET, it is still not good enough for the client-side development.
JavaScript is still considered the best option.
The same goes for the ML (Machine Learning) and NLP (Natural Language Processing) applications. Here Python is number one.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="wrap-up">Wrap Up<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#wrap-up" class="hash-link" aria-label="Direct link to Wrap Up" title="Direct link to Wrap Up">​</a></h2>
<p>The choice between Java and .NET is one between two solid enterprise-level development options.
Though Java now ranks number one in all the ratings, it is highly competitive in terms of the job market and slow in terms of relevant updates.</p>
<p>The other option — .NET — is an open-source object-oriented solution that can be used for developing all sorts of applications, and it has a clear roadmap for the future.
In 2020 there will emerge .NET 5, an update that will unite all the best features of the previous .NET versions.
This makes learning a well-structured, easy, and modern technology even more attractive.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="useful-links">Useful links:<a href="https://korzh.com/blog/dotnet-vs-java-what-development-platform-better#useful-links" class="hash-link" aria-label="Direct link to Useful links:" title="Direct link to Useful links:">​</a></h3>
<ul>
<li>
<p>Looking for a job as a .NET developer? <br>
Use <a href="https://jooble.org/jobs-.net-developer" target="_blank" rel="noopener noreferrer">Jooble</a> to post your CV and start getting job offers.</p>
</li>
<li>
<p>Need to implement CRUD (create, read, update, delete) operations in your ASP.NET Core project and don't know where to start? <br>
<a href="https://github.com/KorzhCom/EasyData" target="_blank" rel="noopener noreferrer">EasyData open-source library</a> to the rescue! With EasyData you can get full CRUD functionality in just 10 minutes and with just a few lines of code.</p>
</li>
<li>
<p>What to improve the user experience of your DB-related .NET application? <br>
<a href="https://korzh.com/easyquery?utm_source=blog&amp;utm_medium=content&amp;utm_campaign=net_vs_java" target="_blank" rel="noopener noreferrer">EasyQuery framework</a> can help you to implement custom search, data-filtering, reporting, data exporting (to CSV, Excel, PDF), and many other tasks that commonly arise in data management solutions.</p>
</li>
</ul>]]></content>
        <category label="NET" term="NET"/>
        <category label="JAVA" term="JAVA"/>
        <category label="PROGRAMMING" term="PROGRAMMING"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Dependency Injection Put Simply]]></title>
        <id>https://korzh.com/blog/dependency-injection-explanation-in-simple-words</id>
        <link href="https://korzh.com/blog/dependency-injection-explanation-in-simple-words"/>
        <updated>2020-01-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[The concept of Dependency Injection can look over complicated especially for beginners.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/dependency-injection-food-app-d23496c67203b37028801d53bc2ab88b.png" width="1000" height="553" class="img_ev3q"></p>
<p>The concept of Dependency Injection can look over complicated especially for beginners.
Here, I have tried to explain it by using a very simple example from the real world: a food delivery app on your mobile phone.</p>
<p>So, imagine you open such an app on your smartphone. Let’s see what we have here:</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="menu-interfaces">Menu (interfaces)<a href="https://korzh.com/blog/dependency-injection-explanation-in-simple-words#menu-interfaces" class="hash-link" aria-label="Direct link to Menu (interfaces)" title="Direct link to Menu (interfaces)">​</a></h2>
<p><img decoding="async" loading="lazy" alt="DI concepts: intefaces are like items in a cafe menu" src="https://korzh.com/blog/assets/images/di-interfaces-as-menu-50acc859ddc628cbf48a572460110880.png" width="1200" height="675" class="img_ev3q"></p>
<p>Photo by <a href="https://unsplash.com/@elizagalevi23?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Levi Elizaga</a> on <a href="https://unsplash.com/s/photos/menu?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></p>
<p>Obviously, this app has a menu with a description of each item available there. To make it more similar to the Dependency Injection (DI) container let’s suppose all items on the menu are more or less generic. So, you have such options as a burger, chips, green salad, a soda drink, coffee, etc. However, you don’t know what meat is used for that burger, how exactly those chips are cooked, and what is the exact soda drink you will get.
These menu items are your <strong>interfaces</strong>. They describe the main characteristics of each snack but don’t bother you with the exact implementation.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="actual-snacks-implementations">Actual snacks (implementations)<a href="https://korzh.com/blog/dependency-injection-explanation-in-simple-words#actual-snacks-implementations" class="hash-link" aria-label="Direct link to Actual snacks (implementations)" title="Direct link to Actual snacks (implementations)">​</a></h2>
<p>Then, you have the actual snacks which are delivered to you when you place an order. These are the <strong>implementations</strong> of your interfaces.
Obviously, the implementations can differ depending on which particular restaurant they are delivered from.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="delivery-injection">Delivery (injection)<a href="https://korzh.com/blog/dependency-injection-explanation-in-simple-words#delivery-injection" class="hash-link" aria-label="Direct link to Delivery (injection)" title="Direct link to Delivery (injection)">​</a></h2>
<p>Now, let’s suppose that your app, in some magical way, has a zero-time delivery function (OK, almost zero-time).
So, at whatever place you are now (whatever part of your project), you select the items you need, press the “Order” button, and the chosen snacks instantly appear in your hands.
We can say that they are <strong>injected</strong> to the place where you are now :)
Moreover, if a snack (like breakfast or combo-meal) depends on other snacks, those dependant snacks are created automatically and also “injected” into your combo-meal.
For example, a Big Mac Combo Meal includes a BigMac burger, some fries, and a drink. You don’t need to order them separately. They will all be created and delivered to you automatically.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="providers">Providers<a href="https://korzh.com/blog/dependency-injection-explanation-in-simple-words#providers" class="hash-link" aria-label="Direct link to Providers" title="Direct link to Providers">​</a></h2>
<p>Now let’s suppose you can choose which restaurant will deliver your snacks.
You can choose either it will be a McDonald’s or Burger King or your favorite cafe nearby.</p>
<p>Moreover, you can also choose the type of food (with meat, fish only, vegetarian, kosher, etc).
So, now you can still order a burger but if you selected “vegetarian”,
our magical app will deliver to you a burger with soy meat or a burger made from that cultured meat popular nowadays.</p>
<p>The main point here is this: with this app, you can easily change the actual provider (so, the implementation)
of your snacks without changing the simplicity and convenience of the whole process.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="wrapping-up">Wrapping up<a href="https://korzh.com/blog/dependency-injection-explanation-in-simple-words#wrapping-up" class="hash-link" aria-label="Direct link to Wrapping up" title="Direct link to Wrapping up">​</a></h2>
<p><img decoding="async" loading="lazy" alt="Dependency Injection Schema" src="https://korzh.com/blog/assets/images/dependency-injection-schema-401653f67386295e83a91127391480fa.png" width="821" height="391" class="img_ev3q"></p>
<p>Your food delivery app here is a <strong>Dependency Injection (DI) Container</strong>. The menu items are <strong>interfaces</strong> of the services you might need somewhere in your project.
The actual snacks are the <strong>implementations</strong> (actual classes that implement those services).
When you need your some snacks (<strong>services</strong>) you tell your app (<strong>DI container</strong>) what you need and they are delivered (<strong>injected</strong> in the place you are now) to you immediately.</p>
<p>You may ask, why DI does matter? What is so special about this concept that has made it so popular?</p>
<p>As you can see from this real-word example, dependency injection has a lot of benefits.
Let’s list the most important ones (again, with a real-world example for each of them):</p>
<ol>
<li>
<p><strong>Simplicity</strong><br>
<!-- -->With DI, you don’t need to write a lot of code just to create an object of a particular class.
Especially when this object requires several other objects for its work (like a combo-meal in our example).
You just “tell” your DI container (your food delivery app) what you need and it’s delivered to you right away.</p>
</li>
<li>
<p><strong>Maintainability</strong><br>
<!-- -->n terms of code, your classes become loosely coupled (each of the classes is less dependent on the concrete implementations of other classes),
so your code will be easier to maintain.
In terms of our food delivery app, it’s easier to maintain one app and order (inject) necessary snacks,
instead of thinking where to buy all the ingredients for each of them and then cook everything yourself.</p>
</li>
<li>
<p><strong>Flexibility</strong><br>
<!-- -->With DI, your code becomes loosely coupled and so, more flexible, since you depend on interfaces and it’s very easy to replace the implementation.
For example, you have a repository interface for storing data about users.
The initial implementation of this repository will save the data to files. After that, you can decide to use the database.
The implementation of the repository is changed but all the code that uses it remains the same.<br>
<!-- -->It’s similar to how you switch the provider of the snacks (or the preferred type of food) in your app. The process of order and delivery remains the same, you just start getting other snacks.</p>
</li>
</ol>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusions">Conclusions<a href="https://korzh.com/blog/dependency-injection-explanation-in-simple-words#conclusions" class="hash-link" aria-label="Direct link to Conclusions" title="Direct link to Conclusions">​</a></h2>
<p>As you can see, Dependency Injection is a very useful and convenient technique,
the main principles of which can be applied not only in coding but in some real-world situations.
In terms of programming, you get more maintainable, more readable, more flexible, and more extensible code.
These are good enough reasons to get better acquainted with this technology and start using it in your projects.</p>]]></content>
        <category label="PROGRAMMING" term="PROGRAMMING"/>
        <category label="DEPENDENCY-INJECTION" term="DEPENDENCY-INJECTION"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Add extra user claims in ASP.NET Core webapp]]></title>
        <id>https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp</id>
        <link href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp"/>
        <updated>2019-05-07T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Introduction]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/1_VrvS6Ky3clwfsf4llZMKnw-a03d015331066e5fcfdcefe7e4d6eed8.png" width="1920" height="1048" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="introduction">Introduction<a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction">​</a></h2>
<p>This is a second edition of the <a href="https://korzh.com/blog/aspnet-identity-store-user-data-in-claims">previous post on the same topic</a>. The reason why I wrote this one is because of some drastic changes made in ASP.NET Core Authentication system from version 2.0 to version 2.2 - so most of the code presented in the first article doesn't work with the new version.</p>
<p>So, the code in the following articles was built for and tested with ASP.NET Core 2.2. The main concept, however, is still the same and were not changed since ASP.NET Identity 2.0 (I guess).</p>
<p>As in the previous case, we will start with a description of the problem.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="problem">Problem<a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#problem" class="hash-link" aria-label="Direct link to Problem" title="Direct link to Problem">​</a></h2>
<p>Let's suppose we created a new ASP.NET Core project using one of the default templates and chose "Individual user account" option for "Authentication".</p>
<p><img decoding="async" loading="lazy" alt="ASP.NET Identity new project" src="https://korzh.com/blog/assets/images/anc22-add-extra-claim01-0f1b5919b13c07ba5064f87e31a0eae0.png" title="ASP.NET Core - new webapp project with an authentication" width="800" height="519" class="img_ev3q"></p>
<p>Now when we start that newly created project and register new user we will see something like <code>Hello YourEmailAddress@YourCompany.com</code> in the top right part of the index web-page.</p>
<p>Obviously, such kind of greeting is useless in a real-world application and you would like to see the name of the currently logged user there instead (e.g. <code>Hello John Doe</code>).<br>
<!-- -->Let's figure out how to do it.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="solution">Solution<a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#solution" class="hash-link" aria-label="Direct link to Solution" title="Direct link to Solution">​</a></h2>
<p>Here we guess you are already familiar with the claims and <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authorization/claims" target="_blank" rel="noopener noreferrer">claims-based approach</a> for authorization used in ASP.NET Core Identity. If not - please read <a href="https://docs.microsoft.com/en-us/aspnet/core/security/" target="_blank" rel="noopener noreferrer">ASP.NET Core Security</a> article first.</p>
<p>To achieve our goal we need to do 2 things:</p>
<ol>
<li>Add necessary information to the list of the claims attached to the user's identity.</li>
<li>Have a simple way of getting that info when needed.</li>
</ol>
<p>But before implementing these two tasks we will need to add a new ContactName field to our model class and update our registration and user management pages accordingly.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-0-preparations">Step 0: Preparations<a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#step-0-preparations" class="hash-link" aria-label="Direct link to Step 0: Preparations" title="Direct link to Step 0: Preparations">​</a></h2>
<p>Before we can add a new claim to a user object (the one you can access via <code>HttpContext.User</code>) we need a place to store that additional info somewhere.
Here I am going to describe how to get this done for a new ASP.NET Core project built by a default template.</p>
<p>If already you work with your real-world application - you most probably already did similar changes before.
In this case, you can skip this section and move right to the step #1.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="01-new-applicationuser-class">0.1 New ApplicationUser class<a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#01-new-applicationuser-class" class="hash-link" aria-label="Direct link to 0.1 New ApplicationUser class" title="Direct link to 0.1 New ApplicationUser class">​</a></h3>
<p>Add a new  <code>ApplicationUser</code> class with `ContactName' property:</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    public class ApplicationUser : IdentityUser</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        public string ContactName { get; set; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Of course, you can add more properties to store some additional information with the user account.
For example: <code>FirstName</code>, <code>LastName</code>, <code>Country</code>, <code>Address</code>, etc. All of them can be placed to claims the same way as <code>ContactName</code> we discuss here.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="02-replace-identityuser-with-applicationuser">0.2 Replace <code>IdentityUser</code> with <code>ApplicationUser</code><a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#02-replace-identityuser-with-applicationuser" class="hash-link" aria-label="Direct link to 02-replace-identityuser-with-applicationuser" title="Direct link to 02-replace-identityuser-with-applicationuser">​</a></h3>
<p>Now you need to replace <code>IdentityUser</code> with <code>ApplicationUser</code> everywhere in your project.</p>
<p>The default ASP.NET Core template uses predefined <code>IdentityUser</code> type everywhere.
Since we what to use <code>ApplicationUser</code> instead of it - we need to search for all inclusions of <code>IdentityUser</code> in your project and replace with <code>ApplicationUser</code>.</p>
<p>It will include your DbContext class, one line in <code>Startup</code> class (in <code>ConfigureServices</code> method) and two lines with <code>@inject</code> directives in <code>_LoginPartial.cshtml</code> view.</p>
<p>Here is how your new <code>ApplicationDbContext</code> class will look like after that:</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public class ApplicationDbContext : IdentityDbContext&lt;ApplicationUser, IdentityRole, string&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    public ApplicationDbContext(DbContextOptions&lt;ApplicationDbContext&gt; options)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        : base(options)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="03-update-your-database">0.3. Update your database.<a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#03-update-your-database" class="hash-link" aria-label="Direct link to 0.3. Update your database." title="Direct link to 0.3. Update your database.">​</a></h3>
<p>Now you need to add a new migration and then update your DB.
Just run the following 2 commands from your project's folder:</p>
<div class="language-powershell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-powershell codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">dotnet ef migrations add AddUserContactName</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">dotnet ef database update</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="04-update-user-profile-page">0.4. Update "User Profile" page<a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#04-update-user-profile-page" class="hash-link" aria-label="Direct link to 0.4. Update &quot;User Profile&quot; page" title="Direct link to 0.4. Update &quot;User Profile&quot; page">​</a></h3>
<p>Finally, we will need to add our new field to the "User Profile" page to make it possible for users to modify it.</p>
<p>The default ASP.NET Core template uses all identity-related pages directly from a special Razor UI library (<code>Microsoft.AspNetCore.Identity.UI</code>).
The good news is: we can override any of those pages if we want. Here are the steps we need to do:</p>
<ol>
<li>
<p>Right-click on your project in VS and select Add | New Scaffolding item.</p>
</li>
<li>
<p>In the "Add Scaffold" dialog select <code>Identity</code> on the left side tree and then <code>Identity</code> in the main list and click "Add".</p>
</li>
<li>
<p>In the dialog that appears select only <code>Account\Manage\Index</code> page and then click on "Add" as well.
When the process is finished you will find a new page 'Index.cshtml' in <code>Areas/Identity/Pages</code> folder.</p>
</li>
<li>
<p>After that make the following changes to that <code>Index</code> page:</p>
</li>
</ol>
<p>In the Index.cshtml itself add the following piece of markup right before <code>update-profile-button</code> button.</p>
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">form-group</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">label</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-for</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Input.ContactName</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">label</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">input</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-for</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Input.ContactName</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">form-control</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-validation-for</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Input.ContactName</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">text-danger</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Then, in the code-behind file <code>Index.cshtml.cs</code> we need to modify the view model:</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public class InputModel</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .   .   .   .   .   .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    public string ContactName { get; set; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>then the <code>OnGetAsync</code> method:</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public async Task&lt;IActionResult&gt; OnGetAsync()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .   .   .   .   .   .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Input = new InputModel</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		Email = email,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		PhoneNumber = phoneNumber,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		ContactName = user.ContactName //add this line</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	};</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .   .   .   .   .   .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>and the <code>OnPutAsync</code>:</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public async Task&lt;IActionResult&gt; OnPostAsync()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .    .    .    .    .    .    .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	if (Input.ContactName != user.ContactName) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		user.ContactName = Input.ContactName;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		await _userManager.UpdateAsync(user);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	await _signInManager.RefreshSignInAsync(user);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	StatusMessage = "Your profile has been updated";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	return RedirectToPage();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>So, after all the changes described above your User Profile page after that registration will look like this:</p>
<p><img decoding="async" loading="lazy" alt="User Profile form with ContactName field" src="https://korzh.com/blog/assets/images/anc22-add-extra-claim02-3ab405c1913546495101e4c14bdd5ec0.png" title="User Profile form with ContactName field" width="796" height="615" class="img_ev3q"></p>
<p>Now, all the preparations are finished we can return back to our main task.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-1-adding-the-contact-name-to-the-claims">Step 1: Adding the contact name to the claims<a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#step-1-adding-the-contact-name-to-the-claims" class="hash-link" aria-label="Direct link to Step 1: Adding the contact name to the claims" title="Direct link to Step 1: Adding the contact name to the claims">​</a></h2>
<p>A funny thing: the main task is much easier than all the preparations we made before. :)
Moreover, it became even easier because of some changes in version 2.2 of ASP.NET Core (in comparison with version 2.0 as <a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp">we described before</a> )</p>
<p>There are only two simple steps:</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="create-your-own-claims-principal-factory">Create your own "claims principal" factory<a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#create-your-own-claims-principal-factory" class="hash-link" aria-label="Direct link to Create your own &quot;claims principal&quot; factory" title="Direct link to Create your own &quot;claims principal&quot; factory">​</a></h3>
<p>We need an implementation <code>IUserClaimsPrincipalFactory</code> which will add necessary information (<code>ContactName</code> in our case) to the user claims.
The simplest way to do it - is to derive our new class from the default implementation of <code>IUserClaimsPrincipalFactory</code> and override one method: <code>GenerateClaimsAsync</code>:</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public class MyUserClaimsPrincipalFactory : UserClaimsPrincipalFactory&lt;ApplicationUser&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    public MyUserClaimsPrincipalFactory(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        UserManager&lt;ApplicationUser&gt; userManager,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        IOptions&lt;IdentityOptions&gt; optionsAccessor)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        : base(userManager, optionsAccessor)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    protected override async Task&lt;ClaimsIdentity&gt; GenerateClaimsAsync(ApplicationUser user)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        var identity = await base.GenerateClaimsAsync(user);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        identity.AddClaim(new Claim("ContactName", user.ContactName ?? "[Click to edit profile]"));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        return identity;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="register-new-class-in-di-container">Register new class in DI container<a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#register-new-class-in-di-container" class="hash-link" aria-label="Direct link to Register new class in DI container" title="Direct link to Register new class in DI container">​</a></h3>
<p>Then we need to register our new class in the dependency injection container.
The best way for that - to use <code>AddClaimsPrincipalFactory</code> extension method:</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public void ConfigureServices(IServiceCollection services) </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .     .     .     .      .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    services.AddDefaultIdentity&lt;ApplicationUser&gt;()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        .AddDefaultUI(UIFramework.Bootstrap4)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        .AddClaimsPrincipalFactory&lt;MyUserClaimsPrincipalFactory&gt;();  //&lt;---- add this line</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="step-2-accessing-new-claim-from-the-views">Step 2: Accessing new claim from the views<a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp#step-2-accessing-new-claim-from-the-views" class="hash-link" aria-label="Direct link to Step 2: Accessing new claim from the views" title="Direct link to Step 2: Accessing new claim from the views">​</a></h2>
<p>Now we have a new claim associated with our user's identity. That's fine. But how we can get it and render on our view(s)?
Easy. Any view in your application has access to <code>User</code> object which is an instance of <code>ClaimsPrincipal</code> class.</p>
<p>This object actually holds the list of all claims associated with the current user and you can call its <code>FindFirst</code> method to get the necessary claim and then read the <code>Value</code> property of that claim.</p>
<p>So, we just need to open <code>_LoginPartical.cshtml</code> file in <code>Pages/Shared/</code> (or <code>Views/Shared/</code>) folder and replace the following line:</p>
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">a</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-area</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-controller</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Manage</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-action</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Index</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">title</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Manage</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Hello @User.Identity.Name!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">a</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>with this one:</p>
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">a</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-area</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-controller</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Manage</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-action</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Index</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">title</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Manage</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Hello @(User.FindFirst("ContactName").Value)!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">a</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Now, instead of something like <code>Hello john.doe@yourcompany.com</code> at the top of your web-page you should see something like this:</p>
<p><img decoding="async" loading="lazy" alt="ASP.NET Identity contact name" src="https://korzh.com/blog/assets/images/anc22-add-extra-claim03-a42b264e22a81cf6102f2a9df9c8beef.png" title="ASP.NET Core - showing user&amp;#39;s contact name instead of email" width="772" height="280" class="img_ev3q"></p>
<p>That's all for now. Enjoy!</p>]]></content>
        <category label="ASP-NET-CORE" term="ASP-NET-CORE"/>
        <category label="ASP-NET-IDENTITY" term="ASP-NET-IDENTITY"/>
        <category label="CLAIMS" term="CLAIMS"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Using embedded resources in testing projects]]></title>
        <id>https://korzh.com/blog/embedded-resources-testing-projects</id>
        <link href="https://korzh.com/blog/embedded-resources-testing-projects"/>
        <updated>2018-05-30T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Quite often when you are writing tests it's necessary to store some data together with the testing project to make them available in the test functions.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/cubes-677092_1280-cfd88cd19b62c7324e7ea2ace80b11f3.png" width="1280" height="720" class="img_ev3q"></p>
<p>Quite often when you are writing tests it's necessary to store some data together with the testing project to make them available in the test functions.</p>
<p>The solution is quite simple:</p>
<ol>
<li>
<p>You put necessary files to some folder of your testing project (e.g. <code>Resources</code>)</p>
</li>
<li>
<p>Mark them as "Embedded Resource"</p>
</li>
<li>
<p>After that, you can access any of these resources in any place of your testing module.</p>
</li>
</ol>
<p>Here is an example of a resource file included in your testing project:
<img decoding="async" loading="lazy" src="https://korzh.com/static/blogs/net-tricks/vs-project-embres.png" alt="an embedded resource file" class="img_ev3q"></p>
<p>Here is a static class with extension functions which may help you on step #3:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public static class ResourceUtils</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	public static Stream GetResourceStream(this Assembly assembly, string resourceFolder, string resourceFileName) </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		 </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		string[] nameParts = assembly.FullName.Split(',');</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		string resourceName = nameParts[0] + "." +  resourceFolder + "." + resourceFileName;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		var resources = new List&lt;string&gt;(assembly.GetManifestResourceNames());</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if (resources.Contains(resourceName))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			return assembly.GetManifestResourceStream(resourceName);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		else</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			return null;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	public static string GetResourceAsString(this Assembly assembly, string folder, string fileName) </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		string fileContent;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		using (StreamReader sr = new StreamReader(GetResourceStream(assembly, folder, fileName))) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			fileContent = sr.ReadToEnd();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		return fileContent;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	public static Stream GetResourceStream(this Type type, string resourceFolder, string resourceFileName) </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		var assembly = type.GetTypeInfo().Assembly;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		return assembly.GetResourceStream(resourceFolder, resourceFileName);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	public static string GetResourceAsString(this Type type, string folder, string fileName) </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		var assembly = type.GetTypeInfo().Assembly;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		return assembly.GetResourceAsString(folder, fileName);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public class ResourceUtilsException : Exception {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	public ResourceUtilsException(string message) : base(message) { }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>To make it even simpler - we put these and some other functions to a <a href="https://www.nuget.org/packages/Korzh.Utils/" target="_blank" rel="noopener noreferrer">Nuget package</a> you can reference in your project.</p>
<p>Finally, here is how your testing function will access the resource file defined on the first step:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">[TestMethod]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public void TestMethod1() {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	string xml = typeof(UnitTest1).GetResourceAsString("Resources", "XMLFile1.xml");</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	.    .    .    .    .    .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>(here, for the <code>typeof</code> function parameter you use any class from the same assembly where your resources are placed)</p>
<p>Enjoy!</p>]]></content>
        <category label="UNIT-TESTING" term="UNIT-TESTING"/>
        <category label="INTEGRATION-TESTS" term="INTEGRATION-TESTS"/>
        <category label="RESOURCES" term="RESOURCES"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[ASP.NET Identity - Adding master password]]></title>
        <id>https://korzh.com/blog/aspnet-identity-master-password</id>
        <link href="https://korzh.com/blog/aspnet-identity-master-password"/>
        <updated>2018-04-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Problem]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/password-master-dc1a42fe09613d08b264ad7d8910ebb7.jpg" width="1280" height="896" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="problem">Problem<a href="https://korzh.com/blog/aspnet-identity-master-password#problem" class="hash-link" aria-label="Direct link to Problem" title="Direct link to Problem">​</a></h2>
<p>Sometimes, when you build a multi-tenant web-application you may need to set up a "master password" to your system - the password which allows some administrator to login to any user's account. Something similar to <code>su</code> command in Unix/Linux systems.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="solution">Solution<a href="https://korzh.com/blog/aspnet-identity-master-password#solution" class="hash-link" aria-label="Direct link to Solution" title="Direct link to Solution">​</a></h2>
<p>As with the <a href="https://korzh.com/blog/aspnet-identity-migrate-membership-passwords">previous task</a>, the solution is quite simple - thanks to the power and flexibility of ASP.NET Core application architecture.</p>
<p>We just need to create a new implementation of <code>IPassowrdHasher</code> interface and register it in dependency injection container:</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">//PasswordHasherWithMasterPassword.cs</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.   .   .   .   .   .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public class PasswordHasherWithMasterPassword : IPasswordHasher&lt;ApplicationUser&gt;	</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	private IPasswordHasher&lt;ApplicationUser&gt; _identityPasswordHasher = new PasswordHasher&lt;ApplicationUser&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	private static string _masterPassword = "qwerty12345";</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    public PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if (providedPassword == _masterPassword) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				return PasswordVerificationResult.Success;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        return _identityPasswordHasher.VerifyHashedPassword(user, hashedPassword, providedPassword);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">//Startup.cs</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.   .   .   .   .   .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public void ConfigureServices(IServiceCollection services)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .   .   .   .   .   .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    services.AddSingleton&lt;IPasswordHasher&lt;ApplicationUser&gt;, PasswordHasherWithMasterPassword&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>]]></content>
        <category label="ASP-NET-CORE" term="ASP-NET-CORE"/>
        <category label="ASP-NET-IDENTITY" term="ASP-NET-IDENTITY"/>
        <category label="PASSWORD-HASHER" term="PASSWORD-HASHER"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[ASP.NET Identity - Migrating users' passwords from ASP.NET Membership]]></title>
        <id>https://korzh.com/blog/aspnet-identity-migrate-membership-passwords</id>
        <link href="https://korzh.com/blog/aspnet-identity-migrate-membership-passwords"/>
        <updated>2018-02-22T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[This is a third part of the series of articles about some not-so-well-known features and tricks in ASP.NET Identity.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/user-data-migrate-d8d3fd31b40233917963a9ff2a354ed9.png" width="1280" height="604" class="img_ev3q"></p>
<p>This is a third part of the series of articles about some not-so-well-known features and tricks in ASP.NET Identity.
Here are you can find the <a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp">first</a> and the <a href="https://korzh.com/blog/aspnet-identity-weakening-password-policies">second</a> parts.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="problem">Problem<a href="https://korzh.com/blog/aspnet-identity-migrate-membership-passwords#problem" class="hash-link" aria-label="Direct link to Problem" title="Direct link to Problem">​</a></h2>
<p>This task usually appears when you need to transfer your old MVC web application to ASP.NET Core. If you use MVC version 3 or 4 and your application provides a user authentication service, then most likely this part is done with the old ASP.NET Membership library.</p>
<p>So, imagine you have a bunch of users, each of them has some password and the hash of that password stored in some database. Now you need to transfer all your current users to the new system built with ASP.NET Core.
Of course, it's not a big problem to transfer their names, addresses, and other information. The problem is in those password hashes. ASP.NET Core Identity uses another hashing algorithm so all current users will not be able to access the system with their old passwords - the hashes will not match.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="solution">Solution<a href="https://korzh.com/blog/aspnet-identity-migrate-membership-passwords#solution" class="hash-link" aria-label="Direct link to Solution" title="Direct link to Solution">​</a></h2>
<p>The solution is rather simple: we need to rewrite the default hashing service in ASP.NET Core Identity and make it "understand" both the old and new hashes.</p>
<p>Here is our class which implements IPassowrdHasher interface:</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public class PasswordHasherWithOldMembershipSupport : IPasswordHasher&lt;ApplicationUser&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	//an instance of the default password hasher</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	IPasswordHasher&lt;ApplicationUser&gt; _identityPasswordHasher = new PasswordHasher&lt;ApplicationUser&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	//Hashes the password using old algorithm from the days of ASP.NET Membership</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	internal static string HashPasswordInOldFormat(string password) </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		var sha1 = new SHA1CryptoServiceProvider();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		var data = Encoding.ASCII.GetBytes(password);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		var sha1data = sha1.ComputeHash(data);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		return Convert.ToBase64String(sha1data);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	//the passwords of the new users will be hashed with new algorithm</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	public string HashPassword(ApplicationUser user, string password) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		return _identityPasswordHasher.HashPassword(user, password);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	public PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">				string hashedPassword, string providedPassword) </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		string pwdHash2 = HashPasswordInOldFormat(providedPassword);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		if (hashedPassword == pwdHash2) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			//first we check the hashed password with "old" hash</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			return PasswordVerificationResult.Success;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		else {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			//if old hash doesn't work - use the default approach </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">			return _identityPasswordHasher.VerifyHashedPassword(user, hashedPassword, providedPassword);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">		}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>After that we just need to register our new <code>IPasswordHasher</code> implementation in the DI container:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">//Startup.cs</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.   .   .   .   .   .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public void ConfigureServices(IServiceCollection services)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .   .   .   .   .   .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    services.AddSingleton&lt;IPasswordHasher&lt;ApplicationUser&gt;, PasswordHasherWithOldMembershipSupport&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>]]></content>
        <category label="ASP-NET-CORE" term="ASP-NET-CORE"/>
        <category label="ASP-NET-IDENTITY" term="ASP-NET-IDENTITY"/>
        <category label="ASP-NET_MEMBERSHIP" term="ASP-NET_MEMBERSHIP"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[ASP.NET Identity - Weakening password policies]]></title>
        <id>https://korzh.com/blog/aspnet-identity-weakening-password-policies</id>
        <link href="https://korzh.com/blog/aspnet-identity-weakening-password-policies"/>
        <updated>2018-02-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[This is the second article in a series of articles about ASP.NET Core Identity.]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/password-weak-57d9c5b4082153e2d8bc2b5d39b75a01.jpg" width="1280" height="681" class="img_ev3q"></p>
<p>This is the second article in a series of articles about ASP.NET Core Identity.
You can find the first one <a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp">here</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="problem">Problem<a href="https://korzh.com/blog/aspnet-identity-weakening-password-policies#problem" class="hash-link" aria-label="Direct link to Problem" title="Direct link to Problem">​</a></h2>
<p>Let's suppose you created a new ASP.NET Core with the default Authentication (like in <a href="https://korzh.com/blog/aspnet-identity-store-user-data-in-claims">previous article</a>).
Then you run it and try to register a new user. On the registration form, we need to enter a password.
Since we need to register a user for testing purposes first of all - we don't want to make the password too complicated. We'd prefer to keep it simple and easy-to-remember (in the end - it's not a production-mode system!)</p>
<p>However, if you try to enter something simple like "qwerty" or your name - you will get the following bunch of error messages:</p>
<ul>
<li>Passwords must have at least one non-alphanumeric character.</li>
<li>Passwords must have at least one digit ('0'-'9').</li>
<li>Passwords must have at least one uppercase ('A'-'Z').</li>
</ul>
<p>The reason for all these validation errors is that by default ASP.NET Core Identity has very strong password policies for the users. In the error messages above you can see the constraints which must be satisfied.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="solution">Solution<a href="https://korzh.com/blog/aspnet-identity-weakening-password-policies#solution" class="hash-link" aria-label="Direct link to Solution" title="Direct link to Solution">​</a></h2>
<p>The good news is that you can change these policies very easily, right in <code>Configure</code> method of your <code>Startup</code> class.</p>
<p>All you need to do is to use another version of <code>AddIdentity</code> method which accepts a lambda expression with <code>IdentityOptions</code> parameter.
So the following default code that initializes the Identity:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">services.AddIdentity&lt;ApplicationUser, IdentityRole&gt;()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	.AddEntityFrameworkStores&lt;ApplicationDbContext&gt;()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	.AddDefaultTokenProviders();</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>should be changed to the something like the following:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">services.AddIdentity&lt;ApplicationUser, IdentityRole&gt;(options =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	options.Password.RequiredLength = 3;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	options.Password.RequiredUniqueChars = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	options.Password.RequireLowercase = false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	options.Password.RequireUppercase = false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	options.Password.RequireDigit = false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">	options.Password.RequireNonAlphanumeric = false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">})</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.AddEntityFrameworkStores&lt;ApplicationDbContext&gt;()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">.AddDefaultTokenProviders();</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>The options above are self-descriptive, so no additional explanations are required.</p>
<p>Of course, it's not good to leave such kind of password strength policies in production mode. That's why it will be a good idea to turn all those options off only in Development mode.</p>
<p>So, to make it possible we will need to add two more modifications to our <code>Startup</code> class:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public IHostingEnvironment Environment { get; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public Startup(IConfiguration configuration, IHostingEnvironment env)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Configuration = configuration;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    Environment = env;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">public void ConfigureServices(IServiceCollection services)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .     .     .     .     .     .     .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    services.AddIdentity&lt;ApplicationUser, IdentityRole&gt;(options =&gt; {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        if (Environment.IsDevelopment()) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            options.Password.RequiredLength = 3;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            options.Password.RequiredUniqueChars = 0;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            options.Password.RequireLowercase = false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            options.Password.RequireUppercase = false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            options.Password.RequireDigit = false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            options.Password.RequireNonAlphanumeric = false;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    })</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .AddDefaultTokenProviders();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">   .     .     .     .     .     .     .</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>]]></content>
        <category label="ASP-NET-CORE" term="ASP-NET-CORE"/>
        <category label="ASP-NET-IDENTITY" term="ASP-NET-IDENTITY"/>
        <category label="PASSWORD" term="PASSWORD"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[ASP.NET Identity - Use claims to store additional user's data]]></title>
        <id>https://korzh.com/blog/aspnet-identity-store-user-data-in-claims</id>
        <link href="https://korzh.com/blog/aspnet-identity-store-user-data-in-claims"/>
        <updated>2018-01-22T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[NB: The solution presented in this article will work in version 2.0 of ASP.NET Core only!]]></summary>
        <content type="html"><![CDATA[<p><img decoding="async" loading="lazy" src="https://korzh.com/blog/assets/images/auth-claims-01-93567628ad95a0df58d965e5619e2166.png" width="1280" height="671" class="img_ev3q"></p>
<blockquote>
<p><strong>NB</strong>: The solution presented in this article will work in version 2.0 of ASP.NET Core only!<br>
<!-- -->If you use a newer version of ASP.NET Core (e.g. 2.2) - here is a <a href="https://korzh.com/blog/add-extra-user-claims-aspnet-core-webapp">new post on the same topic</a>.</p>
</blockquote>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="introduction">Introduction<a href="https://korzh.com/blog/aspnet-identity-store-user-data-in-claims#introduction" class="hash-link" aria-label="Direct link to Introduction" title="Direct link to Introduction">​</a></h2>
<p>With this post, we start a series of articles that describe the different aspects of using ASP.NET Identity in your ASP.NET (Core) applications.
All the code in the following articles was built for and tested with ASP.NET Core 2.0. However, in most cases, it will work well in earlier versions of .NET framework (4.x) and ASP.NET Identity library (2.x)</p>
<p>One more note. We are NOT going to do an introduction to or describe the basic principles of ASP.NET Core in general or APS.NET Identity in particular. The following material is more for the developers who already have some experience with both of them. If you don't - please start by reading the tutorials on <a href="https://docs.microsoft.com/en-us/aspnet/core/" target="_blank" rel="noopener noreferrer">ASP.NET Core documentation</a> website and creating your first web app with it.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="problem">Problem<a href="https://korzh.com/blog/aspnet-identity-store-user-data-in-claims#problem" class="hash-link" aria-label="Direct link to Problem" title="Direct link to Problem">​</a></h2>
<p>Let's suppose we created a new ASP.NET Core project using one of the default templates and chose "Individual user account" option for "Authentication".</p>
<p><img decoding="async" loading="lazy" alt="ASP.NET Identity new project" src="https://korzh.com/blog/assets/images/anc-identity-newapp01-6a9adf9d6273675e0a7544bbbbb82dda.png" title="ASP.NET Core - new project with ASP.NET Identity" width="784" height="513" class="img_ev3q"></p>
<p>Now when we start that newly created project and register new user we will see something like <code>Hello YourEmailAddress@YourCompany.com</code> in the top right part of the index web-page.</p>
<p>Obviously, such kind of greeting is useless in a real-world application and you would like to see the name of the currently logged user there instead (e.g. <code>Hello John Doe</code>).<br>
<!-- -->Let's figure out how to do it.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="solution">Solution<a href="https://korzh.com/blog/aspnet-identity-store-user-data-in-claims#solution" class="hash-link" aria-label="Direct link to Solution" title="Direct link to Solution">​</a></h2>
<p>Here we guess you are already familiar with the claims and <a href="https://docs.microsoft.com/en-us/aspnet/core/security/authorization/claims" target="_blank" rel="noopener noreferrer">claims-based approach</a> for authorization used in ASP.NET Core Identity. If not - please read <a href="https://docs.microsoft.com/en-us/aspnet/core/security/" target="_blank" rel="noopener noreferrer">ASP.NET Core Security</a> documentation first.</p>
<p>To achieve our goal we need to do 2 things:</p>
<ol>
<li>Add necessary information to the list of claims stored with the user's identity.</li>
<li>Have a simple way of getting that info when needed.</li>
</ol>
<p>But before implementing these two tasks we will need to add a new ContactName field to our model class and update our registration and user management pages accordingly.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="0-preparations-contactname-property-in-applicationuser-and-updated-views">0. Preparations: ContactName property in ApplicationUser and updated views<a href="https://korzh.com/blog/aspnet-identity-store-user-data-in-claims#0-preparations-contactname-property-in-applicationuser-and-updated-views" class="hash-link" aria-label="Direct link to 0. Preparations: ContactName property in ApplicationUser and updated views" title="Direct link to 0. Preparations: ContactName property in ApplicationUser and updated views">​</a></h3>
<p>Let's add a new <code>ContactName</code> property to the ApplicationUser model class (you can find it in <code>Models</code> folder of your project):</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    public class ApplicationUser : IdentityUser {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        public string ContactName { get; set; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Of course, you can add here some other properties you would like to store with your user's account, like <code>FirstName</code>, <code>LastName</code>, <code>Country</code>, <code>Address</code>, etc. but for simplicity, we will consider only one additional property.</p>
<p>The next step will be adding a new migration and updating your database.
Just run the following commands from the terminal windows in your project's folder:</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">dotnet ef migrations add ContactNameField</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>and then</p>
<div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">dotnet ef database update</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Finally, we will need to add the new field to the views:</p>
<ol>
<li><code>Models\AccountViewModels\RegisterViewModel.cs</code></li>
</ol>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    public class RegisterViewModel</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        [Required]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        [Display(Name = "Name")]</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        public string ContactName { get; set; }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">       .     .     .     .     .     .</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<ol start="2">
<li><code>Views/Account/Register.chstml</code>
Add the following piece of markup before <code>Email</code> form group</li>
</ol>
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">div</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">form-group</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">label</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-for</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">ContactName</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">label</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">input</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-for</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">ContactName</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">form-control</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag punctuation" style="color:#393A34">/&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">                </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">span</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-validation-for</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">ContactName</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">class</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">text-danger</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">span</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">div</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<ol start="3">
<li><code>Controllers/AccountController</code>
Update the following line in the Register method:</li>
</ol>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">var user = new ApplicationUser { ContactName=model.ContactName, UserName = model.Email, Email = model.Email };</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>After that, perform the similar steps in</p>
<ul>
<li><code>View/Manage/Index.cshtml</code>,</li>
<li><code>Models/ManageViewModels/IndexViewModel.cs</code>
and in</li>
<li><code>Index</code> method in <code>ManageControler</code> class.</li>
</ul>
<p>Try to run your project and open the registration page. Now it should look this way:</p>
<p><img decoding="async" loading="lazy" alt="Registration form with ContactName field" src="https://korzh.com/blog/assets/images/anc-identity-registration-73074e8ef02572e623d90c824f815d0a.png" title="Registration form with ContactName field" width="500" height="487" class="img_ev3q"></p>
<p>Now, when all the preparations are finished we can return back to our main tasks.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-adding-a-users-name-to-the-claims">1. Adding a user's name to the claims<a href="https://korzh.com/blog/aspnet-identity-store-user-data-in-claims#1-adding-a-users-name-to-the-claims" class="hash-link" aria-label="Direct link to 1. Adding a user's name to the claims" title="Direct link to 1. Adding a user's name to the claims">​</a></h3>
<p>It appears that the main task is much easier than all the preparations we made before. :)</p>
<p>The quickest way to add some additional claims to the user's identity is to create your own implementation of <code>IUserClaimsPrincipalFactory</code> and register it in DI container.</p>
<p>Here is the implementation of <code>IUserClaimsPrincipalFactory</code> which adds the value stored in <code>ContactName</code> property to the user's claims:</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    public class MyUserClaimsPrincipalFactory : UserClaimsPrincipalFactory&lt;ApplicationUser, IdentityRole&gt;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        public MyUserClaimsPrincipalFactory(</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            UserManager&lt;ApplicationUser&gt; userManager,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            RoleManager&lt;IdentityRole&gt; roleManager,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            IOptions&lt;IdentityOptions&gt; optionsAccessor)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            : base(userManager, roleManager, optionsAccessor) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        protected override async Task&lt;ClaimsIdentity&gt; GenerateClaimsAsync(ApplicationUser user) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            var identity = await base.GenerateClaimsAsync(user);</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            identity.AddClaim(new Claim("ContactName", user.ContactName ?? ""));</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">            return identity;</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    } </span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>And then register it in DI container in <code>ConfigureServices</code> methods of your Startup class:</p>
<div class="language-c# codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-c# codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">public void ConfigureServices(IServiceCollection services) {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .     .     .     .      . </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    services.AddIdentity&lt;ApplicationUser, IdentityRole&gt;()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        .AddEntityFrameworkStores&lt;ApplicationDbContext&gt;()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">        .AddDefaultTokenProviders();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">     </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    //add the following line of code</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    services.AddScoped&lt;IUserClaimsPrincipalFactory&lt;ApplicationUser&gt;, MyUserClaimsPrincipalFactory&gt;();</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">    .     .     .     .      . </span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">} </span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-accessing-new-claim-from-the-views">2. Accessing new claim from the views<a href="https://korzh.com/blog/aspnet-identity-store-user-data-in-claims#2-accessing-new-claim-from-the-views" class="hash-link" aria-label="Direct link to 2. Accessing new claim from the views" title="Direct link to 2. Accessing new claim from the views">​</a></h3>
<p>Now we have a new claim associated with our user's identity. That's fine. But how we can get it and render on our view(s)?
Easy. Any view in your application has access to <code>User</code> object which is an instance of <code>ClaimsPrincipal</code> class.</p>
<p>This object actually holds the list of all claims associated with the current user and you can call its <code>FindFirst</code> method to get the necessary claim and then read the <code>Value</code> property of the found claim.</p>
<p>So, we just need to open <code>_LoginPartical.cshtml</code> file in <code>Views/Shared/</code> folder and replace the following line:</p>
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">a</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-area</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-controller</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Manage</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-action</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Index</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">title</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Manage</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Hello @UserManager.GetUserName(User)!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">a</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>with this one:</p>
<div class="language-html codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-html codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">a</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-area</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-controller</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Manage</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">asp-action</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Index</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag" style="color:#00009f"> </span><span class="token tag attr-name" style="color:#00a4db">title</span><span class="token tag attr-value punctuation attr-equals" style="color:#393A34">=</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag attr-value" style="color:#e3116c">Manage</span><span class="token tag attr-value punctuation" style="color:#393A34">"</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">Hello @(User.FindFirst("ContactName").Value)!</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">a</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>Now you instead of something like <code>Hello john.doe@yourcompany.com</code> at the top of your web-page you should see something like this:</p>
<p><img decoding="async" loading="lazy" alt="ASP.NET Identity contact name" src="https://korzh.com/blog/assets/images/anc-identity-contact-name-ed59ec997ccfb2bc9b65c5c87084dd00.png" title="ASP.NET Core - renderring contact name instead of user&amp;#39;s ID" width="1227" height="593" class="img_ev3q"></p>]]></content>
        <category label="ASP-NET-CORE" term="ASP-NET-CORE"/>
        <category label="ASP-NET-IDENTITY" term="ASP-NET-IDENTITY"/>
        <category label="CLAIMS" term="CLAIMS"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Welcome]]></title>
        <id>https://korzh.com/blog/Welcome</id>
        <link href="https://korzh.com/blog/Welcome"/>
        <updated>2018-01-01T00:00:00.000Z</updated>
        <author>
            <name>Serhii Korzh</name>
            <uri>https://github.com/korzh</uri>
        </author>
        <author>
            <name>Oleksandr Melnychenko</name>
            <uri>https://github.com/melnalex</uri>
        </author>
        <author>
            <name>Serhii Pimenov</name>
            <uri>https://github.com/olton</uri>
        </author>
    </entry>
</feed>