EntityFramework.Exceptions

repository·main·Indexed 23 days ago

https://github.com/giorgi/entityframework.exceptions

Provides typed exceptions for Entity Framework Core and ADO.NET by transforming generic database error codes into meaningful types such as UniqueConstraintException, MaxLengthExceededException, and DeadlockException. It includes DbExceptionClassifier for database-agnostic ADO.NET error handling and provider-specific packages for PostgreSQL, SQL Server, SQLite, Oracle, and MySQL.

Tokens
2.3K
Snippets
8
Records
10
Agent score
83%

What's inside EntityFramework.Exceptions

  1. Combine multiple classifiers with CompositeExceptionClassifier

    main

    If your application interacts with multiple different database types, use CompositeExceptionClassifier to wrap multiple specific classifiers. The composite classifier will iterate through its registered classifiers and return true if any of them identify the exception type.

    var classifier = new CompositeExceptionClassifier(
        new PostgreSQLExceptionClassifier(),
        new SqlServerExceptionClassifier()
    );
    
    try
    {
        await command.ExecuteNonQueryAsync();
    }
    catch (DbException ex) when (classifier.IsUniqueConstraintError(ex))
    {
        // Works regardless of which database threw the exception
    }
  2. Configure UseExceptionProcessor in DbContext

    main

    To enable the library, call the UseExceptionProcessor() extension method within your DbContext.OnConfiguring method.

    class DemoContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseExceptionProcessor();
        }
    }
    class DemoContext : DbContext
    {
        public DbSet<Product> Products { get; set; }
        public DbSet<ProductSale> ProductSale { get; set; }
    
        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<Product>().HasIndex(u => u.Name).IsUnique();
        }
    
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseExceptionProcessor();
        }
    }
  3. Install DbExceptionClassifier for your database provider

    main

    Install the specific NuGet package corresponding to your database provider to enable classification of provider-specific ADO.NET exceptions.

    Supported providers include:

    • PostgreSQL
    • SQL Server
    • SQLite
    • Oracle
    • MySQL (via DbExceptionClassifier.MySQL or DbExceptionClassifier.MySQL.Pomelo)

    Note for SQLite users: If you want to use a custom native SQLite binary instead of e_sqlite3.dll, install the DbExceptionClassifier.Sqlite.Core package, which depends on Microsoft.Data.Sqlite.Core and does not bundle a native binary.

    dotnet add package DbExceptionClassifier.PostgreSQL
    dotnet add package DbExceptionClassifier.SqlServer
    dotnet add package DbExceptionClassifier.Sqlite
    dotnet add package DbExceptionClassifier.Oracle
    dotnet add package DbExceptionClassifier.MySQL
    dotnet add package DbExceptionClassifier.MySQL.Pomelo
  4. Install EntityFramework.Exceptions for your database

    main

    Install the NuGet package that corresponds to your specific database provider to enable typed exception handling.

    • SQL Server: dotnet add package EntityFrameworkCore.Exceptions.SqlServer
    • MySQL: dotnet add package EntityFrameworkCore.Exceptions.MySQL
    • MySQL (Pomelo): dotnet add package EntityFrameworkCore.Exceptions.MySQL.Pomelo
    • PostgreSQL: dotnet add package EntityFrameworkCore.Exceptions.PostgreSQL
    • SQLite: dotnet add package EntityFrameworkCore.Exceptions.Sqlite
    • Oracle: dotnet add package EntityFrameworkCore.Exceptions.Oracle
    dotnet add package EntityFrameworkCore.Exceptions.SqlServer
  5. Use ExceptionProcessor with DbContext pooling

    main

    If you are using AddDbContextPool, call UseExceptionProcessor() during the service registration instead of inside OnConfiguring. Replace the provider method (e.g., UseNpgsql) with your specific database provider.

    builder.Services.AddDbContextPool<DemoContext>(options => options
        .UseNpgsql(config.GetConnectionString("DemoConnection"))
        .UseExceptionProcessor());
    // Replace UseNpgsql with the sql flavor you're using
    builder.Services.AddDbContextPool<DemoContext>(options => options
        .UseNpgsql(config.GetConnectionString("DemoConnection"))
        .UseExceptionProcessor());
  6. Classify ADO.NET exceptions using IDbExceptionClassifier

    main

    DbExceptionClassifier provides a unified way to identify common database error types from provider-specific DbException instances. This allows you to write database-agnostic error handling logic.

    To use it, instantiate the classifier specific to your database (e.g., PostgreSQLExceptionClassifier) and use its boolean check methods within catch filters.

    Supported error classifications:

    • Unique constraint violation
    • Reference (foreign key) constraint violation
    • Cannot insert null
    • Max length exceeded
    • Numeric overflow
    • Deadlock
    var classifier = new PostgreSQLExceptionClassifier();
    
    try
    {
        // Execute your ADO.NET command
        await command.ExecuteNonQueryAsync();
    }
    catch (DbException ex) when (classifier.IsUniqueConstraintError(ex))
    {
        // Handle unique constraint violation
    }
    catch (DbException ex) when (classifier.IsReferenceConstraintError(ex))
    {
        // Handle foreign key violation
    }
  7. Classify ADO.NET exceptions with DbExceptionClassifier

    main

    If you are not using Entity Framework Core, you can use the DbExceptionClassifier packages to classify ADO.NET exceptions directly using the IDbExceptionClassifier interface. This approach has no EF Core dependency.

    1. Install the provider-specific classifier (e.g., dotnet add package DbExceptionClassifier.PostgreSQL).
    2. Use the classifier in a catch block with a when filter.
    var classifier = new PostgreSQLExceptionClassifier();
    
    try
    {
        await command.ExecuteNonQueryAsync();
    }
    catch (DbException ex) when (classifier.IsUniqueConstraintError(ex))
    {
        // Handle unique constraint violation
    }
    var classifier = new PostgreSQLExceptionClassifier();
    
    try
    {
        await command.ExecuteNonQueryAsync();
    }
    catch (DbException ex) when (classifier.IsUniqueConstraintError(ex))
    {
        // Handle unique constraint violation
    }
  8. IDbExceptionClassifier interface

    main

    The IDbExceptionClassifier interface defines the contract for classifying DbException instances into common error categories. Implementations of this interface are provider-specific (e.g., SQL Server, PostgreSQL).

    public interface IDbExceptionClassifier
    {
        bool IsUniqueConstraintError(DbException exception);
        bool IsReferenceConstraintError(DbException exception);
        bool IsCannotInsertNullError(DbException exception);
        bool IsMaxLengthExceededError(DbException exception);
        bool IsNumericOverflowError(DbException exception);
        bool IsDeadlockError(DbException exception);
    }
  9. Handle typed database exceptions

    main

    Once configured, the library intercepts database-specific error codes and throws meaningful exceptions instead of a generic DbUpdateException.

    Common exceptions include:

    • UniqueConstraintException
    • CannotInsertNullException
    • MaxLengthExceededException
    • NumericOverflowException
    • ReferenceConstraintException
    • DeadlockException

    All these exceptions inherit from DbUpdateException for backwards compatibility.

    Constraint Metadata

    For UniqueConstraintException and ReferenceConstraintException, you can access:

    • ConstraintName: The name of the associated constraint.
    • ConstraintProperties: The properties that are part of the constraint.
    WARNING

    ConstraintName and ConstraintProperties are only populated if the index is defined in the Entity Framework Model. They will not be populated if the index exists in the database but isn't part of the model, if it was added via MigrationBuilder.Sql, or if you are using SQLite.