Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Improving WCF Service Quality (Part 1 - Introduction and Basics)

0.00/5 (No votes)
28 Feb 2022 1  
Basics of improving WCF service quality
This is Part 1 of a 4 part series of articles that discuss how to improve WCF services with Authentication, Authorization, Validation and Logging with Aspect Oriented Programming. This article covers the basics.

Since this will be a long article, I have divided it into several parts to make it more readable.

This is the first part "Introduction & Basics". The next parts can be found by clicking on the links below:

  1. Validation
  2. Authentication/Authorization
  3. Logging

Introduction

This article is based on a refactoring project I have done in my professional career. Due to security policy changes, I have been asked to refactor existing WCF services to make them more secure and traceable. Instead of re-writing the whole service codes, I decided to introduce Authorization, Authentication, Logging and Validation concepts to existing service architecture.

The first idea was to re-write all services but since we are using several hundred service methods currently, this idea would bring overwhelming development time. Instead, I decided to use Aspect Oriented Programming to reduce development work.

Background

There are several good AOP libraries to use with C#, such as Castle, AspectSharp, Aspect.NET and PostSharp where PostSharp is chosen for this project due to ease of use and good documentation and number of examples on communities.

Microsoft SQL Server is used to store data and Entity Framework is used for data access. Although .NET Core might be a modern approach, since we are trying to reduce development work, .NET Framework is used for backwards compatibility.

Using the Code

As a common practice, most developers write a WCF service as follows:

C#
[ServiceContract]
public interface IExampleService
{
    [OperationContract]
    int CreatePlayer(string name, DateTime dateofBirth, int? height, int? weight, string club);
    [OperationContract]
    Player GetPlayerById(int id);
    [OperationContract]
    List<Player> GetAllPlayers();
    [OperationContract]
    List<Player> GetClubPlayers(string club);
}
C#
public class ExampleService : IExampleService
{
    public int CreatePlayer(string name, DateTime dateofBirth, int? height, 
                            int? weight, string club)
    {
        return PlayerRepository.CreateNewPlayer(name, dateofBirth, height, weight, club);
    }
    public List<Player> GetAllPlayers()
    {
        return PlayerRepository.GetAllPlayers();
    }
    public List<Player> GetClubPlayers(string club)
    {
        return PlayerRepository.GetClubPlayers(club);
    }
    public Player GetPlayerById(int id)
    {
        return PlayerRepository.GetPlayerById(id);
    }
}

Our first action is to change multiple input parameters to a class containing the parameters as a Data contract and as a naming convention, since they are a part of the request, name them as the Method name + Request.

C#
[DataContract]
public class CreatePlayerRequest
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public DateTime DateOfBirth { get; set; }
    [DataMember]
    public int? Height { get; set; }
    [DataMember]
    public int? Weight { get; set; }
    [DataMember]
    public string Club { get; set; }
}
C#
[DataContract]
public class GetClubPlayersRequest
{
    [DataMember]
    public string Club { get; set;}
}
C#
[DataContract]
public class GetPlayerByIdRequest
{
    [DataMember]
    public int PlayerId { get; set; }
}

Then create response objects for each particular service method, again with a naming convention Method name + Response.

C#
[DataContract]
public CreatePlayerResponse
{
    [DataMember]
    public int PlayerId { get; set; }
}
C#
[DataContract]
public GetAllPlayerResponse
{
    [DataMember]
    public List<Player> PlayerList { get; set; }
}
C#
[DataContract]
public GetClubPlayersResponse
{
    [DataMember]
    public List<Player> PlayerList { get; set; }
}
C#
[DataContract]
public GetPlayerByIdResponse
{
    [DataMember]
    public Player Player { get; set; }
}

Of course, it is possible to use one Response object instead of GetAllPlayerResponse and GetClubPlayersResponse objects.

The next step is to create a new Class library project named "WCFServices.Shared" and reference it to our existing service. After creating and referencing this project, let's create these two classes respectively.

C#
[DataContract]
public class BaseRequest
{
    [DataMember]
    public string Username { get; set; }
    [DataMember]
    public string Password { get; set; }
}
C#
[DataContract]
public class BaseResponse
{
    [DataMember]
    public bool IsException { get; set; }
    [DataMember]
    public bool IsSuccess { get; set; }
    [DataMember]
    public string[] Messages { get; set; }
}

New created BaseRequest class will be the base class for all our request objects and will hold the necessary information for Authentication and Authorization. In a similar approach, BaseResponse class will be the base class for all our response objects and will hold response status information for all methods in three properties:

  • IsException: Will be set to true if an exception occurs during the method call. If an exception will occur, the IsSuccess property should be set as false and the Messages property should return Exception's Message property.
  • IsSuccess: Will be set to true if the method completes in a successful manner. If this is set to true, IsException property should be set as false and the Messages property set as a constant message.
  • Messages: This property may return one or more lines of messages informing the client about the status of the request. Besides details about Exception (if occurred) or success message, this property can also hold values for failing validations as well.

After creating these classes, all our request and response classes within the service should be updated as follows:

C#
[DataContract]
public class CreatePlayerRequest : BaseRequest
{
    [DataMember]
    public string Name { get; set; }
    [DataMember]
    public DateTime DateOfBirth { get; set; }
    [DataMember]
    public int? Height { get; set; }
    [DataMember]
    public int? Weight { get; set; }
    [DataMember]
    public string Club { get; set; }
}
C#
[DataContract]
public class GetClubPlayersRequest : BaseRequest
{
    [DataMember]
    public string Club { get; set;}
}
C#
[DataContract]
public class GetPlayerByIdRequest : BaseRequest
{
    [DataMember]
    public int PlayerId { get; set; }
}
C#
[DataContract]
public CreatePlayerResponse : BaseResponse
{
    [DataMember]
    public int PlayerId { get; set; }
}
C#
[DataContract]
public GetAllPlayersResponse : BaseResponse
{
    [DataMember]
    public List<Player> PlayerList { get; set; }
}
C#
[DataContract]
public GetClubPlayersResponse : BaseResponse
{
    [DataMember]
    public List<Player> PlayerList { get; set; }
}
C#
[DataContract]
public GetPlayerByIdResponse : BaseResponse
{
    [DataMember]
    public Player Player { get; set; }
}

And our service and interface will be like:

C#
[ServiceContract]
public interface IExampleService
{
    [OperationContract]
    CreatePlayerResponse CreatePlayer(CreatePlayerRequest request);
    [OperationContract]
    GetPlayerByIdResponse GetPlayerById(GetPlayerByIdRequest request);
    [OperationContract]
    GetAllPlayersResponse GetAllPlayers(BaseRequest request);
    [OperationContract]
    GetClubPlayersResponse GetClubPlayers(GetClubPlayersRequest request);
}
C#
public class ExampleService : IExampleService
{
    public CreatePlayerResponse CreatePlayer(CreatePlayerRequest request)
    {
        try
        {
            return new CreatePlayerResponse
            {
                PlayerId = PlayerRepository.CreateNewPlayer(request.Name, 
                  request.DateOfBirth, request.Height, request.Weight, request.Club),
                IsException = false,
                IsSuccess = false,
                Messages = new string[] { "Operation successful" }
            };
        }
        catch(Exception ex)
        {
            return new CreatePlayerResponse
            {
                IsException = true,
                IsSuccess = false,
                Messages = new string[] { ex.Message; }
            };
        }
    }
    public GetAllPlayersResponse GetAllPlayers(BaseRequest request)
    {
        try
        {
            return new GetAllPlayersResponse
            {
                PlayerList = PlayerRepository.GetAllPlayers(),
                IsException = false,
                IsSuccess = false,
                Messages = new string[] { "Operation successful" }
            };
        }
        catch(Exception ex)
        {
            return new GetAllPlayersResponse
            {
                IsException = true,
                IsSuccess = false,
                Messages = new string[] { ex.Message; }
            };
        }
    }
    // I think the first two methods give enough clue about the transformation.
    :
    :
}

This was the first part of our series, explaining the basic approach to make some updates to our service structure and prepare it for our next chapters.

You can read the next part (Validation) here.

History

  • 28th February, 2022: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here