WCF (5) of the in-depth analysis of service contracts [Part I]

I. Introduction

 

In a previous blog post, we created a simple WCF application, which presents the most important concept is WCF endpoint, and endpoint in turn composed of ABC. For Address Address which tells the position of the end WCF service where the customer, while Contract is more important endpoint in a content in the WCF, the contract includes service contracts, data contracts, message contracts and fault contracts in this post will parses the content data under contract, the contract will be on the other three behind the blog post after another introduction.

Second, the problem of extraction operation overloading limit --WCF

   C # language support overloaded operations, but achieved in WCF operation overloading certain restrictions. Wrong operation overloading instance:

1 [ServiceContract(Name = "HellworldService", Namespace = "http://www.Learninghard.com")]
2 public interface IHelloWorld
3 {
4 [OperationContract]
5 string GetHelloWorld();
6
7 [OperationContract]
8 string GetHelloWorld(string name);
9 }

  If you're like heavy-duty operation to achieve the above words, when the open service, you will receive an exception message as shown below:

  However, why WCF does not allow the definition of two identical operating name of it? The reason is simple, because the realization of WCF is XML, which is to be described by a WSDL, and WSDL is based on XML period. In the WSDL, a method of operating a corresponding WCF (operation) tag. We can refer to the following section of XML, it is a WCF interception down from the WSDL in.

<wsdl:import namespace="http://www.Learninghard.com" location="http://localhost:9999/GetHelloWorldService?wsdl=wsdl0"/>
<wsdl:types/>
<wsdl:binding name="BasicHttpBinding_HellworldService" type="i0:HellworldService">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="GetHelloWorldWithoutParam">
<soap:operation soapAction="http://www.Learninghard.com/HellworldService/GetHelloWorldWithoutParam" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="GetHelloWorldWithParam">
<soap:operation soapAction="http://www.Learninghard.com/HellworldService/GetHelloWorldWithParam" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HelloWorldService">
<wsdl:port name="BasicHttpBinding_HellworldService" binding="tns:BasicHttpBinding_HellworldService">
<soap:address location="http://localhost:9999/GetHelloWorldService"/>
</wsdl:port>
</wsdl:service>

  As can be seen from the above code, each represented by a Operation operation XML Element, and each capable of Operation should also have a unique representation of the Operation ID, which is defined by the name attribute. Name property name Operation method is generally used to define the element, so that, if the service contract WCF, comprising two identical operating method name, case violates the predetermined WSDL, which is not used WCF reason overloaded operator .

Third, resolve the problem --WCF achieve operation overloading

   Now, to find a reason not to use WCF in overloaded operation (which must ensure Name attribute Operation element only), this time, in order to achieve operating overloading, there are two ideas: First, two different operating name, mapping the second is to implement a mechanism that enables service contract method name is mapped to a name other methods in order to ensure the uniqueness of the name property. For both Solutions, the first obviously does not work, because different methods were apparently not called heavy-duty operation, so that we can start from the second solution ideas. Fortunately, this solution idea, when implementing Microsoft WCF has helped us to achieve better, we can be defined for each method of operation by the name of a Name property OperationContractAttribute others, generated WSDL will use this alias as the name property operation element, so we only need to define two methods were the same two different aliases can solve the problem of a heavy-duty operation . Since there thinking, Here's a look at the specific code that implements it. Specific service contract implementation method is as follows:

1 namespace Contract
2 {
3 [ServiceContract(Name = "HellworldService", Namespace = "http://www.Learninghard.com")]
4 public interface IHelloWorld
5 {
6 [OperationContract(Name = "GetHelloWorldWithoutParam")]
7 string GetHelloWorld();
8
9 [OperationContract(Name = "GetHelloWorldWithParam")]
10 string GetHelloWorld(string name);
11 }
12 }

  After the above steps will solve the problem of realization of operations overloaded in WCF. Let's done a complete example of heavy-duty operation.

  After the completion of the contract is defined, it is then implemented under the service contract, service contract implementation specific code as follows:

namespace Services
{
public class HelloWorldService : IHelloWorld
{
public string GetHelloWorld()
{
return "Hello World";
}

public string GetHelloWorld(string name)
{
return "Hello " + name;
}
}
}

  Subsequently, to continue to provide a host for the WCF service environment, where the first console application to the host application to implement specific implementation code and configuration code is as follows:

WCFServiceHostByConsoleApp namespace
{
class Program
{
static void the Main (String [] args)
{
the using (= the ServiceHost new new Host the ServiceHost (typeof (Services.HelloWorldService)))
{
host.Opened the delegate + =
{
Console.WriteLine ( "service is turned on, press any key to continue .... ");
};

host.Open ();
Console.ReadLine ();
}
}
}
}

  End of the corresponding service profile is as follows:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="HelloWorldSerBehavior">
<serviceMetadata httpGetEnabled="True" httpGetUrl="http://localhost:9999/GetHelloWorldService"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name ="Services.HelloWorldService" behaviorConfiguration="HelloWorldSerBehavior">
<endpoint address="http://localhost:9999/GetHelloWorldService" binding="basicHttpBinding" contract="Contract.IHelloWorld"/>
</service>
</services>
</system.serviceModel>
</configuration>

  Next, let's create a client to call a WCF service method through a proxy object. First Run as administrator WCFServiceHostByConsoleApp.exe file to open the service, WCF service after a successful open, right in the corresponding client Add Service Reference, enter the address of the WCF service in the open window of the Add Service Reference: http: // localhost: 9999 / after GetHelloWorldService, point the OK button to add the service reference added successfully, VS integrated code generation tools will help us generate the corresponding proxy class. Next, we can create access to the WCF through a proxy object. Specific client implementation code is shown below:

Client3 namespace. 1
2 {
. 3 Program class
. 4 {
. 5 static void the Main (String [] args)
. 6 {
. 7 the using (HellworldServiceClient helloWorldProxy new new HellworldServiceClient = ())
. 8 {
. 9 Console.WriteLine ( "service returns the result: {0} ", helloWorldProxy.GetHelloWorldWithoutParam ());
10 Console.WriteLine (" service returns the result: {0} ", helloWorldProxy.GetHelloWorldWithParam (" Learning Hard "));
. 11}
12 is
13 is Console.ReadLine ();
14}
15 }
16}

  In this way, when you run the client program (be careful not to close the WCF service host program), you will see the results shown below.

  In the above client implementation code from the client's point of view, we do not know that we are overloaded method call, as we call obviously two different method name, which is obviously not the final we want to achieve the effect, at this time, there are two ways to achieve the client to call by the same method name.

  • The first way is to manually edit the generated service contract and service proxy class code to support the heavy load operation, the service agents and service contracts modified code is shown below:

namespace Client3.ServiceReference {


[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://www.Learninghard.com", ConfigurationName="ServiceReference.HellworldService")]
public interface HellworldService {

// 把自动生成的方法名GetHelloWorldWithoutParam修改成GetHelloWorld
[System.ServiceModel.OperationContractAttribute(Name = "GetHelloWorldWithoutParam", Action = "http://www.Learninghard.com/HellworldService/GetHelloWorldWithoutParam", ReplyAction = "http://www.Learninghard.com/HellworldService/GetHelloWorldWithoutParamResponse")]
string GetHelloWorld();

// // the name of the method to automatically generate modified GetHelloWorldAsync GetHelloWorldWithoutParamAsync
[System.ServiceModel.OperationContractAttribute(Name = "GetHelloWorldWithoutParam", Action="http://www.Learninghard.com/HellworldService/GetHelloWorldWithoutParam", ReplyAction="http://www.Learninghard.com/HellworldService/GetHelloWorldWithoutParamResponse")]
System.Threading.Tasks.Task<string> GetHelloWorldAsync();

[System.ServiceModel.OperationContractAttribute(Name = "GetHelloWorldWithParam", Action="http://www.Learninghard.com/HellworldService/GetHelloWorldWithParam", ReplyAction="http://www.Learninghard.com/HellworldService/GetHelloWorldWithParamResponse")]
string GetHelloWorld(string name);

[System.ServiceModel.OperationContractAttribute(Name = "GetHelloWorldWithParam", Action = "http://www.Learninghard.com/HellworldService/GetHelloWorldWithParam", ReplyAction = "http://www.Learninghard.com/HellworldService/GetHelloWorldWithParamResponse")]
System.Threading.Tasks.Task<string> GetHelloWorldAsync(string name);
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public interface HellworldServiceChannel : Client3.ServiceReference.HellworldService, System.ServiceModel.IClientChannel {
}

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class HellworldServiceClient : System.ServiceModel.ClientBase<Client3.ServiceReference.HellworldService>, Client3.ServiceReference.HellworldService {

public HellworldServiceClient() {
}

public HellworldServiceClient(string endpointConfigurationName) :
base(endpointConfigurationName) {
}

public HellworldServiceClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}

public HellworldServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}

public HellworldServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress) {
}

public string GetHelloWorld() {
return base.Channel.GetHelloWorld();
}

public System.Threading.Tasks.Task<string> GetHelloWorldAsync() {
return base.Channel.GetHelloWorldAsync();
}

public string GetHelloWorld(string name) {
return base.Channel.GetHelloWorld(name);
}

public System.Threading.Tasks.Task<string> GetHelloWorldAsync(string name) {
return base.Channel.GetHelloWorldAsync(name);
}
}
}

  In this case, the client implementation code is shown below:

Program class
{
static void the Main (String [] args)
{
the using (HellworldServiceClient helloWorldProxy new new HellworldServiceClient = ())
{
Console.WriteLine ( "service returns the result: {0}", helloWorldProxy.GetHelloWorld ());
Console.WriteLine ( "service returns the result: {0}", helloWorldProxy.GetHelloWorld ( "Learning Hard"));
}

Console.ReadLine ();
}
}

  At this point, the results after the client is running and operating results as above, there is not a map.

  • The second way is to realize his client proxy classes, rather than generating tool by the VS code. Re proxy Class specific implementation code code as follows:

1 using Contract;
2 using System.ServiceModel;
3 namespace Client2
4 {
5 class HellworldServiceClient : ClientBase<IHelloWorld>, IHelloWorld
6 {
7 #region IHelloWorld Members
8 public string GetHelloWorld()
9 {
10 return this.Channel.GetHelloWorld();
11 }
12
13 public string GetHelloWorld(string name)
14 {
15 return this.Channel.GetHelloWorld(name);
16 }
17 #endregion
18 }
19 }

  At this time, the client implementation code and configuration files are as follows:

Client2 namespace
{
class Program
{
static void the Main (String [] args)
{
the using (var = new new HellworldServiceClient Proxy ())
{
// custom proxy class calls the service method to access
Console.WriteLine ( "service returns the result is: {0} ", proxy.GetHelloWorld ());
Console.WriteLine (" service returns the result: {0} ", proxy.GetHelloWorld (" Learning Hard "));
}

Console.Read ();
}
}
}

  Corresponding to the configuration file as follows:

<configuration>
<system.serviceModel>
<client>
<endpoint address="http://localhost:9999/GetHelloWorldService"
binding ="basicHttpBinding"
contract ="Contract.IHelloWorld"/>
</client>
</system.serviceModel>
</configuration>

Fourth, use Windows Service to boarding WCF service

  In a previous blog post, we introduced the WCF hosted in IIS and console application, whereas WCF service can be hosted in any application, such as WPF, WinForms and Windows Services. Here again realize how at the service in Windows Services in boarding WCF. The following step by step to achieve this purpose.

  • Step 1: Create a Windows Service project, specific steps to add Right Solution -> Add -> New Item, select Windows Service template in the template has been installed, the icon shown as follows:

  • Step Two: After you add a Windows service, you will see the directory structure shown below.

  Service1.cs then modify the corresponding file to achieve the following code:

1 // 修改类名
2 public partial class WindowsService : ServiceBase
3 {
4 public WindowsService()
5 {
6 InitializeComponent();
7 }
8
9 public ServiceHost serviceHost = null;
10
11 // 启动Windows服务
12 protected override void OnStart(string[] args)
13 {
14 if (serviceHost != null)
15 {
16 serviceHost.Close();
17 }
18
19 serviceHost = new ServiceHost(typeof(Services.HelloWorldService));
20 serviceHost.Open();
21 }
22
23 // 停止Windows服务
24 protected override void OnStop()
25 {
26 if (serviceHost != null)
27 {
28 serviceHost.Close();
29 serviceHost = null;
30 }
31 }
32 }

  对应的配置文件代码如下所示:

<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="WindowsServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="Services.HelloWorldService" behaviorConfiguration="WindowsServiceBehavior">
<endpoint address=""
binding="wsHttpBinding" bindingConfiguration="" name="WindowsService"
contract="Contract.IHelloWorld" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8888/WCFServiceHostByWindowsService"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>

  • 第三步:在WindowsService的设计界面,右键选择添加安装程序,具体操作如下图所示。

  添加安装程序之后,会多出一个ProjectInstaller.cs文件,然后在其设计页面修改ServiceProcessInstaller和ServiceInstaller对象属性,具体设置的值如下图所示:

  经过上面的步骤,程序的代码就都已经全部实现了,接下来要做的是安装Windows 服务和启动Windows服务。

  首先是安装Windows服务:以管理员身份运行VS2012开发命令提示,进入项目的对应的exe所在的文件夹,这里的指的是WindowsServiceHost.exe所在的文件夹,然后运行 “installutil WindowsServiceHost.exe”命令,命令运行成功后,你将看到如下所示的运行结果:

  安装成功之后,你可以运行 “net start HelloWorldServiceHost” 命令来启动服务。因为开始设置服务的名称是HelloWorldServiceHost。你也可以通过Services中来手动启动服务,启动成功之后,你将在服务窗口看到启动的服务。具体效果如下图所示。

  服务启动后,在客户端中同样是添加服务引用的方式来添加服务引用,在添加服务引用窗口输入地址:http://localhost:8888/WCFServiceHostByWindowsService。点击确定按钮。添加服务引用成功后,对应的客户端调用代码如下所示:

1 namespace Client
2 {
3 class Program
4 {
5 static void Main(string[] args)
6 {
7 using (var proxy = new HellworldServiceClient())
8 {
9 // 通过代理类来调用进行服务方法的访问
10 Console.WriteLine("服务返回的结果是: {0}", proxy.GetHelloWorldWithoutParam());
11 Console.WriteLine("服务返回的结果是: {0}", proxy.GetHelloWorldWithParam("Learning Hard"));
12 }
13
14 Console.Read();
15 }
16
17 }
18 }

  此时的运行结果和前面客户端返回的运行结果是一样的。

五、总结

  到这里,本文的内容就介绍结束了,本文主要解决了在WCF中如何实现操作重载的问题,实现思路可以概括为利用OperationContractAttribute类的Name属性来实现操作重载,而客户端的实现思路可以概括为重新代理类,利用信道Channel类带对对应的服务方法进行调用,最后,实现了把WCF服务寄宿在Windows Services中,这样WCF服务可以作为服务在机器上设置开机启动或其他方式启动了。在下一篇博文中将分享WCF服务契约的继承实现。

 

转自:https://www.cnblogs.com/zhili/p/MSMQ.html,作者:Learning hard。

如有侵权,请联系我删除!

Guess you like

Origin blog.csdn.net/IT_0802/article/details/91501692