0

I'm using C#, .Net Framework 4.5.1 in SharpDevelop.

Is there a way to create a function that accepts "typeof(ISomeInterface)" and "typeof(SomeClass)" as parameters?

Situation: I'm building an application, that is supposed to host several WebServices. Each WebService will be created on it's own System.ServiceModel.ServiceHost. Currently, there are only two WebServices running (Tester1 and Tester2, as I'm just trying to get it to work).

In the future, there will be multiple WebServices running in this application, perhaps even "unknown", but derived from my bases. My current setup works, but, as you can see in the code below, is far from being the most DRY-style code. So, naturally, I would like to move all repeating code into a method.

Currently, I have the following code:

First, I have a base-interface and a base-class:

[ServiceContract]
public interface ITester
{
    [OperationContract]
    void Test( string pText );
}

public class Tester : System.Web.Services.WebService, ITester
{
    public Tester( )
    {
    }
    public virtual void Test( string pText )
    {
    }
}

From which I have derived two child interfaces and classes:

[ServiceContract]
public interface ITester1 : ITester
{
}

public class Tester1 : Tester, ITester1
{
    public override void Test( String pMessage )
    {
        Console.WriteLine( "Message received : \n{0}\n", pMessage );
    }
}

[ServiceContract]
public interface ITester2 : ITester
{
}

public class Tester2 : Tester, ITester2
{
    public Tester2( )
    {
    }

    public override void Test( string pText )
    {
        Console.WriteLine( "Message received : \n{0}\n", pText );
    }
}

At current, the code below creates and runs the WebServices, based on the above interfaces and classes:

public class Program
{
    public static void Main( )
    {
        int lWhatService = 1; // Actual Program uses user input.

        if( lWhatService == 1 )
        {
            string iServer  = "localhost";
            int    iPort    = 80;
            string iService = "Tester1";
            string iWDSL    = "wsdl";

            string iHTTPAddress   = "http://"    + iServer + ":" + iPort.ToString( ) + "/" + iService;
            string iMetaAddress   = "http://"    + iServer + ":" + iPort.ToString( ) + "/" + iService + "/" + iWDSL;
            string iNetTCPAddress = "net.tcp://" + iServer + "/" + iService + "/" + iWDSL;

            ServiceHost lHost;

            Uri iHTTPUri = new Uri( iHTTPAddress );
            BasicHttpBinding binding = new BasicHttpBinding( );
            lHost = new ServiceHost( typeof(Tester1) );

            lHost.AddServiceEndpoint( typeof(ITester1), binding, iHTTPUri );

            ServiceMetadataBehavior metadata = new ServiceMetadataBehavior( );
            metadata.HttpGetUrl = new Uri( iMetaAddress );
            metadata.HttpGetEnabled = true;
            lHost.Description.Behaviors.Add( metadata );
            Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding( );
            Uri mexAddress = new Uri( iNetTCPAddress );
            lHost.AddServiceEndpoint( typeof(IMetadataExchange), mexBinding, mexAddress );

            lHost.Open( );
        }
        else if( lWhatService == 2 )
        {
            string iServer  = "localhost";
            int    iPort    = 80;
            string iService = "Tester2";
            string iWDSL    = "wsdl";

            string iHTTPAddress   = "http://"    + iServer + ":" + iPort.ToString( ) + "/" + iService;
            string iMetaAddress   = "http://"    + iServer + ":" + iPort.ToString( ) + "/" + iService + "/" + iWDSL;
            string iNetTCPAddress = "net.tcp://" + iServer + "/" + iService + "/" + iWDSL;

            ServiceHost lHost;

            Uri iHTTPUri = new Uri( iHTTPAddress );
            BasicHttpBinding binding = new BasicHttpBinding( );
            lHost = new ServiceHost( typeof(Tester2) );

            lHost.AddServiceEndpoint( typeof(ITester2), binding, iHTTPUri );

            ServiceMetadataBehavior metadata = new ServiceMetadataBehavior( );
            metadata.HttpGetUrl = new Uri( iMetaAddress );
            metadata.HttpGetEnabled = true;
            lHost.Description.Behaviors.Add( metadata );
            Binding mexBinding = MetadataExchangeBindings.CreateMexTcpBinding( );
            Uri mexAddress = new Uri( iNetTCPAddress );
            lHost.AddServiceEndpoint( typeof(IMetadataExchange), mexBinding, mexAddress );

            lHost.Open( );
        }

        System.Console.ReadKey( ); // This is where the user normally could switch
    }
}

As can be seen, the two methods (Run1 and Run2) are about 90% identical, except that they use ITester1 & Tester1 or ITester2 & Tester2.

Now, I have replaced some code:

lHost = new ServiceHost( typeof(Tester1) );
lHost.AddServiceEndpoint( typeof(ITester1), binding, iHTTPUri );

and

lHost = new ServiceHost( typeof(Tester2) );
lHost.AddServiceEndpoint( typeof(ITester2), binding, iHTTPUri );

By some more "generic" code:

Type lObj = typeof(Tester1);
lHost = new ServiceHost( lObj );

Type lInt = typeof(ITester1);
lHost.AddServiceEndpoint( lInt, binding, iHTTPUri );

and

Type lObj = typeof(Tester2);
lHost = new ServiceHost( lObj );

Type lInt = typeof(ITester2);
lHost.AddServiceEndpoint( lInt, binding, iHTTPUri );

This, in preparation of moving the code to a method. The code still works, and I still get the response (currently, just the WebServices WSDL) I expect.

The obvious next step, would be to move all code in the if( lWhatService == 1 ) and else if( lWhatService == 2 ) blocks to a single method.

I have used several ways, but below is where I currently am:

public static ServiceHost GetHost<T, U>( string pServer, int pPort, string pService, string pWSDL )
{
    string iHTTPAddress   = "http://"    + pServer + ":" + pPort.ToString( ) + "/" + pService;
    string iMetaAddress   = "http://"    + pServer + ":" + pPort.ToString( ) + "/" + pService + "/" + pWSDL;
    string iNetTCPAddress = "net.tcp://" + pServer + "/" + pService + "/" + pWSDL;

    Type iInterface = typeof(T);
    Type iObject    = typeof(U);
    ServiceHost iHost = new ServiceHost( iObject );

    // HTTP Binding => Create and Prepare
    Uri iHTTPUri = new Uri( iHTTPAddress );
    BasicHttpBinding iHTTPBinding = new BasicHttpBinding( );
    // HTTP Binding => Add to Host
    iHost.AddServiceEndpoint( iInterface, iHTTPBinding, iHTTPAddress );
    ....

    ....
    return iHost;
}

The above code, I would like to call as:

lHost1 = GetHost<ITester1, Tester1>( "localhost", 80, "Tester1", "wsdl" );
lHost1.Open( );
lHost2 = GetHost<ITester2, Tester2>( "localhost", 80, "Tester2", "wsdl" );
lHost2.Open( );

The code works, in the sense that I do not get any compile-time or run-time errors. I also get a response from the application. However, in my previous way (coding both WebServices completely) I got the WSDL-XML as a response. In the current way (calling a method) I get and empty page (not an error, just a blanc page).

I have also tried these before:

public static ServiceHost GetHost( string pServer, int pPort, Type pInterface, Type pObject, string pService, string pWSDL )
{
    Type iInterface = typeof(pInterface);
    Type iObject    = typeof(pObject);
    ServiceHost iHost = new ServiceHost( iObject );
    ....
}

and

public static ServiceHost GetHost( string pServer, int pPort, Type pInterface, Type pObject, string pService, string pWSDL )
{
    Type iInterface = pInterface;
    Type iObject    = pObject;
    ServiceHost iHost = new ServiceHost( iObject );
    ....
}

Calling them like:

lHost = GetHost( "localhost", 80, typeof(ITester1), typeof(Tester1), "Tester1", "wsdl" );

and

lHost = GetHost( "localhost", 80, ITester1, Tester1, "Tester1", "wsdl" );

But that got me an empty page just the same.

Is there any way to get the above work, so I can call it either as:

lHost1 = GetHost<ITester1, Tester1>( "localhost", 80, "Tester1", "wsdl" );    

or

lHost1 = GetHost( "localhost", 80, typeof(ITester1), typeof(Tester1), "Tester1", "wsdl" );    

or (most preferred)

lHost1 = GetHost( "localhost", 80, ITester1, Tester1, "Tester1", "wsdl" );

I have tried these and about 9 more variations (suggestion from over 20 internet pages), but I just cannot seem to get this to work.

Any help would be greatly appreciated.

Zein Makki
  • 29,485
  • 6
  • 52
  • 63
  • Well, have you tried debugging your code by placing a breakpoint in the method, then stepping through the method line by line to see where the issue is? – cbr Dec 12 '15 at 00:17
  • 3
    The result of the `typeof` operator is `System.Type`, any method that has a `System.Type` parameter will accept the result of the `typeof` operator. If you're trying to write a method that works for instances of unknown types, you need a generic method in the form of `MyMethod(T someInstanceofTypeT)`. If you're trying to write a WCF service that will accept an unbounded generic parameter, you're out of luck. WCF is a message exchange, and WCF messages are strongly bound to an interchange format describable by WSDL, which has no ability to describe and/or generate generic message formats. – Preston Guillot Dec 12 '15 at 00:25
  • @cubbr:Yes, I have tried and I get the same results in all my code. It looks like it works, except I get no response from the WebService when calling it. – Mara Masona Dec 12 '15 at 22:23
  • @Preston Guillog: I'm not trying to pass the parameter to the WebService. I am trying to build a method that creates WebServices of a provided type. If have tried both the MyMethod( Type pMyType, string pHost... ) and MyMethod( string pHost...) styles, but to no avail. The MyMethod(T MyType, string pHost...) gives an error: CS0029; Cannot implicitly convert type 'T' to 'System.Type'. – Mara Masona Dec 12 '15 at 22:28

1 Answers1

1

Found it, and I feel so ashamed for not seeing it earlier. My method missed one important line: iMeta.HttpGetEnabled = true; With it, it works fine. Should be place (in the method) directly after: iMeta.HttpGetUrl = iMetaUri;

Thanks goes to:
@cubbr: Thanks for making take a second look at my code.
@Preston Guillog: Thanks for making me think a bit more about what I was doing.

The final function now looks like:

    public static ServiceHost GetHost( string pServer, int pPort, Type pInterface, Type pObject, string pService, string pWSDL )
    {
        string iHTTPAddress   = "http://"    + pServer + ":" + pPort.ToString( ) + "/" + pService;
        string iMetaAddress   = "http://"    + pServer + ":" + pPort.ToString( ) + "/" + pService + "/" + pWSDL;
        string iNetTCPAddress = "net.tcp://" + pServer + "/" + pService + "/" + pWSDL;

        Type iInterface = pInterface;
        Type iObject    = pObject;
        ServiceHost iHost = new ServiceHost( iObject );

        // HTTP Binding => Create and Prepare
        Uri iHTTPUri = new Uri( iHTTPAddress );
        BasicHttpBinding iHTTPBinding = new BasicHttpBinding( );
        // HTTP Binding => Add to Host
        iHost.AddServiceEndpoint( iInterface, iHTTPBinding, iHTTPAddress );

        // MetaData => Create and Prepare
        Uri iMetaUri = new Uri( iMetaAddress );
        ServiceMetadataBehavior iMeta = new ServiceMetadataBehavior( );
        iMeta.HttpGetUrl = iMetaUri;
        iMeta.HttpGetEnabled = true;
        // MetaData => Add to Host
        iHost.Description.Behaviors.Add( iMeta ); 

        Uri iNetTCPUri = new Uri( iNetTCPAddress );
        Binding iNetTCPBinding = MetadataExchangeBindings.CreateMexTcpBinding( );
        iHost.AddServiceEndpoint( typeof( IMetadataExchange), iNetTCPBinding, iNetTCPUri );

        return iHost;
    }

and can be called like:

lHost = GetHost( "localhost", 80, typeof(ITester1), typeof(Tester1), "Tester1", "wsdl" );
lHost.Open( );
Zein Makki
  • 29,485
  • 6
  • 52
  • 63