Entity Framework : how to catch any error

12,012

Solution 1

DbContext.SaveChanges describes which exceptions you may expect. Decide which exceptions you want to catch and which ones not.

If you are not certain which exceptions you get in what situations, use your debugger and some test code to find out which exceptions you actually may expect:

// TODO: create one of your error conditions
try
{
   _context.SaveChanges();
}
catch (Exception e)
{
    Console.WriteLine(e.GetType()); // what is the real exception?
}

When you know which exceptions you a can expect and which ones you really can handle, write your final code:

try
{
   _context.SaveChanges();
}
catch (DbUpdateException e)
{
    // handle the update exception
}
catch (DbEntityValidationException e)
{
    // handle the entity validation exception
}
catch (...)

You probably won't catch System.NotSupportedException, your code should be such that it should only use supported LINQ statements.

Optimization

Keep in mind that the DbSets in your DbContext represent the tables in your database. The classes in the DbSets represent a row in the table: The non-virtual properties represent the columns in the table, the relations between the tables are represented as virtual properties.

You designed these database tables because you wanted to solve a problem. Apparently in your solution it was important that FirstName / LastName etc would not be null.

You'll probably wrap the usage of your DbContext into a class that hides that you use entity framework to keep your data, instead of, for instance Dapper, or any lower level method to query and update the data.

Quite often this wrapper class is called a Repository class: users of your Repository don't know, and really don't care, how and where you save your data: SQL? Mongo? Maybe even a CSV-file?

The nice thing about having a Repository class is, that if you decide to change your table layout, or if you decide to change one of your queries into a stored procedure, or if you decide to store your data in a CSV, changes will be minimal and users won't even notice the change

In your repository you'll have functions to query Persons, to Add / Delete / Update a Person, etc. You decided earlier that your solution should not accept Persons with null names.

Your solution does not depend on how your data is saved. Hence, your solution should not depend on whether your repository checks whether your names are null or nt.

Consider to check the data validity before calling SaveChanges. In this case: check if first name, last name etc. are indeed not null. Your code will

  • look cleaner: less handling of exceptions thrown by parties outside your scope
  • easier to read: readers won't have to guess what happens if data is null,
  • easier to test: your test code can use a simple mock repository without null checks
  • better maintainable: if you decide to allow to add Persons without a first name, your database model does not change, only your repository class

Solution 2

By using following code you can create a more user friendly message for DbEntityValidationException:

try
{
   _context.SaveChanges();
}
catch (DbEntityValidationException dbEx)
{
    var sb = new StringBuilder();
    foreach (var validationErrors in dbEx.EntityValidationErrors)
    {
        foreach (var validationError in validationErrors.ValidationErrors)
        {
           sb.AppendLine(string.Format("Property: {0} Error: {1}",
           validationError.PropertyName,validationError.ErrorMessage));
        }
     }
     throw new Exception(sb.ToString(), dbEx);
}

Then you can catch this new Exception in higher level; to catching other exceptions you can use separate catch blocks.

Share:
12,012

Related videos on Youtube

Admin
Author by

Admin

Updated on June 04, 2022

Comments

  • Admin
    Admin almost 2 years

    I am trying to insert data into a SQL Server table that has a lot of not null constraints:

    CREATE TABLE [dbo].[Customer]
    (
        [CustomerId] [int] IDENTITY(1,1) NOT NULL,
        [FirstName] [varchar](255) NOT NULL,
        [LastName] [varchar](255) NOT NULL,
        [AddressLine] [varchar](255) NOT NULL
        CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED ([CustomerId] ASC)
     )
    

    EF code:

    public virtual DbSet<Customer> Customer { get; set; }
    modelBuilder.Entity<Customer>(entity =>
    {
        entity.Property(e => e.FirstName)
            .HasMaxLength(255)
            .IsRequired()
            .IsUnicode(false);
    
        entity.Property(e => e.LastName)
                .HasMaxLength(255)
                .IsRequired()
                .IsUnicode(false);
    
        entity.Property(e => e.AddressLine)
                .HasMaxLength(255)
                .IsRequired()
                .IsUnicode(false);
      });
    

    When attempting to add data into table, code is missing columns, so it fails to insert into database. Did not know about this, and did not receive 'NOT NULL' errors, as I would see in SQL database.

    var source = new Customer();
    
    source.FirstName = "Joe";  // missing Last Name and Address
    _context.Customer.Add(source);
    

    So I added following code. This solves the issue, however how would I have it fail on any db error, concurrency, wrong data types etc.

    try
    {
       _context.SaveChanges();
    }
    catch (DbUpdateException e)
    {
    }
    

    Following were not working: Method 1 and 2: When these were implemented, the not null error did not show up anymore as we wanted.

    try
    {
       _context.SaveChanges();
    }
    catch (Exception e)
    {
    }
    
    
    try
    {
       _context.SaveChanges();
    }
    catch
    {
    }
    
  • Admin
    Admin about 5 years
    I wanted to catch all exceptions in one scoop without listing any out, is there anyway? thanks
  • er-sho
    er-sho about 5 years
    means you want to get exception of any type within single catch block and without if else? then the parent Excpetion class does all this thing for you