首先釋出了一個名為PersonService的WCF服務。服務契約如下:

[ServiceContract]
public interface IPersonService
{
[OperationContract]
string GetPersonName(string name);
[OperationContract]
Person GetPerson(Person person);
}
[DataContract]
public class Person
{
[DataMember]
public int Age { get; set; }
[DataMember]
public string Address { get; set; }
[DataMember]
public string Name { get; set; }
}
Contract
第一種調用WCF服務的方式是添加服務引用。建立一個控制台項目,并在引用處右鍵選擇添加服務引用。輸入服務的位址,如下圖所示。
在控制台中,執行個體化PersonService.PersonServiceClient,并調用服務中所包含的方法即可。

class Program
{
static void Main(string[] args)
{
PersonService.PersonServiceClient client = new
PersonService.PersonServiceClient();
Console.WriteLine(client.GetPersonName("Lily"));
PersonService.Person person = client.GetPerson(new PersonService.Person() {
Age = 20, Address = "Nanshan district", Name = "Lily" });
Console.WriteLine("Name:" + person.Name + "\r\n" + "Age:" + person.Age +
"\r\n" + "Address:" + person.Address);
Console.Read();
}
}
Console
第二種方法是使用SvcUtil工具生成服務代理。首先在菜單欄-工具-外部工具中,添加SvcUtil工具,一般其位址是:
C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\SvcUtil.exe
選中“使用輸出視窗”和“提示輸入參數”
添加好工具後,點選SvcUtil,在參數欄輸入服務的位址,點選“确定”,就會生成一個的PersonService.cs檔案和一個output.config檔案。将output.config改名為App.config,同PersonService.cs一起複制到控制台應用目錄中

class Program
{
static void Main(string[] args)
{
PersonServiceClient client = new PersonServiceClient();
Console.WriteLine(client.GetPersonName("Lily"));
WcfInvokeDemo.Person person = client.GetPerson(new WcfInvokeDemo.Person() {
Age = 20, Address = "Nanshan district", Name = "Lily" });
Console.WriteLine("Name:" + person.Name + "\r\n" + "Age:" + person.Age +
"\r\n" + "Address:" + person.Address);
Console.Read();
}
}
注:需要在控制台項目中引用System.ServiceModel.dll和System.Runtime.Serialization.dll
第三種方法是使用ChannelFactory,即通道工廠連接配接用戶端和伺服器端的終結點。首先在用戶端需要知道服務契約(ServiceContract)。但是這裡有個問題,關于DataContract。如果存在ComplexType,即本例中的Person類,是需要編譯成一個第三方的assembly,并分别被伺服器端和用戶端引用。
調用CreateChannel()方法獲得代理的引用,使用代理的方法。最後,通過将代理強制轉換為IDisposable類型,調用Dispose()方法關閉代理。也可以将代理強制轉換為ICommunicationObject類型,通過調用Close()方法關閉代理。

class Program
{
static void Main(string[] args)
{
string uri = @"http://localhost:7003/WcfInvokeDemo.PersonService.svc";
EndpointAddress endpointAddress = new EndpointAddress(uri);
BasicHttpBinding basicBind = new BasicHttpBinding();
ChannelFactory<IPersonService> factory = new
ChannelFactory<IPersonService>(basicBind, endpointAddress);
IPersonService channel = factory.CreateChannel();
// 可以使用IDisposable
using(channel as IDisposable)
{
Console.WriteLine(channel.GetPersonName("Lily"));
SharedAssembly.Person person = new SharedAssembly.Person() { Address =
"Nanshan district", Age = 20, Name = "Lily" };
SharedAssembly.Person _person = channel.GetPerson(person);
Console.WriteLine("Name:" + _person.Name + "\r\n" + "Age:" + _person.Age +
"\r\n" + "Address:" + _person.Address);
}
// 也可以使用Close()
IPersonService channel1 = factory.CreateChannel();
Console.WriteLine(channel1.GetPersonName("Spencer"));
ICommunicationObject channel2 = channel1 as ICommunicationObject;
channel2.Close();
Console.Read();
}
}
ChannelFactory
第四種方法使用反射讀取服務的中繼資料,即
http://larissa-pc:7003/WcfInvokeDemo.PersonService.svc?wsdl
是以需要保證<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>都是True。總之,這個方法非常複雜。
先建立代理的執行個體

public static class InvokeService
{
// 建立代理執行個體
public static object GetProxyInstance(ref CompilerResults compilerResults, string
uri, string contractName)
{
object proxyInstance = null;
Uri address = new Uri(uri);
MetadataExchangeClientMode mexMode = MetadataExchangeClientMode.HttpGet;
MetadataExchangeClient metadataExchangeClient = new
MetadataExchangeClient(address, mexMode);
metadataExchangeClient.ResolveMetadataReferences = true;
MetadataSet metadataSet = metadataExchangeClient.GetMetadata();
WsdlImporter wsdlImporter = new WsdlImporter(metadataSet);
Collection<ContractDescription> contracts = wsdlImporter.ImportAllContracts();
ServiceEndpointCollection allEndPoints = wsdlImporter.ImportAllEndpoints();
ServiceContractGenerator serviceContractGenerator = new
ServiceContractGenerator();
var endpointsForContracts = new Dictionary<string,
IEnumerable<ServiceEndpoint>>();
foreach(ContractDescription contract in contracts)
{ serviceContractGenerator.GenerateServiceContractType(contract);
endpointsForContracts[contract.Name] = allEndPoints.Where(x =>
x.Contract.Name == contract.Name).ToList();
}
CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions();
codeGeneratorOptions.BracingStyle = "C";
CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("C#");
CompilerParameters compilerParameters = new CompilerParameters(new string[] {
"System.dll", "System.ServiceModel.dll", "System.Runtime.Serialization.dll" });
compilerParameters.GenerateInMemory = true;
compilerResults = codeDomProvider.CompileAssemblyFromDom(compilerParameters,
serviceContractGenerator.TargetCompileUnit);
if (compilerResults.Errors.Count == 0)
{
Type proxyType = compilerResults.CompiledAssembly.GetTypes().First(t =>
t.IsClass && t.GetInterface(contractName) != null &&
t.GetInterface(typeof(ICommunicationObject).Name) != null);
ServiceEndpoint serviceEndpoint =
endpointsForContracts[contractName].First();
proxyInstance =
compilerResults.CompiledAssembly.CreateInstance(proxyType.Name, false,
BindingFlags.CreateInstance,
null, new object[] { serviceEndpoint.Binding, serviceEndpoint.Address
}, CultureInfo.CurrentCulture, null);
}
return proxyInstance;
}
}
CreatInstance

class Program
{
static void Main(string[] args)
{
// 這裡給出的是WSDL的位址
// 我現在嚴重懷疑這種方法就是SvcUtil工具的實作原理?
string uri = @"http://localhost:7003/WcfInvokeDemo.PersonService.svc?wsdl";
string contractName = "IPersonService";
CompilerResults compilerResults = null;
// 擷取代理執行個體,本例中即為PersonServiceClient
var proxyInstance = InvokeService.GetProxyInstance(ref compilerResults, uri,
contractName);
// 輸出proxyInstance的類型名稱,為“PersonServiceClient"
//Console.WriteLine(proxyInstance.GetType().Name);
string operationName = "GetPersonName";
string operationName1 = "GetPerson";
// 擷取PersonServiceClient中名為“GetPersonName"的方法
//MethodInfo methodInfo = proxyInstance.GetMethod(operationName);
MethodInfo methodInfo1 = proxyInstance.GetType().GetMethod(operationName1);
// 擷取方法所需的參數
//ParameterInfo[] parameterInfo = methodInfo.GetParameters();
ParameterInfo[] parameterInfo1 = methodInfo1.GetParameters();
/*foreach(var item in parameterInfo)
{
// 在本例中,參數為name,類型為String,是以輸出為name:String
Console.WriteLine(item.Name + ":" + item.ParameterType.Name);
}
foreach(var item in parameterInfo1)
// 再試一下,如果為ComplexType,會輸出person:Person
Console.WriteLine(item.Name + ":" + item.ParameterType.Name);*/
// 擷取ComplexType的屬性,這裡是因為隻有一個參數,即為person
var properties = parameterInfo1[0].ParameterType.GetProperties();
foreach (var item in properties)
/**
輸出的結果為:
ExtensionData: ExtensionDataObject
Address:String
Age:Int32
Name:String
其中,ExtensionDataObject類用于存儲已經通過添加新成員擴充的版本化資料協定中的資料
用于解決兩個版本的資料契約之間某個參數被添加或者删除的問題
詳情可見: https://www.cnblogs.com/CharlesLiu/archive/2010/02/09/1666605.html
**/
Console.WriteLine(item.Name + ":" + item.PropertyType.Name);
// 如果調用GetPerson方法,需要給參數的屬性指派
// 建立參數的執行個體
var parameter =
compilerResults.CompiledAssembly.CreateInstance(parameterInfo1[0].ParameterType.FullName,
false, BindingFlags.CreateInstance, null, null, null, null);
//Console.WriteLine(parameter.GetType().Name);
// 給參數的屬性指派時,一定要根據順序來
properties[1].SetValue(parameter, "Home");
properties[2].SetValue(parameter, 20);
properties[3].SetValue(parameter, "Lily");
object[] operationParameters = new object[] { parameter };
// 調用methodInfo1方法
var ans = methodInfo1.Invoke(proxyInstance, operationParameters);
// 輸出傳回值的每個屬性的值
foreach(var item in ans.GetType().GetProperties())
{
Console.WriteLine(item.Name + ":" + item.GetValue(ans));
}
Console.Read();
}
}
此外,還有一種方式,就是把服務釋出為RESTful樣式,直接通過URL通路服務。在服務契約中需要使用WEBGET或者WEBINVOKE,并且使用webHttpBinding綁定方式。

[OperationContract]
[WebGet(UriTemplate="person/name={name}",
RequestFormat =WebMessageFormat.Json,
ResponseFormat =WebMessageFormat.Json,
BodyStyle =WebMessageBodyStyle.WrappedRequest)]
string GetPersonName(string name);

<endpoint address="" binding="webHttpBinding" behaviorConfiguration="WebBehavior"
contract="WcfInvokeDemo.IPersonService">
...
<endpointBehaviors>
<behavior name="WebBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
Config
注:本例中隻編寫了一個簡單的WebGet。關于WebGet和WebInvoke,網上有很多介紹,這裡就不再詳述了。
這樣釋出後,可以直接在浏覽器中通過URL調用服務: