It’s been entirely too long since my last post, and with the olympics in full swing, we’ll go with a volleyball theme for this post.

BUMP

So here’s the scenario. After really digging your teeth into ASP.NET 2.0 and playing around wiih all the cool data access features, you decide to apply a liberal use of the ObjectDataSource for its ability to take care of the paging and sorting calls for you. Now, you begin to convert your site to a service oriented back end and you wonder how you’re going to make calls against your web service. You have a few options in front of you. You can create a thin facade layer that will act as a liaison between your presentation code and your service layer calls, and hook your ObjectDataSource to that. This works especially well when you have a complex object model that makes use of the web service calls behind the scenes. It’s also beneficial when you are generating your proxy classes from metadata or contacting a foreign web service. In the scenario I’m going to demonstrate here, I’m referencing a shared contract and service library, so I’ll be using using the ObjectDataSource to make direct calls to ChannelFactory for this example.

SET

The main ingredients for this technique include a dash of class inheritance, a heaping spoonful of refluxion (as one of my friends says it), and some method overriding, to taste. The main issue here is that since WCF contracts are interfaces, how can we declare them in the ObjectDataSource being that you can’t instantiate an interface. Well, let’s show some code, starting off with a very basic service contract and implementation:

[ServiceContract]
public interface IContract
{
    [OperationContract]
    string SayHello(string name);
}
public class ContractService : IContract
{
    public string SayHello(string name)
    {
        return "Hello " + name + "!";
    }
}

So I need some way of creating an instance of ChannelFactory in my ObjectDataSource in order to call the SayHello method. To do this, we use extend the ObjectDataSource and register our extended class to handle the ObjectCreating event of the ObjectDataSource. This event gives us the ability to set the instance of the object that we want to call our method on. The following code shows how we will set the stage for our reflection extravaganza:

public class WebServiceObjectDataSource : ObjectDataSource
{
    public WebServiceObjectDataSource() : base()
    {
        ObjectCreating += new ObjectDataSourceObjectEventHandler(WebServiceObjectDataSource_ObjectCreating);
    }
    protected virtual void WebServiceObjectDataSource_ObjectCreating(object sender, ObjectDataSourceEventArgs e) { }
}

For our example, we’re going to assume that the user is going to specify the name of the service contract in the existing “TypeName” property of the control. Using this, we’re going to generate our proxy. Here’s the code that creates the actual object:

protected virtual void WebServiceObjectDataSource_ObjectCreating(object sender, ObjectDataSourceEventArgs e)
{
    string contractTypeName = String.IsNullOrEmpty(EndpointContractTypeName) ? TypeName : EndpointContractTypeName;
    Type serviceContractType = BuildManager.GetType(contractTypeName, true);
    Type channelFactoryType = typeof(ChannelFactory<>).MakeGenericType(new[] { serviceContractType });
    var factory = Activator.CreateInstance(channelFactoryType, new[] { EndpointConfigurationName });
    e.ObjectInstance = factory.GetType().GetMethod("CreateChannel", Type.EmptyTypes).Invoke(factory, null);
}

SPIKE

Analyzing the code above, you can see that we use a heavy dose of reflection and some generics to create an instance of ChannelFactory and then call the "CreateChannel" method to get the instance of the proxy to call the SelectMethod. You'll notice the first line chooses between the typename property and a custom made property on the control called EndpointContactTypeName. I'll explain this in the next section. Needless to say, the heart of the work is done by the CreateInstance and Invoke methods that are called to create the ChannelFactory and Contract proxy instance. My example here is pretty basic and doesn't talk about things like connecting to a service proxy using client credentials or one of the other overloads of the ChannelFactory constructor. Just know that these scenarios are all possible by adding additional properties and creative use of reflection to determine the proper constructors and/or methods to call on the classes to get just the proxy type you need.

WATCH OUT FOR THE NET!

One little gotcha in this whole thing. There are cases when you have a master contract, which is an interface that inherits from another interface. Sometimes I do this to consolidate the number of services I expose so that I don’t have a configuration that’s a mile long with 100+ service contracts that each contain one method. consider this contract scenario:

[ServiceContract]
public interface IMainContract : IContract { }

[ServiceContract]
public interface IContract
{
    [OperationContract]
    string SayHello(string name);
}

This is pretty simplistic but you can get the idea. So, in this case, I may expose IMainContract as a service. If I use IMainContract as the “TypeName” property in my extended ObjectDataSource class, it will throw an exception that it could not find the “SayHello” method that I specified as the SelectMethod in the interface. This has to do with how C# deals with interface inheritance. Needless to say that there are workarounds, but because of the way ObjectDataSource was coded, you cannot change the way that it checks for these methods. Also, because the configuration file exposes the service as the IMainContract class, if you specify a TypeName of IContract, then the select method will be found, but you’ll get an error saying that the ChannelFactory could not find a service contract with the type IContract. Quite the Catch 22! The solution for this is to set the TypeName to IContract since our method is defined here. Also, I added a property called EndpointContractTypeName which can be set to IMainContract so that when the object is being created it will generate that type, and since IMainContract implements IContract it follows that once the instance is created, we can call any method that existed on IContract with no problems.

The full source for the version I’m using looks something like this:

public class WebServiceObjectDataSource : ObjectDataSource
{
    public string EndpointConfigurationName { get; set; }
    public string EndpointContractTypeName { get; set; }
    public WebServiceObjectDataSource() : base()
    {
        ObjectCreating += new ObjectDataSourceObjectEventHandler(WebServiceObjectDataSource_ObjectCreating);
    }
    protected virtual void WebServiceObjectDataSource_ObjectCreating(object sender, ObjectDataSourceEventArgs e)
    {
        string contractTypeName = String.IsNullOrEmpty(EndpointContractTypeName) ? TypeName : EndpointContractTypeName;
        Type serviceContractType = BuildManager.GetType(contractTypeName, true);
        Type channelFactoryType = typeof(ChannelFactory<>).MakeGenericType(new[] { serviceContractType });
        var factory = Activator.CreateInstance(channelFactoryType, new[] { EndpointConfigurationName });
        e.ObjectInstance = factory.GetType().GetMethod("CreateChannel", Type.EmptyTypes).Invoke(factory, null);
    }
}