首先發布了一個名為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; } }

在控制台中,實例化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(); } }
第二種方法是使用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(); } }
第四種方法使用反射讀取服務的元數據,即
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; } }

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>
注:本例中只編寫了一個簡單的WebGet。關於WebGet和WebInvoke,網上有很多介紹,這里就不再詳述了。
這樣發布后,可以直接在瀏覽器中通過URL調用服務: