Prism V2 Silverlight - Retrieve data

Topics: Prism v2 - Silverlight 2
Feb 17, 2009 at 8:46 AM
Hi,

I tried to use Prism V2 for Silverlight, and I've got a question about the retreive of data.

In Silverlight we have an asynch retreive of data, so is there any way to implement it with prism ? because all examples use sync.

Thanks.
Mar 5, 2009 at 2:27 PM

Hi

 

Although asynchronous communication is not addressed in the Composite Application Guidance for WPF & Silverlight, there should be no problem in implementing it.

 

The XapModuleTypeLoader class (if you open the source code it is in this path: CompositeApplicationLibrary\Composite.Silverlight\Modularity) downloads modules asynchronously, as you can see in the BeginLoadModuleType method.

You can take a look at this for an example of asynchronous data retrieval using Silverlight.

 

Please let me know if this helps.

 

Damian Schenkelman

http://blogs.southworks.net/dschenkelman
Mar 5, 2009 at 3:06 PM
http://www.Codeplex.com/SDMS is a codeplex project that uses CompositeWPF/Prism with WCF.  Both Desktop (Unit Test) and Silverlight use async communications for Data Access; perhaps it will provide insight to your needs.   Note: There are a number of WebCast on the home page that address SDMS architecture and usage.
Mar 5, 2009 at 3:48 PM
Well, I have been using Prism V2 with Feb 24 Refresh and I have not been very successful in calling a WCF Web Service asynchronously from either WPF of Silverlight. I have looked at Bill Krat's SDMS sample but the Prism library that he uses is different from Prism V2.

On Silverlight.
When the Presenter instantiates the Model and calls the service class, which in turn calls a WCF service to retrieve a list of customers, the outbound call never makes it out.

On WPF
When the Presenter instantiates the Model and calls the service class, which in turn calls a WCF service to retrieve a list of customers, the outbound call makes it out but no call back ever happens. I checked with Fiddler to make sure that the call is routed to the WCF Service and that service actually returns a valid response but no call back ever happens.

I have not tried using the exact same code with Bill Krat's version of Prism implementation.

In a nutshell, Prism V2 does not allow asynchronous WCF calls to be made from the service layer.

I can send some code samples if interested.
Mar 5, 2009 at 6:18 PM
Hmm, that's strange as I only recently upgraded to Feb 2009 release of CompositeWPF/Prism; if you go back a few changesets I was accessing WCF services from CompositeWPF/Prism V2 (drop 9).

Perhaps some code samples would give us some insight into the problem.  Be sure to provide the ServiceReference.ClientConfig, App.Config or Web.Config info (as applicable).
Mar 5, 2009 at 6:22 PM
Something to try.  On both/either Silverlight or WPF, just before making the WCF Service call place a breakpoint.   Then click on Debug | Attach to process | and attach to the applicable process (you can identify it by looking at the port address of your WCF service on the taskbar).   Place a break point on the Method in question (within the WCF service) and trace it through - perhaps there is something else going on?

Bill
Mar 5, 2009 at 9:11 PM
What is the best way to post code here? If I copy and paste, the formatting is going to be hard and unfortunately I do not have public URL where I can post the code. Any other ideas?
Mar 5, 2009 at 10:18 PM
The following has saved me countless of formatting hours:
http://www.global-webnet.net/blogengine/post/2008/08/23/Copy-Source-as-Html.aspx
Mar 6, 2009 at 2:04 AM
Thank you for the very useful link to CSAH plugin. Certainly a great time saver.

I have more update on this matter. I changed the WPF code to call synchronously instead of asynchronously and that works successfully. In any case, as promised here are the code snippets from various places. BTW, I just noticed another thread started by developmentally today which has AccessViolation errors and I get the feeling that the two may be related.


First App.xaml.cs
   26 protected override void OnStartup(StartupEventArgs e)
   27         {
   28             base.OnStartup(e);
   29 
   30             Bootstrapper bootStrapper = new Bootstrapper();
   31             bootStrapper.Run();
   32         }






Next Bootstrapper.cs
internal class Bootstrapper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            Shell shell = this.Container.Resolve<Shell>();
 
#if SILVERLIGHT
            Application.Current.RootVisual = shell;
#else
            shell.Show();
#endif
            return shell;
        }
 
        protected override void InitializeModules()
        {
            IModule findCustomerModule = this.Container.Resolve<FindCustomerModule>();
            findCustomerModule.Initialize();
        }
    }


Next FindCustomerModule.cs
public class FindCustomerModule : IModule
    {
        #region Constructors
 
        public FindCustomerModule(IUnityContainer container, IRegionViewRegistry regionViewRegistry, IRegionManager regionManager)
        {
            this.container = container;
            this.regionViewRegistry = regionViewRegistry;
            this.regionManager = regionManager;
        }
 
        #endregion
 
        #region IModule Members
 
        /// <summary>
        /// Notifies the module that it has be initialized.
        /// </summary>
        public void Initialize()
        {
            // ClientHarness.Initialize();
 
            this.RegisterTypeAndServices();
 
            this.RegisterViewsWithRegions();
        }
 
        #endregion
 
        #region Private Methods
 
        private void RegisterViewsWithRegions()
        {
            this.regionViewRegistry.RegisterViewWithRegion(RegionNames.MainRegion, () => this.container.Resolve<CustomerListPresenter>().View);
 
            this.regionManager.RegisterViewWithRegion(RegionNames.SelectionRegion,
                                                      () => this.container.Resolve<CustomerPresenter>().View);
        }
 
        protected void RegisterTypeAndServices()
        {
            this.container.RegisterType<ICustomersController, CustomersController>();
            this.container.RegisterType<IFindCustomerService, FindCustomerService>(
                new ContainerControlledLifetimeManager());
            this.container.RegisterType<ICustomerListView, CustomerListView>();
            this.container.RegisterType<ICustomerListPresenter, CustomerListPresenter>();
            this.container.RegisterType<ICustomerView, CustomerView>();
            this.container.RegisterType<ICustomerPresenter, CustomerPresenter>();
        }
 
        #endregion
 
        #region Private Fields
 
        private readonly IUnityContainer container;
        private readonly IRegionViewRegistry regionViewRegistry;
        private readonly IRegionManager regionManager;
 
        #endregion
    }


Next CustomerListPresenter.cs
public CustomerListPresenter(ICustomerListView view, IFindCustomerService service)
        {
            this.View = view;
            this.findCustomerService = service;
            this.model = new CustomerListPresentationModel();
            this.View.Model = model;
            //this.model.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(model_PropertyChanged);
            view.Model.Customers = findCustomerService.FindCustomer(FindKeyType.CustomerNumber, "1101502840338");
        }


Next FindCustomerService.cs
        public System.Collections.ObjectModel.ObservableCollection<Infrastructure.SbosProxy.FindCustomerResponseSearchResultsCustomer> FindCustomer(Modules.FindCustomer.BusinessEntities.FindKeyType findKey, string findKeyValue)
        {
            ObservableCollection<FindCustomerResponseSearchResultsCustomer> customerCollection 
                = new ObservableCollection<FindCustomerResponseSearchResultsCustomer>();
 
#if SILVERLIGHT
            client.FindCustomerCompleted += new ClientHarness.CompletedFindCustomer(client_FindCustomerCompleted);
            client.BeginFindCustomer(EnumConvert.GetValue(findKey), findKeyValue);
 
            Thread.Sleep(5000);
 
            int count = Convert.ToInt32(response.SearchResultsCount);
 
            for (int i = 0; i < count; i++)
            {
                customerCollection.Add(response.SearchResults[i].Customer);
            }
#else
            FindCustomerResponseSearchResults[] results = client.FindCustomer(EnumConvert.GetValue(findKey), findKeyValue);
            int count = results.Length;
 
            for (int i = 0; i < count; i++) {
                customerCollection.Add(results[i].Customer);
            }
#endif
 
 
 
            return customerCollection;
        }
 
        void client_FindCustomerCompleted(FindCustomerOperationResponse findCustomerOperationResponse)
        {
            response = findCustomerOperationResponse;
        }


<!--EndFragment-->Next ClientHarness.cs
public class ClientHarness
    {
        public delegate void CompletedFindCustomer(FindCustomerOperationResponse findCustomerOperationResponse);
        public event CompletedFindCustomer FindCustomerCompleted;
 
        public ClientHarness()
        {
#if SILVERLIGHT
            head.Client = Application.Current.Resources["head.Client"].ToString();
            head.Destination = Application.Current.Resources["head.Destination"].ToString();
            head.Region = Application.Current.Resources["head.Region"].ToString();
            head.RoutingArea = Application.Current.Resources["head.RoutingArea"].ToString();
            head.Source = Application.Current.Resources["head.Source"].ToString();
 
            endpoint = Application.Current.Resources["endpointURL"].ToString();
#else
                    head.Client = "802710001000";
        head.Destination = "CCS01";
        head.Region = "QA8A";
        head.RoutingArea = "8027";
        head.Source = "BOS";
 
            endpoint = "http://misd01-xpa/SbosService/SbosService.svc";
#endif
            head.ConversationId = "";
            head.DownstreamNotification = "";
            head.Operator = "";
            head.Origin = "";
            head.RoundTrip = Guid.NewGuid().ToString();
            head.UserId = "";
            head.Version = ""; //2.16
 
 
 
            BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None);
 
            sbosServiceClient = new SbosServiceClient(binding, new EndpointAddress(endpoint));
        }
 
#if !SILVERLIGHT
        public FindCustomerResponseSearchResults[] FindCustomer(string findKeyType, string findKey)
        {
            string resultsCount;
            FindCustomerResponseSearchResults[] results;
 
            sbosServiceClient.FindCustomerOperation(ref head, findKey, findKeyType, out resultsCount, out results);
 
            return results;
        }
#endif
 
        public void BeginFindCustomer(string findKeyType, string findKey)
        {
            sbosServiceClient.FindCustomerOperationCompleted += new EventHandler<FindCustomerOperationCompletedEventArgs>(sbosServiceClient_FindCustomerOperationCompleted);
            sbosServiceClient.FindCustomerOperationAsync(head, findKey, findKeyType);
        }
 
        void sbosServiceClient_FindCustomerOperationCompleted(object sender, FindCustomerOperationCompletedEventArgs e)
        {
            if (e.Result != null)
            {
                if (FindCustomerCompleted != null)
                {
                    FindCustomerOperationResponse response = new FindCustomerOperationResponse();
 
                    response.Response = e.Response;
                    response.SearchResults = e.SearchResults;
                    response.SearchResultsCount = e.SearchResultsCount;
 
                    FindCustomerCompleted(response);
                }
            }
        }
 
        private SbosServiceClient sbosServiceClient;
        private head head = new head();
        private string endpoint;
    }



Next relevant code from VS generated Reference.cs
        public Infrastructure.SbosProxy.Response FindCustomerOperation(ref Infrastructure.SbosProxy.head head, string Key, string KeyType, out string SearchResultsCount, out FindCustomerResponseSearchResults[] SearchResults) {
            Infrastructure.SbosProxy.FindCustomerOperationRequest inValue = new Infrastructure.SbosProxy.FindCustomerOperationRequest();
            inValue.head = head;
            inValue.Key = Key;
            inValue.KeyType = KeyType;
            Infrastructure.SbosProxy.FindCustomerOperationResponse retVal = ((Infrastructure.SbosProxy.ISbosService)(this)).FindCustomerOperation(inValue);
            head = retVal.head;
            SearchResultsCount = retVal.SearchResultsCount;
            SearchResults = retVal.SearchResults;
            return retVal.Response;
        }
<!--EndFragment-->
        public void FindCustomerOperationAsync(Infrastructure.SbosProxy.head head, string Key, string KeyType, object userState) {
            if ((this.onBeginFindCustomerOperationDelegate == null)) {
                this.onBeginFindCustomerOperationDelegate = new BeginOperationDelegate(this.OnBeginFindCustomerOperation);
            }
            if ((this.onEndFindCustomerOperationDelegate == null)) {
                this.onEndFindCustomerOperationDelegate = new EndOperationDelegate(this.OnEndFindCustomerOperation);
            }
            if ((this.onFindCustomerOperationCompletedDelegate == null)) {
                this.onFindCustomerOperationCompletedDelegate = new System.Threading.SendOrPostCallback(this.OnFindCustomerOperationCompleted);
            }
            base.InvokeAsync(this.onBeginFindCustomerOperationDelegate, new object[] {
                        head,
                        Key,
                        KeyType}, this.onEndFindCustomerOperationDelegate, this.onFindCustomerOperationCompletedDelegate, userState);
        }
<!--EndFragment-->

Next the config file as used in WPF client (Silverlight one is very similar)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <basicHttpBinding>
                <binding name="HttpBinding_SLBOS" closeTimeout="00:01:00" openTimeout="00:01:00"
                    receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"
                    bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                    useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <security mode="None">
                        <transport clientCredentialType="None" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="UserName" algorithmSuite="Default" />
                    </security>
                </binding>
            </basicHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost/SbosService/SbosService.svc" binding="basicHttpBinding"
                bindingConfiguration="HttpBinding_SLBOS" contract="SbosProxy.ISbosService"
                name="HttpBinding_SLBOS" />
        </client>
    </system.serviceModel>
</configuration>
<!--EndFragment-->
Nothing that jumps out at me. Thank you for your assistance.
<!--EndFragment-->
Mar 6, 2009 at 2:34 AM
Edited Mar 6, 2009 at 2:37 AM
At a quick glance I noted you did something that had me chasing my tail for quite some time. 
You used Thread.Sleep(5000) assuming, as I did, that the Async call is running on a different thread - it isn't.

Asynchronous operations are queued on the same thread versus across multiple threads.  This is why we can
assign results directly to the UI without having to invoke.  After plugging in extensive logging I found that the
Async call doesn't execute until after the parent method returned to the calling process (sometimes they didn't
run until all processes were done).

This posed a big problem for me because I needed the WCF Call to complete before continuing execution,
consider the following pseudo code:

List<Data> data = new List<Data>;

client.OnDataRequestCompleted += DataRequestHandler;
client.GetData()
Thread.Sleep()
HaveToDoSomethingWithTheData(data)

public void DataRequestHandler(DataCompleted e)
{
     data = e.Result as List<Data>
}

Failed miserably - I had to do something like the following:


--- DAL ----
public event EventHandler<DataEventArg> OnData;
client.OnDataRequestCompleted += DataRequestHandler;
client.GetData();

public void DataRequestHandler(DataCompleted e)
{
    if(OnData!=null)
       OnData(this, new DataEventArgs(e.Result))
}

BLL would subscribe to the OnData event and then be able to execute: 
   HaveToDoSomethingWithTheData(data)

Could this be your problem?



#if SILVERLIGHT
            client.FindCustomerCompleted += new ClientHarness.CompletedFindCustomer(client_FindCustomerCompleted);
            client.BeginFindCustomer(EnumConvert.GetValue(findKey), findKeyValue);
 
            Thread.Sleep(5000);
 
            int count = Convert.ToInt32(response.SearchResultsCount);
 
            for (int i = 0; i < count; i++)
            {
                customerCollection.Add(response.SearchResults[i].Customer);
            }
#else
Mar 6, 2009 at 2:40 AM
I thought I should emphasize - if you use Thread.Sleep() in a Silverlight application you put the entire application to sleep.

I edited the above psuedo code to be more technically correct as I forgot the client.GetData() call. 

--- DAL ----
public event EventHandler<DataEventArg> OnData;
client.OnDataRequestCompleted += DataRequestHandler;
client.GetData();

public void DataRequestHandler(DataCompleted e)
{
    if(OnData!=null)
       OnData(this, new DataEventArgs(e.Result))
}

BLL would subscribe to the OnData event and then be able to execute: 
   HaveToDoSomethingWithTheData(data)

Mar 6, 2009 at 3:44 PM
Hi Mistry
I have noticed a couple of things in your code.

1. You don't have to make the call 'synchronous'. As far as I can tell, all async callbacks are done on the UI thread. So it should be safe to manipulate the UI from there.
2. It appears that you only create the ObservableCollection when the async callback returns. This causes a problem because:
     - When SL sets up the databinding between the view and the viewmodel, your ObservableCollection is still Null. So nothing happens
     - Then when the call returns, you create the observable collection. However, the view is never notified that the collection is created. So nothing happens.
     - When you made it 'synchronous', you already create the observablecollection in the constructor. That's why the view can find your results. 
    -> To fix this: Option 1. Create the ObservableCollection in the Constructor and fill it when the callback returns. This gives the view an object to monitor when the databinding is setup (the observablecollection)
                         Option 2. Implement INotifyPropertyChanged on your Model and raise the PropertyChanged event when your value changes.

You might also look at the webcasts Bob and I did. We didn't show you how to use WCF, however, we did use async communication:
http://blogs.msdn.com/erwinvandervalk/archive/2009/03/03/prism-v2-walkthrough-webcasts.aspx

Hope this help,
Erwin
Mar 6, 2009 at 4:02 PM
BillKrat, as you aptly stated Thread.Sleep was a very bad idea. I am now able to make the call successfully. How naive of me to assume that the asynchronous call will always happen on a different thread. Appreciate your help.

vallekie, you are a step ahead of me and now I will follow up on what you have to say. Once again, appreciate the help.