Kerosene for WCF

Kerosene can be expanded to support WCF scenarios where, in essence, we want that the client application will not have a direct connection with the underlying database, but rather through an intermediate service that, in turn, will be the one in charge to read and write the contents back and forth the real database.

The Server

So we need first a service able to handle the requests it will receive from a client. The Kerosene WCF server is an instance of a class derived from the abstract KServerWCF one, which role is to create the server-side link object that will be associated with this specific client.

There are no restrictions on the type of link object this method will create. Typically you will create a direct link to connect to an underlying database - but nothing restricts you to create another WCF link instance to serve as a bridge to another server if you wish!

In any case, while creating your server's link object you can use a connection package it will receive from the client to, for instance, build the appropriate connection string. This connection package is stored in the server's Package property, and can be either null, if it has not been used, or an instance of the DeepObject class - a dynamic object able to store an arbitrary number of properties. See DeepObject for more details.

The way it works will create a different KServerWCF instance (with its associated link object) per each connection. So a single host application can server many connections simultaneously without having them interfere with each other.

Let's see how to prepare the WCF environment for hosting the Kerosene service:

KTypesWCF.AddType( typeof( CalendarDate ) );
KTypesWCF.AddType( typeof( ClockTime ) );

ServiceHost host = new ServiceHost( typeof( MyServer ) );
host.Open();

Console.Write( "\n-Press [Enter] to finish the service host...\n..." ); Console.ReadLine();
try { host.Close( new TimeSpan( 0, 0, 5 ) ); }
catch { host.Abort(); throw; }

The first two lines register the custom types you are going to send through the wire. The reason for this is because you have to tell WCF that it will have to deal with types in the parameters it has not previous knowledge about. Obviously, those types should be serializable in order to be transmitted with WCF. In the example I have used two custom types for the sake of the argument: they are included in the download just as an example, they should not be considered as part of the Kerosene library. You can use your own types as you wish.

The second group of lines creates the service’s host, registers your Kerosene’s server derived class with it (named "MyServer"), and opens the service. This way each time the host service receives a new connection it will create an instance of your class to serve it. I’m sorry, there is no easy way to register several types – but it should not be a problem as the CreateLink() method can return a different instance each time customized the way you want, for instance with the aid of the connection package.

The third group of lines merely waits until you press a key to close the service’s host. Obviously, you will want to adapt it to your own needs.

Ok, we have created the service that will host our server objects. Now, let’s see how can we create our own server class. It should derive from the abstract KServerWCF class, and it only has to override the above mentioned CreateLink() method, like this:

public override IKLink CreateLink() {
   var connString = "Use your favorite connection string here";
   var link = new KLinkDirectSQL( connString );
   link.AddParameterTransformer<CalendarDate>( x => CalendarDate.ToDateTime( x ) );
   link.AddParameterTransformer<ClockTime>( x => x.ToString() );
   return link;
}

In this case we have created a direct link adapted for Microsoft SQL server (as this is the one included in the library as an example). We have used a regular connection string to instantiate it, as you may recall from the explanations of Kerosene Basics, but the nice thing is that we could have built it using the contents in the connection package.

Also, it is important to register the "transformers" this link object may need to use. You may want to recall that they are in charge of translating, for the command’s parameters, the types they are associated with, to the appropriate SQL types understood by your database (actually, by the ADO.NET engine Kerosene is using behind the scenes). This is not the same as we did when registering them for WCF serialization: one is used with the database, the other with the WCF communication channel.

And that’s it. We have now our server prepared to serve the commands and requests it will receive from the clients.

The Client

The client is just a specialized link object, an instance of the KLinkWCF class that, in turn, implements the IKLink interface. Its constructor takes a first argument being an instance of an IKFactory object that, as you recall, is the one that indicates the "personality" of the database it will connect to. It will be the factory object used to obtain the appropriate parser to translate your dynamic expression into the syntax this database will understand, among other interesting things.

Once you have instantiated this object you can use its ProxyConnect() method to start the connection with the server. This method takes two arguments: the first one is a string with the endpoint that WCF will use to locate the service host, as it is specified in the App.Config file of your client application, and a second an optional argument being the connection package discussed above. There is also a ProxyDisconnect() method that, as its name implies, closes the connection created with the server and frees all its resources. These methods are virtual ones, so you can override them if needed (for instance for logging purposes).

There is a second constructor which takes three arguments: the IKFactory mentioned above, a string with the endpoint, and the optional connection package. In this case, it will start the connection automatically and you won’t have to start it manually. Let’s see an example:

KTypesWCF.AddType( typeof( CalendarDate ) );
KTypesWCF.AddType( typeof( ClockTime ) );

dynamic package = new DeepObject();
package.Login = "MyLogin"; // Just as an example
package.Password = "MyPassword"; // Just as an example

var endpoint = "Your_EndPoint"; // Use the endpoint on you App.config file
var factory = new KFactorySQL(); // Use the appropriate factory
var link = new KLinkWCF( factory, endpoint, package )

As happened with the server, our first task is to register with the WCF machinery the custom types we want to send through the wire. Our second task is to create the connection package – as said before, it is optional, so if you don’t want to use it just set it to null. You can add to it as many properties as needed, as far as their values are serializable. Then we have instantiated the factory. Finally, we have created the link client by using the second override of its constructor, which creates and opens the connection with the server on your behalf.

Once you have this link object you can use it as you had used its direct version cousin. Indeed all the tests included in the download are built agains IKLink objects, so they don't care if the reference they are receiving is a direct one or a WCF one.

A note about security: the assumption is that, if you need to secure the communication between your server and your client, you will use the facilities WCF provides to achieve it. Or, in the case of the connection package, you would encrypt the values (in a serializable form) before sending them through the wire. For this reasons Kerosene does try to reinvent the wheel and so does not provide any custom mechanism to secure the traffic between the server and the client.

Opening and Closing the Database

The client link object inherits all the database related methods of the IKLink interface. In particular the DbOpen() and DbClose() ones that let you open and close manually the connection the server is using with the underlying database. Kerosene takes care of opening such connection when needed, and closing it afterwards, so these methods are rarely used. But they might be helpful in very intensive database operations for performance reasons.

By the way, the client will never have access to the real database: these methods merely instruct the server to operate on the client's behalf. There is no way you can obtain the IDbConnection the server's link is using, in case it is a direct one. No, there is also no no way to obtain from the client what type of link object the server has created to serve its demands.

Transactions

Similarly, the client's link object can use the transaction related methods it has inherit from the IKLink interface. But, again, they are merely a bridge to instruct the server's link object to execute, at the server's side, those methods. There is no way you can obtain from the client any transaction-related object.

Regarding to this, just let me remember you have available the TransactionMode and TransactionState properties that let you interrogate the server for its state and the transaction mode it is using. Depending upon the specific implementation, it might even allow you to set the mode to either Database or Scope - but in this later case, the scope is restricted to the server side.

Intercepting the database operations

It is important to note that the commands you are going to execute are written and parsed at the client's side. The server will only receive the actual SQL code to execute, and the list of associated parameters.

At the server, if you wish, you can override the KServerWCF's EnumeratorCreate() and ExecutorCreate() methods in order to parse the SQL text they are receiving, and decide whether to progress with the operation requested or not (for instance by raising the appropriate exception).

Some users have mentioned me that they are overriding those methods for the sake of logging at the server's side.

Last edited Sep 24, 2012 at 6:45 PM by mbarbac, version 3

Comments

No comments yet.