David Pine edited this page May 16, 2018 · 3 revisions

Getting Started

Hi friends! :wave: This is Centare's first open source project, so we started with something practical, small and useful. This is collection of various C# extension methods that we have found a need for in most of our projects. We hope that you find them useful too!

Install

There are plenty of ways to install NuGet packages. The Centare.Extensions package can be found on NuGet.org here. Once installed, you're ready to consume it.

Usage

Let's take a look at how we can leverage this. First, here is an example extension method that exists within the package.

public static void Deconstruct<T>(
    this T? nullable, 
    out bool hasValue, 
    out T value) where T : struct
{
    hasValue = nullable.HasValue;
    value = nullable.GetValueOrDefault();
}

This extension method leverages part of the tuple feature coming from C# 7. We are permitted to "deconstruct" anything now, as such we can pick apart nullables.

[Fact]
public void DateTimeNullDeconstructTests()
{
    var (hasValue, value) = new DateTime?();
    Assert.False(hasValue);
    Assert.Equal(default, value);
}

[Fact]
public void DateTimeNowDeconstructTests()
{
    var now = DateTime.Now;
    var dateTime = new DateTime?(now);
    var (hasValue, value) = dateTime;
    Assert.True(hasValue);
    Assert.Equal(now, value);
}

Task-Based async Tuple Extension Method(s)

public static async Task TryAwaitOrLogAsync(
    this(Task task, ILogger logger) t)
{
    try
    {
        await t.task;
    }
    catch (Exception ex) when (ex.TryLogException(t.logger))
    {
    }
}

public static async Task<T> TryAwaitOrLogAsync<T>(
    this (Task<T> task, ILogger logger) t,
    T defaultValue = default)
{
    try
    {
        var result = await t.task;
        var isValueType = typeof(T).IsValueType;
        return (isValueType && default(T).Equals(result))
            || (!isValueType && result == null)
            ? defaultValue 
            : result;
    }
    catch (Exception ex) when (ex.TryLogException(t.logger))
    {
        return defaultValue;
    }
}

Consuming It

public class InputOutputBoundRepository<T> : IIOBoundRepository<T> where T : IRecord
{    
    private readonly IDataAccessLayer _iDal;
    private readonly ILogger<InputOutputBoundRepository<IRecord>> _logger;
    
    public InputOutputBoundRepository(
        IDataAccessLayer iDal,
        ILogger<InputOutputBoundRepository<IRecord>> logger)
   {
        _iDal = iDal ?? throw new ArgumentNullException(nameof(iDal));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }
    
    // Avoid the `async` and `await` keyword here
    // Single line expression
    // Generic
    // Task based async
    // Encapsulates try / catch
    // Handles logging
    // Allows for default override
    public Task<IEnumerable<T>> GetAsync(Func<T, bool> predicate)
        => (task: _iDal.GetRecordsWhereAsync(predicate), _logger).TryAwaitOrLogAsync(Enumerable.Empty<T>());
}
Clone this wiki locally
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.