WCF Service Contract之MEPs

 

 

WCF Service ContractMEPs

LazyBee

WCF支持的消息交换模式MEPs(Message Exchange Partten)为三种:请求-响应模式(Request-Replay), 单向模式(One Way),双向模式(Duplex.

[ServiceContract(SessionMode=SessionMode.Required)]

public interface ICalculatorSession

{                        

    [OperationContract(IsOneWay = true)]

    void AddTo(double n);

    [OperationContract]

    double Equals();

}

以上AddTo方法就是单向模式,而Equals就是双向模式。(缺省的IsOneWay的值是false

缺省的消息交换模式就是Request-Replay模式。 

注意:如果指定的方法的消息交换模式是One Way,那么这个方法的返回值必须是void,并且不能有outref的参数。当WCF服务的客户端不应该等到对应的操作的完成并且也不需要处理SOAP错误时,采用这种单向模式。(这个方法的返回值是void并不代表该方法会返回消息给调用者。) 

Duplex模式是客户端和服务之间可以相互独立的使用One WayRequest-Replay进行双向通讯的消息交换模式。 

在使用Duplex进行消息交换的时候,并且客户端调用的是定义为Request-Reply的服务操作,而且在这个服务操作中又调用客户端的另外一个Request-Reply的一个回调方法,这时就会遇到如下错误,你可以根据提示进行修复操作。

错误:This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant(可重入的并发模式) or Multiple on ServiceBehaviorAttribute.

 以下是来自微软的例子中的一个Duplex方式的程序,经过稍加改动(红色的方法是增加的代码):

//服务器端代码
=================================================================
using System;
using System.ServiceModel;
namespace Microsoft.ServiceModel.Samples
{
    
// Define a duplex service contract.
    
// A duplex contract consists of two interfaces.
    
// The primary interface is used to send messages from the client to the service.
    
// The callback interface is used to send messages from the service back to the client.
    
// ICalculatorDuplex allows one to perform multiple operations on a running result.
    
// The result is sent back after each operation on the ICalculatorCallback interface.
    [ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required, CallbackContract=typeof(ICalculatorDuplexCallback))]
    
public interface ICalculatorDuplex
    
{
        [OperationContract(IsOneWay 
= true)]
        
void Clear();
        [OperationContract(IsOneWay 
= true)]
        
void AddTo(double n);
        [OperationContract(IsOneWay 
= true)]
        
void SubtractFrom(double n);
        [OperationContract(IsOneWay 
= true)]
        
void MultiplyBy(double n);
        [OperationContract(IsOneWay 
= true)]
        
void DivideBy(double n);

        [OperationContract]
        String Request_ReplayMethodInDuplex(
int i);
    }


    
// The callback interface is used to send messages from service back to client.
    
// The Result operation will return the current result after each operation.
    
// The Equation opertion will return the complete equation after Clear() is called.
    public interface ICalculatorDuplexCallback
    
{
        [OperationContract(IsOneWay 
= true)]
        
void Result(double result);
        [OperationContract(IsOneWay 
= true)]
        
void Equation(string eqn);
        [OperationContract(IsOneWay
=true)]
        
void ShowMessage(string s);
    }


    
// Service class which implements a duplex service contract.
    
// Use an InstanceContextMode of PerSession to store the result
    
// An instance of the service will be bound to each duplex session
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    
public class CalculatorService : ICalculatorDuplex
    
{
        
double result = 0.0D;  string equation;
        
public CalculatorService()
        
{
            equation 
= result.ToString();
        }


        
public void Clear()
        
{
            Callback.Equation(equation 
+ " = " + result.ToString());
            equation 
= result.ToString();
        }


        
public void AddTo(double n)
        
{
            result 
+= n;
            equation 
+= " + " + n.ToString();
            Callback.Result(result);
        }


        
public void SubtractFrom(double n)
        
{
            result 
-= n;
            equation 
+= " - " + n.ToString();
            Callback.Result(result);
        }


        
public void MultiplyBy(double n)
        
{
            result 
*= n;
            equation 
+= " * " + n.ToString();
            Callback.Result(result);
        }


        
public void DivideBy(double n)
        
{
            result 
/= n;
            equation 
+= " / " + n.ToString();
            Callback.Result(result);
        }


        ICalculatorDuplexCallback Callback
        
{
            
get
            
{
                
return OperationContext.Current.GetCallbackChannel<ICalculatorDuplexCallback>();
            }

        }

        
public string Request_ReplayMethodInDuplex(int i)
        
{
            Callback.ShowMessage(
"Hello World!" + i);
            
return "hello world " + i;
        }

    }

}

 

//Web.Config的内容
===============================================================================================================
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
<system.serviceModel>
    
<services>
      
<service 
          
name="Microsoft.ServiceModel.Samples.CalculatorService"
          behaviorConfiguration
="CalculatorServiceBehavior">
        
<!-- This endpoint is exposed at the base address provided by host: http://localhost/servicemodelsamples/service.svc  -->
        
<endpoint address=""
                    binding
="wsDualHttpBinding"
                    contract
="Microsoft.ServiceModel.Samples.ICalculatorDuplex" />
        
<!-- the mex endpoint is exposed at http://localhost/servicemodelsamples/service.svc/mex -->
        
<endpoint address="mex"
                  binding
="mexHttpBinding"
                  contract
="IMetadataExchange" />
      
</service>
    
</services>

    
<!--For debugging purposes set the includeExceptionDetailInFaults attribute to true-->
    
<behaviors>
      
<serviceBehaviors>
        
<behavior name="CalculatorServiceBehavior">
          
<serviceMetadata httpGetEnabled="True"/>
          
<serviceDebug includeExceptionDetailInFaults="True" />
        
</behavior>
      
</serviceBehaviors>
    
</behaviors>

  
</system.serviceModel>

  
<system.web>
    
<compilation debug="true"/>
  
</system.web>
</configuration>

//Service.svc的内容

===================================================================================

<%@ServiceHost language=c# Debug="true" Service="Microsoft.ServiceModel.Samples.CalculatorService" %>

 如何运行这个服务?

1 首先在IIS中创建一个http://localhost/servicemodelsamples的虚拟目录。

2 如果你是第一次运行WCF Service的话,需要将.svc扩展名和aspnet_isapi.dll关联,具体操作如下:

1.        打开IIS管理器.

2.        右单击 Web Sites 并且选择属性 Properties.

3.        On the Home Directory tab, click 配置Configuration.

4.        In the list of application mappings, verify that the .svc file is mapped to the aspnet_isapi.dll. If the file has not been mapped:

a.                  单击增加按钮.

b.                  Add/Edit Application Extension Mapping 对话框中, 单击浏览按钮

c.                   找到aspnet_isapi.dll并单击Open.

d.                  制定 .svc 扩展名

e.                  确保Check that file exists 选项没有被选中.

f.                    单击确定, 然后再单击确定回到站点属性窗口.

5.        单击确定关闭站点属性窗口。

3然后就可以在IE窗口中测试时候正常运行了。(http://localhost/servicemodelsamples/service.svc.

 如何方便的产生客户端?

由于VS2008自带的WCFTestClient.exe不能测试duplex模式的WCF Service,所以必须手动来产生客户端测试,为了方便应对服务契约的变更,可以在client 的项目属性的Build Events中的Pre-Build event command line的文本框中增加如下命令以帮助自动生成客户端的代理类:

Call "D:\Program Files\Microsoft Visual Studio 9.0\VC\vcvarsall.bat" x86 

svcutil /language:cs /noconfig /out:$(ProjectDir)generatedClient.cs /n:*,Microsoft.ServiceModel.Samples http://localhost/servicemodelsamples/service.svc?wsdl

 客户端的实现文件如下:

============================================================================================================

using System;
using System.ServiceModel;

namespace Microsoft.ServiceModel.Samples
{
    
// The service contract is defined in generatedClient.cs, generated from the service by the svcutil tool.

    
// Define class which implements callback interface of duplex contract
    public class CallbackHandler : ICalculatorDuplexCallback
    
{
        
public void Result(double result)
        
{
            Console.WriteLine(
"Result({0})", result);
        }

        
public void Equation(string eqn)
        
{
            Console.WriteLine(
"Equation({0})", eqn);
        }

        
public void ShowMessage(string s)
        
{
            Console.WriteLine(
"ZXG:" + s);
        }
        
    }


    
class Client
    
{
        
static void Main()
        
{
            
// Construct InstanceContext to handle messages on callback interface
            InstanceContext instanceContext = new InstanceContext(new CallbackHandler());

            
// Create a client
            CalculatorDuplexClient client = new CalculatorDuplexClient(instanceContext);

            Console.WriteLine(
"Press <ENTER> to terminate client once the output is displayed.");
            Console.WriteLine();

            
// Call the AddTo service operation.
            double value = 100.00D;
            client.AddTo(value);
            
// Call the SubtractFrom service operation.
            value = 50.00D;
            client.SubtractFrom(value);
            
// Call the MultiplyBy service operation.
            value = 17.65D;
            client.MultiplyBy(value);
            
// Call the DivideBy service operation.
            value = 2.00D;
            client.DivideBy(value);
            
// Complete equation
            client.Clear();

            Console.WriteLine(client.Request_ReplayMethodInDuplex(
2));
            
            
//Closing the client gracefully closes the connection and cleans up resources
            client.Close();
            Console.ReadLine();
        }

    }

}

以下是客户端的配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  
<system.serviceModel>
    
<client>
      
<endpoint name=""
                address
="http://localhost/servicemodelsamples/service.svc" 
                binding
="wsDualHttpBinding" 
                bindingConfiguration
="DuplexBinding" 
                contract
="Microsoft.ServiceModel.Samples.ICalculatorDuplex" />
    
</client>
    
    
<bindings>
      
<!-- configure a binding that support duplex communication -->
      
<wsDualHttpBinding>
        
<binding name="DuplexBinding" 
                 clientBaseAddress
="http://localhost:8000/myClient/">
        
</binding>
      
</wsDualHttpBinding>
    
</bindings>  
  
</system.serviceModel>
</configuration>

 注意:在客户端代码中Console.WriteLine(client.Request_ReplayMethodInDuplex(2))的这行代码将调用服务端的Request-ReplyRequest_ReplayMethodInDuplex服务操作,并且在Request_ReplayMethodInDuplex的实现中又调用了的客户端的One WayShowMessage回调方法。这个调用运行的非常正常。如果将ShowMessage回调方法变成Request-Replay方式(去掉OperationContract中的IsOneWay=true,或者将其值更改成false),在执行代码Console.WriteLine(client.Request_ReplayMethodInDuplex(2))的时候将出现前面所说的错误。

This operation would deadlock because the reply cannot be received until the current Message completes processing. If you want to allow out-of-order message processing, specify ConcurrencyMode of Reentrant(可重入的并发模式) or Multiple on ServiceBehaviorAttribute.

posted on 2008-01-16 17:14  懒蜜蜂  阅读(1893)  评论(1编辑  收藏  举报