写在前面
接触WCF还是它在最初诞生之处,一个分布式应用的巨作。 从开始接触到现在断断续续,真正使用的项目少之又少,更谈不上深入WCF内部实现机制和原理去研究,最近自己做一个项目时用到了WCF。 从这个小项目中我学会了两个地方: 1、利用IIS部署WCF服务,也就是大家接触到的发布SVC文件。2、动态调用WCF接口。
在这个项目中接触WCF时遇到的其实不仅仅是这两个问题,甚至连IIS支持SVC文件也让我折腾了好几把,IIS都重新卸载了两次。 我在这篇文章里用两种方式来实现。
如何使用
1、第一种方式比较简单,而且也是大家喜欢的,因为不需要任何配置文件就可解决,只需知道服务契约接口和服务地址就可以调用。
2、使用Invoke的方式,但是需要在调用客户端配置WCF,配置后在Invoke类里封装服务契约接口即可。
客户端调用DEMO
1
2
3
4
5
6
7
|
//第一种方式
string
url =
"http://localhost:3000/DoubleService.svc"
;
IDoubleService proxy = WcfInvokeFactory.CreateServiceByUrl<IDoubleService>(url);
int
result = proxy.Add(1, 3);
//第二种方式<br><br>int result1 = WCFInvoke.Invoke(t => t.Add(1, 3));<br><br>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name=
"NewBehavior"
>
<dataContractSerializer maxItemsInObjectGraph=
"65536000"
/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name=
"BasicHttpBinding_IDoubleService"
closeTimeout=
"01:00:00"
openTimeout=
"01:00:00"
sendTimeout=
"01:00:00"
receiveTimeout=
"01:00:00"
maxBufferSize=
"2147483647"
maxBufferPoolSize=
"524288"
maxReceivedMessageSize=
"2147483647"
>
<readerQuotas maxDepth=
"128"
maxStringContentLength=
"2147483647"
maxArrayLength=
"16384"
maxBytesPerRead=
"4096"
maxNameTableCharCount=
"16384"
/>
</binding>
</basicHttpBinding>
<netMsmqBinding>
<binding name=
"NetMsmqBinding_IAsyncSender"
>
<security mode=
"None"
/>
</binding>
</netMsmqBinding>
</bindings>
<client>
<endpoint address=
"http://localhost:3000/DoubleService.svc"
binding=
"basicHttpBinding"
bindingConfiguration=
"BasicHttpBinding_IDoubleService"
contract=
"DoubleStone.WebHost.IDoubleService"
name=
"BasicHttpBinding_IDoubleService"
/>
</client>
</system.serviceModel>
|
第一种调用方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
public
class
WcfInvokeFactory
{
#region WCF服务工厂
public
static
T CreateServiceByUrl<T>(
string
url)
{
return
CreateServiceByUrl<T>(url,
"basicHttpBinding"
);
}
public
static
T CreateServiceByUrl<T>(
string
url,
string
bing)
{
try
{
if
(
string
.IsNullOrEmpty(url))
throw
new
NotSupportedException(
"This url is not Null or Empty!"
);
EndpointAddress address =
new
EndpointAddress(url);
Binding binding = CreateBinding(bing);
ChannelFactory<T> factory =
new
ChannelFactory<T>(binding, address);
return
factory.CreateChannel();
}
catch
(Exception ex)
{
throw
new
Exception(
"创建服务工厂出现异常."
);
}
}
#endregion
#region 创建传输协议
/// <summary>
/// 创建传输协议
/// </summary>
/// <param name="binding">传输协议名称</param>
/// <returns></returns>
private
static
Binding CreateBinding(
string
binding)
{
Binding bindinginstance =
null
;
if
(binding.ToLower() ==
"basichttpbinding"
)
{
BasicHttpBinding ws =
new
BasicHttpBinding();
ws.MaxBufferSize = 2147483647;
ws.MaxBufferPoolSize = 2147483647;
ws.MaxReceivedMessageSize = 2147483647;
ws.ReaderQuotas.MaxStringContentLength = 2147483647;
ws.CloseTimeout =
new
TimeSpan(0, 30, 0);
ws.OpenTimeout =
new
TimeSpan(0, 30, 0);
ws.ReceiveTimeout =
new
TimeSpan(0, 30, 0);
ws.SendTimeout =
new
TimeSpan(0, 30, 0);
bindinginstance = ws;
}
else
if
(binding.ToLower() ==
"nettcpbinding"
)
{
NetTcpBinding ws =
new
NetTcpBinding();
ws.MaxReceivedMessageSize = 65535000;
ws.Security.Mode = SecurityMode.None;
bindinginstance = ws;
}
else
if
(binding.ToLower() ==
"wshttpbinding"
)
{
WSHttpBinding ws =
new
WSHttpBinding(SecurityMode.None);
ws.MaxReceivedMessageSize = 65535000;
ws.Security.Message.ClientCredentialType = System.ServiceModel.MessageCredentialType.Windows;
ws.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows;
bindinginstance = ws;
}
return
bindinginstance;
}
#endregion
}
|
第二种调用方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public
class
WCFInvoke
{
/// <summary>
/// 你需要调用的服务契约
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
public
static
T Invoke<T>(Func<IDoubleService, T> func)
{
IServiceInvoker serviceInvoker=
new
WCFServiceInvoker();
return
serviceInvoker.InvokeService(func);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
public
interface
IServiceInvoker
{
void
InvokeService<T>(Action<T> invokeHandler)
where
T :
class
;
TReslt InvokeService<T, TReslt>(Func<T, TReslt> invokeHandler)
where
T :
class
;
}
public
class
WCFServiceInvoker:IServiceInvoker
{
private
static
readonly
ChannelFactoryManager FactoryManager =
new
ChannelFactoryManager();
private
static
readonly
ClientSection ClientSection =
ConfigurationManager.GetSection(
"system.serviceModel/client"
)
as
ClientSection;
public
void
InvokeService<T>(Action<T> invokeHandler)
where
T :
class
{
KeyValuePair<
string
,
string
> endpointNameAddressPair = GetEndpointNameAddressPair(
typeof
(T));
var
arg = FactoryManager.CreateChannel<T>(endpointNameAddressPair.Key, endpointNameAddressPair.Value);
var
obj2 = (ICommunicationObject)arg;
try
{
invokeHandler(arg);
}
finally
{
try
{
if
(obj2.State != CommunicationState.Faulted)
{
obj2.Close();
}
}
catch
{
obj2.Abort();
}
}
}
public
TReslt InvokeService<T, TReslt>(Func<T, TReslt> invokeHandler)
where
T :
class
{
KeyValuePair<
string
,
string
> endpointNameAddressPair = GetEndpointNameAddressPair(
typeof
(T));
var
arg = FactoryManager.CreateChannel<T>(endpointNameAddressPair.Key, endpointNameAddressPair.Value);
var
obj2 = (ICommunicationObject)arg;
try
{
return
invokeHandler(arg);
}
finally
{
try
{
if
(obj2.State != CommunicationState.Closed || obj2.State != CommunicationState.Faulted)
{
obj2.Close();
}
}
catch
{
obj2.Abort();
}
}
}
private
KeyValuePair<
string
,
string
> GetEndpointNameAddressPair(Type serviceContractType)
{
var
configException =
new
ConfigurationErrorsException(
string
.Format(
"No client endpoint found for type {0}. Please add the section <client><endpoint name=\"myservice\" address=\"http://address/\" binding=\"basicHttpBinding\" contract=\"{0}\"/></client> in the config file."
,
serviceContractType));
if
(((ClientSection ==
null
) || (ClientSection.Endpoints ==
null
)) || (ClientSection.Endpoints.Count < 1))
{
throw
configException;
}
foreach
(ChannelEndpointElement element
in
ClientSection.Endpoints)
{
if
(element.Contract == serviceContractType.ToString())
{
return
new
KeyValuePair<
string
,
string
>(element.Name, element.Address.AbsoluteUri);
}
}
throw
configException;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
public
class
ChannelFactoryManager : IDisposable
{
private
static
readonly
Dictionary<Type, ChannelFactory> Factories =
new
Dictionary<Type, ChannelFactory>();
private
static
readonly
object
SyncRoot =
new
object
();
public
void
Dispose()
{
Dispose(
true
);
}
public
virtual
T CreateChannel<T>()
where
T :
class
{
return
CreateChannel<T>(
"*"
,
null
);
}
public
virtual
T CreateChannel<T>(
string
endpointConfigurationName)
where
T :
class
{
return
CreateChannel<T>(endpointConfigurationName,
null
);
}
public
virtual
T CreateChannel<T>(
string
endpointConfigurationName,
string
endpointAddress)
where
T :
class
{
T local = GetFactory<T>(endpointConfigurationName, endpointAddress).CreateChannel();
((IClientChannel)local).Faulted += ChannelFaulted;
return
local;
}
protected
virtual
ChannelFactory<T> GetFactory<T>(
string
endpointConfigurationName,
string
endpointAddress)
where
T :
class
{
lock
(SyncRoot)
{
ChannelFactory factory;
if
(!Factories.TryGetValue(
typeof
(T),
out
factory))
{
factory = CreateFactoryInstance<T>(endpointConfigurationName, endpointAddress);
Factories.Add(
typeof
(T), factory);
}
return
(factory
as
ChannelFactory<T>);
}
}
private
ChannelFactory CreateFactoryInstance<T>(
string
endpointConfigurationName,
string
endpointAddress)
{
ChannelFactory factory =
null
;
factory = !
string
.IsNullOrEmpty(endpointAddress) ?
new
ChannelFactory<T>(endpointConfigurationName,
new
EndpointAddress(endpointAddress)) :
new
ChannelFactory<T>(endpointConfigurationName);
factory.Faulted += FactoryFaulted;
factory.Open();
return
factory;
}
private
void
ChannelFaulted(
object
sender, EventArgs e)
{
var
channel = (IClientChannel)sender;
try
{
channel.Close();
}
catch
{
channel.Abort();
}
}
private
void
FactoryFaulted(
object
sender, EventArgs args)
{
var
factory = (ChannelFactory)sender;
try
{
factory.Close();
}
catch
{
factory.Abort();
}
Type[] genericArguments = factory.GetType().GetGenericArguments();
if
((genericArguments.Length == 1))
{
Type key = genericArguments[0];
if
(Factories.ContainsKey(key))
{
Factories.Remove(key);
}
}
}
protected
virtual
void
Dispose(
bool
disposing)
{
if
(disposing)
{
lock
(SyncRoot)
{
foreach
(Type type
in
Factories.Keys)
{
ChannelFactory factory = Factories[type];
try
{
factory.Close();
}
catch
{
factory.Abort();
}
}
Factories.Clear();
}
}
}
}
|
总结
第一种方式比较常见,第二种方式是我参考另外一个项目中的写法,其中的有一些细节我还没有搞明白,实现了这个功能后还需要再看看这部分代码,再消化消化。由于是直接在项目中,所以没有提供源代码下载,有朋友需要的话我会整理出demo,稍后放出下载链接。