locked
No record - place to throw exception RRS feed

  • Question

  • User3112162 posted

    I have Business logic layer and DB layer (Entity framework). For example, I receive some data from DB.

    DB layer:

    public SmartphonePhotographerResponseManage ResponseManage(int RequestID)
    {
    SmartphonePhotographerResponseManage response = (from i in db.SmartphonePhotographerResponses
    where i.RequestID == RequestID
    select new SmartphonePhotographerResponseManage()
    {
    ResponseID = i.ID,
    FormattedAddress = i.EditorialPixlocateRequest.FormattedAddress
    }).FirstOrDefault();
    return response;
    }



    BL layer (it's the simplest example, sense of BL layer - just "throw" result from DB to client (ASP.NET MVC in my case, but no matter). Of course, BL method can have any additional logic):

    public SmartphonePhotographerResponseManage ResponseManage(int RequestID)
    {
    return _repository.ResponseManage(RequestID);
    }




    It works and works fine. But I want to throw my own exception if record does not exists (i.e. record was deleted, but user has link to his bookmarks):

    public class RecordNotFoundException<T> : Exception
    {
    public RecordNotFoundException(T ID) : base(String.Format("No Records for passed ID={0}", ID.ToString()))
    {
    }
    }



    I have 2 way to throw it:
    1. In DB layer:

    public SmartphonePhotographerResponseManage ResponseManage(int RequestID)
    {
    SmartphonePhotographerResponseManage response = (from i in db.SmartphonePhotographerResponses
    where i.RequestID == RequestID
    select new SmartphonePhotographerResponseManage()
    {
    ResponseID = i.ID,
    FormattedAddress = i.EditorialPixlocateRequest.FormattedAddress
    }).FirstOrDefault();
    if (response == null)
    throw new RecordNotFoundException<int>(RequestID);
    return response;
    }



    or in the BL layer:

    public SmartphonePhotographerResponseManage ResponseManage(int RequestID)
    {
    var response = _repository.ResponseManage(RequestID);
    if (response == null)
    throw new RecordNotFoundException<int>(RequestID);
    return response;
    }



    and then catch this exception on client side (controller of ASP.NET MVC for example) and handle it by appropriate way. Both approach will work, but where is more logical place to throw such exception?

    Thursday, February 18, 2016 4:30 PM

All replies

  • User37182867 posted

    personal preference / architectural design of the app. Its up to you and how you want to design it.

    Generally speaking separation of concerns is best and the most consistent.

    Throwing an exception at the client is generally not a good idea. Usually the client is looking for a response, you can add error messages or validation message to the response the client is expecting. The business layer should then handle creating those response error messages and sending them to the client no matter where the exception is thrown. So the DL could throw an exception but might just be better to return a null when the record was not found and leave the Business layer to determine what error to send to the Client.

    Thursday, February 18, 2016 4:44 PM
  • User3112162 posted

    Throwing an exception at the client is generally not a good idea. Usually the client is looking for a response, you can add error messages or validation message to the response the client is expecting. The business layer should then handle creating those response error messages and sending them to the client no matter where the exception is thrown. So the DL could throw an exception but might just be better to return a null when the record was not found and leave the Business layer to determine what error to send to the Client.

    But I don't want to throwing exceptions at the client, I want to throw exception at the BL (or DB) layer and catch it at the client. I.e. I call service void method from client and something is going wrong. You propose to change signature of method and returns string instead of void?

    Thursday, February 18, 2016 7:09 PM
  • User37182867 posted

    I want to throw exception at the BL (or DB) layer and catch it at the client

    In your message to the client from the BL include an error message. IE if using web api, in the response object include an error message properties. Often times we will send a message string that the BL wants to communicate to the user, a code of some kind that the client can use to determine the severity level and display it differently. 

    They way in which you communicate the internal errors is dependent on how your client communicates to the business. If your client is directly calling methods on your BL then all of your BL methods could include an out parameter that would contain information that you might need for the client to display any error messages. If communicating over web services then your soap response object would include properties for error messages. The property could even be a list and include many messages. 

    There is no set way of doing this. Try something and see if it works for you, if not figure out why it doesn't work well and see if you can think of a different way to do it that might be better.

    Friday, February 19, 2016 1:05 PM
  • User-821857111 posted

    where is more logical place to throw such exception?
    In the business logic layer. It is not typically an exceptional situation to query a database and find no matching records. However, the rules of your application state that is should be in certain situations. Therefore you should raise your exception in the business rules/logic layer.

    Friday, February 19, 2016 1:35 PM
  • User3112162 posted

    . If your client is directly calling methods on your BL then all of your BL methods could include an out parameter that would contain information that you might need for the client to display any error messages.

    why need to make interface "dirty", if we just can throw exception and catch it at the client (i.e. controller of ASP.NET MVC) and then client prepares friendly message and send it to ViewModel. My opinion, friendly messages are View layer. Also, BL should not depend at the client (web services, web, windows application etc)

    Saturday, February 20, 2016 11:49 PM
  • User3112162 posted

    Mikesdotnetting

    In the business logic layer. It is not typically an exceptional situation to query a database and find no matching records. However, the rules of your application state that is should be in certain situations. Therefore you should raise your exception in the business rules/logic layer.

    why not typically? I.e. user has url in the bookmarks to resource, which is removed long time ago.

    I have been pondering about this approach and I incline now to throw this type of exception in the Database Layer. Because method can have a workflow like the following (Entity Framework):

    1. get record from Table1

    2. update this record 

    3. add record to the Table2

    4. call SaveChanges

    so, I want to save all these changes in the same transaction (can't separate to 2 or more methods)

    real such method from my application:

           public async Task AcceptFileAsync(int ElementID, string smsSid)
            {
                var element = (from i in db.SmartphonePhotographerResponseElements where i.ID == ElementID select i).FirstOrDefault();
                if (element == null)
                    throw new CommonLibrary.RecordNotFoundException<int>(ElementID);
    
                element.ApprovedByEditorial = true;
                element.SmsSID = smsSid;
                EditorialAcceptedFile acceptedFile_db = new EditorialAcceptedFile();
                acceptedFile_db.DateAccepted = DateTime.Now;
                acceptedFile_db.FilenameUnique = element.FilenameUnique;
                db.EditorialAcceptedFiles.Add(acceptedFile_db);
    
                // 1. update editorial balance (-1 credit)
                var creditEditorial = (from i in db.EditorialCredits where i.UserID == editorialUserID select i).FirstOrDefault();
    
                if (creditEditorial == null)
                    throw new NoEnoughMoney(editorialUserID, 0);
                if (creditEditorial.Credit < 1)
                    throw new NoEnoughMoney(editorialUserID, creditEditorial.Credit);
    
                creditEditorial.Credit -= 1;
    
    
                // 2. add editorial credits history
                EditorialCreditHistory editorialCreditHistory_db = new EditorialCreditHistory()
                {
                    UserID = editorialUserID,
                    Credits = -1,
                    DateOperation = DateTime.Now,
                    RemainingCredits = creditEditorial.Credit
                };
                db.EditorialCreditHistories.Add(editorialCreditHistory_db);
    
    
                // 3. update SP credits (+ dollarsPerCreditSP)
                var balanceSP = (from i in db.SmartphonePhotographerBalances where i.UserID == SPUserID select i).FirstOrDefault();
                if (balanceSP == null)
                {
                    balanceSP = new SmartphonePhotographerBalance() { UserID = SPUserID, Balance = dollarsPerCreditSP };
                    db.SmartphonePhotographerBalances.Add(balanceSP);
                }
                else
                {
                    balanceSP.Balance += dollarsPerCreditSP;
                }
    
    
                // 4. add SP credits history
                SmartphonePhotographerCreditHistory spCreditHistory_db = new SmartphonePhotographerCreditHistory()
                {
                    Credits = 1,
                    Balance = dollarsPerCreditSP,
                    DateOperation = DateTime.Now,
                    UserID = SPUserID
                };
                db.SmartphonePhotographerCreditHistories.Add(spCreditHistory_db);
                /* end money */
    
    
    
                await db.SaveChangesAsync();
            }
    

    So, method does not return any entity (which can be null) but element can be null. Especially, method can select 2 different entities and each of them can be null. So, no way to throw NoRecord exception (and other types of exception) so carefully in BL.

    What do you think?

    Sunday, February 21, 2016 12:03 AM
  • User-821857111 posted

    rover83

    why not typically?

    Because databases don't raise errors or exceptions when no matching records are found. 

    rover83

    user has url in the bookmarks to resource, which is removed long time ago.

    Happens all the time. It's not exceptional. However, your business rules dictate this this should be classified as an error condition. Therefore it belongs in your business rules layer.

    rover83

    I incline now to throw this type of exception in the Database Layer.

    You asked for opinions. Mine is that you are looking in the wrong place. What happens if your business rules change and it is now decided that missing/deleted records are no longer to be treated as errors? Surely that change will be made in your business rules layer? And the last place you want to manage business rules is the database.

    Sunday, February 21, 2016 9:50 PM
  • User37182867 posted

    rover83

    why need to make interface "dirty", if we just can throw exception and catch it at the client (i.e. controller of ASP.NET MVC) and then client prepares friendly message and send it to ViewModel. My opinion, friendly messages are View layer. Also, BL should not depend at the client (web services, web, windows application etc)

    Because I was referencing from an SOA architecture. If you are pure MVC and not using services in between your layers then you have the ability to catch at the controller, using SOA you do not and need to pass that information differently.

    Rule of thumb database only throws errors when it hits an exception it does no business rules logic. Your business layer handles business rules. The controller in the MVC app would be a fine place to give the view what ever error data it needs to display the errors to the client. However, the business layer should trap errors from the DB and then bubble them to the controller if needed. You might find that the business layer would want to throw some validation errors when it receives DB error. IE you might throw a null reference exception instead of a DB faulted error.  

    Monday, February 22, 2016 1:51 PM