0

First of, let me start by saying I have no experience of doing endpoint/networking type of stuff, so my question might seem a bit stupid, so please bear with me :)

I am working on porting an App written for Windows Phone 7 onto Windows 8 (Metro app). In the original App, there was a ServicesReference.ClientConfig file which defined the URL, bindings and other bits for the App to connect to the server (addresses changed):

<client>
  <endpoint address="https://someurl.com/userservice.svc"
        binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_UserServices"
        contract="UserApi.UserServices" name="User_Services" />
  <endpoint address="https://someurel.com/dataservice.svc"
      binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_DataServices"
      contract="DataApi.DataServices" name="Data_Services" />

Also, in the WP7 project, there were already two "Service References" added (one for User Services and one for Data Services), with supporting Reference.cs generated files. When I tried adding the service references into the Win8 project (VS2012), it generated a blank reference.cs file, so I simply added the Reference.cs file from the WP7 project into the W8 project, and also copied the ServiceReferences.ClientConfig file into the W8 project (so in terms of directory structure, it looked identical to the WP7 project). I think these Reference.cs files are the ones which provide the interface for the contracts

Now, when I run my W8 app, I get an error during the part where it needs access to the service:

InvalidOperationException was unhandled by user code Could not find endpoint element with name 'User_Services' and contract 'UserApi.UserServices' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element.

So I figured the App isn't using the ServicesReferces.ClientConfig file to pickup the endpoints and network adresses, or it wasn't finding the Reference.cs files which I have importes into the project. So, assuming first it is not finding the endpoints correctly through the ServicesReferences.ClientConfig file, is it possible to do the same in code?

All I got so far is this:

BasicHttpBinding binding = new BasicHttpBinding();
EndpointAddress endpoint = new EndpointAddress(new Uri("https://someurl.com/someservice.svc"));

but I don't how to take this further (I added this into App.xaml.cs)

Hope the question makes sense. If there is any further information you need, please let me know and I will try to find out about it while I go and educate myself more on this endpoint business

Thanks in advance

NullPointer
  • 2,084
  • 7
  • 24
  • 38

1 Answers1

1

I had the same problem and I tried to wrap everything in some classes.. This is what I did: First of all, I created a class called ClientService where it creates and wraps the EndpointAdress:

EDIT for Win8:

public class ClientService
{
   public Type ContractType {get;set;}
   public EndpointAdress EndpointAdress {get;set;}
   public Binding Binding { get; private set; }

   public ClientService(Type contractType)
   {
     ContractType = contractType;
     CreateEndpoint();
     CreateBinding();
   }

   private void CreateEndpoint()
   {
       EndpointAdress = new EndpointAddress(....) //You can get some parameters about the service adress in the Constructor of this class
   } 

   private void CreateBinding()
   {
       Binding = new BasicHttpBinding();  //Or your specific Binding
   }
}

Once I have this, I create a static class with all my client registrations. I add all of them once I start my app. Something like this:

ClientServices.AddClientService(new ClientService(typeof(IYourService));


public static class ClientServices
{
    private static readonly Dictionary<Type, ClientService> _clientServices;

    static ClientServices()
    {
        _clientServices = new Dictionary<Type, ClientService>();
    }

    public static void AddClientService(ClientService clientService)
    {
        if (!_clientServices.ContainsKey(clientService.ContractType))
        {
            _clientServices.Add(clientService.ContractType, clientService);
        }
    }

    public static ClientService GetClientServiceBy(Type contract)
    {
        if (_clientServices.ContainsKey(contract))
        {
            return _clientServices[contract];
        }
        throw new ArgumentException(string.Format("The contract's Type {0} is not registered. Please register the client's endpoint.", contract));
    }
}

So, when my application starts I have all my client endpoints registered in a static class. Now when I want to call a service I have a wrapper called ServiceInvoker. I use it like this whenever I want to call a Service:

var invoker = new ServiceInvoker();
    var result = invoker.InvokeService<IMyService, MyObjectReturnType>(
        proxy => proxy.DoSomething(myParameters));
    return result;

Where InvokeService looks like this:

public TResult InvokeService<TServiceContract, TResult>(Func<TServiceContract, TResult> invokeHandler) where TServiceContract : class
{
    ICommunicationObject communicationObject;
    var arg = CreateCommunicationObject<TServiceContract>(out communicationObject);
    var result = default(TResult);
    try
    {
        result = invokeHandler(arg);
    }
    catch (Exception ex)
    {
        throw;
    }

    finally
    {
        try
        {
            if (communicationObject.State != CommunicationState.Faulted)
                communicationObject.Close();
        }
        catch
        {
            communicationObject.Abort();
        }
    }
    return result;
}

private TServiceContract CreateCommunicationObject<TServiceContract>(out ICommunicationObject communicationObject)
    where TServiceContract : class
{
        var clientService = GetClientService(typeof(TServiceContract));
         var arg = new ChannelFactory<TServiceContract>(clientService.Binding, clientService.EndpointAdress).CreateChannel();
        communicationObject = (ICommunicationObject)arg;
        return arg;
}

private ClientService GetClientService(Type type)
    {
        var clientService = ClientServices.GetClientServiceBy(type);
        return clientService;
    }

The main problem here is that since DLL's cannot be referenced in a Windows Store App, the only way to make this example work is to copy all Service Interfaces and possible ojects that we transfer to a Class Library (Windows Store apps). This way we will be able to create a channel and connect to the WCF service. Copying the interfaces can be a workaround but is NOT a good approach. Service Reference or other code generation tools are the way to go.

In addition, async and await are not possible in this scenario.

**ServiceInvoker apprach is used from creating WCF ChannelFactory

Community
  • 1
  • 1
margabit
  • 2,924
  • 18
  • 24
  • Hope it helps! It's just a simplified example, extend it to fulfill your requirements. – margabit Sep 27 '12 at 10:24
  • Not yet, still trying to understand endpoints and contracts etc. I did try your solution quickly, but couldn't figure out an implementation for GetChannel(). Also, found this link which I'm exploring as well http://msdn.microsoft.com/en-us/library/hh556233.aspx – NullPointer Sep 27 '12 at 12:53
  • Are you using Service Reference from your Win8 app? – margabit Sep 27 '12 at 13:08
  • I think I've got it, I'll try when I get home. We can create the ICommunicationObject by creating a ClientBase. Then InvokeService should have 'async' and needs to call the action invokeHandler with await. I can't try it by now.. but I think you should do it this way ;) – margabit Sep 27 '12 at 13:22
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/17244/discussion-between-nullpointer-and-margabit) – NullPointer Sep 27 '12 at 14:18