Optional is a life style of code

      No Comments on Optional is a life style of code

All I need in Christmas is Optional. In Java there is amazing Object, called Optional. I want Optional in C#, and there is plenty of articles how to.

Here is how I implemented it (source on github) get it on nuget.org

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Optional<T>: IEnumerable<T>
{
    private readonly T[] _data;

    private Optional(T[] data)
    {
        _data = data;
    }

    public static Optional<T> ValueOf(T value)
    {
        return new Option<T>(new[] {value});
    }

    public static Optional<T> Empty()
    {
        return new Option<T>(new T[0]);
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ((IEnumerable<T>)_data).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this._data.GetEnumerator();
    }
}

Now I have my own Optional in C#. Be aware of the IEnumerable, it adds all the LINQ goodies!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class Program
{
    static void Main(string[] args)
    {
        var app = new Program();
        app.run();
    }

    private void run()
    {
        var restaurantRepository = new RestaurantRepository();
        var restaurantSearchResult = restaurantRepository.FindRestaurantByName("TEST");

        //create restaurant view, if restaurant found, fill restaurant view, if not, create default restaurant view
        var view = restaurantSearchResult
            .Select(r => new RestaurantView
            {
                Title = r.Name
            }).DefaultIfEmpty(new RestaurantView { Title = "not found" }).Single();
    }
}

internal class RestaurantRepository
{
    private readonly List<Restaurant> _db = new List<Restaurant>
    {
        new Restaurant
        {
            Id = 1,
            Name = "COD"
        },
        new Restaurant
        {
            Id = 2,
            Name = "KID"
        }
    };

    public Optional<Restaurant> FindRestaurantByName(string name)
    {
        var searchResult = _db.Find(r => r.Name.Equals(name));
        return Optional<Restaurant>.ValueOf(searchResult);
    }
}

internal class Restaurant
{
    public int Id { get; set; }
    public string Name { get; set; }
}

internal class RestaurantView
{
    public string Title { get; set; }
}

This is amazing, less code complexity, and it is more pleasant to read the code.

I will upgrade Optional class

1
2
3
4
5
6
7
public void IfPresent(Action<T> actionToPerform)
{
    if (_data.Length > 0)
    {
        actionToPerform(_data[0]);
    }
}

And now I can do this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Program
{
    static void Main(string[] args)
    {
        var app = new Program();
        app.run();
    }

    private void run()
    {
        var restaurantRepository = new RestaurantRepository();
        var restaurantSearchResult = restaurantRepository.FindRestaurantByName("KID");

        restaurantSearchResult.IfPresent(SendToServiceBus);
    }

    private void SendToServiceBus(Restaurant restaurant)
    {
        Console.WriteLine("send to MS service bus");
    }
}

Add more love to the Optional

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public bool IsPresent()
{
    return _data.Length > 0;
}

public void IfPresent(Action<T> actionToPerform)
{
    if (IsPresent())
    {
        actionToPerform(_data[0]);
    }
}

public T OrElse(T other)
{
    return IsPresent() ? _data[0] : other;
}

Now I can add OrElse fluently as a code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Program
{
    static void Main(string[] args)
    {
        var app = new Program();
        app.run();
    }

    private void run()
    {
        var restaurantRepository = new RestaurantRepository();
        var restaurantSearchResult = restaurantRepository.FindRestaurantByName("KID");

        var restaurant = restaurantSearchResult.OrElse(new Restaurant {Name = "Default entity"});

        SendToServiceBus(restaurant);
    }

    private void SendServiceBus(Restaurant restaurant)
    {
        Console.WriteLine("send to MS service bus " + restaurant.Name);
    }
}

Final product

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class Optional<T> : IEnumerable<T>
{
    private readonly T[] _data;

    private Optional(T[] data)
    {
        _data = data;
    }

    public static Optional<T> ValueOf(T value)
    {
        return value == null ? Empty() : new Optional<T>(new[] { value });
    }

    public static Optional<T> Empty()
    {
        return new Optional<T>(new T[0]);
    }

    public bool IsPresent()
    {
        return _data.Length > 0;
    }

    public void IfPresent(Action<T> actionToPerform)
    {
        if (IsPresent())
        {
            actionToPerform(_data[0]);
        }
    }

    public T OrElse(T other)
    {
        return IsPresent() ? _data[0] : other;
    }

    public T OrElseGet(Func<T> function)
    {
        return IsPresent() ? _data[0] : function();
    }

    public T OrElseThrow(Exception e)
    {
        if (!IsPresent())
        {
            throw e;
        }
        return _data[0];
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ((IEnumerable<T>)_data).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this._data.GetEnumerator();
    }
}

Use case, when Restaurant is retrieved from Redis cache, if cache hit missed, query Restaurant from data base and save result in cache and return result to the caller.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Program
{
    static void Main(string[] args)
    {
        var app = new Program();
        app.run();
    }

    private void run()
    {
        var searchKey = "KID";
        var redisRepository = new RedisRepository();
        var searchResult = redisRepository.FindRestaurantByName(searchKey);

        var restaurant = searchResult.OrElseGet(() => GetFromDBAndAddToCache(searchKey));
    }

    private Restaurant GetFromDBAndAddToCache(string searchKey)
    {
        var restaurantRepository = new RestaurantRepository();
        var searchResult = restaurantRepository.FindRestaurantByName(searchKey);

        searchResult.IfPresent(restaurant =>
        {
            var redisRepository = new RedisRepository();
            redisRepository.SaveToCache(restaurant);
        });

        return searchResult.OrElseThrow(new Exception("Item not found"));
    }
}

I love Optional, it really helps to manage complex things easy.

Leave a Reply

Your email address will not be published. Required fields are marked *