调用WCF服务的几种方式
阅读原文时间:2023年07月09日阅读:1

首先发布了一个名为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();
        }
    }

Console

注:需要在控制台项目中引用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 factory = new
ChannelFactory(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

因此需要保证都是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 contracts = wsdlImporter.ImportAllContracts();
            ServiceEndpointCollection allEndPoints = wsdlImporter.ImportAllEndpoints();
            ServiceContractGenerator serviceContractGenerator = new
ServiceContractGenerator();
            var endpointsForContracts = new Dictionary>();
            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();
        }
    }

Console

此外,还有一种方式,就是把服务发布为RESTful样式,直接通过URL访问服务。在服务契约中需要使用WEBGET或者WEBINVOKE,并且使用webHttpBinding绑定方式。

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

Contract



                                                                                              

Config

注:本例中只编写了一个简单的WebGet。关于WebGet和WebInvoke,网上有很多介绍,这里就不再详述了。

这样发布后,可以直接在浏览器中通过URL调用服务:

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章