Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Performance POC ATOM vs. JSON

5.00/5 (7 votes)
23 Oct 2013CPOL3 min read 15.9K  
Performance POC ATOM vs. JSON.

Background

In our project, we are using SAP as the backend data source and the NWGW (Netweaver Gateway) adapter to consumable from .NET client as OData format. Since the NWGW component is hosted on premise and our .NET client is hosted in Azure, we are consuming this data from Azure through the Service Bus relay. While transferring data from on premise to Azure over SB relay, we are facing performance issues for single user for large volumes of data as well as in relatively small data for concurrent users. So I did some POC for improving performance by consuming the OData service in JSON format.

What I Did?

I’ve created a simple WCF Data Service which has no underlying data source connectivity. In this service when the context is initializing, a list of text messages is generated and exposed as OData.

Here is that simple service code:

C#
[Serializable]
public class Message
{
    public int ID { get; set; }
    public string MessageText { get; set; }
}
public class MessageService
{
    List<Message> _messages = new List<Message>();
    public MessageService()
    {
        for (int i = 0; i < 100; i++)
        {
            Message msg = new Message
            {
                ID = i,
                MessageText = string.Format("My Message No. {0}", i)
            };
            _messages.Add(msg);
           
        }
    }
    public IQueryable<Message> Messages
    {
        get
        {
            return _messages.AsQueryable<Message>();
        }
    }
}
    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class WcfDataService1 : DataService<MessageService>
    {
        // This method is called only once to initialize service-wide policies.
        public static void InitializeService(DataServiceConfiguration config)
        {
            // TODO: set rules to indicate which entity sets 
            // and service operations are visible, updatable, etc.
            // Examples:
            config.SetEntitySetAccessRule("Messages", EntitySetRights.AllRead);
            config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
        }
    } 

Exposing one endpoint to Azure SB so that client can consume this service through SB endpoint. After hosting the service, I’m able to fetch data by simple OData query from browser.

Image 1

I’m also able to fetch the data in JSON format.

Image 2

After that, I create a console client application and consume the service from there.

Sample Client Code
C#
class Program
{
    static void Main(string[] args)
    {
        List<Thread> lst = new List<Thread>();

        for (int i = 0; i < 100; i++)
        {
            Thread person = new Thread(new ThreadStart(MyClass.JsonInvokation));
            person.Name = string.Format("person{0}", i);
            lst.Add(person);
            Console.WriteLine("before start of {0}", person.Name);
            person.Start();
            //Console.WriteLine("{0} started", person.Name);
        }
        Console.ReadKey();
        foreach (var item in lst)
        {
            item.Abort();
        }
    }
}

public class MyClass
{ 
    public static void JsonInvokation()
    {
        string personName = Thread.CurrentThread.Name;
        Stopwatch watch = new Stopwatch();
        watch.Start();
        try
        {
            SimpleService.MessageService svcJson = 
            new SimpleService.MessageService(new Uri
            ("https://abc.servicebus.windows.net/SimpleService /WcfDataService1"));
            svcJson.SendingRequest += svc_SendingRequest;
            svcJson.Format.UseJson();
            var jdata = svcJson.Messages.ToList();
 
            watch.Stop();
            Console.WriteLine("Person: {0} - JsonTime First Call time: {1}", 
              personName, watch.ElapsedMilliseconds);

            for (int i = 1; i <= 10; i++)
            {
                watch.Reset(); watch.Start();
                jdata = svcJson.Messages.ToList();
                watch.Stop();
                Console.WriteLine("Person: {0} - Json Call {1} time: 
                {2}", personName, 1 + i, watch.ElapsedMilliseconds);
            }

            Console.WriteLine(jdata.Count);
        }
        catch (Exception ex)
        {
            Console.WriteLine(personName + ": " + ex.Message);
        }
        Thread.Sleep(100);
    }

    public static void AtomInvokation()
    {
        string personName = Thread.CurrentThread.Name;
 
        try
        {
            Stopwatch watch = new Stopwatch();
            watch.Start();
            SimpleService.MessageService svc = 
            new SimpleService.MessageService(new Uri
            ("https://abc.servicebus.windows.net/SimpleService/WcfDataService1"));
            svc.SendingRequest += svc_SendingRequest;
            var data = svc.Messages.ToList();

            watch.Stop();
            Console.WriteLine("Person: {0} - XmlTime First Call time: {1}", 
              personName, watch.ElapsedMilliseconds);

            for (int i = 1; i <= 10; i++)
            {
                watch.Reset(); watch.Start();
                data = svc.Messages.ToList();
                watch.Stop();
                Console.WriteLine("Person: {0} - Xml Call {1} time: 
                {2}", personName, 1 + i, watch.ElapsedMilliseconds);
            }

            Console.WriteLine(data.Count);
        }
        catch (Exception ex)
        {
            Console.WriteLine(personName + ": " + ex.Message);
        }
        Thread.Sleep(100);
    }
}<span style="font-size: 9pt;"</span>

What I Test After That

I tested two separate scenarios:

Scenario I: Single user with small and large volume of data

Measuring the data transfer time periodically in XML format and then JSON format. You might notice that first call I’ve printed separately in each screen shot as it is taking additional time to connect to SB endpoint. In the first call, the secret key authentication is happening.

Small data set (array size 10): consume in XML format.

Image 3

Consume in JSON format:

Image 4

For small set of data, Json and XML response time over service bus relay is almost same.

Consuming Large volume of data (Array Size 100)

Image 5

Here the XML message size is around 51 KB. Now I’m going to consume the same list of data (Array size 100) in JSON format.

Image 6

So from the above test scenario, it is very clear that JSON response time is much faster than XML response time and the reason for that is message size. In this test, when I’m getting the list of 100 records in XML format message size is 51.2 KB but JSON message size is 4.4 KB.

Scenario II: 100 Concurrent user with large volume of data (array size 100)

In this concurrent user load test, I’ve done any service throttling or max concurrent connection configuration.

Image 7

In the above screen shot, you will find some time out error that I’m getting in XML response. And it is happening due to high response time over relay. But when I execute the same test with JSON response, I found the response time is quite stable and faster than XML response and I’m not getting any time out.

Image 8

How Easy to Use UseJson()

If you are using WCF Data Service 5.3 and above and VS2012 update 3, then to consume the JSON structure from the client, I have to instantiate the proxy / context with <ServiceInstance>.Format.UseJson(). Here you don’t need to load the Edmx structure separately by writing any custom code. .NET CodeGen will generate that code when you add the service reference.

Image 9

But if that code is not generated from your environment, then you have to write a few lines of code to load the edmx and use it as <service instance>.Format.UseJson(LoadEdmx(<service name>));

Sample Code for Loading Edmx
C#
public static IEdmModel LoadEdmx(string srvName)
{
    string executionPath = Directory.GetCurrentDirectory();
    DirectoryInfo di = new DirectoryInfo(executionPath).Parent;
    var parent1 = di.Parent;
    var srv = parent1.GetDirectories("Service References\\" + 
    srvName)[0].GetFiles("service.edmx")[0].FullName;

    XmlDocument doc = new XmlDocument();
    doc.Load(srv);
    var xmlreader = XmlReader.Create(new StringReader(doc.DocumentElement.OuterXml));
    
    IEdmModel edmModel = EdmxReader.Parse(xmlreader);
    return edmModel;
}

Happy coding…

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)