Working with Dapper (Part 2)

One of the problems with trying to define interface method signatures is that it can involve a lot of guesswork and, therefore, a lot of risks. At design time, you can only assume so much. A few months after, it's possible that you'll encounter scenarios that might not be supported. Signatures that are not good enough is an interface designer's nightmare. Years down the line, changing an interface is like declaring war!

You learn something, you can forget. You discover something, you'll always remember. You experience something, it becomes a part of who you are. Experience inspired me to create the ResultInfo class. I just know for sure it will be useful. Procedures can return messages to the caller. They can be about success, failure, something in between, and/or just something for the sake of messaging back. ResultInfo gives results structure.

ResultInfo.cs
using System.Collections.Generic;

namespace Mendz.Library
{
    public class ResultInfo
    {
        public string SourceName { get; set; }

        public Dictionary<string, object> InputValues { get; set; }

        public Dictionary<string, object> OutputValues { get; set; }

        public int AffectedCount { get; set; }

        public List<ResultInfo> ResultInfos { get; set; }

        public ResultInfo(string sourceName, 
            Dictionary<string, object> inputValues, 
            Dictionary<string, object> outputValues, 
            int affectedCount = 0)
        {
            SourceName = sourceName;
            InputValues = inputValues;
            OutputValues = outputValues;
            AffectedCount = affectedCount;
        }

        public ResultInfo(string sourceName, 
            Dictionary<string, object> outputValues, 
            int affectedCount = 0)
            : this(sourceName, null, outputValues, affectedCount)
        {
        }

        public ResultInfo(string sourceName, int affectedCount)
            : this(sourceName, null, null, affectedCount)
        {
        }
    }
}
This code defines a ResultInfo class that can store a set of result information. SourceName can store a procedure name. InputValues lists the parameters passed to SourceName. OutputValues lists the return values from SourceName. AffectedCount provides a count of how much SourceName may have affected. ResultInfos lists ResultInfo instances returned by subprocedures, thus enabling hierarchical results to be gathered.

All of the CRUDS interface methods have an out List<ResultInfo> parameter. Whether they will be used or not doesn't matter. What's important at this point is that if it becomes needed, something is available for developers to use.

Experience also told me that you can never know how much will be needed by a procedure to complete a requirement. You can only define an interface once. In the back of your head, you just know you need to support everything. Further back up your noodle, you just know you can't support everything. Fortunately, .Net has such a thing called dynamic types. Call it bad design or what have you, I need something that can be used as a fallback. If there's no place for it (yet?), I need something that can be easily used as an expansion to the interface's signature. Thus, all CRUDS interface methods have a dynamic expansion parameter. Love it or hate it, it's my disclaimer... a waiver... a lazy (but surprisingly powerful) solution for everything that I can never think of.

In order to design the CRUDS interface method signatures, I first needed to solve the problem about gathering results that can be evaluated as messages. The ResultInfo class enables procedures to communicate with the caller, especially when throwing exceptions does not make sense. Second, I needed a way to support some form of instant expansion to the interface's method signatures when needed. Although not exactly designed for that purpose, I used .Net's dynamic type as a solution. These two elements are enough to give me the confidence I needed to follow a "template" for my CRUDS interface method signatures.

    <returnType> <MethodName>(
        <input...>, 
        dynamic expansion, 
        [output...,] 
        out List<ResultInfo> returnValues);

Comments