Web Client Software Factory and WCF proxy wrappers

Topics: Web Client Software Factory, Project Management Forum, UIP Application Block discussion, User Forum
Jun 13, 2008 at 1:49 PM

I'm wonder if anyone have any experience with using WCF with Web Client Software Factory and how do you organize your code in Visual Studio 2008?
To be more exact; I have two classes wich make my WCF proxy wrappers:

public abstract class ProxyBase<T>
{
 //This class check the state of the WCF channel before each call
 //so that I dont get any timeout exceptions etc.
}

Then I have this one:

public class MyServiceProxy : ProxyBase<IMyService>
{
 //This class create the right instance and connect to the remote WCF-service
 //based on the config-information in Web.Config.

}

I want to create a service for each Module in my WCSF-project. In the Customer-module I have a service like this:

public class CustomerService : MyServiceProxy, ICustomerContract
{
 //Here I have all the methods needed to call methods on the WCF related to
 //the ICustomerContract interface.

}

and in my Supplier-module I have a service like this:

public class SupplierService : MyServiceProxy, ISupplierContract
{
 //Here I have all the methods needed to call methods on the WCF related to
 //the ISupplierContract interface.

}

This does not work yet, but my goal is to achive this if it is possible. Can anyone tell?

One problem I have now is that I'm not able to read the web.config file where I have my WCF-configuration. Im not sure that my MyServiceProxy in App_Code have access to the web.config?? Anyway if MyServiceProxy and BaseProxy is placed in the App_Code folder their not accessible to my modules where I have the concrete implementations of the CustomerService and SupplierService. How could this be solved?

How do your solution folders look like?

Jun 13, 2008 at 4:15 PM


RightCoder wrote:

One problem I have now is that I'm not able to read the web.config file where I have my WCF-configuration. Im not sure that my MyServiceProxy in App_Code have access to the web.config?? Anyway if MyServiceProxy and BaseProxy is placed in the App_Code folder their not accessible to my modules where I have the concrete implementations of the CustomerService and SupplierService. How could this be solved?

You can use the channelFactory which will permit you to define your default end point.  The using statement is important - we experienced leaks during load test without it.

        public WidgetScheduleCollection GetScheduledWidgets(WidgetRequest para)
        {
            WidgetRequest1 request;
            WidgetResponse response;

            string defaultEndPoint = ConfigurationManager.AppSettings["DefaultEndPoint"].ToString();

            BasicHttpBinding binding = new BasicHttpBinding();
            EndpointAddress endPoint = new EndpointAddress(defaultEndPoint);

            // Use Channel so we don't have to rely on config file
            ChannelFactory<WidgetServiceContract> channelFactory =
                new ChannelFactory<WidgetServiceContract>(binding, endPoint);

            WidgetServiceContract service = channelFactory.CreateChannel();
            using (service as IDisposable)
            {
                // Wrap the request with a Channel's WidgetRequest
                request = new WidgetRequest1(para);

                // Get the scheduled Widgets for request
                response = service.GetScheduledWidgets(request);
            }

            // Return the Widget collection
            return response.ScheduledWidgetCollection;
        }


Jun 13, 2008 at 5:23 PM
Similar to what BillKrat said above, in my WCSF service, I have an instance variable that holds my ChannelFactory<T> and use it to create my WCF service instances. I also hide the request / response types within each service method.  I have shown a sample below. It is not a complete example as I do not have access to my code (I'm at home). As BillKrat pointed out, you need to ensure after you call your method that you close the channel.  There are a number of posts out there describing this, Working with Channels and Don’t Wrap Wcf Service Hosts or Clients in a Using Statement.  Please do some reading and pick your own method of ensuring your proxies are closed.  I'm not wanting to get into a discussion regarding the correct/best way to close a WCF proxy. Do what ever works for you.


public class LabService : ILabService
{
    private ChannelFactory<ILabServiceContract> factory;
    public LabService()
    {
        factory = new ChannelFactory<ILabServiceContract>("ILabServiceContract_Endpoint"); // this could come config
    }

    public IEnumerable<Test> ListTests(DateTime from, DateTime thru)
    {
        ListTestsRequest request = new ListTestsRequest{From = from, Thru = thru};
        try
        {
            ILabServiceContract service = factory.CreateChannel();
            ListTestsResponse response = service.ListTests(request);
            List<Test> tests = ConvertResponse(response); //
            return tests;
         }
         catch (...)
         finally {}
    }
}




RightCoder wrote:

I'm wonder if anyone have any experience with using WCF with Web Client Software Factory and how do you organize your code in Visual Studio 2008?
To be more exact; I have two classes wich make my WCF proxy wrappers:


Jun 13, 2008 at 7:15 PM
Edited Jun 13, 2008 at 8:35 PM
This article was very interesting to read regarding using WCF and IDisposable etc.
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=855018&SiteID=1
and a couple of MSDN links:
Avoiding Problems with the Using Statement
Expected Exceptions
Jun 13, 2008 at 11:12 PM

Thank you all for the response! But there is still some unanswered questions from my first post:

From your codesamples I can't see any use of the Dependancy Injection pattern that is widely used in WCSF. How does this fit in your use of your channelfactory-code?

Where do you put your general and base proxycode? In the App_Code folder or do you create Business Module or in a compiled DLL for reference? Im at the start of this so need to do some basics first...... I have learned that the code in App_Code is not available from the Modules-projects, naturally. But then if put in a Business Module or something it cannot read the Web.Config. I see some of you are using:

            string defaultEndPoint = ConfigurationManager.AppSettings["DefaultEndPoint"].ToString();

            BasicHttpBinding binding = new BasicHttpBinding();
            EndpointAddress endPoint = new EndpointAddress(defaultEndPoint);

This is of course a possible solution, but then  it looks that I have to read the config-file for each request....? That seems to be unnecessary....? What about session here? I want my service to be alive as long as the client session is alive. To use my service you need to log in first, then you can do successive calls, so I need state on the WCF-service.

What about making different Interface implementations of the WCF-service in different modules? Is this an good idea and something you have tried? What about session state then? As I said: I need to use the same instance of the service whenever I do Customer-operations in the Customer Module or Supplier-operations in the Supplier Module. Would that be possible given the different interface implementation in different modules approach?

Hope you take time to give me your thoughts on my question because I'm a bit lost here.... ;-)

Jun 14, 2008 at 2:31 AM

In my example, LabService and ILabService are injected into my LabController via ServiceDependency.  I guess I could have called it ChannelFactoryLabService. Alternatively, if I wanted to make it more complicated than needed, I could have created an interface ILabServiceContractProvider which would be a ServiceDependency to my LabService implmenenation. Then created a ChannelFactoryLabServiceContractProvider as the concrete implementation. I didn't as I didn't see the need in my case.

If you do not want to use ConfigurationManager.AppSettings as in the example provided, use your own method such as create your own configuration section, use built-settings feature of .NET assemblies in VS.NET or read it from the database. What ever works for you.  There is no one right way.

As for your comment on proxy code, are you the service provider and consumer?  If so, I would recommend avoid generating proxy classes. It will most likely be more work in the end. If available, your client just references the service contract, message contracts, data contracts and any fault contracts.  See Web Service Software Factory for good example of WCF project structure.  If not, place them in reference class library.

I am a bit concerned about your design with respect to this comment, "I want my service to be alive as long as the client session is alive. To use my service you need to log in first, then you can do successive calls, so I need state on the WCF-service."  You must be developing a small non-scaleable solution.  Does your service have a Login(username, password) type method?  If so, wouldn't you be better to use the username and password to secure the service. The WCF Security Guidance project is a good place to find information.

Granted, sometimes we are not in control of the web service.  If this is the case, you could use the ISessionLocatorService in your service to get/set the proxy in the current user's session. If you not using inproc or state server session state, you may run into serialization problems with the proxy. (Are WCF proxies serializable?).  Also, take care about your WCF service timeouts (defaults to 10 minutes).

Hopefully this helps a bit.


RightCoder wrote:

Thank you all for the response! But there is still some unanswered questions from my first post:

From your codesamples I can't see any use of the Dependancy Injection pattern that is widely used in WCSF. How does this fit in your use of your channelfactory-code?

Where do you put your general and base proxycode? In the App_Code folder or do you create Business Module or in a compiled DLL for reference? Im at the start of this so need to do some basics first...... I have learned that the code in App_Code is not available from the Modules-projects, naturally. But then if put in a Business Module or something it cannot read the Web.Config. I see some of you are using:

            string defaultEndPoint = ConfigurationManager.AppSettings["DefaultEndPoint"].ToString();

            BasicHttpBinding binding = new BasicHttpBinding();
            EndpointAddress endPoint = new EndpointAddress(defaultEndPoint);

This is of course a possible solution, but then  it looks that I have to read the config-file for each request....? That seems to be unnecessary....? What about session here? I want my service to be alive as long as the client session is alive. To use my service you need to log in first, then you can do successive calls, so I need state on the WCF-service.

What about making different Interface implementations of the WCF-service in different modules? Is this an good idea and something you have tried? What about session state then? As I said: I need to use the same instance of the service whenever I do Customer-operations in the Customer Module or Supplier-operations in the Supplier Module. Would that be possible given the different interface implementation in different modules approach?

Hope you take time to give me your thoughts on my question because I'm a bit lost here.... ;-)




Jun 14, 2008 at 1:47 PM


pbolduc wrote:
This article was very interesting to read regarding using WCF and IDisposable etc.
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=855018&SiteID=1
and a couple of MSDN links:
Avoiding Problems with the Using Statement
Expected Exceptions



Thanks for taking the time to provide these references!   This topic wasn't given much emphasis in the WSSF....   After reading your links I revisited the WSSF factory forum and found that SpencerClark (Apr 4) provides wrapper code which implements the code recommended in the links - he indicates that he has "not had a single error where the service was faulted".    Looks like I have a wee bit of refactoring to do on Monday ;)

Jun 14, 2008 at 2:31 PM
Edited Jun 14, 2008 at 2:32 PM
[RightCoder] -- Where do you put your general and base proxycode? In the App_Code folder or do you create Business Module or in a compiled DLL for reference?

Compiled DLL - in my case I have numerous platforms that may need to consume the service so I can't have dependencies/references to any given platform.   I like pbolduc's approach to "hide the request / response types within each service method" which I will adopt when I introduce Unity with the intent of having shared business modules between platforms.  It was actually an earlier message thread from pbolduc that got me looking at ChannelFactory as a solution to this multi-platform shared service/module requirement.

Once you have your method, encapsulated within a service such as pbolduc provided us, e.g., public class LabService : ILabService, the rest is history.  Within that context (and knowing that you've been around the block) I provided the above method as sample code - it actually is from a prototype that I used for a Windows Service, could just as easily been a WCSF or SCSF service.   The method used to set the default endpoint also is a mute point as it depends on your requirements - in the case of a Windows Service the noted code was adequate.  
Jun 16, 2008 at 8:35 AM
Edited Jun 17, 2008 at 8:08 AM
Thank you for good answers and possible design solutions.

Pbolduc you said: 

I am a bit concerned about your design with respect to this comment, "I want my service to be alive as long as the client session is alive. To use my service you need to log in first, then you can do successive calls, so I need state on the WCF-service."  You must be developing a small non-scaleable solution.  Does your service have a Login(username, password) type method?

The reason I have these requirements is that my WCF-service is a kind of wrapper for a COM-server which require this kind of logic. The WCF-service is a way to communicate with this COM-server using C# and .NET-technolgy over HTTP(S) and it make it easier to set up when it comes to firewalls etc.

I too have read the post by SpencerClark how would this change your approach to the better?
Would this be done any different:

           WidgetServiceContract service = channelFactory.CreateChannel();
            using (service as IDisposable)
            {
                // Wrap the request with a Channel's WidgetRequest
                request = new WidgetRequest1(para);

                // Get the scheduled Widgets for request
                response = service.GetScheduledWidgets(request);
            }

I have done it like you have done with MyPortalService : IMyPortalService but I get an exception like this:

"Service MyPortalService is not available in the current context"

I have a Foundational Module that I'm calling ServiceProxies where I have two classes:

MyPortalService and IMyPortalService

public class MyPortalService : IMyPortalService
{
    private ChannelFactory<IMyPortalService> factory; 

 

   public MyPortalService ()
    {
        
WSHttpBinding binding = new WSHttpBinding();
        EndpointAddress endpointAddress = new EndpointAddress(http://localhost:49805/MyService.Host/MyPortalService.svc);
        factory =
new ChannelFactory<IMyPortalService>(binding, endpointAddress);
    }

 

 

   public KlientContract LogIn(string username, string password)
    {
        
IMyPortalService service = factory.CreateChannel();

        
using (service as IDisposable)
        {
            
return service.LogIn(username, password);
        }    
    }
}


This Module add the service to the global services like this:

protected virtual void AddGlobalServices(IServiceCollection globalServices)
{
        globalServices.AddNew<
MyPortalService, IMyPortalService>();
}

 

 

This works ok, it seems. But when the first page is about to render it throws this exception. Which context could this be that is not valid?

 

Jun 16, 2008 at 6:58 PM
Since it is a requirement, I would recommend avoiding hosting your service in IIS.  Disabling application pool recycling may work, but there are some cases when the application pool would still recycle.  I would write a Windows Service to host your service.  I do not have any ideas why you are getting that exception.
Jun 17, 2008 at 9:26 AM
Edited Jun 17, 2008 at 12:34 PM
If I create a Windows Service as host to my service I'll get a more complicated setup when it comes to firewall etc. right?

But back to my problem

"Service MyPortalService is not available in the current context"

Should I have some more information for the channelFactory to make it work? I would like to read information from a App.Config file or something like that..... Now I only do this:

WSHttpBinding binding = new WSHttpBinding();
EndpointAddress endpointAddress = new EndpointAddress(
http://localhost:49805.....);

factory = new ChannelFactory<IMyPortalService>(binding, endpointAddress);

If I want to configure it more in detail this should be read from a Web.Config or App.Config. I tried to put a App.Config file in the module but it does not find the endpointConfigurationName that I use like this:

factory = new ChannelFactory<IMyPortalService>("myPortalServiceEndpointName");

Is my approach to have a foundational module where I register the service as a global service? 
Jun 17, 2008 at 10:15 AM
Edited Jun 17, 2008 at 12:44 PM
The reason I got the errormessage "Service MyPortalService is not available in the current context" was that the interface that I registered it with was not public. I had forgot to the interface as public:

public interface IMyPortalService 
{
}

 
Stupid error from myself, I'm sorry.

I'm still looking for some advise when it comes to Windows Service Host vs. IIS hosting.......
Jun 17, 2008 at 3:47 PM
Edited Jun 17, 2008 at 3:48 PM

Using a Windows Service does not limit your binding options. You can still use HTTP.  The reason I recommended using a Windows Service over IIS is due to IIS's application pool recycling. Normally, application pool recycling is good, however, in your case, your requirement is to connect and to keep open a connection to a COM component inside your service.  If you can be positive the application pool will never recycle while you have a logged in user to your COM component, or that it can recover from application pool recycle, you should be ok to host in IIS.


RightCoder wrote:
If I create a Windows Service as host to my service I'll get a more complicated setup when it comes to firewall etc. right?


Jun 20, 2008 at 3:31 PM
Thanks pbolduc, I will seriously consider a Windows Service because your absolute right.

As I have mention earlier in this post is that I need to hold the state on the service because I have a COM component that has to be logged on before any call to it is made.
That means that I have to keep hold of the WCF service instance state within my client too. Right now I create a new WCF service instance for every call to the service. Like this:

public bool AuthenticateUser(string username, string password, out string errorMessage)
{
    IMyService proxy = service.GetProxy;
    using(proxy as IDisposable)
    {
        return proxy.LogIn(username, password, out errorMessage);
    }
}


My idea was that the using statement should dispose the channel not the service-object that I hoped should live on as a connection to the WCF-service. I can see that my variable names indicates something else here... It seems like the proxy variable, in fact is the proxy so disposing it would require the next call to recreate a new WCF-service instance.... Are you or anyone understanding what I mean here? Please help here....!

The service kode looks like this:

public class MyService : IMyService
{
    public MyService()
    {
        WSHttpBinding = new WSHttpBinding();
        EndpointAddress endpointAddress = new EndpointAddress(
http://localhost.......);

        factory = new ChannelFactory
    }
    
    public IMyService GetProxy
    {
        return factory.CreateChannel();
    }
}

public interface IMyService
{
    IMyService GetProxy {get;}
}


And is registered like this in a foundational module:

protected virtual void AddGlobalServices(IServiceCollection globalServices)
{
    globalServices.AddNew<MyService, IMyService>();
}