Function Result

See the source code on github.

Use on you project via nuget.

Function Result is a pattern for returning more information from the results of a method call.  With the Function Result pattern and library, you can trap errors and other warnings within your method and then return them back to callers in a standard way.  The signature for a method has 3 parts: the name, the parameters if any, and the result.  When using the Function Result pattern, instead of returning your result directly, you wrap the result in the FunctionResult class from the library.  The FunctionResult class looks like this:

public interface IFunctionResult<T>
    {
        void AddException(Exception ex);
        void AddException(string message);
        FunctionResultExtraData ExtraData { get; }
        FunctionResultMessageList Messages { get; set; }
        FunctionResultOperationStatus OperationStatus { get; set; }
        T ReturnValue { get; set; }
        bool WasSuccessful { get; }
    }

By wrapping your result you can return additional information, errors, and warning to callers in a standard way.  The FunctionResult class is implemented with generics so that you can return your real type easily.  In addition to your return value, you also return an operation status, a list of simple messages that can be used to indicate errors, warnings, or validation failures, and a simple extra data dictionary that you can use to return additional values as well.  The Function Result pattern works best for calls between application boundaries like UI and Business layers.

Basic Example

Callee Code
public class MyService
{
     public IFunctionResult<int> AddTwoNumbers(int x, int y)
     {
          var ret = new FunctionResult<int>();
          try  {
               ret.ReturnValue = x + y;
          }
          catch (Exception ex)  {
               ret.AddException(ex);
          }
          return ret;
     }

     public IFunctionResult<int> DivideTwoNumbers(int x, int y)
     {
          var ret = new FunctionResult<int>();
          try  {
               ret.ReturnValue = x / y;
          }
          catch (Exception ex) {
               ret.AddException(ex);
          }
          return ret;
     }
}
Caller Code
var srv = new MyService();

var addnums = srv.AddTwoNumbers(5, 5);

if (!addnums.WasSuccessful)
{
       //handle errors
        Console.Write(addnums.Messages.GetMessagesAsText());
}
else
{
       //do something with value
       Console.Write(addnums.ReturnValue);
}

In the above example, all methods for MyService return an IFunctionResult instance.  The methods have mostly the same body which includes creating a return value of type FunctionResult, wrapping the main method code in a try/catch which adds the caught exception to the FunctionResult return value, and then returning the FunctionResult to the caller.  The caller then simply checks the WasSuccessful property and proceeds based on the value: if true continue on and do something with the result, if false do something with the errors/warnings/validation failures.

Advanced Example

Callee Code
public IFunctionResult<int> AddTwoEvenNumbers(int x, int y)
{
     var ret = new FunctionResult<int>();

     try
     {
          //test for even numbers
          var mod = x % 2;
          if (mod > 0)
          {
                ret.Messages.AddMessageRuleWarning("Parameter X is not an even number", "X");
                ret.OperationStatus = FunctionResultOperationStatus.FailureValidtion;
          }
          else
          {
                 mod = y % 2;
                 if (mod > 0)
                {
                      ret.Messages.AddMessageRuleWarning("Parameter Y is not an even number", "Y");
                      ret.OperationStatus = FunctionResultOperationStatus.FailureValidtion;
                 }
                 else
                 {
                       ret.ReturnValue = x + y;
                  }
          }
     }
     catch (Exception ex)
     {
          ret.AddException(ex);
     }
     return ret;
}
Caller Code
var srv = new MyService();

var addevennums = srv.AddTwoEvenNumbers(2, 5);

if (!addevennums.WasSuccessful)
{
     switch (addevennums.OperationStatus)
     {
          case FunctionResultOperationStatus.FailureExceptions:
               //Show error messages
               break;

          case FunctionResultOperationStatus.FailureValidtion:
               //Apply validation errors messages to fields on page
               break;

          case FunctionResultOperationStatus.SuccessWarnings:
               //Finish the operation but warn the user about the possible problem
               break;
     }
}

In this example, both the service code and the caller code have been enhanced to provide and handle more information.  The service code now does validation on the input values and if they are not valid, returns to the caller with an operation status of ValidationFailure.  The field that failed and the failure message is also return to the caller via the Messages property on the FunctionResult instance.  The caller code can now inspect the type of failure and perform the appropriate action.

API Documentation

The FunctionResult class has a very simple interface with just a few methods.

Methods
void AddException(Exception ex)

Provides an easy way to add an exception chain to the FunctionResult messages collection.  This will walk the inner exception chain and add a message for each exception.  This allows the caller to get a detailed list of all errors that happened via this exception.  Using this method will automatically set the operation status to FailureExceptions.

void AddException(string message)

This will take a string, create an exception, and add it to the messages collection. Using this method will automatically set the operation status to FailureExceptions.

Properties
FunctionResultExtraData ExtraData

This is a simple data dictionary that can be used to return additional information to a caller.  Values are stored as objects and retrieved via their string key names.  The FunctionResultExtraData class also provides some helper functions to convert to/from Int, String, Date, and Bool values.

FunctionResultMessageList Messages

The messages collection contains any messages set in the callee code.  Messages have the following types:

Undefined = 1, Error = 2, Warning = 3, RuleError = 4, RuleWarning = 5, Success = 6

FunctionResultOperationStatus OperationStatus

The operation status indicates whether or not the function was successful.  Here are the possible outcomes:

Success = 1, SuccessWarnings = 2, FailureExceptions = 3, FailureValidtion = 4

T ReturnValue

The return value is implemented as a generic.  When you create an instance of the FunctionResult class you specify the type for the return value as the first generic parameter.  Setting this property will automatically set the operation status to Success.  You should set this property as the last operation before returning to the caller in your callee code.

bool WasSuccessful

This should be checked by the caller code to determine if the operation was successful.  If an attempt to access the ReturnValue property is made before checking this value, an exception will be thrown.  The return value should not be used if the operation status is not successful.  Throwing the exception helps ensure you always check the operation status before accessing the return value.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>