<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Jordan</title>
    <description>The latest articles on DEV Community by Michael Jordan (@michael_jordan_87eaf96f24).</description>
    <link>https://dev.to/michael_jordan_87eaf96f24</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2760772%2Fc5bbe993-f5f7-480d-b9d7-9b6c4976965b.jpg</url>
      <title>DEV Community: Michael Jordan</title>
      <link>https://dev.to/michael_jordan_87eaf96f24</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michael_jordan_87eaf96f24"/>
    <language>en</language>
    <item>
      <title>Render a PDF to an image in .NET — with a pure-C# PDF engine</title>
      <dc:creator>Michael Jordan</dc:creator>
      <pubDate>Wed, 24 Jun 2026 19:51:33 +0000</pubDate>
      <link>https://dev.to/michael_jordan_87eaf96f24/render-a-pdf-to-an-image-in-net-with-a-pure-c-pdf-engine-a7o</link>
      <guid>https://dev.to/michael_jordan_87eaf96f24/render-a-pdf-to-an-image-in-net-with-a-pure-c-pdf-engine-a7o</guid>
      <description>&lt;p&gt;Most PDF libraries in .NET are wrappers. PDFium, MuPDF, Ghostscript — solid engines, but they ship native binaries you P/Invoke into. That buys you per-RID packages, Native AOT friction, the "works on my box, throws &lt;code&gt;DllNotFoundException&lt;/code&gt; in the Alpine container" dance, and a deployment story that's never quite boring.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nuget.org/packages/Lxman.PdfLibrary" rel="noopener noreferrer"&gt;PdfLibrary&lt;/a&gt; takes the other road: the PDF engine and every image codec are pure managed C#. No vendored native PDF library, no native image decoders. I'll be precise about the one native piece (SkiaSharp, for rasterization) further down — because a "no native dependencies" claim that quietly ignores SkiaSharp would be a lie, and you'd find out the first time you deployed.&lt;/p&gt;

&lt;p&gt;Here's the part you came for — rendering the first page of a PDF to a PNG:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;PdfLibrary.Structure&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;PdfLibrary.Rendering.SkiaSharp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;// 0-based page index&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDpi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;150&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"page1.png"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No engine init, no global handle to dispose, no &lt;code&gt;SetDllDirectory&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Lxman.PdfLibrary
dotnet add package Lxman.PdfLibrary.Rendering.SkiaSharp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core package parses, creates, edits, and optimizes PDFs. The &lt;code&gt;.Rendering.SkiaSharp&lt;/code&gt; package is the rasterization backend — add it only when you actually need pixels.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bit more control
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// High-DPI render with a custom background, straight to an SKImage&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithDpi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithBackgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SKColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;250&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;240&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;// antique white&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Or render just a region of the page&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;crop&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithScale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WithCropBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                   &lt;span class="c1"&gt;// x, y, width, height&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToImage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.WithScale(1.0)&lt;/code&gt; is 72 DPI (1 PDF point = 1 pixel); &lt;code&gt;.WithDpi(n)&lt;/code&gt; is the same knob expressed the way you usually think about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest "no native dependencies" section
&lt;/h2&gt;

&lt;p&gt;This matters, so here's the exact breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Lxman.PdfLibrary&lt;/code&gt; (core)&lt;/strong&gt; — parsing, content streams, fonts, and &lt;strong&gt;all image decoding&lt;/strong&gt; are 100% managed C#. That includes the codecs people usually reach for a native lib to handle: baseline &lt;strong&gt;and&lt;/strong&gt; progressive JPEG, JPEG 2000, JBIG2, CCITT Group 3/4 fax, LZW, and Flate. Zero native dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Lxman.PdfLibrary.Rendering.SkiaSharp&lt;/code&gt; (rasterizer)&lt;/strong&gt; — uses &lt;a href="https://github.com/mono/SkiaSharp" rel="noopener noreferrer"&gt;SkiaSharp&lt;/a&gt; as the 2D canvas to turn the parsed page into pixels. SkiaSharp carries a native component.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the rule of thumb:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parsing, text extraction, editing, optimizing → &lt;strong&gt;fully managed, ship no native code.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Rasterizing a page to an image → add SkiaSharp.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SkiaSharp is a well-behaved, broadly-supported cross-platform dependency, so this is a very different proposition from bundling and P/Invoking a PDF engine yourself — but it's not nothing, and you should know exactly where the line is.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's not just a renderer
&lt;/h2&gt;

&lt;p&gt;Rendering is the flashy demo, but the managed core is the actual point. A few things that need &lt;strong&gt;only&lt;/strong&gt; the core package (no SkiaSharp):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extract text:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ExtractText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Edit an existing document:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;edit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Edit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RemoveAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;     &lt;span class="c1"&gt;// delete the 3rd page&lt;/span&gt;
&lt;span class="n"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// rotate the 1st page 90°&lt;/span&gt;
&lt;span class="n"&gt;edit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"edited.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Optimize / shrink:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PdfDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"optimized.pdf"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;PdfOptimizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Optimize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// lossless by default&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Threading
&lt;/h2&gt;

&lt;p&gt;PdfLibrary is built for the &lt;strong&gt;one-document-per-request&lt;/strong&gt; model — the standard pattern for ASP.NET Core. Load a &lt;code&gt;PdfDocument&lt;/code&gt; per request, render it on its own target, dispose both. Under that model it's thread-safe, and the process-wide caches (glyph paths, font resolution, ICC profiles) are synchronized. The one thing you must not do is share a single &lt;code&gt;PdfDocument&lt;/code&gt; (or a &lt;code&gt;SkiaSharpRenderTarget&lt;/code&gt;) across threads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;NuGet: &lt;a href="https://www.nuget.org/packages/Lxman.PdfLibrary" rel="noopener noreferrer"&gt;Lxman.PdfLibrary&lt;/a&gt; · &lt;a href="https://www.nuget.org/packages/Lxman.PdfLibrary.Rendering.SkiaSharp" rel="noopener noreferrer"&gt;Lxman.PdfLibrary.Rendering.SkiaSharp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source (MIT): &lt;a href="https://github.com/lxman/PdfLibrary" rel="noopener noreferrer"&gt;github.com/lxman/PdfLibrary&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Targets .NET 8, 9, and 10.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it and something doesn't render the way you expect, open an issue with the PDF attached — edge cases in the wild are how this kind of library gets better.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>pdf</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>MongoDB-backed ASP.NET Core Identity, without the EF Core detour</title>
      <dc:creator>Michael Jordan</dc:creator>
      <pubDate>Wed, 24 Jun 2026 19:42:41 +0000</pubDate>
      <link>https://dev.to/michael_jordan_87eaf96f24/mongodb-backed-aspnet-core-identity-without-the-ef-core-detour-848</link>
      <guid>https://dev.to/michael_jordan_87eaf96f24/mongodb-backed-aspnet-core-identity-without-the-ef-core-detour-848</guid>
      <description>&lt;p&gt;If you're already running MongoDB and you reach for ASP.NET Core Identity, the&lt;br&gt;
official story points you at Entity Framework Core. That's a fine answer if you&lt;br&gt;
have a relational database. If you don't, you end up bolting a second data&lt;br&gt;
access stack onto an app that has exactly one store. You don't need it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;AspNetCoreIdentity.MongoDriver&lt;/code&gt; is a store provider that talks to Mongo through&lt;br&gt;
the official &lt;code&gt;MongoDB.Driver&lt;/code&gt; directly — no EF Core, no SQL, no second&lt;br&gt;
persistence model. It implements the Identity store interfaces, ships a&lt;br&gt;
&lt;code&gt;UserStore&lt;/code&gt; and &lt;code&gt;RoleStore&lt;/code&gt;, and wires up &lt;code&gt;UserManager&lt;/code&gt;/&lt;code&gt;RoleManager&lt;/code&gt; the way&lt;br&gt;
you already expect.&lt;/p&gt;
&lt;h2&gt;
  
  
  The 15-line version
&lt;/h2&gt;

&lt;p&gt;Register the provider in &lt;code&gt;Program.cs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddIdentityMongoDbProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MongoUser&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;MongoRole&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;identity&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequireUniqueEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;mongo&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ConnectionString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Configuration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetConnectionString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"MongoDb"&lt;/span&gt;&lt;span class="p"&gt;)!;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A connection string like &lt;code&gt;mongodb://localhost:27017/Identity&lt;/code&gt; creates an&lt;br&gt;
&lt;code&gt;Identity&lt;/code&gt; database and stores the collections there. That's the whole setup.&lt;br&gt;
Now inject the managers wherever you need them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AccountController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserManager&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;MongoUser&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;userManager&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They're registered as scoped services — let the container manage their&lt;br&gt;
lifetime. Don't call &lt;code&gt;BuildServiceProvider()&lt;/code&gt; yourself, and don't cache the&lt;br&gt;
managers in long-lived objects.&lt;/p&gt;
&lt;h2&gt;
  
  
  Your key type is yours
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;MongoUser&lt;/code&gt; and &lt;code&gt;MongoRole&lt;/code&gt; classes are generic, so the primary key isn't&lt;br&gt;
forced to be a &lt;code&gt;Guid&lt;/code&gt;. Want &lt;code&gt;string&lt;/code&gt; keys? Use &lt;code&gt;MongoUser&amp;lt;string&amp;gt;&lt;/code&gt;. If you use&lt;br&gt;
&lt;code&gt;string&lt;/code&gt; keys and don't assign an &lt;code&gt;Id&lt;/code&gt; on create, the store generates an&lt;br&gt;
ObjectId-style string for you.&lt;/p&gt;

&lt;p&gt;If you do go with &lt;code&gt;Guid&lt;/code&gt;, register the serializer once before the code above so&lt;br&gt;
Mongo stores them in the standard representation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;BsonSerializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;GuidSerializer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GuidRepresentation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Standard&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Migrations that survive a rolling deploy
&lt;/h2&gt;

&lt;p&gt;Here's the part that usually bites people who hand-roll a Mongo store. On the&lt;br&gt;
first store operation — not during service registration — the library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;applies any pending schema migrations, &lt;strong&gt;guarded by a distributed lock&lt;/strong&gt; so
that if several app instances boot at once, the migrations apply exactly
once; and&lt;/li&gt;
&lt;li&gt;creates its indexes: a unique index on &lt;code&gt;NormalizedUserName&lt;/code&gt;, an index on
&lt;code&gt;NormalizedEmail&lt;/code&gt;, a compound index on &lt;code&gt;Logins.LoginProvider&lt;/code&gt; /
&lt;code&gt;Logins.ProviderKey&lt;/code&gt;, and a unique index on the role &lt;code&gt;NormalizedName&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That distributed lock matters the moment you run more than one instance. Two&lt;br&gt;
pods starting simultaneously won't race each other into a half-applied schema.&lt;/p&gt;

&lt;p&gt;Because the &lt;code&gt;NormalizedUserName&lt;/code&gt; index enforces real uniqueness, index creation&lt;br&gt;
will fail if your existing data already contains duplicate user names — clean&lt;br&gt;
those up before you upgrade. If you'd rather own these concerns yourself, set&lt;br&gt;
&lt;code&gt;mongo.DisableIndexCreation = true&lt;/code&gt; and/or &lt;code&gt;mongo.DisableAutoMigrations = true&lt;/code&gt;.&lt;br&gt;
And if you want the work done at startup instead of on first request, resolve&lt;br&gt;
&lt;code&gt;MongoIdentityInitializer&lt;/code&gt; and await &lt;code&gt;EnsureInitializedAsync()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  It won't silently clobber concurrent writes
&lt;/h2&gt;

&lt;p&gt;Updates and deletes use optimistic concurrency through Identity's&lt;br&gt;
&lt;code&gt;ConcurrencyStamp&lt;/code&gt;. If the document changed since you loaded your copy, the&lt;br&gt;
operation returns a &lt;code&gt;ConcurrencyFailure&lt;/code&gt; instead of overwriting the other&lt;br&gt;
write. Reload, reapply, retry — the normal Identity dance.&lt;/p&gt;

&lt;h2&gt;
  
  
  One honest limitation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;options.Stores.ProtectPersonalData&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; supported. The store doesn't&lt;br&gt;
encrypt personal data at rest, and rather than letting you flip that switch and&lt;br&gt;
quietly store unprotected data anyway, enabling it throws at runtime. If you&lt;br&gt;
need encryption-at-rest for PII, handle it at a different layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package AspNetCoreIdentity.MongoDriver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Package: &lt;a href="https://www.nuget.org/packages/AspNetCoreIdentity.MongoDriver" rel="noopener noreferrer"&gt;https://www.nuget.org/packages/AspNetCoreIdentity.MongoDriver&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source: &lt;a href="https://github.com/lxman/AspNetCoreIdentity.MongoDriver" rel="noopener noreferrer"&gt;https://github.com/lxman/AspNetCoreIdentity.MongoDriver&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're on Mongo and Identity, this is the short path. Issues and PRs welcome.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>mongodb</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
