none
WCF and Generics

    Question

  • Hi all,

    I'm currently experimenting with using WCF and .NET generics together, in an attempt to have a service that exposes the following service contract:

     [ServiceContract]
     public interface IGenericServer
     {
      [OperationContract]
      void MakeRequest<IService>();
     }

    Is this possible? Upon trying my driver for this service the first time, I received a System.NotSupportedException--"Method MakeRequest is not supported on this proxy, this can happen if the method is not marked with OperationContractAttribute or if the interface type is not marked with ServiceContractAttribute."
    Has anyone else tried this with different results, or is this simply not supported in WCF?

    Thanks!

    Wednesday, January 17, 2007 7:24 PM

Answers

  • There are a few problems here:

    1. WSDL does not support operation overloads. Service contracts produce WSDL. So by definition, you must expose a concrete operation uniquely named.
    2. WSDL only knows about concrete types, so interfaces evaluate to "object" thus your MakeRequest<IService>() becomes MakeRequest<object> which is useless anyways, even if it worked. You would have to supply a base type.

    From what I can gather, it sounds like you are trying to have the client decide which service to call, anyways. And, you want a central service to delegate those calls. Presumably this is for tracking purposes, as in an intermediary service.

    One approach would be to expose a common service contract:

     [ServiceContract(Namespace="MyNamespace")]
     public interface IGenericService
     {
      [OperationContract]
      void DoSomething();
     }

    and then, you expose different services that all implement this base contract:

     public class ServiceA : IGenericService
     {
      public void DoSomething()  {
       // do ServiceA work  }
     }

     public class ServiceB : IGenericService
     {
      public void DoSomething()  {
       // do ServiceB work  }
     }

    and then, you expose an intermediary service that can detect which service the client was calling, based on the Uri:

     [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
     public class GenericService : IGenericService
     {
      private ServiceCollection services;

      public void DoSomething()
      {
       GetService.DoSomething();
      }
     }

    Where the internals of GetService() check the message headers for the actual service Uri being sent.

    Now, I am making the assumption that you are looking for an intermediary service to be a pass through. Another approach is to create a real intermediary that can receive ANY message, but in your case it looks like you want a specific type of message (DoSomething()) thus a strongly typed intermediary.

    It would be best if you can explain what the goals are for the client. Do they know ahead of time which service they are hitting? Do you want the client to have a proxy that works for ANY valid IGenericService implementation? In that case, what I am suggesting will work, but you have to modify the WSDL for each service to use the <service> address of the GenericService instead of the service address of each individual ServiceA, ServiceB, etc.

     

    Thursday, January 18, 2007 7:44 PM

All replies

  • Hello,

    Can you please explain what you're trying to achieve with this service contract definition?  I'm trying to understand your design decision so I can hopefully help you with the issue.

    Thanks.

    Wednesday, January 17, 2007 8:27 PM
  • The idea is more experimentation than anything--I want to see if it's possible to make a generic method call from just inside the endpoint of a service without having to do multiplexing on the type via some enum or type comparisons. Right now, I'm doing something like this:

     [ServiceContract]
     public interface IGenericService
     {
      [OperationContract]
      void MakeRequest(ServiceTypeEnum service);
     }

     public interface IService
     {
      void DoSomething();
     }

     [ServiceBehavior(InstanceContextMode = InstanceContectMode.Single)]
     public class GenericService : IGenericService
     {
      private ServiceCollection services;
      public void MakeRequest(ServiceTypeEnum service)
      {
       switch (service)
       {
        case ServiceTypeEnum.ServiceA:
         services.Get<ServiceA>.DoSomething();
         break;
        case ServiceTypeEnum.ServiceB:
         services.Get<ServiceB>.DoSomething();
         break;
        case ServiceTypeEnum.ServiceC:
         services.Get<ServiceC>.DoSomething();
         break;
        case ServiceTypeEnum.ServiceD:
         services.Get<ServiceD>.DoSomething();
         break;
       }
      }
     }

    I'm not sure it's possible, but it would be more elegant if I could do this, though:

     [ServiceContract]
     public interface IGenericService
     {
      [OperationContract]
      void MakeRequest<IService>();
     }

     public interface IService
     {
      void DoSomething();
     }

     [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
     public class GenericService : IGenericService
     {
      private ServiceCollection services;
      public void MakeRequest<IServiceType>() where IServiceType : IService
      {
       services.Get<IServiceType>.DoSomething();
      }
     }

    It just seemed a bit clunky to have to explicitly declare generic arguments based on some other conditions instead of being able to pass the generic argument on through the WCF connection. I haven't been able to find any other discussion on the topic so far.

    Wednesday, January 17, 2007 9:15 PM
  • There are a few problems here:

    1. WSDL does not support operation overloads. Service contracts produce WSDL. So by definition, you must expose a concrete operation uniquely named.
    2. WSDL only knows about concrete types, so interfaces evaluate to "object" thus your MakeRequest<IService>() becomes MakeRequest<object> which is useless anyways, even if it worked. You would have to supply a base type.

    From what I can gather, it sounds like you are trying to have the client decide which service to call, anyways. And, you want a central service to delegate those calls. Presumably this is for tracking purposes, as in an intermediary service.

    One approach would be to expose a common service contract:

     [ServiceContract(Namespace="MyNamespace")]
     public interface IGenericService
     {
      [OperationContract]
      void DoSomething();
     }

    and then, you expose different services that all implement this base contract:

     public class ServiceA : IGenericService
     {
      public void DoSomething()  {
       // do ServiceA work  }
     }

     public class ServiceB : IGenericService
     {
      public void DoSomething()  {
       // do ServiceB work  }
     }

    and then, you expose an intermediary service that can detect which service the client was calling, based on the Uri:

     [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
     public class GenericService : IGenericService
     {
      private ServiceCollection services;

      public void DoSomething()
      {
       GetService.DoSomething();
      }
     }

    Where the internals of GetService() check the message headers for the actual service Uri being sent.

    Now, I am making the assumption that you are looking for an intermediary service to be a pass through. Another approach is to create a real intermediary that can receive ANY message, but in your case it looks like you want a specific type of message (DoSomething()) thus a strongly typed intermediary.

    It would be best if you can explain what the goals are for the client. Do they know ahead of time which service they are hitting? Do you want the client to have a proxy that works for ANY valid IGenericService implementation? In that case, what I am suggesting will work, but you have to modify the WSDL for each service to use the <service> address of the GenericService instead of the service address of each individual ServiceA, ServiceB, etc.

     

    Thursday, January 18, 2007 7:44 PM
  • Well, ok. To take a couple steps back, I was examining the possibility of making generic method calls through WCF because I want to enable the service to make generic method calls without having to hardcode the generic type used.

    Now, my first thought pursuing that idea was to experiment with making the generic calls directly through WCF. It seems, however, that this is impossible. So continuing from there, it's possible to go from generic type argument to type using typeof, but is there a way to procedurally synthesize a generic type argument, essentially making a generic call using something like an instance of the Type class?

    Friday, January 19, 2007 11:08 PM
  • You can use reflection to dynamically create a generic type and then create instances of it.  Look at the methods of System.Type; you can use Type.IsGenericType to determine if a type is generic and then Type.GetGenericArguments to get the type arguments (and parameters, if applicable).

    Thanks.

    Saturday, January 20, 2007 12:03 AM