在Entity Framework和存储库模式中处理数据库异常的位置

时间:2012-03-22 07:03:56

标签: entity-framework exception error-handling repository

您将如何设计此场景(使用Entity Framework 4.1,Code First和存储库模式): Visual Studio解决方案包含以下项目

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

因此在模型项目中有各种类。假设我们在那里有一个名为User的类,其定义如下(剥离):

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; }

    ....
}

在DAL项目中驻留存储库方法(Insert,Update等)以及Initializer类:

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;
        }
    }
}

我的工作单元类的Commit方法如下所示:

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

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

    return errorMessage;
}

如您所见,我在工作单元类的DbUpdateException方法中处理Commit();这意味着对于每个可能导致更新错误的类,这将在这里处理。

假设使用以下数据插入用户记录:

(UserId,....,Email,...)
1, ... , person1@mail.com , ...
2, ... , person1@mail.com , ...

很明显,这会导致发生DbUpdateException。当然,这可以被捕获并传播到它应该出现的地方。我觉得这种设计是完全错误的:

  1. 每个属性都应单独进行验证:对于字段值的唯一性,这不应该也是如此吗?这是否意味着我必须将DAL和MODEL合并到一个项目中?

  2. 如何处理由于表A中的fieldA,field B中的fieldB,表C中的fieldC违反了fieldA的唯一性而导致的错误?使用通用错误消息“值已存在”或“唯一性违规”不是很具描述性!

  3. 我应该插入另一个负责此类错误处理的项目 - 业务层吗?

  4. 我应该处理执行更新的(ASP.NET MVC)操作/控制器中的错误吗?

  5. 如何在多语言应用程序中处理正确的错误消息?

1 个答案:

答案 0 :(得分:4)

我面临同样的情况,目前我正在控制器中处理异常。

考虑以下实体:

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

我在数据库的“Number”字段上设置了唯一约束,因此如果输入重复值,则会抛出异常。这就是我处理异常的方式:

[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);
}

所有这一切都返回到同一视图并向用户显示验证错误,如下所示:

enter image description here

我不确定这是多么'正确',但我现在无法想到一个更好的方法来解决这个问题(即使我在我的DbContext派生类中发现了这个问题,我也提出了一个更具体的例外情况我仍然需要在控制器中处理它,以便在运行时对它做任何事情。)

我也不确定是否需要检查内部异常。我修改了this post中的代码,该代码基本上检查内部异常中的SqlException,并在向用户报告之前检查错误号(在本例中为2627,这是一个唯一的键约束)。如果SQL错误号是其他内容,则会显示通用错误消息。

<强>更新

我现在处理域服务类中的异常,它是example shown here的衍生物,它允许我在控制器之外处理异常。