Introduction
Visual Studio's front end implementation for adding service references to the project doesn't quite always work as intended.
In addition to this, should there be any errors generating the service references they can be almost impossible to detect as the to the reason.
Running SVCUtil on the other hand, gives you complete control over the generation of the proxy model code, however, it is rather cumbersome to write,
especially if you have more than three external references.
The reason for creating the User interface it to try and bride the gap between having to manually type
in all the extensive arguments manually and then keep them in some undisclosed location for safety so that no one tampers with it for the next time you may require the service to be generated.
Using the Application
To generate a service reference successfully, add in a Service Reference URL (or in the case where the WSDL and XML Files are known, use the file paths with a space between each file).
The svcutil itself has a timeout of 5 minutes, so in order to combat this issue I implemented a service poll mechanism
utilizing an HTTPClient. The reasoning behind this is that the services I needed to generate were so large that they took nearly 45 minutes to compile the WSDL information after being
successfully rebuilt. The HTTPClient ensures that they compile successfully in their hosted
environment so they can be delivered successfully when asked for by the svcutil exe.
In the case of WSDL creation, the tick box for bypassing the poll service will need to be checked or the application will fail.
Available .NET and Silverlight Versions
The available .NET and Silverlight versions are determined by the installed SDK instances on the machine on which the application is run. The application will determine from the SDK folders which versions have a viable version of the SVCUtil and SLSVCUtil executables and allow those versions as choices.
How it Works
The application shells out all the work to the SVCUtil or SLSVCUtil directly utilising the process and then just reading the responses.
This is done asynchronously utilising the Task Parallelism framework.
Action runProcessAction = () =>
{
Process process = new Process();
process.StartInfo = new ProcessStartInfo(GetSVCUtilityFileName(),
ConfigViewModel.GetServiceProcessArguments());
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.OutputDataReceived +=
new DataReceivedEventHandler(OnServiceProcess_OutputDataReceived);
process.ErrorDataReceived +=
new DataReceivedEventHandler(OnServiceProcess_OutputDataReceived);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
process.OutputDataReceived -=
new DataReceivedEventHandler(OnServiceProcess_OutputDataReceived);
process.ErrorDataReceived -=
new DataReceivedEventHandler(OnServiceProcess_OutputDataReceived);
};
Depending on whether or not the poll service box is ticked, we decide on whether or not the
startTask
will initialize with the HTTPClient request
task or dive straight into the process task.
startTask = System.Threading.Tasks.Task.Factory.StartNew(
() =>
{
var handle = new System.Net.Http.HttpClientHandler()
{
Proxy = System.Net.HttpWebRequest.GetSystemWebProxy(),
};
var client = new System.Net.Http.HttpClient(handle, true);
client.Timeout = TimeSpan.FromMinutes(ConfigViewModel.Timeout);
var testResultTask = client.GetAsync(ConfigViewModel.ServiceReference)
.ContinueWith(messageWithResult =>
{
if (messageWithResult.Result.IsSuccessStatusCode == false)
{
throw new Exception("Unable to locate the service reference");
}
});
testResultTask.Wait();
})
.ContinueWith(t1 => runProcessAction());
On completion of the run process task however, through either mechanism, the continuation task will let the main form know that this has completed successfully,
and add a completed message to messages list.
startTask.ContinueWith(
(completionTask) =>
{
LoadingText = "Done";
Messages.Add("--------------------------");
Messages.Add("Service creation Completed");
Messages.Add("--------------------------");
messageShower.ScrollIntoView(Messages.LastOrDefault());
if (ConfigViewModel.OpenFolderComplete == true)
{
OpenOuputFolder();
}
IsCompleted = true;
},
System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext())
Interesting Features
The drop-down lists have an interesting caveat in that they bind directly to the enum properties to locate their collections. This is implemented through a little bit
of reflection that I came across a while back. The detailing bit though, is that the enum members are marked with an attribute that decides on whether that particular member
should or should not be displayed for choice within the collection. It also has a display name property so that an alternate text value can be given - for
example, the enum value
is CSharp, whereas the display value should be C#.
This is particularly useful as a lot of the time, the enumerations have "default" values that you do not want as selections, but are needed in the background.
In addition to this there is a Converter that will take the display value for the enum such as MyFavouriteProperty and display it as My Favourite Property
so that the user sees it correctly. This converter also looks to see if the attribute mentioned above has the display property specified so and uses that instead.
public enum AvailableLanguages
{
[Attributes.MemberVisible(false)]
Unknown = 0,
[Attributes.MemberVisible(true, DisplayName="C#")]
CSharp,
VisualBasic
}
History
- 25 August 2013 - Initial post.