首先发布了一个名为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调用服务: