Search

Entries in .NET (7)

Wednesday
Sep302009

Programmatically setting MaxItemsInObjectGraph in WCF

Recently I ported an Asp.Net web service based application to use WCF. Everything was working great until I tried to return an array with 10,416 items in it. Normally this is an easy fix. Simply create a DataContractSerializer behavior with a larger MaxItemsInObjectGraph setting in your config file.

    4 <behaviors>

    5       <endpointBehaviors>

    6         <behavior name="MaxObjects">

    7           <dataContractSerializer maxItemsInObjectGraph="3000000" />

    8         </behavior>

    9       </endpointBehaviors>

   10     </behaviors>

   29 <endpoint address="net.tcp://localhost:4200"

                               behaviorConfiguration="MaxObjects" />

 

This needs to be done on both the client and the server. Unfortunately, this method won’t work for my client app. The client creates all the endpoints programmatically.

The process of setting this in code isn’t quite as straightforward as I’d expected. It turns out the DataContractSerializer behaviors have to be applied to the constructor of the serializer. Normally when you create a ServiceEndPoint in code you aren’t creating the DataContractSerializer, the ServiceEndPoint constructor is. I’ve seen examples online of people creating custom serializers but that’s overkill for just changing this setting.

I found it much easier to change the setting on the Operation behaviors after the EndPoint has been created. The only tricky thing is you need to change it for every Operation that needs a larger MaxItem count. For simplicity, I created a function to chang it for all operations on the endpoint.

  166 ServiceEndpoint endpoint = new ServiceEndpoint(

  168 UpdateMaxItemsBehavior(endpoint, 300000000);

protected void UpdateMaxItemsBehavior(ServiceEndpoint endpoint, int maxItems)

{

   foreach (OperationDescription operation in endpoint.Contract.Operations)

   {

       operation.Behaviors.Find<

                               DataContractSerializerOperationBehavior

                               >().MaxItemsInObjectGraph = maxItems;

  }

}

Tuesday
Sep222009

WCF Host and Client in Same Application

I recently found a need to host a WCF service within the same application as the client. The only real difference is the need to start the service in it’s own thread. When I originally tried hosting the service in the same thread as the client it deadlocked.

This is a simple enough matter to fix. The only gotcha, is the need to ensure the service has actually started up before making your first call from the client. I handled it with a simple flag.

   23 private ServiceHost _serviceHost;

   24 private MTGServer _server;       

   26 private bool _serverRunning;

...

 

   51 new Thread(new ParameterizedThreadStart(StartHost)).Start(hostConfiguration.Port);

   52 

   53 while (!_serverRunning)

   54 {

   55   Thread.Sleep(100);

   56 }

   57 

   58 JoinGame("localhost", hostConfiguration.Port);

...

 

   62 private void StartHost(object port)

   63 {           

   64  _server = new MTGServer();

   65 

   66  string address = String.Format("net.tcp://localhost:{0}", port);

   67 

   68  _serviceHost = new ServiceHost(_server, new Uri(address));

   69  _serviceHost.AddServiceEndpoint(typeof(Shared.IMTGServer), new NetTcpBinding(), address);

   70 

   71  _serviceHost.Open();

   73  _serverRunning = true;

 

 

In my case, I’m using a service in Singleton Context Mode. I’m not sure if this issue would exist using one of the other context modes.

Sunday
Aug092009

Proxy Data Provider with WCF – Client

This article is a continuation of a longer tutorial. If you haven’t done so already you should read it from the beginning.

Client

Now that we have the service and contract built, we can finally build the client. The client consists of two assemblies, the ClientProxies assembly contains the code to talk to the service. The DbProvider assembly is the actual data provider which will be used by the final application.

ClientProxies

   10 public class WcfDataProviderProxy : IWcfDataProvider

   11     {

   12         private IWcfDataProvider _channel;

   13 

   14         public WcfDataProviderProxy(string host)

   15         {

   16             string enpointAddress = "net.tcp://" + host;

   17             ServiceEndpoint endPoint = new ServiceEndpoint(

   18                 ContractDescription.GetContract(typeof(IWcfDataProvider)),

   19                 new NetTcpBinding(),

   20                 new EndpointAddress(enpointAddress));

   21 

   22             _channel = new ChannelFactory<IWcfDataProvider>(endPoint).CreateChannel();

   23         }

 

This class is quite simple. The constructor takes the host address as a parameter and constructs an IWcfDataProvider Channel. One thing to note is that the endpoint is being hard coded as NetTcpBinding. Usually with WCF this would be configured in the App.Config. However, we can’t allow that for this library. The end client for the data provider will often be out of our control. This forces us to create the endpoint manually.

All the rest of the methods in the proxy class are straight wrappers around calls to the channel.

   27 public string CreateConnection(string invariantName)

   28         {

   29             return _channel.CreateConnection(invariantName);

   30         }

   31 

   32         public void SetConnectionString(string token, string connectionString)

   33         {

   34             _channel.SetConnectionString(token, connectionString);

   35         }

 

Data Provider

The final piece are the actual data provider classes. These are the classes which implement the IDb* interfaces and are used directly by the consuming application.

WcfDbConnection

The data provider starts with the connection. As with all data providers, all connection information must be supplied through the connection string. In our case we actually have two distinct parts to the connection string. It must contain connection information so the provider can talk to the service. It also needs the connection string information the service will use to connect to the database.

To make this example simple, the connection string requires a ServiceHost parameter then an InvariantName parameter. The normal connection string will then follow.

   83 private void SetConnectionString(string connectionString)

   84         {

   85             string tempConnectionString = connectionString;

   86             string parameterName;

   87             string parameterValue;

   88 

   89             //Get First Parameter which MUST be ServiceHost

   90             if (!GetNextConnectionStringParameter(ref tempConnectionString, out parameterName, out parameterValue)

   91                 || parameterName != "ServiceHost")

   92             {

   93                 throw new Exception("Connection String Must begin with ServiceHost parameter");

   94             }

   95 

   96             _host = parameterValue;

   97 

   98             //Get Second Parameter which MUST be InvariantName

   99             if (!GetNextConnectionStringParameter(ref tempConnectionString, out parameterName, out parameterValue)

  100                 || parameterName != "InvariantName")

  101             {

  102                 throw new Exception("Connection String Must have InvariantName as second parameter");

  103             }

  104 

  105             _invariantName = parameterValue;

  106 

  107             CreateProxy();

  108 

  109             _provider.SetConnectionString(_token, tempConnectionString);

  110         }

 

After setting the connection string, the provider will instantiate the client proxy class and use it to create a connection on the server. It will store it’s token internally and use it for all future calls to the proxy.

   21 private void CreateProxy()

   22         {

   23             _provider = new WcfDataProviderProxy(_host);

   24             _token = _provider.CreateConnection(_invariantName);

   25         }

   26 

   27         private string _token;

   28         public string Token

   29         {

   30             get { return _token; }

   31             set

   32             {

   33                 _token = value;

   34             }

   35         }

 

All other interface calls are pretty much direct wrappers of the proxy class. At this point it’s important to note that the connection string must be set before any other calls are made. If you try to perform anything such as checking the ConnectionState before the proxy is created (connection string set), you will get a null reference exception.

The Command and DataReader classes follow pretty much the same pattern.

Usage

At this point we can now use the data provider much like any other.

   30 string connectionString = @"ServiceHost=localhost:4200;InvariantName=System.Data.SqlClient;Data Source=Holly\SQLExpress;Initial Catalog=AdventureWorks;Integrated Security=True;";

   31 

   32             using (IDbConnection connection = new WcfDbConnection())

   33             {

   34                 connection.ConnectionString = connectionString;

   35                 connection.Open();

   36 

   37                 using (IDbCommand command = connection.CreateCommand())

   38                 {

   39                     command.CommandText = "select * from [Person].[Contact]";

   40 

   41                     using (IDataReader reader = command.ExecuteReader())

   42                     {

   46                         while (reader.Read())

   47                         {

 

 

Final Notes

The code presented here shows the method and beginnings of a Data Provider. A more complete implementation will require these same steps to be used to create a WCF wrapper classes for IDataAdapter, IDbDataParameter and other provider specific classes.

Source Code

Previous: Service

Sunday
Aug022009

Proxy Data Provider with WCF – Service

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.

  1. Get underlying Ado.Net object from dictionary using the Token as the key.
  2. Execute the method on object
  3. 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         }

 

Source Code

Previous: Contract

Next: Client

 

Sunday
Jul262009

Proxy Data Provider with WCF – Contract

Contract

The contract defines which operations the WCF Service provides. This is also the only assembly that both the client and server will have in common.

The WCF Data Provider is just a proxy on top of other data providers. This means that most of the methods will be close to a 1:1 mapping of the methods and properties in the ADO.Net data provider interfaces (IDbConnection, IDbCommand, etc).

Click to read more ...