简体   繁体   中英

Configure endpoint programmatically

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM