Search

Entries in WCF (6)

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
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 ...

Sunday
Jul262009

Proxy Data Provider with WCF - Architecture

I’ve structured the system according to the recommendations of Miguel Castro on Show #122 or dnrTV. I highly recommend you view this tutorial.

The full solution will consist of 4 required assemblies and two optional ones.

image

Click to read more ...