Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time

CommandQuery ⚙️

build CodeFactor

Command Query Separation for .NET and C#

Commands

Commands change the state of a system but [traditionally] do not return a value. They write (create, update, delete) data.

Commands implement the marker interface ICommand and command handlers implement ICommandHandler<in TCommand>.

public class FooCommand : ICommand
{
    public string Value { get; set; }
}

public class FooCommandHandler : ICommandHandler<FooCommand>
{
    private readonly ICultureService _cultureService;

    public FooCommandHandler(ICultureService cultureService)
    {
        _cultureService = cultureService;
    }

    public async Task HandleAsync(FooCommand command, CancellationToken cancellationToken)
    {
        if (command.Value == null) throw new FooCommandException("Value cannot be null", 1337, "Try setting the value to 'en-US'");

        _cultureService.SetCurrentCulture(command.Value);

        await Task.CompletedTask;
    }
}

Commands can also return a result.

public class BazCommand : ICommand<Baz>
{
    public string Value { get; set; }
}

public class Baz
{
    public bool Success { get; set; }
}

public class BazCommandHandler : ICommandHandler<BazCommand, Baz>
{
    private readonly ICultureService _cultureService;

    public BazCommandHandler(ICultureService cultureService)
    {
        _cultureService = cultureService;
    }

    public async Task<Baz> HandleAsync(BazCommand command, CancellationToken cancellationToken)
    {
        var result = new Baz();

        try
        {
            _cultureService.SetCurrentCulture(command.Value);

            result.Success = true;
        }
        catch
        {
            // TODO: log
        }

        return await Task.FromResult(result);
    }
}

Commands with result implement the marker interface ICommand<TResult> and command handlers implement ICommandHandler<in TCommand, TResult>.

Queries

Queries return a result and do not change the observable state of the system (are free of side effects). They read and return data.

Queries implement the marker interface IQuery<TResult> and query handlers implement IQueryHandler<in TQuery, TResult>.

public class BarQuery : IQuery<Bar>
{
    public int Id { get; set; }
}

public class Bar
{
    public int Id { get; set; }

    public string Value { get; set; }
}

public class BarQueryHandler : IQueryHandler<BarQuery, Bar>
{
    private readonly IDateTimeProxy _dateTime;

    public BarQueryHandler(IDateTimeProxy dateTime)
    {
        _dateTime = dateTime;
    }

    public async Task<Bar> HandleAsync(BarQuery query, CancellationToken cancellationToken)
    {
        var result = new Bar { Id = query.Id, Value = _dateTime.Now.ToString("F") };

        return await Task.FromResult(result);
    }
}

Samples