Entity Framework : 如何捕获任何错误
Entity Framework : how to catch any error
我正在尝试将数据插入 SQL 服务器 table,它有很多 not null
约束:
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代码:
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);
});
尝试将数据添加到 table 时,代码缺少列,因此无法插入数据库。不知道这一点,也没有收到 'NOT NULL' 错误,正如我在 SQL 数据库中看到的那样。
var source = new Customer();
source.FirstName = "Joe"; // missing Last Name and Address
_context.Customer.Add(source);
所以我添加了以下代码。这解决了这个问题,但是我如何让它在任何数据库错误、并发、错误的数据类型等上失败。
try
{
_context.SaveChanges();
}
catch (DbUpdateException e)
{
}
以下无效:
方法 1 和 2:当这些实现时,not null 错误不再像我们想要的那样出现。
try
{
_context.SaveChanges();
}
catch (Exception e)
{
}
try
{
_context.SaveChanges();
}
catch
{
}
try {
var source = new Customer();
source.FirstName = "Joe";
_context.Customer.Add(source);
_context.SaveChanges();
}
catch (Exception ex) {
// ...
}
您可以在 catch 块中检查异常类型以获得准确的异常消息,例如
try
{
//Your code here
}
catch (Exception ex)
{
if (ex.GetType() == typeof(DbEntityValidationException))
{
//Exception thrown from System.Data.Entity.DbContext.SaveChanges when validating entities fails.
}
else
if (ex.GetType() == typeof(DbUnexpectedValidationException))
{
//Exception thrown from System.Data.Entity.DbContext.GetValidationErrors when an
//exception is thrown from the validation code.
}
else
{
//All remaining exception here
}
}
或者您可以对每种异常类型使用不同的 catch 块
try
{
//Your code here
}
catch (DbEntityValidationException de_ex)
{
//Exception thrown from System.Data.Entity.DbContext.SaveChanges when validating entities fails.
}
catch (DbUnexpectedValidationException du_ex)
{
//Exception thrown from System.Data.Entity.DbContext.GetValidationErrors when an
//exception is thrown from the validation code.
}
catch (Exception ex)
{
//All remaining exception here
}
DbContext.SaveChanges 描述了您可能会遇到的异常情况。确定要捕获哪些异常,哪些不需要。
如果您不确定在什么情况下会遇到哪些异常,请使用您的调试器和一些测试代码来找出您实际可能期望的异常:
// TODO: create one of your error conditions
try
{
_context.SaveChanges();
}
catch (Exception e)
{
Console.WriteLine(e.GetType()); // what is the real exception?
}
当您知道可以预期哪些异常以及您真正可以处理哪些异常时,请编写您的最终代码:
try
{
_context.SaveChanges();
}
catch (DbUpdateException e)
{
// handle the update exception
}
catch (DbEntityValidationException e)
{
// handle the entity validation exception
}
catch (...)
您可能不会理解 System.NotSupportedException,您的代码应该只使用受支持的 LINQ 语句。
优化
请记住,DbContext
中的 DbSets
代表数据库中的 table。 DbSets
中的class表示table中的一行:非虚拟属性表示table中的列,table之间的关系s 表示为虚拟属性。
你设计这些数据库 table 是因为你想解决一个问题。显然在您的解决方案中,名字/姓氏等不为空很重要。
您可能会将 DbContext 的使用包装到 class 中,隐藏您使用 entity framework 来保存数据,而不是例如 Dapper 或任何较低级别的方法来保存数据查询和更新数据。
这个包装器 class 经常被称为 Repository
class:您的 Repository
的用户不知道,而且真的不在乎,如何和您保存数据的位置:SQL? Mongo?甚至是 CSV 文件?
拥有 Repository
class 的好处是,如果您决定更改 table 布局,或者如果您决定将其中一个查询更改为存储的过程,或者如果您决定将数据存储在 CSV 中,更改将是最小的,用户甚至不会注意到更改
在您的存储库中,您将拥有查询人员、添加/删除/更新人员等功能。您之前决定您的解决方案不应接受名称为空的人员。
您的解决方案不取决于数据的保存方式。因此,您的解决方案不应取决于您的存储库是否检查您的名称是 null 还是 nt。
考虑在调用 SaveChanges 之前检查数据有效性。在这种情况下:检查名字、姓氏等是否确实不为空。您的代码将
- 看起来更干净:对范围外的各方抛出的异常的处理更少
- 更易于阅读:读者不必猜测如果数据为空会发生什么,
- 更容易测试:您的测试代码可以使用一个简单的模拟存储库,无需空检查
- 更好维护:如果您决定允许添加没有名字的人,您的数据库模型不会改变,只有您的存储库 class
通过使用以下代码,您可以为 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);
}
然后你就可以在更高的层次上捕捉这个新的Exception
;要捕获其他异常,您可以使用单独的 catch
块。
我正在尝试将数据插入 SQL 服务器 table,它有很多 not null
约束:
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代码:
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);
});
尝试将数据添加到 table 时,代码缺少列,因此无法插入数据库。不知道这一点,也没有收到 'NOT NULL' 错误,正如我在 SQL 数据库中看到的那样。
var source = new Customer();
source.FirstName = "Joe"; // missing Last Name and Address
_context.Customer.Add(source);
所以我添加了以下代码。这解决了这个问题,但是我如何让它在任何数据库错误、并发、错误的数据类型等上失败。
try
{
_context.SaveChanges();
}
catch (DbUpdateException e)
{
}
以下无效: 方法 1 和 2:当这些实现时,not null 错误不再像我们想要的那样出现。
try
{
_context.SaveChanges();
}
catch (Exception e)
{
}
try
{
_context.SaveChanges();
}
catch
{
}
try {
var source = new Customer();
source.FirstName = "Joe";
_context.Customer.Add(source);
_context.SaveChanges();
}
catch (Exception ex) {
// ...
}
您可以在 catch 块中检查异常类型以获得准确的异常消息,例如
try
{
//Your code here
}
catch (Exception ex)
{
if (ex.GetType() == typeof(DbEntityValidationException))
{
//Exception thrown from System.Data.Entity.DbContext.SaveChanges when validating entities fails.
}
else
if (ex.GetType() == typeof(DbUnexpectedValidationException))
{
//Exception thrown from System.Data.Entity.DbContext.GetValidationErrors when an
//exception is thrown from the validation code.
}
else
{
//All remaining exception here
}
}
或者您可以对每种异常类型使用不同的 catch 块
try
{
//Your code here
}
catch (DbEntityValidationException de_ex)
{
//Exception thrown from System.Data.Entity.DbContext.SaveChanges when validating entities fails.
}
catch (DbUnexpectedValidationException du_ex)
{
//Exception thrown from System.Data.Entity.DbContext.GetValidationErrors when an
//exception is thrown from the validation code.
}
catch (Exception ex)
{
//All remaining exception here
}
DbContext.SaveChanges 描述了您可能会遇到的异常情况。确定要捕获哪些异常,哪些不需要。
如果您不确定在什么情况下会遇到哪些异常,请使用您的调试器和一些测试代码来找出您实际可能期望的异常:
// TODO: create one of your error conditions
try
{
_context.SaveChanges();
}
catch (Exception e)
{
Console.WriteLine(e.GetType()); // what is the real exception?
}
当您知道可以预期哪些异常以及您真正可以处理哪些异常时,请编写您的最终代码:
try
{
_context.SaveChanges();
}
catch (DbUpdateException e)
{
// handle the update exception
}
catch (DbEntityValidationException e)
{
// handle the entity validation exception
}
catch (...)
您可能不会理解 System.NotSupportedException,您的代码应该只使用受支持的 LINQ 语句。
优化
请记住,DbContext
中的 DbSets
代表数据库中的 table。 DbSets
中的class表示table中的一行:非虚拟属性表示table中的列,table之间的关系s 表示为虚拟属性。
你设计这些数据库 table 是因为你想解决一个问题。显然在您的解决方案中,名字/姓氏等不为空很重要。
您可能会将 DbContext 的使用包装到 class 中,隐藏您使用 entity framework 来保存数据,而不是例如 Dapper 或任何较低级别的方法来保存数据查询和更新数据。
这个包装器 class 经常被称为 Repository
class:您的 Repository
的用户不知道,而且真的不在乎,如何和您保存数据的位置:SQL? Mongo?甚至是 CSV 文件?
拥有 Repository
class 的好处是,如果您决定更改 table 布局,或者如果您决定将其中一个查询更改为存储的过程,或者如果您决定将数据存储在 CSV 中,更改将是最小的,用户甚至不会注意到更改
在您的存储库中,您将拥有查询人员、添加/删除/更新人员等功能。您之前决定您的解决方案不应接受名称为空的人员。
您的解决方案不取决于数据的保存方式。因此,您的解决方案不应取决于您的存储库是否检查您的名称是 null 还是 nt。
考虑在调用 SaveChanges 之前检查数据有效性。在这种情况下:检查名字、姓氏等是否确实不为空。您的代码将
- 看起来更干净:对范围外的各方抛出的异常的处理更少
- 更易于阅读:读者不必猜测如果数据为空会发生什么,
- 更容易测试:您的测试代码可以使用一个简单的模拟存储库,无需空检查
- 更好维护:如果您决定允许添加没有名字的人,您的数据库模型不会改变,只有您的存储库 class
通过使用以下代码,您可以为 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);
}
然后你就可以在更高的层次上捕捉这个新的Exception
;要捕获其他异常,您可以使用单独的 catch
块。