Question

We have the following code in our Service Layer where we add a new user to the database using 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);
        }         
    }

And this is called by the Service Layer via this method:

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

We do certain client and server validations before we reach the AddUser however I am not sure about how to handle the unhandled exception(s).

For instance if somehow context.SaveChanges() throws an error how do we bubble the error details back to the Presentation layer?

I am thinking of putting try / catch within the AddUser like this:

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

but the return type of the AddUser is User and I'm not sure what I should be returning when an exception happens. If I can return the exception details to the UserResponse method I can populate some fields within our response object which are there to hold error details, but now sure what to return from within the catch section.

I am trying to understand how to identify exceptions that should be caught and handled in a multi layered application, one with a data access layer, service layer, presentation layer etc. Mainly trying to figure out how to surface the exception details to the user for resolution.

Thanks

Was it helpful?

Solution

I think "BusinessExceptions" are the thing you are looking for.

In a n-tier application it's quite common to have trouble getting or creating business rules exceptions from the service layer. I guess that you are trying to validate some business rules with your example, maybe that the username is valid or that it doesn't already exist in the DB.

For this kind of issues I always work with FaultExceptions and custom objects to send a message "understandable" to the client side. This way I can distinguis between system exceptions, bug exceptions and business rules exceptions (the ones that the user want to know).

This is how I usually do it. I have a BusinessFault object which contains the data of the business rule broken that will be procesed by the client and showed, if necessary, to the user:

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

}

So whenever I need to check a rule I use it like this (using your example):

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

In my service contract I should specify my BusinessFault as a knowntype. This way this faults will get to the client side:

    [OperationContract]
    [FaultContract(typeof(BusinessFault))]
    void AddUser(User user);                              

So in the client-side all I need is a try-catch specific for the BusinessFault:

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

There's a very good article about this: http://www.c-sharpcorner.com/UploadFile/afenster/wcf-error-handling-and-faultexceptions/

OTHER TIPS

You only need to catch the exception in your lower layers if you are going to do something with it. For example, you might catch it, write to an exception log, then re-throw it.

As for return values, you shouldn't, in my opinion, be returning from multiple places.

Rather, I think this is better:

User user = null;

try
{
   //Try to set up user
}
catch(Exception ex)
{
   //Log exception
   throw;
}

return user;

but the return type of the AddUser is User and I'm not sure what I should be returning when an exception happens

That depends - usually, you don't want to return value but you'll log the exception and then call throw; (or you'll throw your own exception that'a wrapping the original exception, your choice) and bubble the exception up, until somewhere you'll catch it, display nice message to the user, and log it.

If you want to show nice message to the user (and/or do the logging somewhere in upper level), use throw new ApplicationException("Failed to create user", ex); this way you can get nice message, and bubble up the exception with the stack trace so you can understand and fix the problem later.

In some cases, you can return null and just log the exception. usually, you do this when you don't want to bubble up the exception, if it's something not critical, that you don't want to exit the current code path because of it.

Good luck!

You don't need to catch exception in Data Access Layer, let them throw naturally. The rule is you just catch exception if you need to do business process on it. If not, let it be.

So, all exceptions will catch on the top layer of application in order to log for trouble shooting.

If your application is web, you can use some log library which supports log handler like Elmah to intercept in request level.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top