My Create
, Update
and Delete
methods should return a success
and errors
list depending up on the result. I want to avoid the out parameters in the method. Therefore I came up with an implementation of ResultModel
class which gets returned for the above mentioned methods.
Is my implementation elegant enough to go ahead with the real world project? Or could there be other better ways to return the result?
Here is the sample code which actually works. I have inserted comments
in the code where I think is necessary.
public sealed class CountryModel
{
public string CountryName { get; set; } //mandatory and upto 32 chars
public string CountryCode { get; set; } //mandatory and upto 5 chars
}
/// <summary>
/// To hold the results, be it the list of errors or just the success message.
/// </summary>
public sealed class ResultModel
{
private object _success; // to hold boolean and int value.
public object Success { get { return _success; } }
private List<string> _errors; // to hold multiple errors, could be validation errors.
public List<string> Errors { get { return _errors; } }
public void AddSuccess(object any)
{
this._success = any;
}
public void AddError(string text)
{
if (string.IsNullOrEmpty(text))
{
return;
}
if (this._errors == null)
{
this._errors = new List<string>();
}
this._errors.Add(text);
}
}
public sealed class CountryManager
{
private const string EmptyErrorSuffix = "is empty.";
private const string LengthErrorSuffix = "has exceeded it's length.";
private const short CountryCodeMaxLength = 5;
private const short CountryNameMaxLength = 32;
public ResultModel AddCountry(CountryModel model)
{
var result = this.ValidateTheModel(model);
if (result.Errors == null) //if no errors then perform the Database operation, in this case Add a record.
{
try
{
result.AddSuccess(DatabaseMockClass.Add(model));
}
catch (Exception ex)
{
result.AddError(ex.Message); //add to the same model, if DB operation throws a unhandled exception.
throw;
}
}
return result;
}
private ResultModel ValidateTheModel(CountryModel model)
{
var _result = new ResultModel();
if (model == null)
{
_result.AddError(string.Format("Country Model{0}", EmptyErrorSuffix));
}
else
{
_result.AddError(CheckString(model.CountryName, "Country Name", CountryNameMaxLength));
_result.AddError(CheckString(model.CountryCode, "Country Code", CountryCodeMaxLength));
}
return _result;
}
private string CheckString(string text, string fieldName, short maxLength)
{
string errorText = string.Empty;
if (string.IsNullOrEmpty(text))
{
errorText = string.Format("{0} {1}", fieldName, EmptyErrorSuffix);
}
else
{
if (text.Length > maxLength)
{
errorText = string.Format("{0} {1}", fieldName, LengthErrorSuffix);
}
}
return errorText;
}
}
internal static class DatabaseMockClass
{
public static int Add(CountryModel model)
{
return 1; //Assume everything went well and return the Identity Value from the insert which is CountryId
}
public static bool Update(CountryModel model)
{
return true;
}
public static bool Update(int countryId)
{
return true;
}
}
Here is the Program
class to call the above code for testing. It also has some helper methods which are mentioned in the Main
function.
using System;
namespace TestConsole
{
public class Program
{
static void Main(string[] args)
{
HelperAddMethod(string.Empty, "123456"); //Returns:
//Country Name is empty.
//Country Code has exceeded it's length.
HelperAddMethod(string.Empty, "12345"); //Returns: Country Name is empty.
HelperAddMethod("Anywhere in the world.", "12345"); //Returns: 1.
}
private static void HelperAddMethod(string countryName, string countryCode)
{
var model = new CountryModel()
{
CountryName = countryName,
CountryCode = countryCode
};
var result = new CountryManager().AddCountry(model);
if (result.Errors == null)
{
PrintSuccess(result);
}
else
{
PrintErrors(result);
}
}
private static void PrintErrors(ResultModel model)
{
foreach (var item in model.Errors)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
private static void PrintSuccess(ResultModel model)
{
Console.WriteLine("Success value: {0}.", model.Success);
Console.ReadKey();
}
}
}
For validations, I will be using FluentValidation
library. So, the validations I have written here is just a sample (I didn't want to complicate this question's code).
exceeded it's length
- should beits
. Also instring.Format("Country Model{0}", EmptyErrorSuffix)
, you need a space e.g.Model {0}
. \$\endgroup\$object
instead of a generic type parameter? \$\endgroup\$List<T>
for example: you can create aList<string>
, which only holds strings, or aList<int>
, which only holds integers. Likewise, if you turnResultModel
into a generic class, you can create aResultModel<bool>
or aResultModel<int>
, and have theirSuccess
property be typed accordingly. This makes it obvious what your methods return, and prevents you from accidentally returning (or casting to) the wrong type. \$\endgroup\$