views:

303

answers:

0

First of all, thank you for reading this and I hope that you can bear with me. I have a rather complex problem here and I am about to turn to Microsoft for some answers.

I have written a WCF service that implements the FlexNet API to automatically search an update server for product updates, and installs them on a client's machine if an update is avaliable. This WCF service is hosted through a standard Windows service which consumes the WCF service. The Windows service will consume the methods contained in the service periodically based on a time set up in the app.config.

All that is pretty standard and is working fine. Now here's my issue -

I have a client (Windows form), that needs to recieve status updates from the WCF service. In order to do so I am implementing duplex callbacks. I am using a delegate to process the UI changes on the client side. It's rather complicated so here's some sample code -

public void SendStatusMessage(string psStatusMessage)
    {
        string sStatus = null;
        if (psStatusMessage != string.Empty)
        {
            sStatus = psStatusMessage;
        }

        else
        {
            sStatus = "FlexNet Service is Idle...";
        }

        // The UI thread won't be handling the callback, but it is the only one allowed to update the controls.  
        // So, we will dispatch the UI update back to the UI sync context.
        SendOrPostCallback oCallback = delegate(object state)
        {
            UpdateStatus(sStatus);
        };

        _uiSyncContext.Post(oCallback, sStatus);

    }

And here is the Update status method which the delegate invokes -

        private void UpdateStatus(string psMessage)
    {
        lblStatus.Text = psMessage;
    }

Here is the operation in the WCF service which invokes this callback -

 public System.Data.DataSet GetInstalledProducts(bool pbUpdatesOnly)
    {
        DataSet dsProd = new DataSet();
        DataTable dtProducts = new DataTable("AmCadProductList");
        string sProdVersion = null;
        string sProdName = null;
        string sProdCompany = null;

        try
        {
            CallStatusUpdates(); //This method invokes the callback to the client
            dtProducts.Columns.Add("Product Code", typeof(string));
            dtProducts.Columns.Add("Product Name", typeof(string));
            dtProducts.Columns.Add("Publisher", typeof(string));
            dtProducts.Columns.Add("Product Version", typeof(string));

            Type oType = Type.GetTypeFromProgID("WindowsInstaller.Installer");
            if (oType != null)
            {
                moInstaller = (WindowsInstaller.Installer)Activator.CreateInstance(oType);
            }

            WindowsInstaller.StringList sProducts = moInstaller.Products;
            foreach (string sProdCode in sProducts)
            {

                sProdCompany = moInstaller.get_ProductInfo(sProdCode, "Publisher");
                sProdName = moInstaller.get_ProductInfo(sProdCode, "InstalledProductName");
                sProdVersion = moInstaller.get_ProductInfo(sProdCode, "VersionString");

                if (sProdCompany.ToUpper().Contains("AMCAD"))
                {
                    if (pbUpdatesOnly)
                    {
                        if ((CheckForUpdates(sProdCode, sProdVersion) == PublicVars.UPDATES_AVAILABLE))
                        {
                            dtProducts.Rows.Add(sProdCode, sProdName, sProdCompany.ToString().ToUpper(), sProdVersion);
                        }
                    }
                    else
                    {
                        dtProducts.Rows.Add(sProdCode, sProdName, sProdCompany.ToString().ToUpper(), sProdVersion);
                    }
                }
            }
            dsProd.Tables.Add(dtProducts);
        }

        catch (Exception ex)
        {
            Logger.LogError("AMCAD Updater - Error - Occured in GetInstalledProducts", ex);
            throw ex;
        }
        finally
        {
            //Free up resources
            moInstaller = null;
            moAgent = null;
        }

        return dsProd;

    }

Finally, here is the method which invokes the callback from the WCF service -

        private void CallStatusUpdates()
    {
        _callBackList = new List<IFlexNetCallBack>();
        IFlexNetCallBack oCallBack = OperationContext.Current.GetCallbackChannel<IFlexNetCallBack>();

        if (!_callBackList.Contains(oCallBack))
        {
            _callBackList.Add(oCallBack);
        }

        _callBackList.ForEach(delegate(IFlexNetCallBack callback) { callback.SendStatusMessage("Getting Installed Products"); });
    }

I am using ConcurrencyMode.Single and InstanceContextMode.PerSession in case you were wondering.

All of this code works, no real problems there, but the method which the delegate invokes, to update the client's UI does not happen until the WCF operation completes.

I am not sure what I need to do to solve this problem. Do I need to implement multithreading in my WCF service by using ConcurrencyMode.Multiple?

I have seen several articles suggesting that the solution is to use ConcurrencyMode.Reentrant but that is not working either.

Does anyone have any ideas?

Thanks again for reading this huge wall of text!