調用WCF服務的幾種方式


首先發布了一個名為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服務的方式是添加服務引用。新建一個控制台項目,並在引用處右鍵選擇添加服務引用。輸入服務的地址,如下圖所示。
9e29c7ada4d94840cab1977cdc3147fc.png  

在控制台中,實例化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
選中“使用輸出窗口”和“提示輸入參數”

ff66d36e523b22c60bfdb816a62aef4a.png  

添加好工具后,點擊SvcUtil,在參數欄輸入服務的地址,點擊“確定”,就會生成一個的PersonService.cs文件和一個output.config文件。將output.config改名為App.config,同PersonService.cs一起復制到控制台應用目錄中

e8bc312f5c6ddc2b3ebb462b39c1f6a1.png  

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<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();
        }
    }
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
<endpoint address="" binding="webHttpBinding" behaviorConfiguration="WebBehavior" 
contract="WcfInvokeDemo.IPersonService">
...
<endpointBehaviors>
                       <behavior name="WebBehavior">
                              <webHttp/>
                       </behavior>
               </endpointBehaviors>
Config

注:本例中只編寫了一個簡單的WebGet。關於WebGet和WebInvoke,網上有很多介紹,這里就不再詳述了。

這樣發布后,可以直接在瀏覽器中通過URL調用服務:bce83e41ca6be9dfa9a4535b4e2fe6a8.png  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM