Prism/WCF - single configuration file for ASP.NET, Silverlight and WCF

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: , , ,
Categories: CompositeWPF


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Prism/WCF - Cannot find ServiceReferences.ClientConfig in .xap application package

I'm using Prism (V8) to create a Silverlight application.  The main Project is the SDMS.Silverlight project which loads the EmployeeModule via it's BootStrapper.GetModuleCatalog() method.

The EmployeeModule references a WCF Web Service to obtain the Employee list - the ServiceProvider service (shown in EmployeeModule/ServiceReferences below) has a corresponding ServiceReferences.ClientConfig file which is not being found by Silverlight when the EmployeeService.GetEmployeeList() executes.

When I launch the application I receive the the following error:

     Cannot find 'ServiceReferences.ClientConfig' in the .xap application package  

 

Silverlight obviously found the EmployeeModule assembly but cannot find the ServiceReferences.ClientConfig it contains.

The cure is to provide the Application assembly (SDMS.Silverlight) with a reference to it.  This can be easily done by right clicking on the file and selecting copy - it will copy the fullpath to the clipboard.

Next you'll want to go to the main assembly, in our case SDMS.Silverlight, and add an existing item - the IMPORTANT PART is to click the down triangle on the "Add" button and select "Add Link" as shown in the image below:

Now as the EmployeeModule's ServiceProvider is updated the main assembly will also be updated.  Our silverlight application now runs without issue. 


Tags: , ,
Categories: Unity


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Integrating IIS WCF and Unity

Our open source Solution Development Management System (SDMS) will use WCF services as a data source for both it's Silverlight and Desktop applications.  To maximize reusability it was necessary to implement Unity so that we can utilize dependency injection within our services.

Initial google research quickly revealed that WCF Behaviors would provide the necessary hooks so that a Unity Container could be instantiated, configured and used to resolve the applicable service.

Of the resources available I found the following to be instrumental to understanding and integrating unity into WCF via behaviors; the MSDN article provides details on WCF Behaviors: 

  Integrating the Policy Injection Application Block with WCF Services
  http://msdn.microsoft.com/en-us/magazine/cc136759.aspx

In this Webcast (Windows 7 users may have to right click and Save Target as to view it) I demonstrate how we can easily switch between the Services and ServicesStub classes below with a simple Web.Config configuration change.  Note: both of these classes implement IServices.

 

Below we show the "Stubs" configuration being utilized:

We'll change "Stubs" to "Services" (see arrow below) to utilize the other class.

And receive the following results:

To implement the Unity Behavior all that was required was to add a reference to the UnityBehavior project and make the following Web.Config changes within the Service to utilize the behavior.

Note:   If you do not want to provide a Unity Configuration in the Web.Config all you would have to do is change the line below (pointed to by the arrow) to:

     return container.Resolve(type);

And it will resolve the default service - in this case SDMSService.Services.

Source code available HERE on the Source Code tab (changeset 27387)


Tags: ,
Categories: Unity


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

WCF / Silverlight - An error occurred ... attempting to access a service in a cross domain way

When creating a WCF Service Application for your Silverlight application you will encounter a "cross domain" error with default configurations.  

Below I show the default Service application code (only class name changed) and made the necessary Silverlight WCF configuration change for basicHttpBinding as shown by the arrow. 

I then created a Web Service reference for both my Silverlight application and Test application.  Note that Silverlight uses Async calls while the Test project uses Sync call to obtain data.

When I run the Silverlight application I get the following error:

I added the following clientaccesspolicy.xml file per THIS LINK (you can copy code from it) to my WCF Service and both my Silverlight and Unit test run without issue.

 

WCFService.zip (709.94 kb)  <= SOURCE CODE


Tags: ,
Categories: Silverlight


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

Web Service Software Factory - BlueYonderAirLines tutorial contined....

The Web Service Software Factory was my introduction to WCF and DSL's and I must say I was very VERY impressed.   The Web Service Software factory will be my tool of choice when working with WCF.   As great as the Tutorial was it assumed a wee bit to much knowledge on my part and ended somewhat abruptly - without and idea of how to use the many projects and classes that were generated for me.   For those like myself new to WCF the following is a continuation that will let you use the demo you created :)

New to WCF/WSSF?  Visit http://code.msdn.microsoft.com/ServiceFactory 

  • Topics Covered
    • Hands-on Lab continued - how to consume a WCF service (with and without Config file)
    • Assumptions that had me googling and burning hours...
    • Configure IIS for WCF

BYA.Mfg.SCM.Svc.WCF :: Tests :: BYA.Mfg.SCM.Svc.WCF.Client :: MainForm.cs 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

using BYA.Mfg.SCM.Svc.WCF.Client.MaterialMgmtProxy;

namespace BYA.Mfg.SCM.Svc.WCF.Client
{
    public partial class MainForm : Form
    {
        PartsMgmtServiceContractClient client;

            public MainForm()
        {
            InitializeComponent();
            client = new PartsMgmtServiceContractClient("DefaultEndpoint");
        }

        // Utilizes App.Config
        private void ExecuteButton_Click(object sender, EventArgs e)
        {
            // Configure the request utilizing input - valid input
            // are "one" and "two" - all others will return 0 values
            DemandRequest request = new DemandRequest();
            request.AircraftPart = new AircraftPart();
            request.AircraftPart.Part = SearchText.Text;

            ResultsGrid grid = new ResultsGrid(client.GetRequirementDemand(request));
            grid.Show();
        }

-- OR --

        // Utilizes Channel
        private void ExecuteButton_Click(object sender, EventArgs e)
        {
            // Configure the request utilizing input - valid input
            // are "one" and "two" - all others will return 0 values
            DemandRequest request = new DemandRequest();
            request.AircraftPart = new AircraftPart();
            request.AircraftPart.Part = SearchText.Text;

            BasicHttpBinding binding = new BasicHttpBinding();
            EndpointAddress endPoint = new EndpointAddress("http://localhost:2035/BYA.Mfg.SCM.Svc.WCF.Host/MaterialMgmt.svc");

            ChannelFactory<PartsMgmtServiceContract> channelFactory =
                new ChannelFactory<PartsMgmtServiceContract>(binding, endPoint);

            PartsMgmtServiceContract service = channelFactory.CreateChannel();

            DemandResponse response = service.GetRequirementDemand(new DemandRequest1(request));

            ResultsGrid grid = new ResultsGrid(response.PartLevel);
            grid.Show();

        }
    }
}

Be sure to read this blog on WCF Proxy Wrapper - it holds important information disposing!


Tags: , ,
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know

WCF proxy wrappers

Like most developers trying to keep up with Microsoft technology I'm somewhat overwhelmed.  I love working with the Smart Client Software Factory and Web Client Software Factory (we use them at work) and used to love being involved in the forums; unfortunately WPF, XBAP, CompositeWPF, Unity, WCF, Silverlight and SharePoint Services are on the front burner  (to learn) which leaves little to no time for having fun and being involved like I could (and want to be).   I should note that all of the above are my personal learning time, which I also have to share with my wife (who is my best friend).  At work I'm diving into MicroStation (engineering CAD program), SQL Server 2005 Notification Services and the list goes on....

So I've abandoned the SCSF/WCSF forums (won't be blogging about them) however the following information, although left in the SCSF forum, still has great value for many of the above mentioned applications.


RightCoder wrote:

One problem I have now is that I'm not able to read the web.config file where I have my WCF-configuration. Im not sure that my MyServiceProxy in App_Code have access to the web.config?? Anyway if MyServiceProxy and BaseProxy is placed in the App_Code folder their not accessible to my modules where I have the concrete implementations of the CustomerService and SupplierService. How could this be solved?

You can use the channelFactory which will permit you to define your default end point.  The using statement is important - we experienced leaks during load test without it.


        public WidgetScheduleCollection GetScheduledWidgets(WidgetRequest para)
        {
            WidgetRequest1 request;
            WidgetResponse response;

            string defaultEndPoint = ConfigurationManager.AppSettings["DefaultEndPoint"].ToString();

            BasicHttpBinding binding = new BasicHttpBinding();
            EndpointAddress endPoint = new EndpointAddress(defaultEndPoint);

            // Use Channel so we don't have to rely on config file
            ChannelFactory<WidgetServiceContract> channelFactory =
                new ChannelFactory<WidgetServiceContract>(binding, endPoint);

            WidgetServiceContract service = channelFactory.CreateChannel();
            using (service as IDisposable)
            {
                // Wrap the request with a Channel's WidgetRequest
                request = new WidgetRequest1(para);

                // Get the scheduled Widgets for request
                response = service.GetScheduledWidgets(request);
            }

            // Return the Widget collection
            return response.ScheduledWidgetCollection;
        }

 

pboldc later shares important information about using IDisposable with WCF - my response to his information follows:

pbolduc wrote:
This article was very interesting to read regarding using WCF and IDisposable etc.

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=855018&SiteID=1

and a couple of MSDN links:

Avoiding Problems with the Using Statement
Expected Exceptions




Thanks for taking the time to provide these references!   This topic wasn't given much emphasis in the WSSF....   After reading your links I revisited the WSSF factory forum and found that SpencerClark (Apr 4) provides wrapper code which implements the code recommended in the links - he indicates that he has "not had a single error where the service was faulted".    Looks like I have a wee bit of refactoring to do on Monday ;)

If your looking to create a WCF Proxy Wrapper I would suggest reading the entire message thread here:
http://www.codeplex.com/websf/Thread/View.aspx?ThreadId=29604


Tags:
Categories:


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know