Quantcast
Channel: Firedancer Unleashed!
Viewing all articles
Browse latest Browse all 84

Data Access Extension Method

$
0
0
This is a follow-up to my previous post on Exploring Extension Methods. As you can see, it was dated many years back and I'm glad that I finally had the time to look into it now. I took one of my layered application samples and converted it to test the concept.

Just a brief recap, I use Entities (classes with just properties) in my architecture to represent data and they don't have any methods. To validate or make use of the data for processing logic, I use a Business Component and to persist their data I use Data Access Components (DAC). So, if I have a Leave object, it will look something like this in the business componentto save it to the database:

// Instantiate data access component.
var leaveDAC = newLeaveDAC();

// Create leave.
leaveDAC.Create(leave);

But it would be nice if I can just do:

// Create leave. 
leave.Create();

The code will look much cleaner and I do not need to worry about instantiating which DACs to persist the Entities. I will only need to focus on the Entities instead. This is where extension methods can come into play. By converting the DAC into a "Data Access Extension" class, the above syntactic experience can be achieved.

Let's look at the original DAC.

publicpartialclassLeaveDAC : DataAccessComponent
{
    publicvoid Create(Leaveleave)
    publicvoid UpdateStatus(Leave leave)
    publicvoid SelectById(long leaveID)
    publicvoid Select(int maximumRows, int startRowIndex, 
                       string sortExpression, string employee, 
                       LeaveCategories? category, LeaveStatuses? status)
    publicint Count(stringemployee, LeaveCategories? category, 
                     LeaveStatuses? status)
    publicbool IsOverlap()
}

After conversion to Extension Methods, the DAC will look like the following:

publicstaticpartialclassLeaveDAC
{
    publicstaticvoid Create(thisLeave leave)
    publicstaticvoid UpdateStatus(thisLeave leave)
    publicstaticvoid SelectById(thisLeave leave, longleaveID)
    publicstaticvoid Select(thisList<Leave> leaves, int maximumRows,
                              int startRowIndex, string sortExpression,
                              string employee, LeaveCategoriescategory, 
                              LeaveStatuses? status)
    publicstaticint Count(thisList<Leave> leaves, string employee, 
                            LeaveCategories? category, LeaveStatuses? status)
    publicstaticbool IsOverlap(thisLeave leave)
}

Noticed that the DAC don't inherit the DataAccessComponent anymore? It is actually a limitation and I will discuss about that later.

Here's how a method in my business component looked like originally:

privatevoid UpdateStatus(Leave leave)
{
    LeaveStatusLog log = CreateLog(leave);

    // Data access component declarations.
    var leaveDAC = newLeaveDAC();
    var leaveStatusLogDAC = newLeaveStatusLogDAC();

    using (TransactionScope ts =
        newTransactionScope(TransactionScopeOption.Required))
    {
        // Step 1 - Calling UpdateById on LeaveDAC.
        leaveDAC.UpdateStatus(leave);

        // Step 2 - Calling Create on LeaveStatusLogDAC.
        leaveStatusLogDAC.Create(log);

        ts.Complete();
    }
}

Here's the UpdateStatus method after conversion:

privatevoid UpdateStatus(Leave leave)
{
    LeaveStatusLog log = CreateLog(leave);

    using (TransactionScope ts =
        newTransactionScope(TransactionScopeOption.Required))
    {
        // Step 1 - Calling Update status.
        leave.UpdateStatus();

        // Step 2 - Calling Create on log.
        log.Create();

        ts.Complete();
    }
}

Pretty clean right? Let's look at another method that does data retrieval. Here's the original version:

publicList<Leave> ListLeavesByEmployee(int maximumRows, int startRowIndex,
    string sortExpression, string employee, LeaveCategories? category,
    LeaveStatuses? status, outint totalRowCount)
{
    List<Leave> result = default(List<Leave>);

    if (string.IsNullOrWhiteSpace(sortExpression))
        sortExpression = "DateSubmitted DESC";

    // Data access component declarations.
    var leaveDAC = newLeaveDAC();

    // Step 1 - Calling Select on LeaveDAC.
    result = leaveDAC.Select(maximumRows, startRowIndex, sortExpression,
        employee, category, status);

    // Step 2 - Get count.
    totalRowCount = leaveDAC.Count(employee, category, status);

    return result;
}

And here's the ListLeavesByEmployee method using data extensions:

public List<Leave> ListLeavesByEmployee(int maximumRows, int startRowIndex,
    string sortExpression, string employee, LeaveCategories? category,
    LeaveStatuses? status, out int totalRowCount)
{
    var result = new List<Leave>();

    if (string.IsNullOrWhiteSpace(sortExpression))
        sortExpression = "DateSubmitted DESC";

    // Step 1 - Calling Select.
    result.Select(maximumRows, startRowIndex, sortExpression,
        employee, category, status);

    // Step 2 - Get count.
    totalRowCount = result.Count(employee, category, status);

    return result;
}

Up till now, the results have been quite satisfying but there are some limitations that needs to be dealt with. A static class can only inherit from object and not other classes, which leads to the previous highlighted problem that prevents any reusable methods to be encapsulated in the abstract base DataAccessComponent class. It has to be converted into a normal class with its properties and methods also converted to static.

Original DataAccessComponent base class:

publicabstractclassDataAccessComponent
{
    protectedconststring CONNECTION_NAME = "default";

    protected T GetDataValue(IDataReader dr, string columnName)
    {
        int i = dr.GetOrdinal(columnName);

        if (!dr.IsDBNull(i))
            return (T)dr.GetValue(i);
        else
            returndefault(T);
    }
}

Converted to function like a utility class:

publicsealedclassDataAccessComponent
{
    publicconststring CONNECTION_NAME = "default";

    publicstatic T GetDataValue(IDataReader dr, string columnName)
    {
        int i = dr.GetOrdinal(columnName);

        if (!dr.IsDBNull(i))
            return (T)dr.GetValue(i);
        else
            returndefault(T);
    }
}

This makes DAC methods like the following

privateLeave LoadLeave(IDataReader dr)
{
    // Create a new Leave
    Leave leave = newLeave();

    // Read values.
    leave.LeaveID = base.GetDataValue<long>(dr, "LeaveID");
    leave.CorrelationID = base.GetDataValue<Guid>(dr, "CorrelationID");
    leave.Category = base.GetDataValue<LeaveCategories>(dr, "Category");
    
    // other fields...

    return leave;
}

to become a little expanded:

privatestaticLeave LoadLeave(IDataReader dr, Leave leave)
{
    // Read values.
    leave.LeaveID = DataAccessComponent.GetDataValue<long>(dr, "LeaveID");
    leave.CorrelationID = DataAccessComponent.GetDataValue<Guid>(dr, "CorrelationID");
    leave.Category = DataAccessComponent.GetDataValue<LeaveCategories>(dr, "Category");

    // other fields...
    return leave;
}

Now the limitation of not being able to do inheritance has somewhat make me feel that this feat might not be a good idea. Furthermore, I also discovered that reflecting the extension methods may somewhat be a challenge if I want to apply this in LASG.

Most people will treat extension methods as 'syntactic sugar'. In this experiment, it does show that other than its original purpose of just purely extending class functionalities, extension methods can also make code look a bit more readable and easier to understand.

In terms of performance, there isn't seem to be any impact (or improvements). You can check-out Sylvester Lee's post to get some in-depth details on the performance benchmark he did for me in this research.

At this point in time, I have slight reluctance in using extension methods for data access. What do you think? Will you consider this method?

Viewing all articles
Browse latest Browse all 84

Trending Articles