Use Case
| • | You want to use built-in WCF configuration for your bindings and endpoints in order to avoid hard-coding them. Note: if you are not using the CC Framework classes, then you can probably ignore this section. |
Remarks
You may have noticed in the preceding examples that the WCF bindings and endpoints are hard-coded. This is simply because it is the easiest way to get things working so that you, the developer, can concentrate on your application's functionality. However, for a deployed application, this is not a flexible, long-term solution. Rather than creating your own settings unnecessarily, we will briefly discuss using WCF configuration, which is extremely flexible. In fact, if you have a look at the code that actually starts the automation service, you will see that it is entirely configuration-based. This is done to provide the utmost flexibility to the consumer of the service. Let's have a look at it:
<system.serviceModel> <services> <service behaviorConfiguration="ViewerAutomationBehaviour" name="ViewerAutomation"> <endpoint binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IViewerAutomation" name="ViewerAutomation" contract="IViewerAutomation" /> <endpoint binding="netNamedPipeBinding" bindingConfiguration="NetNamedPipeBinding_IViewerAutomation" name="ViewerAutomation" contract="IViewerAutomation" /> <host> <baseAddresses> <add baseAddress="http://localhost:51124/ClearCanvas/ImageViewer/Automation" /> <add baseAddress="net.pipe://localhost/ClearCanvas/ImageViewer/Automation" /> </baseAddresses> </host> </service> <service behaviorConfiguration="StudyLocatorBehaviour" name="StudyLocator"> <endpoint binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IStudyRootQuery" name="StudyLocator" contract="IStudyRootQuery" /> <endpoint binding="netNamedPipeBinding" bindingConfiguration="NetNamedPipeBinding_IStudyRootQuery" name="StudyLocator" contract="IStudyRootQuery" /> <host> <baseAddresses> <add baseAddress="http://localhost:51124/ClearCanvas/ImageViewer/StudyLocator" /> <add baseAddress="net.pipe://localhost/ClearCanvas/ImageViewer/StudyLocator" /> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors> <behavior name="ViewerAutomationBehaviour"> <serviceDebug includeExceptionDetailInFaults="false" /> <serviceMetadata httpGetEnabled="true" /> </behavior> <behavior name="StudyLocatorBehaviour"> <serviceDebug includeExceptionDetailInFaults="false" /> <serviceMetadata httpGetEnabled="true" /> </behavior> </serviceBehaviors> </behaviors> <bindings> <basicHttpBinding> <binding name="BasicHttpBinding_IViewerAutomation" /> <binding name="BasicHttpBinding_IStudyRootQuery" maxBufferSize="26214400" maxReceivedMessageSize="26214400" /> </basicHttpBinding> <netNamedPipeBinding> <binding name="NetNamedPipeBinding_IViewerAutomation" /> <binding name="NetNamedPipeBinding_IStudyRootQuery" maxBufferSize="26214400" maxReceivedMessageSize="26214400" /> </netNamedPipeBinding> </bindings> <client> <endpoint address="net.pipe://localhost/ClearCanvas/ImageViewer/StudyLocator" binding="netNamedPipeBinding" bindingConfiguration="NetNamedPipeBinding_IStudyRootQuery" contract="IStudyRootQuery" name="NetNamedPipeBinding_IStudyRootQuery" /> <endpoint address="net.pipe://localhost/ClearCanvas/ImageViewer/Automation" binding="netNamedPipeBinding" bindingConfiguration="NetNamedPipeBinding_IViewerAutomation" contract="IViewerAutomation" name="NetNamedPipeBinding_IViewerAutomation" /> </client> </system.serviceModel> |
This can look pretty daunting to someone who has never seen it before. Luckily, there are really only a couple of parts, if any, that you will ever have to worry about. Let's briefly go over the main sections/elements.
system.ServiceModel
| • | Encapsulates the entire WCF configuration model for both services and clients. |
services
| • | Configuration for services/hosts, like the automation service. This section should never need to appear in your application's configuration file unless, of course, you are hosting your own services. |
behaviours
| • | Behaviour configuration for services. Behaviours can be configured for the service as a whole, or individually by endpoint. This section should never need to appear in your application's configuration file. |
bindings
| • | Binding configuration for both services and clients. Notice how the service and client endpoint sections refer to the same binding configuration (by name). This is because the client proxy's binding must be the same as the service's in order for it to work properly. |
client
| • | Endpoint configuration for client proxies. |
So, now you may be asking: how do I use this in my application? It's pretty easy, really—simply follow these steps:
| • | Open your application's .config file in a text editor or Visual Studio. |
| • | Under the root configuration element, create an element called system.ServiceModel (the case is important). |
| • | Copy both the bindings and client sections from the sample above into the system.ServiceModel section. |
Now, you can change all of your client proxy code to simply use the default (parameterless) constructor. Meaning the previous samples change from something like this:
internal static class AutomationHelper
{
public static IList<StudyRootStudyIdentifier> FindStudyByPatientId(string patientId)
{
BasicHttpBinding binding = new BasicHttpBinding();
EndpointAddress endpoint = new EndpointAddress("http://127.0.0.1:51124/ClearCanvas/ImageViewer/StudyLocator?wsdl");
StudyRootQueryServiceClient client = new StudyRootQueryServiceClient(binding, endpoint);
try
{
client.Open();
StudyRootStudyIdentifier identifier = new StudyRootStudyIdentifier();
identifier.PatientId = patientId;
IList<StudyRootStudyIdentifier> results = client.StudyQuery(identifier);
client.Close();
return results;
}
catch(Exception)
{
client.Abort();
throw;
}
}
}
to this:
internal static class AutomationHelper
{
public static IList<StudyRootStudyIdentifier> FindStudyByPatientId(string patientId)
{
StudyRootQueryServiceClient client = new StudyRootQueryServiceClient();
try
{
client.Open();
StudyRootStudyIdentifier identifier = new StudyRootStudyIdentifier();
identifier.PatientId = patientId;
IList<StudyRootStudyIdentifier> results = client.StudyQuery(identifier);
client.Close();
return results;
}
catch(Exception)
{
client.Abort();
throw;
}
}
}
Did you notice that you also just changed your client's communication protocol from HTTP to Named Pipes (look at the client endpoints and bindings)? ... just like that!