<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Entity Framework | Giorgi Dalakishvili | Personal Website</title><link>https://www.giorgi.dev/categories/entity-framework/</link><atom:link href="https://www.giorgi.dev/categories/entity-framework/index.xml" rel="self" type="application/rss+xml"/><description>Entity Framework</description><generator>Source Themes Academic (https://sourcethemes.com/academic/)</generator><language>en-us</language><copyright>© Giorgi Dalakishvili 2019 - 2026</copyright><lastBuildDate>Thu, 12 Mar 2020 07:35:16 +0000</lastBuildDate><image><url>img/map[gravatar:%!s(bool=false) shape:circle]</url><title>Entity Framework</title><link>https://www.giorgi.dev/categories/entity-framework/</link></image><item><title>EntityFramework.Exceptions 3.1.1 - Support for Entity Framework Core 3 and Improved API</title><link>https://www.giorgi.dev/entity-framework/refactoring-entityframework-exceptions/</link><pubDate>Thu, 12 Mar 2020 07:35:16 +0000</pubDate><guid>https://www.giorgi.dev/entity-framework/refactoring-entityframework-exceptions/</guid><description>&lt;p>
&lt;a href="https://www.giorgi.dev/entity-framework/introducing-entityframework-exceptions/">In the previous article&lt;/a> I introduced EntityFramework.Exceptions, a library which simplifies handling exceptions in Entity Framework Core but the library had one important limitation: In order to
use it you had to inherit your custom DbContext from &lt;code>ExceptionProcessorContextBase&lt;/code> class. This means that if you wanted to use some other base class for your DbContext you were out of luck. The
latest version of the library solves this issue by replacing one of the internal services used by entity framework core with a custom implementation and also adds support for Entity Framework Core 3.1.1&lt;/p>
&lt;p>The service that needs to be replaced is
&lt;a href="https://github.com/dotnet/efcore/blob/master/src/EFCore/ChangeTracking/Internal/IStateManager.cs" target="_blank" rel="noopener">IStateManager&lt;/a> and is used by the ChangeTracker. The custom
implementation of &lt;code>IStateManager&lt;/code> interface inherits from the built in
&lt;a href="https://github.com/dotnet/efcore/blob/master/src/EFCore/ChangeTracking/Internal/StateManager.cs" target="_blank" rel="noopener">StateManager&lt;/a> class and overrides &lt;code>SaveChanges&lt;/code> and &lt;code>SaveChangesAsync&lt;/code> methods. Let&amp;rsquo;s see how it works:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">abstract&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ExceptionProcessorStateManager&lt;/span>&amp;lt;T&amp;gt; : StateManager &lt;span style="color:#66d9ef">where&lt;/span> T : DbException
{
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">readonly&lt;/span> Dictionary&amp;lt;DatabaseError, Func&amp;lt;DbUpdateException, Exception&amp;gt;&amp;gt; ExceptionMapping = &lt;span style="color:#66d9ef">new&lt;/span> Dictionary&amp;lt;DatabaseError, Func&amp;lt;DbUpdateException, Exception&amp;gt;&amp;gt;
{
{DatabaseError.MaxLength, exception =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> MaxLengthExceededException(&lt;span style="color:#e6db74">&amp;#34;Maximum length exceeded&amp;#34;&lt;/span>, exception.InnerException) },
{DatabaseError.UniqueConstraint, exception =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> UniqueConstraintException(&lt;span style="color:#e6db74">&amp;#34;Unique constraint violation&amp;#34;&lt;/span>, exception.InnerException) },
{DatabaseError.CannotInsertNull, exception =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> CannotInsertNullException(&lt;span style="color:#e6db74">&amp;#34;Cannot insert null&amp;#34;&lt;/span>, exception.InnerException) },
{DatabaseError.NumericOverflow, exception =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> NumericOverflowException(&lt;span style="color:#e6db74">&amp;#34;Numeric overflow&amp;#34;&lt;/span>, exception.InnerException) },
{DatabaseError.ReferenceConstraint, exception =&amp;gt; &lt;span style="color:#66d9ef">new&lt;/span> ReferenceConstraintException(&lt;span style="color:#e6db74">&amp;#34;Reference constraint violation&amp;#34;&lt;/span>, exception.InnerException) }
};
&lt;span style="color:#66d9ef">protected&lt;/span> ExceptionProcessorStateManager(StateManagerDependencies dependencies) : &lt;span style="color:#66d9ef">base&lt;/span>(dependencies)
{
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> SaveChanges(&lt;span style="color:#66d9ef">bool&lt;/span> acceptAllChangesOnSuccess)
{
&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">base&lt;/span>.SaveChanges(acceptAllChangesOnSuccess);
}
&lt;span style="color:#66d9ef">catch&lt;/span> (DbUpdateException originalException)
{
&lt;span style="color:#66d9ef">var&lt;/span> exception = GetException(originalException);
&lt;span style="color:#66d9ef">if&lt;/span> (exception != &lt;span style="color:#66d9ef">null&lt;/span>)
{
&lt;span style="color:#66d9ef">throw&lt;/span> exception;
}
&lt;span style="color:#66d9ef">throw&lt;/span>;
}
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">async&lt;/span> Task&amp;lt;&lt;span style="color:#66d9ef">int&lt;/span>&amp;gt; SaveChangesAsync(&lt;span style="color:#66d9ef">bool&lt;/span> acceptAllChangesOnSuccess, CancellationToken cancellationToken = &lt;span style="color:#66d9ef">new&lt;/span> CancellationToken())
{
&lt;span style="color:#66d9ef">try&lt;/span>
{
&lt;span style="color:#66d9ef">var&lt;/span> result = &lt;span style="color:#66d9ef">await&lt;/span> &lt;span style="color:#66d9ef">base&lt;/span>.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
&lt;span style="color:#66d9ef">return&lt;/span> result;
}
&lt;span style="color:#66d9ef">catch&lt;/span> (DbUpdateException originalException)
{
&lt;span style="color:#66d9ef">var&lt;/span> exception = GetException(originalException);
&lt;span style="color:#66d9ef">if&lt;/span> (exception != &lt;span style="color:#66d9ef">null&lt;/span>)
{
&lt;span style="color:#66d9ef">throw&lt;/span> exception;
}
&lt;span style="color:#66d9ef">throw&lt;/span>;
}
}
&lt;span style="color:#66d9ef">private&lt;/span> Exception GetException(DbUpdateException ex)
{
&lt;span style="color:#66d9ef">if&lt;/span> (ex.GetBaseException() &lt;span style="color:#66d9ef">is&lt;/span> T dbException &amp;amp;&amp;amp; GetDatabaseError(dbException) &lt;span style="color:#66d9ef">is&lt;/span> DatabaseError error &amp;amp;&amp;amp; ExceptionMapping.TryGetValue(error, &lt;span style="color:#66d9ef">out&lt;/span> &lt;span style="color:#66d9ef">var&lt;/span> ctor))
{
&lt;span style="color:#66d9ef">return&lt;/span> ctor(ex);
}
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">null&lt;/span>;
}
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">abstract&lt;/span> DatabaseError? GetDatabaseError(T dbException);
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The abstract &lt;code>ExceptionProcessorStateManager&lt;/code> class catches any database exception thrown during &lt;code>SaveChanges&lt;/code> call and tries to translate it into one of the supported exception instances. If it succeeds it throws the new exception and if it doesn&amp;rsquo;t it simply rethrows the original exception. The &lt;code>GetDatabaseError&lt;/code> is overriden in database specific projects and returns &lt;code>DatabaseError&lt;/code> based on the
specific &lt;code>DbException&lt;/code> that was thrown:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">SqlServerExceptionProcessorStateManager&lt;/span>: ExceptionProcessorStateManager&amp;lt;SqlException&amp;gt;
{
&lt;span style="color:#66d9ef">public&lt;/span> SqlServerExceptionProcessorStateManager(StateManagerDependencies dependencies) : &lt;span style="color:#66d9ef">base&lt;/span>(dependencies)
{
}
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> ReferenceConstraint = &lt;span style="color:#ae81ff">5&lt;/span>&lt;span style="color:#ae81ff">4&lt;/span>&lt;span style="color:#ae81ff">7&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> CannotInsertNull = &lt;span style="color:#ae81ff">5&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> CannotInsertDuplicateKeyUniqueIndex = &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">6&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> CannotInsertDuplicateKeyUniqueConstraint = &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">6&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">7&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> ArithmeticOverflow = &lt;span style="color:#ae81ff">8&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>;
&lt;span style="color:#66d9ef">private&lt;/span> &lt;span style="color:#66d9ef">const&lt;/span> &lt;span style="color:#66d9ef">int&lt;/span> StringOrBinaryDataWouldBeTruncated = &lt;span style="color:#ae81ff">8&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>&lt;span style="color:#ae81ff">2&lt;/span>;
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> DatabaseError? GetDatabaseError(SqlException dbException)
{
&lt;span style="color:#66d9ef">switch&lt;/span> (dbException.Number)
{
&lt;span style="color:#66d9ef">case&lt;/span> ReferenceConstraint:
&lt;span style="color:#66d9ef">return&lt;/span> DatabaseError.ReferenceConstraint;
&lt;span style="color:#66d9ef">case&lt;/span> CannotInsertNull:
&lt;span style="color:#66d9ef">return&lt;/span> DatabaseError.CannotInsertNull;
&lt;span style="color:#66d9ef">case&lt;/span> CannotInsertDuplicateKeyUniqueIndex:
&lt;span style="color:#66d9ef">case&lt;/span> CannotInsertDuplicateKeyUniqueConstraint:
&lt;span style="color:#66d9ef">return&lt;/span> DatabaseError.UniqueConstraint;
&lt;span style="color:#66d9ef">case&lt;/span> ArithmeticOverflow:
&lt;span style="color:#66d9ef">return&lt;/span> DatabaseError.NumericOverflow;
&lt;span style="color:#66d9ef">case&lt;/span> StringOrBinaryDataWouldBeTruncated:
&lt;span style="color:#66d9ef">return&lt;/span> DatabaseError.MaxLength;
&lt;span style="color:#66d9ef">default&lt;/span>:
&lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">null&lt;/span>;
}
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>In order to actually replace &lt;code>IStateManager&lt;/code> with the custom implementation you need to install either
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.SqlServer" target="_blank" rel="noopener">SQL Server&lt;/a>,
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.PostgreSQL" target="_blank" rel="noopener">PostgreSQL&lt;/a> or
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.MySQL" target="_blank" rel="noopener">MySQL&lt;/a> nuget package and call &lt;code>UseExceptionProcessor&lt;/code>
method of the &lt;code>ExceptionProcessorExtensions&lt;/code> from the database specific package:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">DemoContext&lt;/span> : DbContext, IDemoContext
{
&lt;span style="color:#66d9ef">public&lt;/span> DbSet&amp;lt;Product&amp;gt; Products { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">public&lt;/span> DbSet&amp;lt;ProductSale&amp;gt; ProductSale { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> OnModelCreating(ModelBuilder builder)
{
builder.Entity&amp;lt;Product&amp;gt;().Property(b =&amp;gt; b.Price).HasColumnType(&lt;span style="color:#e6db74">&amp;#34;decimal(5,2)&amp;#34;&lt;/span>).IsRequired();
builder.Entity&amp;lt;Product&amp;gt;().Property(b =&amp;gt; b.Name).IsRequired().HasMaxLength(&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>);
builder.Entity&amp;lt;Product&amp;gt;().HasIndex(u =&amp;gt; u.Name).IsUnique();
}
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(&lt;span style="color:#e6db74">@&amp;#34;Data Source=(localdb)\ProjectsV13;Initial Catalog=Test;Integrated Security=True;Connect Timeout=30;&amp;#34;&lt;/span>)
.UseExceptionProcessor();
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The &lt;code>UseExceptionProcessor&lt;/code> method is very simple and all it does is a call to
&lt;a href="https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbcontextoptionsbuilder.replaceservice?view=efcore-3.1" target="_blank" rel="noopener">DbContextOptionsBuilder.ReplaceService&amp;lt;TService,TImplementation&amp;gt;&lt;/a> method:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> &lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">ExceptionProcessorExtensions&lt;/span>
{
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> DbContextOptionsBuilder UseExceptionProcessor(&lt;span style="color:#66d9ef">this&lt;/span> DbContextOptionsBuilder self)
{
self.ReplaceService&amp;lt;IStateManager, SqlServerExceptionProcessorStateManager&amp;gt;();
&lt;span style="color:#66d9ef">return&lt;/span> self;
}
&lt;span style="color:#66d9ef">public&lt;/span> &lt;span style="color:#66d9ef">static&lt;/span> DbContextOptionsBuilder&amp;lt;TContext&amp;gt; UseExceptionProcessor&amp;lt;TContext&amp;gt;(&lt;span style="color:#66d9ef">this&lt;/span> DbContextOptionsBuilder&amp;lt;TContext&amp;gt; self) &lt;span style="color:#66d9ef">where&lt;/span> TContext : DbContext
{
self.ReplaceService&amp;lt;IStateManager, SqlServerExceptionProcessorStateManager&amp;gt;();
&lt;span style="color:#66d9ef">return&lt;/span> self;
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once you have done it you will start getting database specific exceptions instead of &lt;code>DbUpdateException&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> demoContext = &lt;span style="color:#66d9ef">new&lt;/span> DemoContext())
{
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>
});
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>
});
&lt;span style="color:#66d9ef">try&lt;/span>
{
demoContext.SaveChanges();
}
&lt;span style="color:#66d9ef">catch&lt;/span> (UniqueConstraintException e)
{
&lt;span style="color:#75715e">//Unique index was violated. Show corresponding error message to user.
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>The full source code of the library is available on GitHub:
&lt;a href="https://github.com/Giorgi/EntityFramework.Exceptions" target="_blank" rel="noopener">EntityFramework.Exceptions&lt;/a> If you have questions or suggestions feel free to leave a comment, create an issue and star the repository.&lt;/p></description></item><item><title>Introducing EntityFramework.Exceptions</title><link>https://www.giorgi.dev/entity-framework/introducing-entityframework-exceptions/</link><pubDate>Tue, 11 Dec 2018 07:35:16 +0000</pubDate><guid>https://www.giorgi.dev/entity-framework/introducing-entityframework-exceptions/</guid><description>&lt;p>When using Entity Framework Core for data access all database exceptions are wrapped in &lt;code>DbUpdateException&lt;/code>. If you need to know whether the exception was caused by a unique constraint, value being too long or value missing for a required column you need to dig into the concrete &lt;code>DbException&lt;/code> subclass instance and check the error number to determine the exact cause.&lt;/p>
&lt;p>&lt;a href="https://github.com/Giorgi/EntityFramework.Exceptions" rel="noopener" target="_blank">EntityFramework.Exceptions&lt;/a> simplifies this by handling all the database specific details and throwing different exceptions for different cases. All you have to do is&lt;/p>
&lt;p>inherit your &lt;code>DbContext&lt;/code> from &lt;code>ExceptionProcessorContext&lt;/code> and handle the exception(s) (such as &lt;code>UniqueConstraintException&lt;/code>, &lt;code>CannotInsertNullException&lt;/code>, &lt;code>MaxLengthExceededException&lt;/code>, &lt;code>NumericOverflowException&lt;/code>) you need.&lt;/p>
&lt;h3 id="the-problem-with-entity-framework-exceptions">The Problem with Entity Framework Exceptions&lt;/h3>
&lt;p>Let’s say we have a &lt;code>Product&lt;/code> table which has Name column with a unique index and Price column. Entity Framework context will look like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">DemoContext&lt;/span> : DbContext
{
&lt;span style="color:#66d9ef">public&lt;/span> DbSet&amp;lt;Product&amp;gt; Products { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> OnModelCreating(ModelBuilder builder)
{
builder.Entity&amp;lt;Product&amp;gt;().Property(b =&amp;gt; b.Price).HasColumnType(&lt;span style="color:#e6db74">&amp;#34;decimal(5,2)&amp;#34;&lt;/span>).IsRequired();
builder.Entity&amp;lt;Product&amp;gt;().Property(b =&amp;gt; b.Name).IsRequired().HasMaxLength(&lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">5&lt;/span>);
builder.Entity&amp;lt;Product&amp;gt;().HasIndex(u =&amp;gt; u.Name).IsUnique();
}
&lt;span style="color:#66d9ef">protected&lt;/span> &lt;span style="color:#66d9ef">override&lt;/span> &lt;span style="color:#66d9ef">void&lt;/span> OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(&lt;span style="color:#e6db74">@&amp;#34;Data Source=localhost;Initial Catalog=Test;Integrated Security=True;Connect Timeout=30;&amp;#34;&lt;/span>);
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>If we try to insert two records with the same name we will get &lt;code>DbUpdateException&lt;/code>. As we have already mentioned &lt;code>DbUpdateException&lt;/code> is thrown every time when saving changes to the database fails. In order to check that the exception was caused by the unique index we need to get the specific database &lt;code>DbException&lt;/code> subclass instance and check the error number. In case of SQL Server we can do it like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> demoContext = &lt;span style="color:#66d9ef">new&lt;/span> DemoContext())
{
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>
});
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>
});
&lt;span style="color:#66d9ef">try&lt;/span>
{
demoContext.SaveChanges();
}
&lt;span style="color:#66d9ef">catch&lt;/span> (DbUpdateException e)
{
&lt;span style="color:#66d9ef">var&lt;/span> sqlException = e.GetBaseException() &lt;span style="color:#66d9ef">as&lt;/span> SqlException;
&lt;span style="color:#75715e">//2601 is error number of unique index violation
&lt;/span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#66d9ef">if&lt;/span> (sqlException != &lt;span style="color:#66d9ef">null&lt;/span> &amp;amp;&amp;amp; sqlException.Number == &lt;span style="color:#ae81ff">2&lt;/span>&lt;span style="color:#ae81ff">6&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>&lt;span style="color:#ae81ff">1&lt;/span>)
{
&lt;span style="color:#75715e">//Unique index was violated. Show corresponding error message to user.
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>While this works it has several disadvantages. First of all it is repetitive to write this code every time we try to save changes to database. Secondly, there are other database errors to handle such as trying to insert null in non-null column or trying to insert longer value than the column allows. Finally, the error numbers are different for different database servers.&lt;/p>
&lt;p>To avoid these issues I have created a library
&lt;a href="https://github.com/Giorgi/EntityFramework.Exceptions" target="_blank" rel="noopener">EntityFramework.Exceptions&lt;/a> which handles database specific errors and throws different exceptions for different database errors.&lt;/p>
&lt;h3 id="entityframeworkexceptions-8211-easy-way-to-handle-exceptions">EntityFramework.Exceptions – Easy Way to Handle Exceptions&lt;/h3>
&lt;p>With EntityFramework.Exceptions we can rewrite the above code like this:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> demoContext = &lt;span style="color:#66d9ef">new&lt;/span> DemoContext())
{
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>
});
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>&lt;span style="color:#ae81ff">0&lt;/span>
});
&lt;span style="color:#66d9ef">try&lt;/span>
{
demoContext.SaveChanges();
}
&lt;span style="color:#66d9ef">catch&lt;/span> (UniqueConstraintException e)
{
&lt;span style="color:#75715e">//Unique index was violated. Show corresponding error message to user.
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see we no longer have to deal with database specific exception and error numbers. Instead &lt;code>UniqueConstraintException&lt;/code> is thrown when either a unique index or unique constraint is violated. As a result our code is cleaner, shorter and easier to understand. What’s more the library provides other exceptions such as &lt;code>CannotInsertNullException&lt;/code>, &lt;code>MaxLengthExceededException&lt;/code>, &lt;code>NumericOverflowException&lt;/code>. For example if we try insert a product with Name longer than 15 characters we will get &lt;code>MaxLengthExceededException&lt;/code> exception:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">using&lt;/span> (&lt;span style="color:#66d9ef">var&lt;/span> demoContext = &lt;span style="color:#66d9ef">new&lt;/span> DemoContext())
{
demoContext.Products.Add(&lt;span style="color:#66d9ef">new&lt;/span> Product
{
Name = &lt;span style="color:#e6db74">&amp;#34;Moon Lamp Change 3 Colors&amp;#34;&lt;/span>,
Price = &lt;span style="color:#ae81ff">1&lt;/span>
});
&lt;span style="color:#66d9ef">try&lt;/span>
{
demoContext.SaveChanges();
}
&lt;span style="color:#66d9ef">catch&lt;/span> (MaxLengthExceededException e)
{
&lt;span style="color:#75715e">//Max length of Name column exceeded. Show corresponding error message to user.
&lt;/span>&lt;span style="color:#75715e">&lt;/span> }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;p>Apart from this EntityFrameworkCore.Exceptions supports other database servers such as PostgreSQL and MySQL so if you ever switch to different database server your exception handling code will stay the same.&lt;/p>
&lt;p>To get started with &lt;code>EntityFramework.Exceptions&lt;/code> all you need to do is install either
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.SqlServer" target="_blank" rel="noopener">SQL Server&lt;/a>,
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.PostgreSQL" target="_blank" rel="noopener">PostgreSQL&lt;/a> or
&lt;a href="https://www.nuget.org/packages/EntityFrameworkCore.Exceptions.MySQL" target="_blank" rel="noopener">MySQL&lt;/a> nuget package and inherit your DbContext from ExceptionProcessorContext:&lt;/p>
&lt;div class="highlight">&lt;pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4">&lt;code class="language-cs" data-lang="cs">&lt;span style="color:#66d9ef">class&lt;/span> &lt;span style="color:#a6e22e">DemoContext&lt;/span> : ExceptionProcessorContext
{
&lt;span style="color:#66d9ef">public&lt;/span> DbSet&amp;lt;Product&amp;gt; Products { &lt;span style="color:#66d9ef">get&lt;/span>; &lt;span style="color:#66d9ef">set&lt;/span>; }
}
&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="how-does-entityframeworkexceptions-work-">How Does EntityFramework.Exceptions Work ?&lt;/h3>
&lt;p>The &lt;a href="https://github.com/Giorgi/EntityFramework.Exceptions" rel="noopener" target="_blank">implementation&lt;/a> is pretty straightforward. There is an &lt;code>ExceptionProcessorContextBase&lt;/code> class in &lt;code>EntityFramework.Exceptions.Common&lt;/code> project which inherits from &lt;code>DbContext&lt;/code>, overrides &lt;code>SaveChanges&lt;/code> and handles any exception that occurs. It gets the database specific exception instance and asks derived classes to tell which exception it should throw for the &lt;code>DbException&lt;/code> that occurred. For more details please check the
&lt;a href="https://github.com/Giorgi/EntityFramework.Exceptions" target="_blank" rel="noopener">GitHub repository.&lt;/a> If you find the library useful don’t forget to &lt;em>Star&lt;/em> the repository. If you have any questions or suggestions you are welcome to submit an issue or send a PR.&lt;/p></description></item></channel></rss>