Intro

I’ve been working in a multitenant application where some services need to know what’s the current tenant for each request. It would be possible to identify the tenant in upper layers (controllers) and pass that information all the way down to the services, but that generates a lot of repetitive/polluted code. A natural idea is to think about using Dependency Injection to pass this information around.

Injecting Runtime Data into Services

Immutable data (like connection strings or any other configuration data that is used during app initializatio) are things that certainly need to be injected, and are NOT considered runtime data.

Runtime data is information that may change across requests, like current logged user, or current tenant (organization).

Using dependency-injection to inject runtime data into your services is a common but controversial practice. The argument in favor of injecting runtime info is that it can improve code readability/maintenance by avoiding having to add a parameter all the way up the chain (in all direct and indirect consumers).

This is an example of how we can inject runtime data into our services:

// Registration
services.AddTransient<ClaimsPrincipal>(s =>
    s.GetService<IHttpContextAccessor>().HttpContext.User);

// Usage
public class MyService
{
    public MyService(ClaimsPrincipal user)
    {
        // now you have user.Identity.Name;
    }
}

In the example aboveClaimsPrincipal is calculated by ASP.NET Identity middleware (it’s a cheap call based solely on cookies) and it’s saved into HttpContext.User. So whoever calls this service can just invoke it without having to pass the current logged user.

Using the same idea it would be possible to add to the pipeline a middleware to resolve the current tenant and make it available in HttpContext:

app.Use(async (context, next) =>
{
    var context = context.RequestServices
        .GetRequiredService<IHttpContextAccessor>().HttpContext;
    var tenant = await ResolveTenantAsync(context);
    context.Items["Tenant"] = tenant;
    await next.Invoke();
});

// Registration
services.AddTransient<ITenantContext>(s =>
    (ITenantContext) s.GetService<IHttpContextAccessor>().HttpContext.Items["Tenant"]);

The first problem here is that this approach forces all requests in the pipeline to resolve a Tenant (which might be expensive), so unless you really need it in all requests (or unless you can decide based on the request if you need it) then I think it’s a better idea to just rely on DI and let the container resolve your tenant only when required (and cache it), instead of resolving in the request pipeline.

The other problem here is that your runtime data have a short lifetime and you don’t want to inject that by mistake into objects with a longer lifetime (like singletons). In other words, if MyService is a singleton then it would be storing internally the identity of the user making the first request, and all subsequent requests (from other users) would be associated to the wrong user, so you will have to correctly (re)define the lifetime of your components.

So injecting runtime data is a bad idea?

Many developers believe that you should not inject runtime data to services. This article explains that components should ideally be stateless, and that runtime data should not contain behavior and should be passed across methods (instead of injected into constructors).

But then the same article explains that passing runtime data across a chain of callers can pollute the code, and shows examples of components that can benefit from having runtime data injected into it (Repository and more specifically Unit of Work).

Injecting runtime data is especially helpful for lower layers (like data-access) because the lowest you go the more layers you would have to modify just to pass repetitive/boilerplate data (all the way up to the layer where the runtime data can be obtained).

To sum we want a solution where we don’t need to pass repetitive runtime data through a bunch of layers but where we don’t have to directly inject transient runtime data as it would affect the lifetime of our components.

Adapters

Those problems can be solved writing an adapter to resolve your runtime data (e.g. identify your tenant). The lifetime mismatch is now handled in a single place:

public class AspNetTenantAccessor : ITenantAccessor
{ 
  private IHttpContextAccessor _accessor;
  public AspNetTenantAccessor(IHttpContextAccessor accessor)
  {
    _accessor = accessor;
  }

  // or maybe Task<AppTenant> GetCurrentTenantAsync()
  public AppTenant CurrentTenant
  {
    get
    {
      // resolve (only when required) based on _accessor.HttpContext
      // cache it (per-request) in _acessor.HttpContext.Items["Tenant"]
    }
  }
}

// Registrations:
builder.Services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddSingleton<ITenantAccessor, AspNetTenantAccessor>();

Now in your service layer you can just inject ITenantAccessor and get the current tenant (or any other runtime data), and we won’t have runtime dependency-resolution problems (we don’t need to test if all different consumers are correctly resolving the current tenant).

You may be wondering: Why can’t you just inject IHttpContextAccessor and get the current tenant directly from HttpContext.Items["Tenant"]? That’s because it would be a leaky abstraction - you don’t want your services to be tightly coupled to this Http abstraction (it would be harder to reuse them from other callers like unit tests).

So is that the “right way” of doing it? What does “Microsoft tells us to do”?

In 2015 David Fowler (from ASP.NET Core team) showed an example of how to Inject ClaimsPrincipal in Service Layer by using an IUserAccessor adapter (exactly like our example above):

public class UserAccessor : IUserAccessor
{ 
      private IHttpContextAccessor _accessor;
      public UserAccessor(IHttpContextAccessor accessor)
      {
           _accessor = accessor;
      }
      public ClaimsPrincipal User => _accessor. HttpContext.User;
}

That example alone might be interpreted as if injecting transient runtime data is just plain wrong, but then more recently in 2021 someone suggested that ClaimsPrincipal should be made available (as injectable) in ASP.NET Core Minimal APIs (avoiding dependence on IHttpContext(Accessor), and the same David Fowler accepted that suggestion and the PR that registers ClaimsPrincipal / HttpRequest / HttpResponse (they are all transient runtime data) - so this is a hint that the “don’t inject runtime info” rule is not set in stone, and might be just a design choice.

Configurable Pipeline

So now I have an ITenantAccessor interface, and I can switch between multiple implementations.

For ASP.NET I could have an accessor that would identify tenants by the hostname, for local (localhost) debugging or for tests I can use a different accessor that would fake my test tenant.

But then there was another requirement: tenants will be usually identified by their hostnames, but in some cases the same hostname will be shared among all tenants and they should be identified by the request path or by some request header or cookie. So I wanted a configurable pipeline where I can eventually add or remove some steps, and more importantly: I don’t need to run all steps - each step may short-circuit the next steps.

The Pipeline Design Pattern is a common design-pattern where we have a Pipeline with a list of steps and they are processed in order, usually one task builds upon the results of the previous task. One nice implementation of this pattern is the ASP.NET Core Request Pipeline, where each step creates (and runs) a Task that receive the next step as input and it can can short-circuit the request (skip the next steps) or it can proceed to the next step in the pipeline. I was familiar with using this request pipeline (e.g. adding middlewares) but I hadn’t had a chance to look how it works internally, so I decided to make something similar, which led me to this StackOveflow answer that led me to the source of Microsoft.AspNetCore.Builder.ApplicationBuilder.Build()

Basically this is how it works:

  • Each step in the pipeline is a Task, but they are registered as a factory Func that get the next Task (if any) and create the step Task
  • Since those Func factories always get the next pipeline step (the next Task) each Task can decide to run the next step or skip it (short-circuit).
  • The Build() method starts by creating the Task for the last step, which is a fallback step to check if things went fine or if there is a misconfigured pipeline. Then it goes in reverse order through the previous pipeline steps, chaining each step with the subsequent step. The last Task created in the loop is actually the first step where the pipeline starts, and that’s what’s returned by Build().
  • Tasks from ASP.NET request pipeline don’t return anything and besides the next Task they also get HttpContext as input.
  • In my case I don’t get any input but each step can return a Tenant.

Based on that code I’ve created a quick implementation to resolve tenants using a generic/configurable pipeline:

public class PipelineTenantResolver<TTenant> : ITenantAccessor
{
    // Steps from my pipeline don't get anything, 
    // and may short-circuit to return a TTenant
    public delegate Task<TTenant> IPipelineTenantRepositoryDelegate();

    // Steps from ASP.NET pipeline (for comparison) get HttpContext 
    // but don't return anything:
    //public delegate Task RequestDelegate(HttpContext context)

    protected List<Func<IPipelineTenantRepositoryDelegate,
        IPipelineTenantRepositoryDelegate>> _steps = new();

    // After Build() we cache pipeline first step
    protected IPipelineTenantRepositoryDelegate? _built;

    public void Use(Func<IPipelineTenantRepositoryDelegate,
        IPipelineTenantRepositoryDelegate> step)
    {
        _steps.Add(step);
        _built = null;
    }

    protected IPipelineTenantRepositoryDelegate Build()
    {
        // We start building pipeline with last step
        IPipelineTenantRepositoryDelegate app = () =>
        {
            // If we reach the end of the pipeline then no Tenant was found
            throw new TenantNotFoundException();

            // A non-throwing alternative would be:
            //return Task.FromResult(default(TTenant)!);
        };

        // Then we go back passing the next step to each previous step
        for (var c = _steps.Count - 1; c >= 0; c--)
            app = _steps[c](app);

        _built = app;
        return app;
    }

    public async Task<TTenant?> GetTenantAsync() => await (_built ?? Build()).Invoke();
}

Sample test:

var pipelineResolver = new PipelineTenantResolver<AppTenant>();

// First step
Task<AppTenant> resolveTenant = Task.FromResult(new AppTenant());
pipelineResolver.Use((next) => {
    if (shouldShortCircuit)
        return () => resolveTenant;
    return next;
});
// next steps...
//pipelineResolver.Use(next => next); // etc..
// Last step (defined in Build()) will just throw

var tenant = await pipelineResolver.GetTenantAsync();

Finally, in my services registration I register different implementations for ITenantResolver depending on the entry-point (web app, web api, unit tests, etc), and Task<AppTenant> is registered with a factory that depends on ITenantResolver.ResolveAsync():

builder.Services.AddTransient<Task<AppTenant>>(sp => sp.GetRequiredService<ITenantAccessor>().ResolveAsync());

PS: all code above assumes that resolving a tenant is an async call, that’s why I’ve registered a Task<> (if the code wasn’t async it would be even easier). If you want to inject directly the resolved AppTenant (instead of a Task<AppTenant>) that would be a blocking call (not recommended in constructors) and mixing sync with async (not recommended). In Autofac that’s possible (registering a factory that uses async code), I’m not sure if that’s also possible in Microsoft.Extensions.DependencyInjection - but anyway - blocking calls during injection/constructors is not recommended so it’s best to inject Task<AppTenant> instead.

It’s been a while since I don’t write anything, so I hope you enjoyed this article.

A few days ago my wife just deleted by mistake some old videos from our daughter (when she was like saying her first words) which were only stored in her WhatsApp. I don’t even have to mention how those videos were important to us (she wasn’t following my advice about how to backup her media) - so I had to help recover the videos.

Since WhatsApp stores backups in Google Drive I thought I could just uninstall and reinstall and it would get the backups. That WOULD be easy, but her WhatsApp was still using an old phone number from Brazil which wasn’t even here with us in America (yeah, I know - she just didn’t want to change the number that all her family had, etc). Since we wouldn’t be able to reactivate WhatsApp using that old number the first thing I made was updating her number, to allow me to restore from the Google Drive backup of previous week.

After we updated her WhatsApp number, I disabled backups or else I would be overwriting the backup of the previous week and would lose the videos she was looking for.

So after uninstalling and reinstalling the app, I’ve learned the hard way that WhatsApp backups (both the messages and the media) which were taken using one phone number cannot be restored to a new number. So basically I lost not only all her messages but also all her media.

So then I found this great tool which allows us to Extract (download) the full WhatsApp backup from Google Drive: https://github.com/YuriCosta/WhatsApp-GD-Extractor-Multithread.
Basically you get your device id, then you fill it together with your Google username/password, and the script will download all pictures/videos and even the encrypted backup of the chats (msgstore.db.crypt14).

Then to restore her chat history back we had to change her number again back to the old number in Brazil, and ask her family in Brazil to get the activation code for us, so we could restore from the Google Drive Backup.

In the end of the day, I didn’t even had to dump the Google Drive backup, but at least I learned something new that can be useful.

And also, while trying to recover her messages, I learned about this other whapa toolkit which contains tools to encrypt or decrypt WhatsApp datastore (requires private key), merge two data stores (merge chat histories from different phones, also requires private key), as well as extracting media from Google Drive (I haven’t tested) or iCloud. If you’re trying to decrypt new format (15) you may need this other tool.

And last: if you need to extract the private key of your WhatsApp and your Android is not rooted, you can use this tool - https://github.com/YuvrajRaghuvanshiS/WhatsApp-Key-Database-Extractor (I had to use python wa_kdbe.py --allow-reboot) which basically will install an old build of WhatsApp and exploit it to dump the private key using adb.

Fun learning anyway.

Exceptions vs Error Codes

A question that frequently arises in software engineering is whether/when you should throw exceptions or return error codes.

There are many different opinions about this subject, and there’s a large number of C#/Java developers who throw Exceptions to indicate all kinds of errors. However, there’s also a large number of developers who believe that Exceptions should not be used as control flow and argue that exceptions act like non-local GOTO statements but even more evil because it’s difficult to know where the control goes to (some people compare exceptions to invisible gotos or gotos which lead to invisible labels).

I tend to agree with the second group: I think exceptions are for unexpected and unhandleable situations - when there’s nothing I can do with the error code, and the only expected action is to bubble-up the error in the stack and let the upper levels handle it (to display the error, maybe offer a retry mechanism). This means that for all kinds of “expected errors” I expect them to be returned by my methods, and treated (or intentionally ignored) by the caller method.

GO Language: Panic vs Errors, and Explicit Error Checking

When talking about Exceptions vs Error Codes I usually mention the GO language which has some clear guidelines about error handling.

One of their design principles is that they have “panic” for fatal unexpected situations (which is pretty much like Exceptions in Java/C#) and they also have “Errors” (any object that you return which implements Error interface) which should be used for regular expected situations. This is pretty much like the distinction which I explained above about when I like to use exceptions and when I like to use error codes.

This means that the language encourages you to explicitly check for errors where they occur, as opposed to the paradigm that expects you to throw (and catch) exceptions even for expected errors. Since the language has this clear distinction between exceptions and return errors, it has some conventions and constructs for error handling that allows developers to easily (and concisely) get and test errors.

Basically, all functions where errors are expected to happen should always return an error object, and if the function is also expected to return some other object (in case of success) then it should use multiple return values so that it can simultaneously return both the expected result objects (at the first position) and then return the possible errors (at the last position), like this:

file, err := os.Open("filename.txt")
if err != nil {
    // abort the program...
}
fmt.Print(string(file))

This multiple return values feature makes the code more concise and it’s now also available in C# 7. And by receiving the error as a regular return value our error handling becomes less verbose, more explicit, and easier to read since it uses regular if/else statements.

Another nice convention is that when returning multiple parameters the return should always be ONE OR ANOTHER, meaning that if there’s an error you can expect that the other object is null, and vice-versa. A simple convention that makes error handling even easier.

Last, another major benefit of explicitly returning error codes is that exceptions can easily be ignored while it’s much harder to ignore errors if your methods force you to receive the returned error. Returning codes obviously don’t allow us to bubble-up automatically (as exceptions do), but the idea is exactly that - you want to check (and act) on your errors right where they occur.

Returning Errors as Enums instead of Classes

As I’ve explained above, errors are about regular expected situations where the caller code should know the possible results and should decide how to handle each possible error. If what you get is a class instance (either as a thrown Exception or as a regular returned object) you really don’t know what kind of errors you might receive.

The first problem about using exceptions is that the caller code (or compiler) doesn’t know what exceptions each method may throw. Java has checked exceptions but all other languages learned that checked exceptions are evil and don’t even have that alternative. Obviously you could describe in your documentation instructions like “This CreateUser() method may throw an exception of CreateUserException”, and then the caller code would know what to expect, but what’s the point if you could just define what your method returns in the return type?

The second problem about returning classes (either as exceptions or return objects) is that developers usually design the different errors as subtypes (subtype polymorphism), and the caller code (or compiler) can’t know/test for all possible errors.

try
{
    // CreateUserCommand xmldoc will warn me that it may throw a CreateUserCommandException
    User createdUser = CreateUserCommand(newUserInfo);
}
catch (CreateUserCommandException)
{
    // How many possibilities do we have here?
    // Which subtypes should we check?
}

One alternative for this second problem is adding an enum CreateUserCommandErrorEnum inside CreateUserCommandException. Then we could use if/switch statements, and the compiler can check if we cover all possible errors. But that looks too complex (both an exception and an enum for each possible method) and without any benefit - in the end it’s all about Enums, since there are a predefined number of possible outcomes.

To sum, I prefer returning enums directly (instead of throwing exceptions or returning error classes) because:

  • The caller code is forced to receive the error, less prone to ignore the possibility of an error
  • The caller code will know in advance all possible errors
  • We can treat the possible errors with switch statements and we can easily ensure that all possible returns are handled.
  • If I add a new possible return in my method, I can even check all callers if they are covering that new value in a switch statement for example.
  • I believe that we should explicitly test for expected errors right where they occur.

Given the reasons above, for the rest of this post I’ll assume that your methods are returning errors as enum, like this one:

public enum CreateUserCommandError
{
    USERNAME_NOT_AVAILABLE,
    WEAK_PASSWORD,
}

In the next sections I’ll show a few different ways of returning multiple parameters in C#.

Using OUT parameters

Using out parameters is as simple as this:

public User CreateUserCommand(UserDTO newUserInfo, out CreateUserCommandError? error)
{
    if (somethingBad)
    {
        error = CreateUserCommandError.USERNAME_NOT_AVAILABLE;
        return null;
    }
    // ...
    error = null; // out parameters need to be assigned even if null
    return user;
}

CreateUserCommandError? error = null;
User createdUser = CreateUserCommand(newUserInfo, out error);
if (error != null)
{
    // early abort..
}
LoginUser(createdUser);

In the new C# 7 we don’t even have to define the out variables anymore, we can use implicitly typed local variable (out var):

User createdUser = CreateUserCommand(newUserInfo, out var error);

Using regular Tuples

Using Tuples is a little more verbose, but returns all parameters in a single Tuple object:

public Tuple<User, CreateUserCommandError?> CreateUserCommand(UserDTO newUserInfo)
{
    if (somethingBad)
        return new Tuple<User, CreateUserCommandError?>(null, CreateUserCommandError.USERNAME_NOT_AVAILABLE);
    // ...
    return new Tuple<User, CreateUserCommandError?>(user, null);
}

var result = CreateUserCommand(newUserInfo);
if (result.Item2 != null) // Item2 is the Error
{
    // early abort..
}
LoginUser(result.Item1); // Item1 is the User returned

Using the new ValueTuple

In the new C# 7 there’s this new ValueTuple struct, where we can give more meaningful names to the tuple members, and we can also use a simplified syntax both for creating new ValueTuple and for deconstructing the ValueTuple:

public (User createdUser, CreateUserCommandError? error) CreateUserCommand(UserDTO newUserInfo)
{
    if (somethingBad)
        return (null, CreateUserCommandError.USERNAME_NOT_AVAILABLE);
    // ...
    return (user, null);
}

var result = CreateUserCommand(newUserInfo);
// The names "error" and "createdUser" come directly from the method signature above
if (result.error != null)
{
    // early abort..
}
LoginUser(result.createdUser);

We can deconstruct the ValueTuple in a single statement which can both declare the variables and assign values to them:

var (user, error) = CreateUserCommand(newUserInfo);
// or: (var user, var error) = CreateUserCommand(newUserInfo);
// or: (User user, CreateUserCommandError? error) = CreateUserCommand(newUserInfo);

if (error != null) // Error
{
    // early abort..
}
LoginUser(user);

Using Generics

Another popular method is to Wrap your returns in a generic class which wraps both your return object and the possible error (actually it should return ONE OR ANOTHER, but not both).

public class CommandResult<TEntity, TError>
        where TEntity : class
        where TError : struct, Enum
{
    public TEntity Entity { get; set; }
    public TError? Error { get; set; }
    public bool IsSuccess => (Error == null);

    // Many developers also include a "Message" property
    // which usually can be both the success message or the error description
    // public string Message { get; set; }

    public static CommandResult<TEntity, TError> Success(TEntity entity)
    {
        return new CommandResult<TEntity, TError>() { Entity = entity };
    }

    public static CommandResult<TEntity, TError> Fail(TError errorCode)
    {
        return new CommandResult<TEntity, TError>() { Error = errorCode };
    }
}

public CommandResult<User, CreateUserCommandError> CreateUserCommand(UserDTO newUserInfo)
{
    if (somethingBad)
        return CommandResult<User, CreateUserCommandError>.Fail(CreateUserCommandError.USERNAME_NOT_AVAILABLE);
    // ...
    return CommandResult<User, CreateUserCommandError>.Success(user);
}

var result = CreateUserCommand(newUserInfo);
if (result.Error != null) // Error
{
    // early abort..
}
LoginUser(result.Entity);

Using generics is more verbose than the ValueTuple syntax but it’s much more powerful since we can enhance the class with extra information. Later I’ll show how to enhance this class with more information.

But this verbose syntax makes me a little annoyed, so let’s try to get the best of both solutions…

Combining Generics with the concise ValueTuple Syntax

There are a few tricks that we can use to make the Generics version more friendly and less verbose.

First, inside the CommandResult<> class we can create an implicit conversion operator that will convert (at the compiler level) a ValueTuple to a CommandResult<>:

public static implicit operator CommandResult<TEntity, TError>(ValueTuple<TEntity, TError?> tuple)
{
    if (tuple.Item1 != null && tuple.Item2 == null)
        return Success(tuple.Item1);
    if (tuple.Item1 == null && tuple.Item2 != null)
        return Fail(tuple.Item2.Value);
    throw new NotImplementedException("When error is returned you cannot return any other value together");
}

Then we can return our results as if they were ValueTuples:

//return CommandResult<User, CreateUserCommandError>.Fail(CreateUserCommandError.USERNAME_NOT_AVAILABLE);
return (null, CreateUserCommandError.USERNAME_NOT_AVAILABLE);

//return CommandResult<User, CreateUserCommandError>.Success(user);
return (user, null);

Inside the CommandResult<> class we can also create a deconstruct method so that CommandResult<> can be deconstructed into its two parts:

public void Deconstruct(out TEntity entity, out TError error) => (entity, error) = (this.Entity, this.Error);

This mean that we can deconstruct (declare the variables deconstruct the different parts) in a single call like this:

var (user, error) = CreateUserCommand(newUserInfo);
if (error != null) // Error
{
    // early abort..
}
LoginUser(user);

So cool and so easy, isn’t it?

Enhancing the Error enum

In all previous examples the returned error was only an enum. In the last example above (Generics with concise syntax) the enum was part of the Generic class:

public class CommandResult<TEntity, TError>
        where TEntity : class
        where TError : struct, Enum
{
    public TEntity Entity { get; set; }
    public TError? Error { get; set; }
    public ErrorResult<TError> Error { get; set; }
    public bool IsSuccess => (Error == null);
    // ...
}

But we can enhance the error object (TError?) with more information by wrapping in inside another class:

public class ErrorResult<TError>
    where TError : struct, Enum
{
    /// <summary>
    /// This MAY or may not be defined (even if an error happened!).
    /// If this is null, you should check the <see cref="ValidationErrors"/> to see why the command failed.
    /// </summary>
    public TError? ErrorCode { get; set; }
    public string ErrorMessage { get; set; }
    public IList<ValidationError> ValidationErrors;
}
public class ValidationError
{
    public string PropertyName { get; set; }
    public string ErrorMessage { get; set; }
}

public class CommandResult<TEntity, TError>
        where TEntity : class
        where TError : struct, Enum
{
    public TEntity Entity { get; set; }
    public ErrorResult<TError> Error { get; set; } // THIS!
    public bool IsSuccess => (Error == null);
    // ...
}

This design gives us a few advantages over a simple enum:

  • We can add a descriptive string ErrorMessage
  • We can add a list of ValidationErrors which can be helpful to display in the UI
  • We don’t need to define enums for all possible situations - the caller code can just assume that if CommandResult.Error is non-null then some error happened, and it can use the ErrorMessage and/or ValidationErrors. It only needs to test the TError? ErrorResult.ErrorCode if it needs to handle specific cases - else it can just have this “general” error handling.
  • ValidationErrors could be filled automatically. In my next post, I’ll show how this can be done using MediatR pipeline (behaviors) to check for validation errors using FluentValidation and automatically fill CommandResult.ValidationErrors, without even hitting our Command Handlers.

Well, at the beginning of this post I wrote that I would return simple enums, and in the end I’m returning classes. But basically, this is just a thin wrapper to enhance the enum with additional features - but it doesn’t invalidate the downsides that I’ve mentioned earlier (about type polymorphism uncertainty, and about how throwing exceptions is bad when you need to handle the outcomes).

Last, this wrapper around ErrorCode enum shouldn’t stop us from doing direct comparisons. We can still compare the error with the enum values as long as we overload the equality operators like this:

public static bool operator ==(ErrorResult<TError> left, TError right)
{
    return left.ErrorCode != null && left.ErrorCode.Value.Equals(right)
}

var (user, error) = CreateUserCommand(newUserInfo);
if (error == CreateUserCommandError.USERNAME_NOT_AVAILABLE) { ... }
else if (error == CreateUserCommandError.WEAK_PASSWORD) { ... }
else if (error == null)
{
   // ...
}

You can find full source code here.