A pattern / library for methods that return a status, including localization

Last Updated: January 26, 2023 | Created: January 26, 2023

This article is about a pattern / library that allows you to create methods that can return a status, that is a success / fail value, plus the error messages – I call this “returning a status”. It then goes on to describe another library called Net.LocalizeMessagesAndErrors (shortened to Localize-ME) that returns a status where the errors can be in multiple languages (known as localization).

This article talks about a library called GenericServices.StatusGeneric (shortened to StatusGeneric) that implements the “return a status” pattern. Then it talks about a different library called Net.LocalizeMessagesAndErrors (shortened to Localize-ME) that contains an implementation of “return a status” pattern, but handles multiple languages (known as localization) of the error messages.

This article is part of series about handling localization. The full list of the articles in this series are:

  1. Improving the support of multiple languages in .NET applications
  2. Applying the improved multiple languages support to .NET applications
  3. A pattern / library for methods that return a status, including localization (this article)

TL;DR; – Summary of this article

  • The “return a status” pattern is useful wherever a method could return an error.
  • The StatusGeneric library provides a simple, but powerful implementation of the “return a status” pattern and this article provides information on how to use the StatusGeneric library.
  • A second library, Localize-ME, contains an implementation of  the “return a status” pattern, where the error’s message can be returned in different languages. The localization part uses Localize-ME library, which has extra features on top of the .NET localization services.

1. Introduction to the StatusGeneric library

In 2014 I created a library using Entity Framework (known as EF6) which contains the “return a status” pattern inside it. Then in 2019, when .NET and EF Core were stable, I built a number of libraries that used the “return a status” pattern, so I created the standalone StatusGeneric library so that I could use it in lots of places / libraries. So far, this library has been downloads > 200,000 times so obviously others find it useful too.

The following subsections will introduce you to the StatusGeneric library and how you can use it, starting with what the returned status contains.

The returned status: IStatusGeneric and IStatusGeneric<out T>

The key of the “return a status” is the IStatusGeneric, which defines what the returned status contains. This list below defined each property in the

I created an interface, which I refer to as a status, that is returned to the calling method. This interface, called IStatusGeneric, has the following properties:

  • A IReadOnlyList Errors property where errors are stored. Each error is contained in a ValidationResult class, which contains the error message and optionally the name of the member that error is linked to (ASP.NET Core uses this to show the error next to the actual entry that has an error.
  • A boolean IsValid property, which is true if there are no errors.
  • A boolean HasErrors property, which is true if there are errors.
  • A string Message property, which can contain a success message if there aren’t any Errors or contains “Failed with nn errors” if there are in the Errors property.

I also created the IStatusGeneric<out T> interface, which adds a Result property to the IStatusGeneric for methods that want to return a value as part of the status. The Result property is set to default if there are errors to ensure that

How to use the StatusGeneric library in your code

The first step is to add the StatusGeneric’s NuGet package to the project you want use the StatusGeneric library. Then you can create a method that returns a status, as shown below. Below the code I give a list of the various parts in the code:

public IStatusGeneric CheckNull(string? month)
{
    var status = new StatusGenericHandler();
    status.Message = "All went well";

    if (month == null)
        return status.AddError("input must not be null", 
             nameof(month));

    return status;
}

The list below contains a description of the use of the StatusGeneric library in the method above

  • Line 1: The method returns a class that matches the IStatusGeneric.
  • Line 3: You need to create a status using the StatusGenericHandler constructor. This sets up an empty status, i.e. has no errors.
  • Line 4: Optionally you can set a success Message. Note that if there are errors, then the Message contains the string “Failed with nn errors”;
  • Line 7: This adds a error to the status and then returns the status
    • Line 8: Optionally you can add the name of the member that has the error.
  • Line 10: This returns the status. In this case there are no errors, so it is valid.

NOTE: The name of the member that has the error (line 8) must have the actual name of the property in a class, which in the case above would be Month, not month. I use a method called CamelToPascal (see this extension class in the Localize-ME library) that changes the first character to a capital character.

And the code below provides an example of how use the CheckNull method in an ASP.NET Core application, with a list of the various parts after the code:

NOTE: You need to add the EfCore.GenericServices.AspNetCore NuGet package to obtain access to the  CopyErrorsToModelState method to copy and errors to the Razor pages.

[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult CheckNull(string? month)
{
    var status = _exampleMethods.CheckNull(month);
    if (status.IsValid)
        return RedirectToAction(nameof(Index), 
             new { message = status.Message });

    status.CopyErrorsToModelState(ModelState);
    return View();
}

The list below contains a description of how the CheckNull method could be used in an ASP.NET Core application:

  • Line 5: You call the CheckNull method and you get back a status, which is of type IStatusGeneric.
  • Line 6: If the status is valid, then you go back to the Index page.
  • Line 8: The success message from the CheckNull method to show to the user.
  • Line 9: Otherwise, the status has errors, so we want to show the error(s).
  • Line 10: Using the CopyErrorsToModelState method from EfCore.GenericServices.AspNetCore library the errors (which are stored using the ValidationResult class) are added to the ASP.NET Core’s ModelState.
  • Line 11: This returns to the Get action and the error(s) in the ModelState are shown to the user.

Different ways to add errors to the status

The last section provided a simple example of using the StatusGeneric library, but in real life the checking for errors can be must more complex. Here are some of the situations you might come across and how the StatusGeneric library:

  1. Using multiple checks to return all the errors to the user
  2. Combining errors from multiple statuses

1. Using multiple checks to return all the errors to the user

The example below show you might apply many checks so that the user gets all the errors in one go. This pattern is good for users as all the errors are returned at the same time.

public IStatusGeneric CheckPassword(string password)
{
    var status = new StatusGenericHandler();

    //series of tests and then return all the errors together
    //Good because the user gets all the errors at once
    if (password.Length < 10)
        status.AddError("A password must be 10 or more in length",
            nameof(password));
    if (!password.Any(char.IsUpper))
        status.AddError("A password must contain an upper case character",
            nameof(password));
    if (!password.Any(char.IsLower))
        status.AddError("A password must contain a lower case character",
            nameof(password));
    if (!password.Any(char.IsDigit))
        status.AddError("A password must contain an number",
            nameof(password));
    
    return status;
}

2. Combining errors from multiple statuses

Sometimes the testing for errors is best coded by called to other “return a status” methods so the StatusGeneric library has a CombineStatuses method. This method will copy the errors from one the called “return a status” method into the caller’s status.

The code below shows an example of logging in with tests on the email, password and the actual login part and, if successful, then returns the userId.

public IStatusGeneric<string> Login
    (string email, string password)
{
    var status = new StatusGenericHandler<string>();

    status.CombineStatuses(
        CheckValidEmail(email));

    if (status.HasErrors)
        return status;

    if (status.CombineStatuses(
            CheckPassword(password)).HasErrors)
        return status;

    var loginStatus = LoginUser(email, password);
    status.CombineStatuses(loginStatus);

    status.SetResult(loginStatus.Result);

    return status;
}

The list below contains a description of how the Login method works:

  • Line 1: The Login method will return the logged-in user’s userId.
  • Lines 6,7: The Login method calls a CheckValidEmail method that returns a status which is copying into the caller’s status via the CombineStatuses method.
  • Lines 9,10: This returns the combine status if the status has errors.
  • Lines 12 to 14: This shows shorter way to return on a combined status that has errors.
  • Lines 16: The LoginUser method returns a status that contains (if there are no errors) the logged-in user’s userId.
  • Line 17: Its status is combined to pick up any errors.
  • Line 19: This sets the string Result to send back with the method’s status. NOTE: if there are errors the Result is set to default, which for a string is null.

Real-world examples of using the StatusGeneric library

I built a large library called AuthPermissions.AspNetCore (shortened to AuthP) which allow developers to create multi-tenant applications (and other features). Up to version 4.0.0 the AuthP  library uses the StatusGeneric library, and here is a link to AuthTenantAdminService in AuthP 4.0.0 version, which shows it handles errors (tip: start at line 126, because that’s where the error handling starts).

2. How to use the Localize-ME library

And in the end of 2022 I created another library, referred to as to Localize-ME, that includes a version that supports the StatusGeneric’s interfaces, but handles multiple languages of the error messages (known as localization). I created this library to add localization to my AuthP library and because because the AuthP already used the StatusGeneric library I added the StatusGenericLocalizer / StatusGenericLocalizer<T> classes to replace the StatusGenericHandler / StatusGenericHandler<T> classes.

The design of the StatusGenericLocalizer (and its IDefaultLocalizer service) is to have a default message / error within your code, in your selected language you define when register the IDefaultLocalizer service. This has two benefits, a) the code is easier to understand and b) if localization isn’t turned on it can still provide message / error (useful in NuGet packages).

NOTE: The Localize-ME library was build to add features that overcome certain restrictions in the .NET localization services – see this article for more on this.

The Localize-ME library has comprehensive documentation so this section is really about understanding the differences between the StatusGeneric library and the StatusGenericLocalizer class and its IDefaultLocalizer service in the Localize-ME library.

How the StatusGenericLocalizer is different from the StatusGeneric?

The code below does exactly as the StatusGeneric shown in the “How to use the StatusGeneric library in your code” section ?LINK? but it’s a lot longer than the StatusGeneric version. That’s because we need to provide the service and keys to display the Message and Errors in different languages. The list after the code explains the different parts from the original StatusGeneric version ?LINK?.

public IStatusGeneric CheckNull(string? month)
{
    var status = new StatusGenericLocalizer(
         _defaultLocalizer);
    status.SetMessageString(
        "Success".ClassMethodLocalizeKey(this, false), 
        "All went well");

    if (month == null)
        return status.AddErrorString(
            "NullParam".JustThisLocalizeKey(this), 
            "The input must not be null.", 
            nameof(month).CamelToPascal())

    return status;
}

The list below contains a description of the use of the StatusGenericLocalizer class in the method above:

  • Lines 3,4: The StatusGenericLocalizer constructor needs a IDefaultLocalizer service. This requires to localize your application and  register the IDefaultLocalizer service on startup.
  • Lines 5 to 7: This adds a default success Message with two parts:
    • Line 6:  This creates a unique key (see this document) to lookup the message/error in the localization resource files.
    • Line 7: This is the message to use if the culture (language) of the user / app matches the default culture. Note that if there are errors, then the Message contains the string “Failed with nn errors”;
  • Lines 10 to 13: This adds an error to the status and then returns the status
    • Line 10: This a method that adds an error in the form of a constant string. Other methods allow FormattableStrings, which can contain dynamic data in the error.
    • Line 11: Creates a key to lookup the error message in the localization resource files.
    • Line 12: The error string in the default culture.
    • Line 13: Optionally you can add the name of the member that has the error. In this case the Localize-ME library contains an implementation of the CamelToPascal method, which makes the member name used in a class.

Real-world examples of using the Localize-ME library

As I said I have a library called AuthP and I released version 4.1.0 with localization via the Localize-ME library. In the “Real-world examples of using the StatusGeneric library” section ?LINK? I gave you a link to the AuthTenantAdminService in AuthP 4.0.0, before the localization.

This link to the AuthTenantAdminService comes from the main branch, which does the same as the AuthP 4.0.0 version, but it supports localization (tip: start at line 128, because that’s where the error handling starts).

Conclusions

The “returning a status” method pattern is simple, but powerful pattern. I have used this pattern so many times that I build “return a status” code in 2014. Then in 2019, when .NET was stable, I released the StatusGeneric library with an improved version over the 2014 implementation due to feedback of using the first version for many years.

What I didn’t do well on StatusGeneric library was documentation, and this article is one way to fix that issue, although I have also improved the StatusGeneric’s README file a few times to provide better documentation.

The Localize-ME library DOES have good documentation right from the start, with this page for the StatusGenericLocalizer classes. It needed the documentation because localization is MUCH more complex than the “return a status” pattern, but if you want to support multiple languages then you need it.

Happy coding.

5 1 vote
Article Rating
Subscribe
Notify of
guest
1 Comment
Newest
Oldest Most Voted
Inline Feedbacks
View all comments