本文继续前面几篇关于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)
{
...
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;
}

标签: none

添加新评论