我们的服务层中有以下代码,我们使用EF将新用户添加到数据库。
public User AddUser(User user)
{
using (var context = DataObjectFactory.CreateContext())
{
var userEntity = Mapper.Map(user);
context.AddObject("UserEntities", userEntity);
context.SaveChanges();
return Mapper.Map(userEntity);
}
}
这是由服务层通过这种方法调用的:
public UserResponse GetSubmitUser(UserRequest request)
{
var response = new UserResponse(request.RequestId);
var user = Mapper.FromDataTransferObject(request.User);
response.User = Mapper.ToDataTransferObject(UserDao.AddUser(user));
return response;
}
在我们到达AddUser之前,我们会进行某些客户端和服务器验证,但是我不确定如何处理未处理的异常。
例如,如果以某种方式context.SaveChanges()抛出错误,我们如何将错误细节冒泡回到表示层?
我正在考虑在AddUser中添加try / catch,如下所示:
public User AddUser(User user)
{
try
{
using (var context = DataObjectFactory.CreateContext())
{
var userEntity = Mapper.Map(user);
context.AddObject("UserEntities", userEntity);
context.SaveChanges();
return Mapper.Map(userEntity);
}
}
catch (Exception)
{
return ??
}
}
但AddUser的返回类型是User,我不确定发生异常时应该返回什么。如果我可以将异常细节返回给UserResponse方法,我可以在响应对象中填充一些字段来保存错误详细信息,但现在确定从catch部分返回什么。
我试图了解如何识别应该在多层应用程序中捕获和处理的异常,一个带有数据访问层,服务层,表示层等的主要问题。主要是试图弄清楚如何将异常细节表面化用户解决。
由于
答案 0 :(得分:2)
我认为“BusinessExceptions”是您正在寻找的东西。
在n层应用程序中,很难从服务层获取或创建业务规则异常。 我想您正在尝试使用您的示例验证某些业务规则,可能是用户名有效或者数据库中尚不存在。
对于这类问题,我总是使用FaultExceptions和自定义对象向客户端发送“可理解”的消息。这样我可以区分系统异常,错误异常和业务规则异常(用户想要知道的异常)。
这就是我通常这样做的方式。我有一个BusinessFault对象,其中包含将由客户端执行的业务规则的数据,并在必要时显示给用户:
[DataContract]
public class BusinessFault
{
[DataMember]
public BusinessFaultType Type { get; set; }
[DataMember]
public string Details { get; set; }
[DataMember]
public ErrorCode ErrorCode { get; set; }
public BusinessFault(BusinessFaultType type, ErrorCode errorCode)
{
Type = type;
ErrorCode = errorCode;
}
public BusinessFault(BusinessFaultType type, string details, ErrorCode errorCode)
: this(type, errorCode)
{
Details = details;
}
}
所以每当我需要检查规则时,我都会这样使用它(使用你的例子):
public void AddUser(User user)
{
if(!IsValidUser(user))
throw new FaultException<BusinessFault>(new BusinessFault(BusinessFaultType.Validation, "Username",ErrorCode.AlreadyExists);
using (var context = DataObjectFactory.CreateContext())
{
var userEntity = Mapper.Map(user);
context.AddObject("UserEntities", userEntity);
context.SaveChanges();
return Mapper.Map(userEntity);
}
}
在我的服务合同中,我应该将我的BusinessFault指定为知识类型。这样,这些故障将传达到客户端:
[OperationContract]
[FaultContract(typeof(BusinessFault))]
void AddUser(User user);
所以在客户端,我需要的只是针对BusinessFault的try-catch:
try
{
//WebService call
}
catch (FaultException<BusinessFault> ex)
{
var exception = ex.ToBusinessExeption(); //I use extensions Methods to convert it in a custom Exception with the parameters I sent.
throw exception;
}
有一篇非常好的文章:http://www.c-sharpcorner.com/UploadFile/afenster/wcf-error-handling-and-faultexceptions/
答案 1 :(得分:1)
如果您打算使用它,您只需要在较低层中捕获异常。例如,您可能会捕获它,写入异常日志,然后重新抛出它。
至于返回值,我认为你不应该从多个地方返回。
相反,我认为这更好:
User user = null;
try
{
//Try to set up user
}
catch(Exception ex)
{
//Log exception
throw;
}
return user;
答案 2 :(得分:1)
但是AddUser的返回类型是User,我不确定我是什么 应该在异常发生时返回
这取决于 - 通常,你不想返回值但你会记录异常,然后调用throw;
(或者你会抛出你自己的异常'a包装原始异常,您的选择)并将异常冒泡,直到您将捕获它,向用户显示好消息,并记录它。
如果你想向用户显示好消息(和/或在上层某处进行日志记录),请使用throw new ApplicationException("Failed to create user", ex);
这样你可以得到好消息,并用堆栈跟踪冒出异常所以你可以在以后理解并解决问题。
在某些情况下,您可以返回null并只记录异常。通常,当你不想冒出异常时,你会这样做,如果它不是关键的,你不想因为它而退出当前的代码路径。
祝你好运!答案 3 :(得分:1)
您不需要在数据访问层中捕获异常,让它们自然地抛出。如果你需要对它进行业务流程,那么规则就是捕获异常。如果没有,那就让它成为。
因此,所有异常都会占用应用程序的顶层,以便记录故障排除。
如果您的应用程序是Web,则可以使用一些支持Elmah等日志处理程序的日志库来拦截请求级别。