9

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, ... , person1@mail.com , ...
2, ... , person1@mail.com , ...

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?

Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100
Savvas Sopiadis
  • 8,213
  • 10
  • 34
  • 53

1 Answers1

5

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.

Community
  • 1
  • 1
Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100