天天看點

WCF開發架構形成之旅--WCF應用常見問題處理

本文繼續前面幾篇關于WCF開發架構的随筆,繼續介紹WCF的一些經驗和知識,其中主要介紹在使用WCF開發中碰到的問題以及解決方法,為自己做個記号,也為後來者提供解決思路,其中包括有動态修改 WCF配置内容、規範WCF用戶端的調用和處理。 

1、 動态修改WCF配置内容

由于在軟體登入界面中,需要提供使用者切換内網、外網的功能,而配置檔案中内外網的位址配置是不一樣的,是以需要動态修改應用程式的配置檔案,然後更新其中節點内容,界面如下所示。

修改WCF節點的C#代碼如下所示

        private void ChangeConfig() 

        {

            bool isIntranet = radNetType.EditValue.ToString() == "内網";

            if (isIntranet)

            {

                UpdateConfig("192.168.1.2", "8002");

            }

            else

                UpdateConfig("219.136.1.2", "8002");

        }

        private void UpdateConfig(string serverIPAddress, string serverPort)

            //Configuration config = ConfigurationManager.OpenExeConfiguration(Assembly.GetEntryAssembly().Location);  

            Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);

            ConfigurationSectionGroup sct = config.SectionGroups["system.serviceModel"];

            ServiceModelSectionGroup serviceModelSectionGroup = sct as ServiceModelSectionGroup;

            ClientSection clientSection = serviceModelSectionGroup.Client;

            foreach (ChannelEndpointElement item in clientSection.Endpoints)

                string pattern = "://.*/";

                string address = item.Address.ToString();

                if (address.ToLower().Contains("localhost"))

                    return;

                string replacement = string.Format("://{0}:{1}/", serverIPAddress, serverPort);

                address = Regex.Replace(address, pattern, replacement);

                item.Address = new Uri(address);

            config.Save(ConfigurationSaveMode.Modified);

            ConfigurationManager.RefreshSection("system.serviceModel");

其中為了調試友善,在修改配置檔案代碼裡面,判斷位址如果是localhost的則不進行修改切換。

2、 規範WCF用戶端的調用處理。

在建立WCF服務用戶端執行個體的時候,我們可能會這樣共建立用戶端并調用,就是在窗體的頂部,建立一個該窗體内的全局WCF服務用戶端執行個體。

    public partial class FrmParkUser : BaseDock

    {

        private DeviceUserServiceClient client = new DeviceUserServiceClient();

        public string ID = string.Empty;

        public FrmParkUser()

            InitializeComponent();

        .................  

實際使用wcf用戶端的時候,我們可能會這樣調用。

            this.winGridViewPager1.PagerInfo.RecordCount = client.GetRecordCount2(where);

            this.winGridViewPager1.DataSource = client.SearchParkUser(where, this.winGridViewPager1.PagerInfo);

OK,其實這樣使用看起來是沒什麼問題的,而且也能順利使用,不過,由于wcf用戶端都有一個逾時時間,可能靜止過了一段時間,你在界面重新整理資料的時候,你會發現出現下面的錯誤:"通信對象 System.ServiceModel.Channels.ServiceChannel 無法用于通信,因為其處于“出錯”狀态。" 

或者是一些奇怪的錯誤資訊。

既然上面的調用不好,那麼我們應該如何調用用戶端呢,有人這樣調用。

using (var client = new SomeWCFServiceClient()) 

{

    //Do something with the client 

}  

其實這樣操作,更不好,也會出現上面紅色的錯誤,微軟建議的調用方式應該是這樣的

try

    ...

    client.Close();

}

catch (CommunicationException e)

    client.Abort();

catch (TimeoutException e)

catch (Exception e)

    throw;

但如果調用頻繁,這樣實在不雅,管理也非常難受。有沒有更好的方式,避免出錯,又能夠正确調用wcf客戶嗎,當然有,下面這樣方式就是比較好的一種解決方案,經過實際測試,效果不錯。

1、 首先建立一個擴充輔助類,代碼如下所示

    /// <summary>

    /// WCF服務包裝類,避免使用Using等方式導緻服務出錯的問題

    /// </summary>

    public static class WcfExtensions

        public static void Using<T>(this T client, Action<T> work)

            where T : ICommunicationObject

            try

                work(client);

                client.Close();

            catch (CommunicationException e)

                client.Abort();

            catch (TimeoutException e)

            catch (Exception e)

                throw;

    }   

然後實際調用的時候,如下即可,看起來還是非常簡單的,這樣是即需建立的代理用戶端,即使很久不操作,也不會發生逾時等錯誤資訊了。

        private void GetTable()

            new EnterpriseServiceClient().Using(enterpriseClient =>

                DataTable dt = enterpriseClient.GetAllForLookUp();

                this.searchPark.Properties.DisplayMember = "PARK_NAME";

                this.searchPark.Properties.ValueMember = "ID";

                this.searchPark.Properties.DataSource = dt;

            }); 

            new ManufacturerServiceClient().Using(manufacturerClient =>

                ManufacturerInfo[] manuList = manufacturerClient.GetAll();

                this.searchCompany.Properties.DisplayMember = "CompanyName";

                this.searchCompany.Properties.ValueMember = "ID";

                this.searchCompany.Properties.DataSource = manuList;

       } 

 或者如下例子。

            ManufacturerInfo info = null;

                {

                    info = manufacturerClient.FindByID(searchCompany.EditValue.ToString());

                });

            if (info != null)

                this.txtCompanyAddr.Text = info.CompanyAddr;

            }