none
WCF Data Service calling another WCF Data Service

    Question

  • Hi,

    I have a WCF Data Service in my local solution and I will create a service reference to it by right clicking and add service reference.

    However, instead of this local WCF Data Service work with the EF and DB directly, I would like it to call another WCF Data Service that is located on another server.

    Is it possible to achieve that?

    1 - How will the local WCF Data service generate the proxy since it is not working with the EF class directly?

    2 - What generic parameter should I use for my local WCF Data Service since it is not referencing the EF directly?

    DataService<??????>

    3 - It seems to me that the remote WCF Data Service will have to produce the pass it to the local WCF Data Service and then the local WCF Data Service will have to generate the proxy for the client application that in this case is a Silverlight App.

    Has someone got any sample code to share with us?

    Example:

     
    LOCAL WCF DATA SERVICE
     
    public class TaskTagService : DataService<????????>
        {
            public static void InitializeService(DataServiceConfiguration config)
            {
                config.SetEntitySetAccessRule("*", EntitySetRights.All);
               config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
            }
        }
     
    REMOTE WCF DATA SERVICE that references the Entity framework class and have access to the DB
     
     
    public class TaskTagService : DataService<TaskTagEntities>
        {
            public static void InitializeService(DataServiceConfiguration config)
            {
                config.SetEntitySetAccessRule("*", EntitySetRights.All);
               config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
            }
        }
     
    Cheers
     
    Claudio

    Monday, October 10, 2011 4:29 PM

Answers

  • Hi,

    I have a solution for this problem that I think it will definately work. However, I had to work on another project and did not have time to test it.

    I will give you the steps below to try it. If you can get it to work, could you please provide your sample demo code so that myself and other people in the forum could have a starting point?

    Maybe you could try it with the Northwind database and Entity Framework since everyone knows this database.

    You want to create a local WCF DataService as a bridge to call this remote service, then you are going to call this local data service from your Silverlight code. 

    1) Add the Service Reference of the Remote WCF DataService to your Web project.  Say your service is called TaskService.

    2) Add a new WCF DataService called TaskTagService to your Web project.

    public class TaskTagService : DataService<TaskService.TaskTagEntities>
        {
            public static void InitializeService(DataServiceConfiguration config)
            {
                config.SetEntitySetAccessRule("*", EntitySetRights.All);
               config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
            }
        }

    3) Add Service Reference to your Silverlight project to point to this local TaskTagService.

    4) You need to override the CreateDataSource function in your TaskTagService

     protected override TaskService.TaskTagEntities CreateDataSource()
            {
                return new TaskService.TaskTagEntities(new Uri("YourRemoteServiceURL"));
            }

    5) After you done this,  Test your new Service by setting TaskTagService.svc as start up page and the Web project as start up project. 

    You can compare the two services:

    http://YourRemoteServiceURL

    http://YourNewLocalServiceURL

    You should see all the entities in your remote service are exposed from this new service as well.

    public class TaskTagService : DataService<TaskService.TaskEntities>
        {
            // This method is called only once to initialize service-wide policies.
            public static void InitializeService(DataServiceConfiguration config)
            {         
                config.SetEntitySetAccessRule("*", EntitySetRights.All);                
                config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
                config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;           
            }

            protected override TaskService.TaskEntities CreateDataSource()
            {
                return new TaskService.TaskEntities(new Uri("XXX"));
            }
            protected override void OnStartProcessingRequest(ProcessRequestArgs args)
            {
                base.OnStartProcessingRequest(args);
            }
            protected override void HandleException(HandleExceptionArgs args)
            {
                base.HandleException(args);
            }
        }

    Please let us know if that works and please provide your test sample code.

    Cheers

    C

    Tuesday, March 06, 2012 12:56 PM

All replies

  • This is similar to another question on this board where the person wanted to connect to multiple databases. Basicaly what you need to provide in your own implementation is the code that calls the remote service instead of connecting to the database.

    I'm not familiar with EF or anything, so I cannot provide you with a concrete code example. But basicaly, in your service where you define your entities and the provider that performs the actuall work, you have that provider call the remote service by using the webclient for example to fetch and parse the data that it needs.

    So for your local TaskTagService, you need to define a context class which defines your available operations. In the code behind those operations, you don't connect to the database, but actually make the call to the remote service and return the data there. I'm sure that if you generate the the code through the webreference, you can probably map the call directly to the service and pipe the result.

    Monday, October 10, 2011 8:10 PM
  • Hi Arne,

    Thanks for your help.

    You said:

    "So for your local TaskTagService, you need to define a context class which defines your available operations"

    In WCF Data Services (OData), we normally do not define operations.

    When you add a reference to the service, it will create proxy classes on the client so that you can interact with the service using LINQ queries. It is all based on REST.

    You can have get and update interceptors.

    Therefore, I am not sure if I understand your suggestion. Could you please clarify that to me?

    Cheers

    C

     

     

     

     

     

    Monday, October 10, 2011 8:42 PM
  • For your "local service" you need to define a Context class that will be used as the generic parameter of the Service you will be running. This Context class should have all the properties that return IQueryable collections representing the various entities you wish to be exposing through the Local service.

    What you need to do now, is tweak those properties in such a manner that instead of returning local copies of entities as an IQueryable operation, they return the same entity from the remote service as IQueryable object.

    That's best I can do to explain it sorry.

    Tuesday, October 11, 2011 6:55 AM
  • Hi,

    This is not actually easy to do (for multiple reasons).

    If you need to just maintain the service as is, you could consider a simple HTTP proxy service (although in that case you will need to modify the backend service so that it can behave as if hosted on the address of the proxy service since it generates URLs into the payloads).

    If you need to somehow change the service then it's quite hard.

    The approach suggested by Arne is to use the DataServiceContext derived class (which gets generated for your by AddServiceReference) as the context class for a DataService. This will work for the simple queries. But it will break for more complex queries (especially $expand and/or $select, but other cases as well). The reason is that the LINQ provider implemented by DataServiceContext doesn't support the full extent of queries generated by the WCF DS Server.

    There's a sample project how this might look one day. It uses a custom provider and lot of expression tree manipulation to turn the WCF DS Server qeury into a LINQ expression which can be executed by the WCF DS Client LINQ provider. It's part of the OData Provider Toolkit (http://www.odata.org/developers/odata-sdk) under the "Experimental" folder. It's called something like AstoriaOverAstoria (or so).

    In any case, currently it's not actually possible to fully support all of the server behaviors in such a service (known or by design limitations and so on). And also note that correctly implementing write support (which won't work in either of the cases described above) is also non-trivial.

    Just curious, why do you need to "proxy" the service like this? (There are valid scenarios, I'm just wondering :-))

    Thanks,


    Vitek Karas [MSFT]
    Tuesday, October 11, 2011 7:52 AM
  • Hi Vitek,

    Thanks for your help. You said:

    "Just curious, why do you need to "proxy" the service like this? (There are valid scenarios, I'm just wondering :-))"

    I cannot call the remote WCF Data Service from Silverlight. The server where it is based does not accept calls from client applications.

    In the old days of ASMX web services, that was what I did. I would create a local ASMX web service and then call a remote ASMX web service that had access to the remote database.

    This issue is to do with something called DMZ. Basically, for security reasons some people put their database and business servers behind a firewall and only allow server side code to call its business logic that in turn have access to the database.

    But with WCF Data Services this has been screwed up and I am stuck.

    I need to figure out a way to get my local Wcf data service to generate the client proxy for the silverlight app but instead of using the Entity Framework directly to get data from the database, I need it to call another WCF Data Service that is on a remote server.

    Any more ideas? Do you know of any real world tutorial using WCF Data Services?

    Cheers

    Wednesday, October 12, 2011 3:35 PM
  • Hi Arne,

    Do you have some sample code you could share with me?

    I am stuck with this one. With the old ASMX web services this was so easy to do but with WCF Data services this seems to be impossible.

    Cheers

    C

    Wednesday, October 12, 2011 3:39 PM
  • not at the moment sorry.

    But from your description, why not setup a service that maps the incomming calls to the "remote" service and maps the response back? I'm not entirely sure how the details would be done, but I'd go for a small service that listens on incomming HTTP calls, globs up everything and just puts everything that forms the query string to the remote service, and then return the response.

    In short:

     

    - client calls your service like so : http://<application>/<your_service>/querystring

    - your service collects querystring and calls the remote service like so : http://<remote_service>/querystring

    An example would be something like this:

    - client calls http://<application>/MyService/Users(1)/Firstname

    - the client sees /Users(1)/Firstname as the query and maps that call to http://<remote_service>/Users(1)/Firstname, literally copying the string.

    - the service gets the response and sends it back to the client.

     

    With that approach you don't have to worry about OData or mapping anything, just let the service map it directly, including all Headers & query operations.

    I think this should be fairly easy to implement, no?

    Wednesday, October 12, 2011 4:56 PM
  • Hi Arne,

    Thanks for your suggestion.

    This is something I could try.

    The only problem I see with this implementation is that I would lose all change tracking.

    This is so important in WCF Data Services since we can send updates as a unit of work and the service knows how to update the database.

    Another problem I see is that this might not work wth with Get and Update Interceptors.

    Cheers

    C

     

    Wednesday, October 12, 2011 5:45 PM
  • potentially yes, however the HTTP request could be mimed, meaning a GET stays a GET and POST becomes POST etc. I'm not really sure how cookies could be handled, but this should be mappable as well.

    As for change tracking, kinda violates REST principles where you're stateless, but You should be able to map those changes as well with headers or something.

    Wednesday, October 12, 2011 5:52 PM
  • what was your solution to solve this problem ? I'm also in same scenorio like yours. I would appreciate any help.

    Thank you.

    Tuesday, March 06, 2012 3:20 AM
  • Hi,

    I have a solution for this problem that I think it will definately work. However, I had to work on another project and did not have time to test it.

    I will give you the steps below to try it. If you can get it to work, could you please provide your sample demo code so that myself and other people in the forum could have a starting point?

    Maybe you could try it with the Northwind database and Entity Framework since everyone knows this database.

    You want to create a local WCF DataService as a bridge to call this remote service, then you are going to call this local data service from your Silverlight code. 

    1) Add the Service Reference of the Remote WCF DataService to your Web project.  Say your service is called TaskService.

    2) Add a new WCF DataService called TaskTagService to your Web project.

    public class TaskTagService : DataService<TaskService.TaskTagEntities>
        {
            public static void InitializeService(DataServiceConfiguration config)
            {
                config.SetEntitySetAccessRule("*", EntitySetRights.All);
               config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
            }
        }

    3) Add Service Reference to your Silverlight project to point to this local TaskTagService.

    4) You need to override the CreateDataSource function in your TaskTagService

     protected override TaskService.TaskTagEntities CreateDataSource()
            {
                return new TaskService.TaskTagEntities(new Uri("YourRemoteServiceURL"));
            }

    5) After you done this,  Test your new Service by setting TaskTagService.svc as start up page and the Web project as start up project. 

    You can compare the two services:

    http://YourRemoteServiceURL

    http://YourNewLocalServiceURL

    You should see all the entities in your remote service are exposed from this new service as well.

    public class TaskTagService : DataService<TaskService.TaskEntities>
        {
            // This method is called only once to initialize service-wide policies.
            public static void InitializeService(DataServiceConfiguration config)
            {         
                config.SetEntitySetAccessRule("*", EntitySetRights.All);                
                config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
                config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;           
            }

            protected override TaskService.TaskEntities CreateDataSource()
            {
                return new TaskService.TaskEntities(new Uri("XXX"));
            }
            protected override void OnStartProcessingRequest(ProcessRequestArgs args)
            {
                base.OnStartProcessingRequest(args);
            }
            protected override void HandleException(HandleExceptionArgs args)
            {
                base.HandleException(args);
            }
        }

    Please let us know if that works and please provide your test sample code.

    Cheers

    C

    Tuesday, March 06, 2012 12:56 PM