Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where to handle DB exceptions in Entity Framework and repository pattern

how would you design this scenario (using Entity Framework 4.1, Code First and repository pattern): the Visual Studio solution contains the following projects

Solution
|-Web Application Project
|-DAL Project
|-Model Project

So in the Model Project there are various classes. Suppose we have in there a class called User with the following definition (stripped down):

public class User{

    [Key]
    public int UserId { get; set; }

    ....

    //this property has a unique constraint created in a custom DB Initializer class
    public string email { get; set; }

    ....
}

In the DAL Project reside the repository methods (Insert, Update etc.) and also the Initializer class:

public class MyDatabaseInitializer : IDatabaseInitializer<MyDatabase>
{
    public void InitializeDatabase(MyDatabase context)
    {
        try
        {
            if (!context.Database.Exists())
            {
                context.Database.Create();
                context.Database.ExecuteSqlCommand(
                    "ALTER TABLE Users ADD CONSTRAINT uc_Email UNIQUE(Email)");
            }
        }
        catch (Exception ex)
        {
            throw ex.InnerException;
        }
    }
}

The Commit method of my Unit of Work class looks like this:

public string Commit()
{
    string errorMessage = string.Empty;

    try
    {
        Database.Commit();
    }
    catch (DbUpdateException updExc)
    {                                 
        errorMessage = updExc.InnerException.Message;            
    }                       

    return errorMessage;
}

As you see I'm handling DbUpdateException in the Commit() method of the Unit of Work class; this means for each class which could cause an update error, this would be handled here.

Suppose one inserts the User records with the following Data:

(UserId,....,Email,...)
1, ... , [email protected] , ...
2, ... , [email protected] , ...

It 's obvious that this will cause a DbUpdateException to occur. Of course this can be caught and propagated to the place where it should show up. I have the feeling that this design is completely wrong:

  1. Validation should occur for each property separately: shouldn't this be true also for the uniqueness of values of field? Does this mean that I have to merge DAL and MODEL into one project?

  2. How would I handle errors caused by a violation of the uniqueness for fieldA in table A, fieldB in table B, fieldC in table C? Using a generic error message "The value already exists" or "Uniqueness violation" is not very descriptive!

  3. Should I insert another project-Business layer which takes care of such error handling?

  4. Should I handle the errors in the (ASP.NET MVC) Action/Controller which does the update?

  5. How to handle a proper error message in a multi language application?

like image 421
Savvas Sopiadis Avatar asked Mar 22 '12 07:03

Savvas Sopiadis


1 Answers

I am facing the same situation and at the moment I am handling the exception in my controller.

Consider the following entity:

public class Part
{
    public int Id { get; set; }
    public string Number { get; set; }
}

I have a unique constraint set up on the 'Number' field in the database so if a duplicate value is entered an exception will be thrown. This is how I am handling the exception:

[HttpPost]
public ActionResult Create(Part part)
{
    if (ModelState.IsValid)
    {
        try
        {
            db.Parts.Add(part);
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        catch (DbUpdateException e)
        {
            SqlException s = e.InnerException.InnerException as SqlException;
            if (s != null && s.Number == 2627)
            {
                ModelState.AddModelError(string.Empty,
                    string.Format("Part number '{0}' already exists.", part.Number));
            }
            else
            {
                ModelState.AddModelError(string.Empty,
                    "An error occured - please contact your system administrator.");
            }
        }
    }
    return View(part);
}

All this does is return to the same view and display a validation error to the user like this:

enter image description here

I'm not sure how 'proper' this is but I can't currently think of a better way to handle this (E.G. even if I caught this in my DbContext derived class and threw a more specific exception I would still need to handle it in the controller in order to do anything with it at runtime).

I'm also not sure if I need to check the inner exception. I modified the code from this post which basically checks for a SqlException in the inner exceptions and checks the error number (in this case 2627 which is a unique key constraint) before reporting it to the user. If the SQL error number is something else a generic error message is displayed instead.

Update:

I now handle exceptions in a domain service class which is a derivative of the example shown here which allows me to handle the exception outside of the controller.

like image 94
Benjamin Gale Avatar answered Sep 20 '22 02:09

Benjamin Gale