C# Extend with Extension Method

Many C# developers know the existence of Extension Methods. It gives developers the ability to extend functionalities of the existing API without modifying the original source code.

A simple example is to check if a string is a numeric value. There are more than one solution to solve that problem. And one of them is that we want to write code like this:


string houseNumber = "123";
bool isNumber = houseNumber.IsNumeric();

The IsNumeric is not a built-in method on the string type. With the extension method, we could implement the IsNumeric easily.

public static bool IsNumeric(this string input)
{
    return Regex.IsMatch(input, "^[0-9]+$");
}

That is the basic concept. LINQ is the main consumer of the benefit. Or we might say that LINQ was the reason why the extension method concept was born. Of course, we do not have to know it to use LINQ.

So what is this post about? We could take advantages of the extension method to improve our design and implementation even when the code is under development, which means we are free to change our code.

Take a look at this simple interface

public interface IDocumentRepository
{
    /// <summary>
    /// Get a document of given type <T> by id. Return null if not found
    /// </summary>
    T GetDocument<T>(Guid id);
}

A simple, generic repository. The detail implementation does not matter here.

Later in the development, there are many places having this kind of logic (code)

var document = _repository.GetDocument<Product>(id);

if(document == null)
    throw new ObjectNotFoundException();

// Do something with the document here

Just some checking. It is not a big deal, except the fact that we need to do it in many places. So we want the ability to write this code

var document = _repository.GetOrThrow<Product>(id);

So what are options?

Modify the interface and implementation

One could go with this approach

public interface IDocumentRepository
{
    /// <summary>
    /// Get a document of given type <T> by id. Return null if not found
    /// </summary>
    T GetDocument<T>(Guid id);

    T GetOrThrow<T>(Guid id);
}

And implement the method in all derived classes. The approach works but I do not like it much for a couple of reasons

  1. That logic does not fit there naturally. It is a logic added on the existing functionality
  2. All the implemented classes are modified. In the best case, there is only one implemented class
  3. And what about later we need other kind of that functionalities? Will we keep expanding the interface?
  4. Usually the interface is at the infrastructure, but the extended functions are in the service or small bounded contexts. Some functions do not make any sense in other boundaries

Extend by extension methods

We could keep the interface clean as it was designed and extend the function by using extension methods as shown

public static class DocumentRepositoryExtensions
{
    public static T GetOrThrow<T>(this IDocumentRepository repository, Guid id)
    {
        var document = repository.GetDocument<Product>(id);

        if(document == null)
            throw new ObjectNotFoundException();

        return document;
    }
}

And we are free to place this code in the place that uses it. If it is required in many layers, assemblies, consider to put it in a separate assembly.

However, be reasonable otherwise you end up with extension methods everywhere. It is about placing the responsibilities in the right place.

I have been using this approach in my recent projects. And I am glad I have done it!

Retrospective – Lily Grade 2 Speaking Contest Preparation 2020

With the spreading of virus nCoV-2019 (Covid-19), schools are closed for weeks. My daughter teachers sent homework, recommendations to us so we can support kids during the slow time. Out of many subjects, there was an instruction to prepare for the school speaking contest. My daughter is grade 2, and this year topic is Go Green—Save the Planet.

Note: English is foreign language. We are Vietnamese.

My daughter delivered a speech to me. Wow, fantastic Lily! I really meant it—every single word. A few days later, the teacher gave her example speech in written form and a video. Oops! "it is not that simple", I uttered.

I knew there is a lot of work that my daughter and I have to go through. But, wait, I have not seen anything like this in my school years including the high school. Maybe there was, but not in my memory. I went through a quick reflection. That I had problems with explaining in writing. That a major of my colleagues had the same problem. We are developers, we write code. But we have problems with writing the design, explaining the root cause and solution in … writing. I wondered what if they were given this challenge. I did not bet on that.

For a speech, a thesis, there are usually 3 parts: Introduction, Main Ideas, and Conclusion. And that is the easiest part. My daughter knew that from Youtube. However, what includes in each part? Explaining that to a grade 2 kid is not easy.

Rewrite the whole speech

So the first thing I did was to convince my daughter to rewrite her teacher’s speech. She read the speech, watched the video. She understood and liked it. However, I was sure that she did not have a visualization of the scope—of how big the speech is, what are in the speech.

"Why do I have to write it?", she asked many times. Good question, kid! Why? Because "we are going to analyze the speech and that will help you do your own speech", I answered. She understood the reward. Let’s grasp a pencil and paper.

After 1 hour, with the motivation that we will play after completed the writing, she finished the task. WOW! "That’s a lot", she said.

I asked her to read it out loud. My mind was blown away with her result. I smiled with a happy thought that she is in a good school with great teachers.

Why do I ask her to write down?

  1. Writing will help you remember longer, easy to visualize things, scope. With the developing of technologies, people can access information easily, and most of them stop at the reading stage—not a good habit IMO
  2. Develop a habit of thinking on paper
  3. Learn vocabularies

Analyze the structure

By analyzing her teacher’s speech structure, I had a better chance to convince her what are included in a speech. And the importance is that we have to do it together. Get her involved in the process.

The introduction paragraph is easy. She knows and does it well. The topic and introduction are general information.

They include

  1. Theme (Go green, save planet): The importance of bees
  2. Topic: Specific areas that bees contribute—food, plant

Lily chose Birds. So she had to do some research how the birds contribute to the environment. She did her first research with Google. People found what they looked for. So did she.

The main paragraphs is challenging. Here they are

  1. 1 paragraph about role and contribution of the bees with numbers—facts, statistics
  2. 1 paragraph about they are in danger and why with numbers—facts, statistics
  3. 1 paragraph about calling for actions, what actions we should do

Of course, we do not focus too much on the scientific facts.

We went through each paragraph and read it out loud. We prepared another paper. I asked Lily what the paragraph was about. She explained and I helped her rephrasing them. After 1 hour or less, we had our second paper filled with the structure of her teacher’s speech.

We practiced saying the structure without looking at the paper, if could not remember, read them again. I wanted her to visualize the structure. So she will have a mental map of the speech.

So far so good! Lunch time.

Let’s write the speech

Nothing happens if we do not take action. Lily wrote her first draft of the speech base on the structure and facts we found on the internet–fact is crucial, not so detail, but they have to be there.

She could not make it in one go. But she did it beyond my expectation. I was impressed with how far she’s done. She completed the final paragraphs the next day.

It turned out this step was easier than I thought. She was comfortable with writing once she knew what to write.

Review and feedback

I asked her to write them again in a Word document (stored in OneDrive) so that I could send it to her English and Science teacher (thank you for your support); another good chance for her to remember the speech and ICT skill (computer skill in her academic program).

I didn’t do any correction. I sent whatever she wrote, original version. Her teacher was so nice that she gave a correction version with corrections highlighted. She did not change the content, she corrected the words, sentences, and asked questions for sentences that were not clear. I was so graceful to have my daughter in her class.

Lily and I went through the feedback.

"Are all the corrections correct?", I asked.
"Yes", confirmed after she compared changes with her version.

There was one sentence that was not clear in term of the intention.

"Should we remove it?", I asked.
"Yes. It is not important.", she replied.

Practice speaking and recording

After going through class lessons, we did not have much time to practice the speaking. We have practiced and recorded many times and finally submitted the recording.

Result

She got to the final ground in the campus. She was one of the 2 best, and her friend was chosen to compete in the school round. She was happy with the result. We made a promise to participant every year regardless of the result. The benefit that she have gained from the preparation is far more the prize.

Summary

I was amazing what a grade 2 kid can do. Many people think that it is too hard, too soon for them to work at that level. However, from that experience, I can say that it is not true. Everything is a matter of technique, preparation, a sense of humor. Learning, competition are fun if you prepare your kid mental in that direction.

Learn to encourage the kids, participate with them, support them but do not do it for them.

I wrote this post a diary without modifications to keep it honest and real. It was a journey and we handled the pandemic in the best way we could.

I am looking forwards to writing the same post next year.

Event Sourcing – Generate Consecutive Ordering

Interesting things happen when system is under load with many concurrent requests. I got one when working on a big project. We got the problem during the development where the system was under many concurrent requests and there were many requests to insert data.

Our system employed Event Sourcing (ES) architecture. It has been chosen before I joined the project. It had been working great until that moment.

We got many exceptions in the system. And soon we tracked down the root cause of those exceptions was that some events were not projected thus resulting inconsistent data in the query model (the read side). But how could that happen? What was the root cause of that root cause?

Our approach depends on the order of events persisted in the Event Store which is a SQL implementation. And some of the events were not in the right order that they were supposed to be.

Let say there are 2 requests R1 and R2

  1. R1 generates 3 events in order: A1, A2, A3
  2. R2 generates 3 events in order: B1, B2, B3

Events in a request are sent to an Event Store in a transaction. And we expect them to have consecutive orders. With the 2 above requests, we expected the orders is

A1, A2, A3, B1, B2, B3

Or

B1, B2, B3, A1, A2, A3

And it has been working like that for years.

We designed the Events table with Order column is the primary key with Identity(1,1) so SQL handles the ordering for us.

One day!

When the system is under load with many concurrent requests

The event ordering is not consecutive.

What does it mean? Instead of having the orders as described earlier, we got this in the database:

A1, A2, B1, A3, B2, B3

Notice the order of B1 and A3

Because the way our projection was designed and implemented, when the ordering was wrong, not consecutive, some events were not projected.

But how could that happen? According to SQL documentation, there is no guarantee that the values generated by Identity column are consecutive in a transaction. If you want to ensure consecutiveness, you have to set proper isolation level and locking. Unfortunately, we could not do that. It will hurt the write performance. We want to write events as fast as possible.

Consecutive values within a transaction – A transaction inserting multiple rows is not guaranteed to get consecutive values for the rows because other concurrent inserts might occur on the table. If values must be consecutive then the transaction should use an exclusive lock on the table or use the SERIALIZABLE isolation level.

So what could we do? There is always a solution.

If you need orders, you have to be in charged

The first thing is that we have to manage orders manually instead of delegating to SQL Identity feature.

We know the number of events in a request/transaction. So if we know the current order number, we can preserve a range of values from Current + 1 to Current + Number Of Events. And then set the current order to the max in the range. If we can ensure that preserving a range and update the current order is performed in an atomic manner, other requests will get correct orders. It is impossible to have 2 requests with the same range. And even if there is, the SQL will throw an unique key constraint exception.

We need a EventOrdering table to store the current ordering.

Take the power of SQL to preserve a range. SQL allows getting values from an update statement. This query does all the power. It allows setting new value in an atomic operation and gets back the old and new values

const string sql = "UPDATE EventOrdering " +
                    "SET CurrentOrdering = CurrentOrdering + @numberOfEvents " +
                    "OUTPUT Deleted.CurrentOrdering + 1 as Lowest,Inserted.CurrentOrdering as Highest " +
                    "WHERE EventOrderingId=@eventOrderingId";

To access the old value, use the Deleted temp table. To access the new value, use the Inserted temp table

/// <summary>
/// Get the current (latest) ordering and reserve a range of ordering from [Current + 1] to [Current + <see cref="numberOfEvents"/>]
///
/// Example: Current:        11
///          numberOfEvents: 5
///          reserved range: 12, 13, 14, 15, 16
///          Updated Current:16
///
/// The returned value: [12 - 16] where
///     12: Lowest
///     16: Highest
/// </summary>
/// <param name="numberOfEvents"></param>
/// <returns></returns>
private OrderingRange GetOrderingRange(int numberOfEvents)
{
    const string sql = "UPDATE EventOrdering " +
                    "SET CurrentOrdering = CurrentOrdering + @numberOfEvents " +
                    "OUTPUT Deleted.CurrentOrdering + 1 as Lowest,Inserted.CurrentOrdering as Highest " +
                    "WHERE EventOrderingId=@eventOrderingId";

    using (var conn = new SqlConnection(_connectionString).AsOpen())
    {
        var param = new
        {
            numberOfEvents = numberOfEvents,
            eventOrderingId = EventOrderingId
        };
        var ordering = conn.QueryFirstOrDefault<OrderingRange>(sql, param, commandTimeout: CommandTimeout);
        if (ordering == null)
        {
            throw new InvalidOperationException("Cannot read the global ordering record from the table EventOrdering. " +
                                                "Ensure that the table is in the database with one row");
        }

        return ordering;
    }
}

/// <summary>
/// We might just need the Lowest value and increase by 1 for each event.
/// However, having both values sounds right with the range, and might support the debugging/logging
/// </summary>
public class OrderingRange
{
    public long Lowest { get; set; }
    public long Highest { get; set; }
}

Once we got the range, we simply assigned them to the events to be saved.

The problem solved! And I have learned new things about SQL.