For a functional demo, with the following principals applied, see our http://www.CodePlex.com/SDMS project.
In a recent blog I noted issues with Prism/Silverlight, WCF and configuration files. I have since resolved this issue and respond to a programmers question below.
Hi,
I've read your post about problems with wcf service reference and have a question about your solution.
What if i have different wcf services and have more than one ServiceReference.ClientConfig. It would be impossible to add them all as link to shell project.
See Related WEBCAST
I resolved my issues with configuration files by programmatically setting the Binding and Address – I now have “one” configuration file (Web.Config) that services my ASP.NET, Silverlight and WCF Service(s). The following is my DoctorServiceDAL (Presentation Tier’s Data Layer); after I do an “Add Web Reference” or “Update Web Reference” I delete the configuration file (not required).
public DoctorServiceDAL(IUnityContainer container)
{
try
{
// Set container reference for class use
Container = container;
IServerConfiguration config = container.Resolve<IServerConfiguration>();
// Use basic HttpBinding on both Silverlight and Desktop
BasicHttpBinding binding =
new BasicHttpBinding(BasicHttpSecurityMode.None);
binding.Name = "bndDoctorService";
// Get the WebServiceURI from the unity container
Uri baseAddress = new Uri(config.WebServiceURI);
EndpointAddress address = new EndpointAddress(baseAddress);
// Instantiate our service using configured data
client = new wcf.DoctorServiceClient(binding, address);
// Subscribe to the Completed events. The DSEAxxxxx classes that
// will actually handle setting up the event argument (which will be
// raised below) are in the Data project (for service references).
client.GetProviderListCompleted += AsyncCompleted;
client.GetConfigurationCompleted += AsyncCompleted;
// Note: be sure to update applicable modules, i.e., PatientModule.cs
// after updating the above to ensure DSEAxxxx can be resolved!
}
catch (Exception ex)
{
Error = ex;
}
}
/// <summary>
/// Requests the doctor list. When list is available it will
/// be directed to the AsyncCompleted event (subscribed to in
/// the Constructor)
/// </summary>
/// <param name="doctorID">The doctor ID.</param>
public void RequestProviderData(int doctorID)
{
client.GetProviderListAsync(doctorID);
}
/// <summary>
/// Request the system configuration parameter string
/// </summary>
public void RequestConfigurationString()
{
client.GetConfigurationAsync();
}
/// <summary>
/// Executes when async calls are completed.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e"></param>
/// instance containing the event data.</param>
void AsyncCompleted(object sender, AsyncCompletedEventArgs e)
{
// All Async callbacks will end up here
// prepare global event argument
IDoctorServiceEventArgs args = new DoctorServiceEventArgs();
// Retrieve the name of the process that just completed
// and strip off the CompletedEventArgs
string name = e.GetType().Name.Replace("CompletedEventArgs", "");
try // Use Unity Container as a factory
{
// Resolve it - it must be configured in the Module(s) that
// use this data layer (reference PatientModule.cs)
args = Container.Resolve<IDoctorServiceEventArgs>(name);
// Set the properties to the result
args.SetArgProperties(e);
}
catch (Exception ex)
{
Error = new Exception(string.Format("{0} was not configured in {1} \r\n{2}{3}",
name, GetType().FullName, ex.Message, ex.StackTrace));
}
// If anyone is subscribing to the event (which they should be)
// then raise the event with the data returned
if (OnDoctorServiceEvent != null)
OnDoctorServiceEvent(this, (DoctorServiceEventArgs) args);
}
}
My Default.aspx.cs, code-behind file, has the following which provides information I will need. Note that I pass it into Xaml1 for Silverlight (read BLOG) and to my WCF Service DoctorServiceClient which is injected into the WCF unity container for successive use (related BLOG).
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Retrieve Connection String
string connectionString =
ConfigurationManager.ConnectionStrings["PRMS"].ConnectionString;
// Retrieve Web Service URIs
string webServiceURI_Dev =
ConfigurationManager.AppSettings["WebServiceURI-DEV"];
string webServiceURI_IIS =
ConfigurationManager.AppSettings["WebServiceURI-IIS"];
// Retrieve Web Service Path
string webServicePath =
Request.ApplicationPath == "/" ? webServiceURI_Dev : webServiceURI_IIS;
// Set InitParameters for Silverlight use
Xaml1.InitParameters = string.Format(
"InitParameters=" +
"ServerURL=http://{0};" +
"ServerPath={1};" +
"ConnString={2};" +
"WebServiceURI={3}",
Request.Url.Authority,
Server.MapPath("~"),
connectionString,
webServicePath);
// Send parameters to WCF Service for its use
new DoctorServiceClient().SetConfiguration(Xaml1.InitParameters);
}
}
After the above code runs I can obtain my configuration information from anywhere in the application (ASP.NET, Silverlight or WCF) by resolving the IServerConfiguration.
IServerConfiguration config = container.Resolve<IServerConfiguration>();
Below is the class that implements IServerConfiguration and does the heaviy lifting, note that I provide a mechanism to strongly type the values stored in InitParam:
using Microsoft.Practices.Unity;
using System.Collections;
using System.Collections.Generic;
using System;
namespace Library.Interface.Configuration
{
public class ServerConfiguration : AppBase, IServerConfiguration
{
private IDictionary<string, string> constructorParam;
public IDictionary<string, string> InitParam { get; set; }
public ServerConfiguration(IUnityContainer container) : base(container)
{
try
{
InitParam = new Dictionary<string, string>();
constructorParam = container.Resolve<IDictionary<string, string>>("InitParameters");
string paraString = constructorParam["InitParameters"];
string[] paraList = paraString.Split(';');
foreach (string para in paraList)
{
string[] keyValue = para.Split('=');
InitParam.Add(new
KeyValuePair<string, string>(keyValue[0], keyValue[1]));
}
}
catch (Exception ex)
{
Error = ex;
}
}
public string WebServerURL
{
get { return InitParam["ServerURL"]; }
}
public string WebServerPath
{
get { return InitParam["ServerPath"]; }
}
public string WebServiceURI
{
get { return InitParam["WebServiceURI"]; }
}
}
}
Tags:
prism,
unity,
wcf,
architecture
Categories:
CompositeWPF