Proxy Data Provider with WCF – Service
Sunday, August 2, 2009 at 1:21PM This article is a continuation of a longer tutorial. If you haven’t done so already you should read it from the beginning.
Service
With the architecture and contract in place we are finally ready to build the service. For the most part WCF makes implementing the service extremely easy. We simply implement the IWcfDataProvider interface and have the various methods return the results generated by the underlying ADO.Net objects.
Base Class Definition
10 public class WcfDataProviderService : IWcfDataProvider
11 {
12 private Dictionary<string, IDbConnection> _connections;
13 private Dictionary<string, IDbCommand> _commands;
14 private Dictionary<string, string> _commandConnections;
15 private Dictionary<string, IDbDataParameter> _parameters;
16 private Dictionary<string, IDataReader> _readers;
17
18 public WcfDataProviderService()
19 {
20 _connections = new Dictionary<string, IDbConnection>();
21 _commands = new Dictionary<string, IDbCommand>();
22 _commandConnections = new Dictionary<string, string>();
23 _parameters = new Dictionary<string, IDbDataParameter>();
24 _readers = new Dictionary<string, IDataReader>();
25 }
26
27 private string GetToken()
28 {
29 return System.Guid.NewGuid().ToString("B");
30 }
The class is quite simple. There is very little to the class other than the methods it has to implement as part of the contract. There are 5 dictionaries which are used to store the underlying ADO.Net objects. The key foe these dictionaries are the Tokens as discussed in the Contract section. Whenever one of the Contract methods is called the token passed in to the method is used to retrieve the underlying object.
There is also a GetToken method which simply returns a new GUID.
Hosting
For this service to function properly the Dictionaries containing the IDb* objects must persist across multiple calls. For this example, I use a Console Application which instantiates a single instance of the service and uses it for all calls. However, other hosts may choose to destroy and recreate instances of the services. Others may choose to create multiple instances simultaneously. Depending on your host you may have to make all the dictionaries Static and instantiate them in the classes static constructor.
Common Interface Implementation
The rest of the class simply implements the interface contract. Most methods follow the exact same pattern.
- Get underlying Ado.Net object from dictionary using the Token as the key.
- Execute the method on object
- Return results
Example:
69 public int GetConnectionTimeout(string token)
70 {
71 return _connections[token].ConnectionTimeout;
72 }
Very few methods deviate from this model. We’ll focus only on those methods that do.
Ado.Net Object Creation
Connection
34 public string CreateConnection(string invariantName)
35 {
36 IDbConnection connection = System.Data.Common.DbProviderFactories.GetFactory(invariantName).CreateConnection();
37 string token = GetToken();
38
39 _connections.Add(token, connection);
40
41 return token;
42 }
Create Connection is the only contract method that doesn’t require a token. It accepts a single parameter, the invariantName. This is the invariant name of the Data Provider you wish to instantiate. The method is quite simple, it creates a new IDbConnection for that provider using the DbProviderFactory’s CreateConnection method. It then generates a new token, stores the connection and returns the token to the client.
Command
75 public string CreateCommand(string token)
76 {
77 string commandToken = GetToken();
78 IDbCommand command = _connections[token].CreateCommand();
79 _commands.Add(commandToken, command);
80
81 return commandToken;
82 }
To create a command object the CreateCommand method uses a connection token to retrieve the connection for which the command will be created. It then calls the CreateCommand method on the IDbConnection object and stores the resulting command. The returned token points to the new Command object.
IDataReader
The GetCurrentReaderRecord is the only method that significantly deviates from the Common Interface Implementation. Rather than implement all the GetString, GetInt, etc. methods we return the entire row. Calls to these methods on the client will then pull the data from the cached DataTable.
311 public DataTable GetCurrentReaderRecord(string token)
312 {
313 DataTable table = new DataTable("CurrentRecord");
314
315 IDataReader reader = _readers[token];
316
317 for (int i = 0; i < reader.FieldCount; i++)
318 {
319 DataColumn column = new DataColumn(reader.GetName(i), reader.GetFieldType(i));
320 table.Columns.Add(column);
321 }
322
323 DataRow row = table.NewRow();
324
325 for (int i = 0; i < reader.FieldCount; i++)
326 {
327 row[i] = reader[i];
328 }
329
330 table.Rows.Add(row);
331
332 return table;
333 }
Service Host
The final piece of the Service is the hosting application. For this example I’ve kept it to a simple console application. I’ve also hard coded the endpoint to be NetTcpBindings rather than use App.Config for configuration.
11 static void Main(string[] args)
12 {
13 using (ServiceHost serviceHost = new ServiceHost(typeof(WcfDataProviderService), new Uri("net.tcp://localhost:4200")))
14 {
15 serviceHost.AddServiceEndpoint(typeof(IWcfDataProvider), new NetTcpBinding(), "net.tcp://localhost:4200");
16
17 serviceHost.Open();
18
19 Console.WriteLine("Service Started. Press Enter to Exit.");
20 Console.ReadLine();
21 }
22 }
Previous: Contract
Next: Client
.NET,
WCF in
WCF Data Provider
Reader Comments