Pages

Friday, May 22, 2020

The SOLID Object Oriented Programming(OOPS) Principles

SOLID Principles

What does it take to be an Object-Oriented Programmer? There was a time where I believed all that meant was that you worked with a language such as C#, C++, or Java. However, the more I get acquainted with newer technologies, the more I realize that there is a set of fundamentals core to the title. And really, these fundamentals are about architecting the best, most update-able, scalable systems. Just yesterday while diving into DataObjects.NET, I was greeted by Domain-Driven Design(DDD)-- a popular architectural abstraction. It motivated me to think about the basics, which is the purpose of this article.

The S.O.L.I.D. Principles of Class Design

The SOLID Principals seem to be the least common denominator of creating great classes; even before Design Patterns. I recommend taking some time to really think about each of them and how you can apply them. Let's dive in, one by one.

The Single Responsibility Principle

There should never be more than one reason for a class to change. Basically, this means that your classes should exist for one purpose only. For example, let's say you are creating a class to represent a SalesOrder. You would not want that class to save to the database, as well as export an XML-based receipt. Why? Well if later on down the road, you want to change database type (or if you want to change your XML schema), you're allowing one responsibility's changes to possibly alter another. Responsibility is the heart of this principle, so to rephrase there should never be more than one responsibility per class.

The Open Closed Principle

Software entities (classes, modules, functions, etc.) should be open for extension but closed for modification. At first, this seems to be contradictory: how can you make an object behave differently without modifying it? The answer: by using abstractions, or by placing behavior(responsibility) in derivative classes. In other words, by creating base classes with override-able functions, we are able to create new classes that do the same thing differently without changing the base functionality. Further, if properties of the abstracted class need to be compared or organized together, another abstraction should handle this. This is the basis of the "keep all object variables private" argument.

The Liskov Substitution Principle

Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. In other words, if you are calling a method defined at a base class upon an abstracted class, the function must be implemented properly on the subtype class. Or, "when using an object through its base class interface, [the] derived object must not expect such users to obey preconditions that are stronger than those required by the base class." The ever-popular illustration of this is the square-rectangle example. Turns out a square is not a rectangle, at least behavior-wise.

The Dependency Inversion Principle

Depend on abstractions, not on concretions or High-level modules should not depend upon low-level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions. (I like the first explanation the best.) This is very closely related to the open-closed principle we discussed earlier. By passing dependencies (such as connectors to devices, storage) to classes as abstractions, you remove the need to program dependency specific. Here's an example: an Employee the class that needs to be able to be persisted to XML and a database. If we placed ToXML() and ToDB() functions in the class, we'd be violating the single responsibility principle. If we created a function that took a value that represented whether to print to XML or to DB, we'd be hard-coding a set of devices and thus be violating the open-closed principle. The best way to do this would be to:
 
   Create an abstract class (named DataWriter, perhaps) that can be inherited from for XML (XMLDataWriter) or DB (DbDataWriter) Saving, and then
CCreate a class (named EmployeeWriter) that would expose an Output(DataWriter saveMethod) that accepts a dependency as an argument. See how the Output method is dependent upon the abstractions just as the output types are? The dependencies have been inverted. Now we can create new types of ways forEmployee data to be written, perhaps via HTTP/HTTPS by creating abstractions, and without modifying any of our previous code! No rigidity--the desired outcome.

The Interface Segregation Principle

Clients should not be forced to depend upon interfaces that they do not use. My favorite version of this is written as "when a client depends upon a class that contains interfaces that the client does not use, but that other clients do use, then that client will be affected by the changes that those other clients force upon the class." Kinda sounds like the inheritance specific single responsibility principle.

Sources

Wednesday, May 20, 2020

Insert bulk record in sql server

Often, we have a need to generate and insert many rows into a SQL Server Table. For example, for testing purposes or performance tuning. It might be useful to imitate production volume in the testing environment or to check how our query behave when challenged with millions of rows.

CREATE
TABLE dbo.Table1 (
        id int PRIMARY KEY
        ,number int
        ,name nvarchar(10)
);

Procedure:
IF OBJECT_ID ('dbo.addRows', 'P') IS NOT NULL
        DROP PROCEDURE dbo.addRows;
GO
CREATE PROCEDURE dbo.addRows 
        @rowsNumber int
AS
BEGIN
        SET NOCOUNT ON
        -- start point for adding rows
        DECLARE @id INT = ISNULL((SELECT MAX(id) FROM dbo.Table1)+1, 1)
        DECLARE @iteration INT = 0
        WHILE @iteration < @rowsNumber
                BEGIN
                       --get a random int from 0 to 100
                       DECLARE @number INT = CAST(RAND()*100 AS INT)
                       -- generate random nvarchar
                       -- get a random nvarchar ascii char 65 to 128
                       DECLARE @name NVARCHAR(10) = N'' --empty string for start
                       DECLARE @length INT = CAST(RAND() * 10 AS INT) --random length of nvarchar
                       WHILE @length <> 0 --in loop we will randomize chars till the last one
                               BEGIN
                                       SELECT @name = @name + CHAR(CAST(RAND() * 63 + 65 as INT))
                                       SET @length = @length - 1 --next iteration
                               END
                       --insert data
                       INSERT INTO dbo.Table1 (id, number, name)
                       VALUES (@id, @number, @name)
                       SET @iteration += 1
                       SET @id += 1
               END
        SET NOCOUNT OFF
END

Using this procedure will enable us to add the requested number of random rows.
Sample:
EXEC dbo.addRows 1000 --elapsed time ~0.11
EXEC dbo.addRows 10000 --elapsed time ~1.1
EXEC dbo.addRows 100000 --elapsed time ~9.64
EXEC dbo.addRows 1000000 --elapsed time ~2:11.88

Sunday, July 28, 2019

RestSharp Client in C#

My team has been doing a lot of REST API consumption, and our favorite tool to use for that is the terrific RestSharp Library. Our latest project is very large and uses lots of different APIs, and so we needed a way to create many different client classes that could consume many different APIs but still use common features like caching and error logging.

And so, I present to you, the Ultimate RestSharp Client!

In this post, we'll build a RestSharp client which can serve as a base class to many other clients who need to consume RESTful APIs. Said base class will need to implement common functionality (e.g. the aforementioned caching and error logging) and be able to deserialize known objects in which the APIs return. Come along with me as I condense a week's worth of research and coding into one 1700-word blog post and build the Ultimate RestSharp Client!

Requirements

The awesome RestSharp client that we're going to build needs to support the following scenarios:

  • Deserialize objects using an injected serializer.
  • Store into and retrieve from a cache.
  • Check for timeouts.
  • Use custom injected error logging.
 Prerequisites

Before we build our ultimate RestSharp client, let's first define the three items which need to be injected into the client.

Cache Service

Here's our simple cache service interface and implementation:

 

using System;
using System.Runtime.Caching;
 
public interface ICacheService
{
    T Get<T>(string cacheKey) where T : class;
    void Set(string cacheKey, object item, int minutes);
}
 
public class InMemoryCache : ICacheService
{
    public T Get<T>(string cacheKey) where T : class
    {
        return MemoryCache.Default.Get(cacheKey) as T;
    }
    public void Set(string cacheKey, object item)
    {
        if (item != null)
        {
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(30));
        }
    }
}

 

 

Deserializer

As of recently, RestSharp no longer requires JSON.NET as its default serializer (see this readme). So, we must inject a serializer into our ultimate client, which will use JSON.NET anyway because that's what I like. I stole the following class from this fantastic blog post and it's worked pretty well for us.

 
using Newtonsoft.Json;
using RestSharp.Deserializers;
using RestSharp.Serializers;
using System.IO;
 
public class JsonSerializer : ISerializer, IDeserializer
{
    private readonly Newtonsoft.Json.JsonSerializer _serializer;
 
    public JsonSerializer()
    {
        ContentType = "application/json";
        _serializer = new Newtonsoft.Json.JsonSerializer
        {
            MissingMemberHandling = MissingMemberHandling.Ignore,
            NullValueHandling = NullValueHandling.Include,
            DefaultValueHandling = DefaultValueHandling.Include,
            DateFormatHandling=DateFormatHandling.IsoDateFormat,
            DateTimeZoneHandling=DateTimeZoneHandling.Unspecified
        };
    }
 
    public JsonSerializer(Newtonsoft.Json.JsonSerializer serializer)
    {
        ContentType = "application/json";
        _serializer = serializer;
    }
 
    public string Serialize(object obj)
    {
        using (var stringWriter = new StringWriter())
        {
            using (var jsonTextWriter = new JsonTextWriter(stringWriter))
            {
                jsonTextWriter.Formatting = Formatting.Indented;
                jsonTextWriter.QuoteChar = '"';
 
                _serializer.Serialize(jsonTextWriter, obj);
 
                var result = stringWriter.ToString();
                return result;
            }
        }
    }
 
    public string DateFormat { get; set; }
    public string RootElement { get; set; }
    public string Namespace { get; set; }
    public string ContentType { get; set; }
 
    public T Deserialize<T>(RestSharp.IRestResponse response)
    {
        var content = response.Content;
 
        using (var stringReader = new StringReader(content))
        {
            using (var jsonTextReader = new JsonTextReader(stringReader))
            {
                return _serializer.Deserialize<T>(jsonTextReader);
            }
        }
    }
}
 
 

Error Logger

This is the simplest of the three injection prerequisites. You will want to replace this implementation with one of your own design. Here's a very basic error logger interface and class:

public interface IErrorLogger
{
    void LogError(Exception ex, string infoMessage);
}
 
public class ErrorLogger : IErrorLogger
{
    public void LogError(Exception ex, string infoMessage)
    {
        //Log the error to your error database
    }
}
 
 

With all three prerequisite items in place, we can now start to code our ultimate RestSharp client!

Class and Constructor

In order to build our ultimate RestSharp client, we'll first need a class that inherits from RestSharp's RestClient class:

We also need a constructor to inject our three prerequisite classes into this client, as well as a base URL for which each child client will be using for their APIs. Such a constructor looks like this:

using RestSharp;
using RestSharp.Deserializers;
using System;
using System.Linq;
 
public class BaseClient : RestSharp.RestClient
{
    protected ICacheService _cache;
    protected IErrorLogger _errorLogger;
    public BaseClient(ICacheService cache, IDeserializer serializer, IErrorLogger errorLogger, string baseUrl)
    {
        _cache = cache;
        _errorLogger = errorLogger;
        AddHandler("application/json", serializer);
        AddHandler("text/json", serializer);
        AddHandler("text/x-json", serializer);
        BaseUrl = new Uri(baseUrl);
    }
}
 
 

Note that we are using JSON for this ultimate client. You can use other formats (e.g. XML), but I prefer JSON for this kind of client so that's what we'll use here.

Once we've got the constructor build, it's time to start coding the guts of our ultimate client, starting with...

Error Logging

We have two different kinds of error logging in this system: the generic error logging and the more specific Timeout checks. We implemented timeout checks in an earlier post, go check that out if you want more details. For now, here's some code to accomplish each of these.

 

private void LogError(Uri BaseUrl, IRestRequest request, IRestResponse response)
{
    //Get the values of the parameters passed to the API
    string parameters = string.Join(", ", request.Parameters.Select(x => x.Name.ToString() + "=" + ((x.Value == null) ? "NULL" : x.Value)).ToArray());
 
    //Set up the information message with the URL, the status code, and the parameters.
    string info = "Request to " + BaseUrl.AbsoluteUri + request.Resource + " failed with status code " + response.StatusCode + ", parameters: "
        + parameters + ", and content: " + response.Content;
 
    //Acquire the actual exception
    Exception ex;
    if (response != null && response.ErrorException != null)
    {
        ex = response.ErrorException;
    }
    else
    {
        ex = new Exception(info);
        info = string.Empty;
    }
 
    //Log the exception and info message
    _errorLogger.LogError(ex,info);
}
 
private void TimeoutCheck(IRestRequest request, IRestResponse response)
{
    if (response.StatusCode == 0)
    {
        LogError(BaseUrl, request, response);
    }
}

 

 

Get and Execute

Once we've got the error logging in place, we can now discuss the kinds of operations we want this client to be able to perform. In our client, there are three possible actions that the client can perform against the API:

A basic EXECUTE operation, which will return an IRestResponse, which is the generic Response interface defined by RestSharp.

GET operation, which will call the EXECUTE operations and return a deserialized class.

 GET FROM CACHE operation, which will check the cache service for a matching object, return that object if it exists, or call the EXECUTE operations if it doesn't.

EXECUTE

Because the GET operations will call the EXECUTE operations and will return deserialized objects, we need both generic and non-generic versions of the EXECUTE operations. Here's those EXECUTE operations, which override the underlying RestSharp methods and call the TimeoutCheck() method from earlier:

 

public override IRestResponse Execute(IRestRequest request)
{
    var response = base.Execute(request);
    TimeoutCheck(request, response);
    return response;
}
public override IRestResponse<T> Execute<T>(IRestRequest request)
{
    var response = base.Execute<T>(request);
    TimeoutCheck(request, response);
    return response;

}

 

 

GET

The GET operation is just a tiny bit more complex. It must check the status code of the response, and if that code is 200 OK it will deserialize an object; otherwise, it must log an error and return a default instance of the class it is trying to deserialize. We can do this via a generic method, like so:

public T Get<T>(IRestRequest request) where T : new()
{
    var response = Execute<T>(request);
    if (response.StatusCode == System.Net.HttpStatusCode.OK)
    {
        return response.Data;
    }
    else
    {
        LogError(BaseUrl,request,response);
        return default(T);
    }
}
 
 

GET FROM CACHE

The GET FROM CACHE methods are more complex still, though they really just add an extra step onto the GET operations:

public T GetFromCache<T>(IRestRequest request, string cacheKey) where T : class, new()
{
    var item = _cache.Get<T>(cacheKey);
    if (item == null) //If the cache doesn't have the item
    {
        var response = Execute<T>(request); //Get the item from the API call
        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            _cache.Set(cacheKey, response.Data); //Set that item into the cache so we can get it next time
            item = response.Data;
        }
        else
        {
            LogError(BaseUrl, request, response);
            return default(T);
        }
    }
    return item;
}
 
 

The Complete Client

For posterity's sake, here's the complete code for the ultimate RestSharp client:

 

using RestSharp;
using RestSharp.Deserializers;
using System;
using System.Linq;
 
public class BaseClient : RestSharp.RestClient
{
    protected ICacheService _cache;
    protected IErrorLogger _errorLogger;
    public BaseClient(ICacheService cache, IDeserializer serializer, IErrorLogger errorLogger, string baseUrl)
    {
        _cache = cache;
        _errorLogger = errorLogger;
        AddHandler("application/json", serializer);
        AddHandler("text/json", serializer);
        AddHandler("text/x-json", serializer);
        BaseUrl = new Uri(baseUrl);
    }
 
    private void TimeoutCheck(IRestRequest request, IRestResponse response)
    {
        if (response.StatusCode == 0)
        {
            LogError(BaseUrl, request, response);
        }
    }
 
    public override IRestResponse Execute(IRestRequest request)
    {
        var response = base.Execute(request);
        TimeoutCheck(request, response);
        return response;
    }
    public override IRestResponse<T> Execute<T>(IRestRequest request)
    {
        var response = base.Execute<T>(request);
        TimeoutCheck(request, response);
        return response;
    }
 
    public T Get<T>(IRestRequest request) where T : new()
    {
        var response = Execute<T>(request);
        if (response.StatusCode == System.Net.HttpStatusCode.OK)
        {
            return response.Data;
        }
        else
        {
            LogError(BaseUrl,request,response);
            return default(T);
        }
    }
 
    public T GetFromCache<T>(IRestRequest request, string cacheKey) where T : class, new()
    {
        var item = _cache.Get<T>(cacheKey);
        if (item == null)
        {
            var response = Execute<T>(request);
            if (response.StatusCode == System.Net.HttpStatusCode.OK)
            {
                _cache.Set(cacheKey, response.Data);
                item = response.Data;
            }
            else
            {
                LogError(BaseUrl, request, response);
                return default(T);
            }
        }
        return item;
    }
 
    private void LogError(Uri BaseUrl, IRestRequest request, IRestResponse response)
    {
        //Get the values of the parameters passed to the API
        string parameters = string.Join(", ", request.Parameters.Select(x => x.Name.ToString() + "=" + ((x.Value == null) ? "NULL" : x.Value)).ToArray());
 
        //Set up the information message with the URL, the status code, and the parameters.
        string info = "Request to " + BaseUrl.AbsoluteUri + request.Resource + " failed with status code " + response.StatusCode + ", parameters: "
        + parameters + ", and content: " + response.Content;
 
        //Acquire the actual exception
        Exception ex;
        if (response != null && response.ErrorException != null)
        {
            ex = response.ErrorException;
        }
        else
        {
            ex = new Exception(info);
            info = string.Empty;
        }
 
        //Log the exception and info message
        _errorLogger.LogError(ex,info);
    }
}

 

Yooo, Now Restsharp client is ready for you

Using the Awesome RestSharp Client

To use this client, we treat it as a base class for other clients. Let's imagine that we have the following object which will be returned by our API:

public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int UserID { get; set; }
    public DateTime DateOfBirth { get; set; }
}

We also have APIs at the following URLs which will return this object or a collection of this object:

/users/{id}

/users/all

We can build a client for this situation very easily with our Ultimate RestSharp Client. First, we need to inherit from our BaseClient class and implement the constructor:

public class UsersClient : BaseClient
{
    public UsersClient (ICacheService cache, IDeserializer serializer, IErrorLogger errorLogger)
        : base(cache, serializer, errorLogger, "http://baseUrl.com") { }
}
 
 

Then we need to create two methods, one which will get the users by ID and one which will get all the users. Let's add a further requirement and say that the method which will get the users by ID will have its results be cached. The complete UsersClient now looks like this:

 
public class UsersClient : BaseClient
{
    public UsersClient (ICacheService cache, IDeserializer serializer, IErrorLogger errorLogger)
        : base(cache, serializer, errorLogger, "http://yourBaseUrl.com") { }
 
    public Contact GetByID(int id)
    {
        RestRequest request = new RestRequest("users/{id}", Method.GET);
        request.AddUrlSegment("id", id.ToString());
        return GetFromCache<User>(request, "User" + id.ToString());
    }
 
    public List<Contact> GetByID(int id)
    {
        RestRequest request = new RestRequest("users/all", Method.GET);
        return Get<List<User>>(request);
    }
}
 
 

That's all there is to it! Now if you have many client classes, they can all share the same base class and easily implement error logging and caching!

Summary

This Ultimate RestSharp client is already in use in one of our biggest projects, and it's saved us so much time and effort once we got it together. I hope it helps you all out as well, dear reader!

As always, I welcome suggestions on how to improve the code in this post. Let me know if you've got any ideas in the comments!

Happy Coding!