Monday, January 20, 2014

C# Extension Methods Performance

Today I read a blog regarding extension methods from Serena Yeoh. In her blog, she shows that how extension methods can be very helpful in making the code look more meaningful and cleaner. If you are interested to checkout how she uses extension methods to beautify the data access component in layered architecture, feel free to visit her blog.

I know a lot of people including myself are concerned about the performance of using extension methods. Therefore, I have done some comparison and testing to find out whether is it bad to use extension methods?

Now, I am comparing the layered architecture sample code that uses extension methods which is given by Serena versus the same sample that uses normal way (object methods) which you can find it from HERE. The following are the code snippets from both sample code:

Extension Method Way

public Leave Apply(Leave leave)
{
    leave.Status = LeaveStatuses.Pending;
    leave.DateSubmitted = DateTime.Now;
    leave.IsCompleted = false;

    LeaveStatusLog log = CreateLog(leave);

    Validations.ValidateLeaveDates(leave);

    // Check for overlapping leaves.
    if (leave.IsOverlap())
    {
        throw new ApplicationException("Date range is overlapping with another leave.");
    }

    using (TransactionScope ts =
        new TransactionScope(TransactionScopeOption.Required))
    {
        // Step 1 - Calling Create.
        leave.Create();

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

        ts.Complete();
    }

    return leave;

}

Object Method Way

public Leave Apply(Leave leave)
{
    leave.Status = LeaveStatuses.Pending;
    leave.DateSubmitted = DateTime.Now;
    leave.IsCompleted = false;

    LeaveStatusLog log = CreateLog(leave);

    // Data access component declarations.
    var leaveDAC = new LeaveDAC();
    var leaveStatusLogDAC = new LeaveStatusLogDAC();

    Validations.ValidateLeaveDates(leave);

    // Check for overlapping leaves.
    if (leaveDAC.IsOverlap(leave))
    {
        throw new ApplicationException("Date range is overlapping with another leave.");
    }

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

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

        ts.Complete();
    }

    return leave;
}

Note the yellow highlighted differences. First is the extension method way, second is the normal object or instance method way.

Compare #1: MSIL - Microsoft Intermediate Language

First, let's find out how the above sample codes get translated into MSIL. Open up the intermediate language disassembler first by opening the Visual Studio Command Tool, then execute the ildasm.exe.

Next, use the IL disassembler to open up the compiled dll.



A bunch of IL codes will appear. Keep the window open. Then, repeat the same steps above to open another IL disassembler and open another compiled dll that contain the source code that use normal object method way.

Extension Method Way

  // Code size       136 (0x88)
  .maxstack  2
  .locals init ([0] class [LeaveSample.Entities]LeaveSample.Entities.LeaveStatusLog log,
           [1] class [System.Transactions]System.Transactions.TransactionScope ts,
           [2] class [LeaveSample.Entities]LeaveSample.Entities.Leave CS$1$0000,
           [3] bool CS$4$0001)

The following IL codes are from within try catch block only.

  .try
  {
    IL_004b:  nop
    IL_004c:  ldarg.1
    IL_004d:  call       void [LeaveSample.Data]LeaveSample.Data.LeaveDAC::Create(class [LeaveSample.Entities]LeaveSample.Entities.Leave)
    IL_0052:  nop
    IL_0053:  ldloc.0
    IL_0054:  ldarg.1
    IL_0055:  callvirt   instance int64 [LeaveSample.Entities]LeaveSample.Entities.Leave::get_LeaveID()
    IL_005a:  callvirt   instance void [LeaveSample.Entities]LeaveSample.Entities.LeaveStatusLog::set_LeaveID(int64)
    IL_005f:  nop
    IL_0060:  ldloc.0
    IL_0061:  call       class [LeaveSample.Entities]LeaveSample.Entities.LeaveStatusLog [LeaveSample.Data]LeaveSample.Data.LeaveStatusLogDAC::Create(class [LeaveSample.Entities]LeaveSample.Entities.LeaveStatusLog)
    IL_0066:  pop
    IL_0067:  ldloc.1
    IL_0068:  callvirt   instance void [System.Transactions]System.Transactions.TransactionScope::Complete()
    IL_006d:  nop
    IL_006e:  nop
    IL_006f:  leave.s    IL_0081

  }  // end .try

Object Method Way

  // Code size       157 (0x9d)
  .maxstack  2
  .locals init ([0] class [LeaveSample.Entities]LeaveSample.Entities.LeaveStatusLog log,
           [1] class [LeaveSample.Data]LeaveSample.Data.LeaveDAC leaveDAC,
           [2] class [LeaveSample.Data]LeaveSample.Data.LeaveStatusLogDAC leaveStatusLogDAC,
           [3] class [System.Transactions]System.Transactions.TransactionScope ts,
           [4] class [LeaveSample.Entities]LeaveSample.Entities.Leave CS$1$0000,
           [5] bool CS$4$0001)

The following IL codes are from within try catch block only.

  .try
  {
    IL_005a:  nop
    IL_005b:  ldloc.1
    IL_005c:  ldarg.1
    IL_005d:  callvirt   instance class [LeaveSample.Entities]LeaveSample.Entities.Leave [LeaveSample.Data]LeaveSample.Data.LeaveDAC::Create(class [LeaveSample.Entities]LeaveSample.Entities.Leave)
    IL_0062:  pop
    IL_0063:  ldloc.0
    IL_0064:  ldarg.1
    IL_0065:  callvirt   instance int64 [LeaveSample.Entities]LeaveSample.Entities.Leave::get_LeaveID()
    IL_006a:  callvirt   instance void [LeaveSample.Entities]LeaveSample.Entities.LeaveStatusLog::set_LeaveID(int64)
    IL_006f:  nop
    IL_0070:  ldloc.2
    IL_0071:  ldloc.0
    IL_0072:  callvirt   instance class [LeaveSample.Entities]LeaveSample.Entities.LeaveStatusLog [LeaveSample.Data]LeaveSample.Data.LeaveStatusLogDAC::Create(class [LeaveSample.Entities]LeaveSample.Entities.LeaveStatusLog)
    IL_0077:  pop
    IL_0078:  ldloc.3
    IL_0079:  callvirt   instance void [System.Transactions]System.Transactions.TransactionScope::Complete()
    IL_007e:  nop
    IL_007f:  nop
    IL_0080:  leave.s    IL_0094

  }  // end .try

Appendix for IL instruction:

nop = do nothing, for breakpoint purpose
ldloc = load local variable
ldarg = load argument
callvirt = call a late-bound object method
call = call a method
pop = pop a value from stack

The Differences

The method that use extension methods has smaller code size. The local variable count also lesser during initialization because missing LeaveDAC and LeaveStatusLogDAC objects.

Notice the call and callvirt IL instruction? Extension method is translated into call instruction, while the object method is translated into callvirt instruction. Understood that the extension methods are declared as static method, no doubt it is early binding, but the newly instantiated object method is considered as late-binding, serious? @.@

Anyway, the IL codes are later needed to be compiled with JIT compiler. By looking at the IL codes, it may not sufficient to tell which perform better.

Compare #2: Profiling

Let's see the response time comparison for both extension method and object method by using instrumentation profiling with Visual Studio 2013 Premium or Ultimate edition. You can use any other profiler tool to perform the similar testing. Please refer to this post to see how to use the Visual Studio Profiler.

Test Scenario: Submit 10 leave application.

Here is the result of using extension method:
Avg Elapsed Exclusive Time : 0.06 ms




Here is the result of using object method:
Avg Elapsed Exclusive Time : 0.07 ms


The Difference:

Woot! @.@
Extension method win by 0.01ms difference. Unbelievable!

Compare #3: Stress Test

Let's see what happen if the Apply leave method is called 10,000 times. I wrote a very simple unit test to stress the service that would call the extension method or object method for 10,000 times.

[TestMethod]
public void StressTest()
{
    //Prepare the test data first
    List<Leave> leaves = new List<Leave>();
    for (int i = 1; i <= 10000; i++)
    {
        leaves.Add(new Leave()
        {
            StartDate = DateTime.Today,
            EndDate = DateTime.Today,
            Duration = 1,
            Category = LeaveCategories.Annual,
            Employee = "Unit Test " + i.ToString(),
            Description = "Unit Test",
        });
    }
           
    LeaveController upc = new LeaveController();
    Stopwatch watch = new Stopwatch();

    //Start the timing
    watch.Start();

    //Start stressing the WCF service
    foreach(Leave leave in leaves)
    {
        upc.Apply(leave);
    }

    //Stop the timing
    watch.Stop();

    Debug.WriteLine("Total milisecond: " + watch.ElapsedMilliseconds.ToString());
}

I ran the unit test for 3 times in order to be fair.

The extension method result after completed the test:
Round 1: 221695 ms
Round 2: 218811 ms
Round 3: 228312 ms
Average: 222939 ms

The object method result after completed the test:
Round 1: 226168 ms
Round 2: 218680 ms
Round 3: 214087 ms
Average: 219645 ms

The Differences:

The object method win! When multiple instances versus singleton, logically multiple instances should win. When there are a lot of tasks need to be completed, a group of people work on it is always quicker to finish compare to one person works on it.

Summary

The extension method performance has not much different compare to object method. If the extension methods really can make your code look cleaner and meaningful, IMHO, just go for it. For more information about implementing layered architecture with extension methods, please visit Serena blog.

If you find my test above is not accurate to tell which is perform better, feel free to share your thought in the comment section below.


Sunday, January 12, 2014

SSDT : External Database Reference Error

Today I face a challenge with SSDT (SQL Server Data Tools). I encounter some errors when I have multiple database references in my SQL Server Database Project in Visual Studio 2013.

The errors that I am facing now:

SQL71561: View: [dbo].[View_1] has an unresolved reference to object [DatabaseB].[dbo].[Table_1].
SQL71501: View: [dbo].[View_1] contains an unresolved reference to an object. Either the object does not exist or the reference is ambiguous because it could refer to any of the following objects: [DatabaseB].[dbo].[Table_1].[Column1] or [dbo].[Table_1].[t2]::[Column1].

After googling around, the suggested cause of the errors is I am using 3 part name for the same database and then using the table which comes from different database in my query without having database reference in my project. The actual root cause is the database project itself perform database object validation at the background, and it cannot find the other database.

SELECT t1.*
FROM Database1.dbo.Table_1 AS t1
LEFT JOIN Database2.dbo.Table_1 AS t2
ON t1.Column1 = t2.Column1

Therefore, I have added other database projects that I need into my solution like the following screenshot.


Then, add the required database reference. Note that I do not need database variable, so I left the field empty. The example usage is correctly showing how I should and would use the database reference.


After that, I rebuild my project but I am still getting the same error. I have no choice but to remove the 3 part name for current database and remain the 2 part name as highlighted red in above query.

Finally, no more error has occur and my project is able to be built. Happy! But, later discover that there are more challenges await me.

See my solutions explorer screenshot above, I have more than two databases. I have a lot more complicated queries that need to deal with multiple databases. For example:

In Database1:

SELECT t1.*
FROM dbo.Table_1 AS t1
LEFT JOIN Database2.dbo.Table_1 AS t2
ON t1.Column1 = t2.Column1

In Database2:

SELECT bla bla bla
FROM dbo.Table_1
UNION ALL
SELECT bla bla bla
Database1.dbo.Table_2

So, as you can see Database1 need to add Database2 as database reference and then Database2 need to add Database1 as database reference too. If you go and do that in Visual Studio, you will get the following error:


A reference to library 'Database' cannot be added. 
Adding this project as a reference would cause a circular dependency.

It seem like Visual Studio treat the database reference as assembly reference. Having both database projects referred to each other is considered as circular reference. So, how am I suppose to do now?

I found a workaround but not everyone may accept it. I realize that by adding Data-tier Application Package (*.dacpac) as database reference, Visual Studio will not complain about circular dependency. Therefore, I try to extract dacpac for all the related databases by using SQL Server Management Studio (SSMS).



Just click Next button all the way until the Finish. By default, the Data-tier Application is extracted and stored at C:\Users\[UserName]\Documents\SQL Server Management Studio\DAC Packages\[DBName].dacpac. I would recommend to extract the DAC package files to a centralized location, so that it is easier to retrieve, track and manage the packages later.

While extracting the DAC package file, you may encounter the same error SQL71561 and SQL71501 again.


You have to extract the package manually by using SQLPackage.exe.

The location of the SQLPackage.exe is C:\Program Files (x86)\Microsoft SQL Server\[version]\DAC\bin

Below is the command line that I use to extract dacpac:

sqlpackage.exe /Action:Extract /ssn:. /sdn:Database1 /tf:"E:\DAC Packages\Database1.dacpac"

/ssn = Source server name
/sdn = Database name
/tf = Target file

More parameters info can be found HERE.



After you have all the DAC packages ready in one centralized location, now back to Visual Studio to add them as database reference.



My current setup is:
Database1 has Database2 as reference.
Database2 has Database1 as reference.

No more circular dependency complaint and no more database reference not found error. Also, another advantage of using DAC package as database reference is you need not to create or add other database project which is not developed by you to be included into the solution.




Monday, December 16, 2013

File System Watcher Integrate with Windows Workflow Foundation

Today I want to share how to integrate file system watcher in workflow. There are a few ways to do it. Option 1 (simple way) is whenever there is a file is dropped into a folder, the file system watcher will kick in and spawn a workflow instance to process your file. Option 2 (hard way) is to integrate file system watcher into the workflow which mean you need just 1 workflow instance to wait for file watcher event fire then process the file. I want to share the hard way.

Concept

Here is the flow of the concept. Picture is worth a thousand words.


Challenges

In order to implement the above flow, I need a custom workflow activity to handle the logic. I chose to use NativeActivity is because I need to use the bookmarking feature. Bookmarking can induce idle, I want to keep my workflow instance idle state whenever there is no file exists in the folder.

Next challenge is whenever an instance has gone idle, the activity context will be different when the file system watcher event kick in to wake the instance to resume the process. Therefore, I need a custom workflow instance extension to handle the bookmark resume by implementing IWorkflowInstanceExtension.

Implementation

First, prepare the custom workflow instance extension which can support the resume bookmark functionality.

public class FileReceiveExtension : IWorkflowInstanceExtension
{
    private WorkflowInstanceProxy _proxy;

    public IEnumerable<object> GetAdditionalExtensions()
    {
        return null;
    }

    public void SetInstance(WorkflowInstanceProxy instance)
    {
        _proxy = instance;
    }

    public void ResumeBookmark(Bookmark bookmark, FileInfo file)
    {
        IAsyncResult result = _proxy.BeginResumeBookmark(bookmark, file,
            (asyncResult) =>
            {
                    
            }, null);
    }
}

Next, create a custom NativeActivity then add the custom workflow instance extension into the activity cache metadata.

protected override void CacheMetadata(NativeActivityMetadata metadata)
{
    metadata.AddDefaultExtensionProvider<FileReceiveExtension>(() => new FileReceiveExtension());
    base.CacheMetadata(metadata);
}

Set the CanInduceIdle property value to true.

protected override bool CanInduceIdle
{
    get
    {
        return true;
    }
}


Write the custom activity implementation to create file system watcher and register the necessary event handler.

protected override void Execute(NativeActivityContext context)
{
    //Get the folder path from the activity parameter
    string monitoringPath = context.GetValue(this.MonitoringPath);

    FileSystemWatcher watcher = new FileSystemWatcher();
    watcher.Path = monitoringPath;
    watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
    watcher.Created += watcher_Created;
    watcher.Error += watcher_Error;
    watcher.EnableRaisingEvents = true;

    _extension = context.GetExtension<FileReceiveExtension>();
    _bookmark = context.CreateBookmark(BookmarkResumed);
}

Write the implementation of file system watcher created event.

private void watcher_Created(object sender, FileSystemEventArgs e)
{
    //Resume the bookmark and pass the FileInfo object as activity parameter
    _extension.ResumeBookmark(_bookmark, new FileInfo(e.FullPath));
}

Write the implementation of bookmark resume event.

private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value)
{
    FileInfo file = (FileInfo)value;

    //Call your own file processor method
    //Note: I am using async for better performance (Optional)
    Task task = _fileProcessor.ProcessAsync(file);

    task.ContinueWith((e) =>
    {
        if (e.IsFaulted)
        {
            Console.WriteLine(e.Exception);
        }
    });

    //Create a new bookmark immediately after a file is being processed
    _bookmark = context.CreateBookmark(BookmarkResumed);
}

Draw the workflow with the new custom activity. The Start and Stop receive activities are used to control my file system watcher process by making service call.



Start hosting the workflow service, then call the Start web method to test it out. You only need to call the method once to spawn one workflow instance only. Since I had created the custom file receive activity that accept monitoring path, you can spawn another workflow instance to have another instance of file system watcher which monitor different file path.


If you are interested with the source code, feel free to download it from HERE.



Send Transactional SMS with API

This post cover how to send transactional SMS using the Alibaba Cloud Short Message Service API. Transactional SMS usually come with One Tim...