Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change Address/Port of WSDL EndPointAddress at runtime?

So I currently have 2 WSDLs added as Service References in my solution. They look like this in my app.config file (I removed the "bindings" field, because it's uninteresting):

<system.serviceModel>
  <client>
    <endpoint address="http://localhost:8080/query-service/jse" binding="basicHttpBinding" bindingConfiguration="QueryBinding" contract="QueryService.Query" name="QueryPort" />
    <endpoint address="http://localhost:8080/dataimport-service/jse" binding="basicHttpBinding" bindingConfiguration="DataImportBinding" contract="DataService.DataImport" name="DataImportPort" />
  </client>   
</system.serviceModel>

When I utilize a WSDL, it looks something like this:

using (DataService.DataClient dClient = new DataService.DataClient())
{
  DataService.importTask impt = new DataService.importTask();
  impt.String_1 = "someData";
  DataService.importResponse imptr = dClient.importTask(impt);
}

In the "using" statement, when instantiating the DataClient object, I have 5 constructors available to me. In this scenario, I use the default constructor:

   new DataService.DataClient()

which uses the built-in Endpoint Address string, which I assume is pulled from app.config. But I want the user of the application to have the option to change this value.

1) What's the best/easiest way of programatically obtaining this string?

2) Then, once I've allowed the user to edit and test the value, where should I store it?

I'd prefer having it be stored in a place (like app.config or equivalent) so that there is no need for checking whether the value exists or not and whether I should be using an alternate constructor. (Looking to keep my code tight, ya know?)

Any ideas? Suggestions?

EDIT

Maybe I should ask about these Alternate constructors as well.

For example, one of them looks like this:

   new DataService.DataClient(string endPointConfigurationName, 
                              string remoteAddress)

What values could get passed for "endPointConfigurationName" and "remoteAddress"?

EDIT2

Answering my own questions here, the "endPointConfigurationName" appears to be the same as the "name" in the app.config XML and the "remoteAddress" is formatted the same as "endpoint address" in the app.config XML.

Also! The answer to my first question about getting the EndPointAddresses is the following:

ClientSection clSection =
   ConfigurationManager.GetSection("system.serviceModel/client") as ClientSection;

ChannelEndpointElementCollection endpointCollection =
   clSection.ElementInformation.Properties[string.Empty].Value as ChannelEndpointElementCollection;

Dictionary<string, string> nameAddressDictionary = 
   new Dictionary<string, string>();

foreach (ChannelEndpointElement endpointElement in endpointCollection)
{
   nameAddressDictionary.Add(endpointElement.Name, 
                             endpointElement.Address.ToString());
}

EDIT3

Ok, I think I've figured out the 2nd half (and thus, full solution) to my problem. I found this on another website and I modified it to meet my needs:

Configuration configuration; 
ServiceModelSectionGroup serviceModelSectionGroup;
ClientSection clientSection;

configuration = 
    ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
serviceModelSectionGroup = 
    ServiceModelSectionGroup.GetSectionGroup(configuration);
clientSection = serviceModelSectionGroup.Client;

foreach (ChannelEndpointElement endPt in clientSection.Endpoints)
{
  MessageBox.Show(endPt.Name + " = " + endPt.Address);
}
configuration.Save();

With this code, we have access to the clientSection.Endpoints and can access and change all the member properties, like "Address". And then when we're done changing them, we can do configuration.Save() and all the values get written out to a user file.

Now here's the catch. In debug mode, the "configuration.save()" does not appear to actually persist your values from execution to execution, but when running the application normal (outside of debug mode), the values persist. (Which is good.) So that's the only caveat.

EDIT4

There is another caveat. The changes made to the WSDLs do not take effect during runtime. The application needs to be restarted to re-read the user config file values into memory (apparently.)


The only other thing that I might be interested in is finding a way (once the values have been changed) to revert the values to their defaults. Sure, you can probably delete the user file, but that deletes all of the custom settings.

Any ideas?

EDIT5

I'm thinking Dependency Injection might be perfect here, but need to research it more...

EDIT 6

I don't have comment privileges but you need to run

ConfigurationManager.RefreshSection("client");

to have the cache updated so that changes happen immediately.

like image 543
Pretzel Avatar asked Nov 06 '22 14:11

Pretzel


1 Answers

If you're using Microsoft Add Web Reference to create your service reference, then I think you may have trouble changing the connection programmatically. Even if you did change the auto generated code, as soon as you did an Update Service Reference it'd be overwritten.

You're best bet is to scratch Microsoft's auto generated code and build your own WCF classes. It's not difficult, and offers lots of flexibility / scalability.

Here's an excellent article on this very subject.

As for storing the custom addresses, it would depend on your app whether it's a Silverlight, Windows or web app. My personal choice is the database.

like image 116
Matt Avatar answered Nov 13 '22 04:11

Matt