WCF开发框架形成之旅--WCF应用常见问题处理

来源:岁月联盟 编辑:exp 时间:2011-12-10

 

本文继续前面几篇关于WCF开发框架的随笔,继续介绍WCF的一些经验和知识,其中主要介绍在使用WCF开发中碰到的问题以及解决方法,为自己做个记号,也为后来者提供解决思路,其中包括有动态修改WCF配置内容、规范WCF客户端的调用和处理。

 

1、 动态修改WCF配置内容

 

由于在软件登录界面中,需要提供用户切换内网、外网的功能,而配置文件中内外网的地址配置是不一样的,因此需要动态修改应用程序的配置文件,然后更新其中节点内容,界面如下所示。

 

/

 

 

修改WCF节点的C#代码如下所示 www.2cto.com

 

        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)

{

    ...

    client.Abort();

}

catch (Exception e)

{

    ...

    client.Abort();

    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)

            {

                client.Abort();

            }

            catch (Exception e)

            {

                client.Abort();

                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;

            new ManufacturerServiceClient().Using(manufacturerClient =>

                {

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

                });

            if (info != null)

            {

                this.txtCompanyAddr.Text = info.CompanyAddr;

            }

 

 

 

 

主要研究技术:代码生成工具、Visio二次开发、送水管理软件等共享软件开发

   

  转载请注明出处:

撰写人:伍华聪  http://www.iqidi.com