Dynamics AX 2012 R2 Service Middle Tier WCF WCF转发

参考了蒋金楠老师08年的文章。好吧,那时候我才大二、大三,大神果然是大神。 http://www.cnblogs.com/artech/archive/2008/09/01/1280939.html

在使用AX2012 AIF服务的时候,有一个问题一直困扰着我,那就是访问权限的认证。

众所周知AX2012的权限认证是基于AD的,但有些客户端机器的AD账户,并没有AX2012的访问权限,甚至没有加入AD中(如PDA客户端)。

按照微软的最佳实践,应该使用声明账户和可信中介这样两样技术,来实现客户端-》WCF中间层(可信中介)-》AIF服务。这样做好处,是客户端不用加入AD中,并且可以将客户端登陆账户权限的管理,纳入到AX中。

如果使用AX2012的自定义服务,会在AOS中有多个数据契约和服务操作,如果一个一个再在WCF中间层去重复造轮子,有些麻烦。能不能将中间层进行简化成,只是将客户端请求转发给AIF,将AIF的响应返回给客户端呢?

答案肯定是可以的咯,不然我也不会写这篇文章。具体可以参考上面蒋金楠老师博文的链接。

1新建一个WCF类库程序,代码如下:

namespace MessageInterceptor

{

[ServiceContract]

public interface IIntercept

{

[OperationContract(Action ="*", ReplyAction="*")]

Message Intercept(Message request);

}

 

[ServiceBehavior(UseSynchronizationContext = false, AddressFilterMode = AddressFilterMode.Any)]

public class InterceptService : IIntercept

{

 

public System.ServiceModel.Channels.Message Intercept(System.ServiceModel.Channels.Message request)

{


										using (ChannelFactory<IIntercept> channelFactory = new
										ChannelFactory<IIntercept>("calculateService"))

{

IIntercept interceptor = channelFactory.CreateChannel();

using (interceptor as IDisposable)

{

MessageBuffer requstBuffer = request.CreateBufferedCopy(int.MaxValue);

Message response = interceptor.Intercept(requstBuffer.CreateMessage());

MessageBuffer responseBuffer = response.CreateBufferedCopy(int.MaxValue);

Console.WriteLine(string.Format("Request:{0}{1}{0}", Environment.NewLine, request));

Console.WriteLine(string.Format("Response:{0}{1}{0}", Environment.NewLine, response));

return responseBuffer.CreateMessage();

}

 

}

}

}

 

}

该数据契约有以下两个特点:

  • Intercept的参数和返回值都是Message对象。
  • OperationActionReplyAction*

无论参数的个数,类型,次序是怎样的,但WCF的调用最终是基于Message的,也就是参数或返回值最终都会呈现为Message对象。

Operation Selection的匹配规则是:Contract Namespacedefaulthttp://tempuri.org/Contract NamedefaultInterface name/Actiondefaultmethod name= action in SOAP header如果将Action设为"*",则意味着该服务的调用,无路SOAP Headeraction是什么,都将交付Intercept来处理。

ChannelFactory构造函数的参数,是配置中,Client节点的endpoint的name值。

AddressFilterMode = AddressFilterMode.Any:在上面我们提到过,ChannelDispatcher在选择EndpointDispacher的时候是基于两个Message FilterAddress FilterContract Filter。也就是说,ChannelDispatcher通过这两个Filter选择合适Endpoint。在默认的情况下,Address Filter是根据SOAPTo Message HeaderURI来进行栓选的,所以需要EndpointAddressTo Header中的Addres完全匹配。但是在我们CalculateService的例子中,由于Client最终是访问的时CalculateService,所以生成的SOAPTo Headler的地址是CalculateService的地址:http://127.0.0.1:9999/calculateservice,而我们需要是用InterceptService 来处理该请求,Address Filtering肯定是不能通过的。好在我们可以在ServiceBehavior设置AddressFilterMode 来改变Address Filtering的方式。AddressFilterMode = AddressFilterMode.Any意味着,Address Filtering会被忽略。

CreateBufferedCopy:可能有人会奇怪,为什么不对request messageresponse message进行直接操作(将他们显示在TextBox上)?这是应为MessageWCF有一个特殊的处理机制:只有MessageStateCreated的时候,才能获取MessageBody的内容,否则会抛出异常。而我们在对Message进行相应操作的时候,会改变Message StateRead,Written,Copied,Closed)。所以对response message来讲,对message的显示实际上将Sate改为Read,如何将response message直接返回到client,对该message的读取操作将是不允许的,所以先调用CreateBufferedCopy创建该message的一个memory buffer,最有返回的时通过该buffer重新创建的Message

2新建Windows服务,作为上面WCF服务的宿主程序。

代码如下:

namespace MiddleTierWcfWindowsService

{

public partial class Service : ServiceBase

{

ServiceHost host;

 

public Service()

{

InitializeComponent();

}

 

protected override void OnStart(string[] args)

{

host = new ServiceHost(typeof(MessageInterceptor.InterceptService));

host.Open();

}

 

protected override void OnStop()

{

host.Close();

}

}

}

3Windows服务添加应用程序配置App.config

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<system.serviceModel>

<bindings>

<netTcpBinding>

<binding name="aaaaaaaaa" />

</netTcpBinding>

</bindings>

<client>

<endpoint address="net.tcp://bbbbbbbbb"

binding="netTcpBinding" bindingConfiguration="aaaaaaaaa"

contract="eeeeeeeee" name="ccccccccc">

 

</endpoint>

</client>

<services>

<service name="HHHHHHHHH">

<endpoint binding="netTcpBinding" bindingConfiguration="aaaaaaaaa"

contract="eeeeeeeee"

address="net.tcp://ddddddddd"/>

 

</service>

 

</services>

</system.serviceModel>

</configuration>

AAAAAAAAA:这里我使用了NetTcpBinding,作为客户端-WCF中间层(可信中介)-AIF服务端的终结点的binding,你也可以选择适合自己的。

BBBBBBBBB:这里是AIF中,服务的URI。

CCCCCCCCC:这里是上面WCF中间层类库接口的实现中,ChannelFactory构造函数的参数,本例中是calculateService

DDDDDDDDD:这里是WCF中间层的终结点地址。

EEEEEEEEE:是client和service的contract契约,都使用的是WCF中间层类库中的接口的"命名空间+接口名"。本例子中是MessageInterceptor.IIntercept

HHHHHHHHH:是接口的实现。本例中是MessageInterceptor.InterceptService

4设置Windows服务的Service设计视图的ServiceName,用于在日志中显示。

5在Windows服务的Service设计视图上点右键,添加安装程序。

6在新添加的安装程序中,将serviceProcessInstaller1的Account设为LocalService。即,启动时,以本地服务方式登录。

7将serviceInstaller1的Description,DisplayName,ServiceName,都设置下,用于在系统服务中显示。

8安装生成的服务。

9在系统服务中,找到该服务,右键,属性,登录,登录为AX中的系统服务账户,如BC账户,密码为BC账户的密码。

10客户端的配置文件里,新增一个终结点行为。

<system.serviceModel>

<behaviors>

<endpointBehaviors>

<behavior name="FFFFFFFFF">

<clientVia viaUri="net.tcp://DDDDDDDDD" />

</behavior>

</endpointBehaviors>

</behaviors>

<bindings>

<netTcpBinding>

<binding name="AAAAAAAAA" />

</netTcpBinding>

</bindings>

<client>

<endpoint address="net.tcp://BBBBBBBBB"

behaviorConfiguration="FFFFFFFFF"

binding="netTcpBinding" bindingConfiguration="AAAAAAAAA"

contract="GGGGGGGGG" name="CCCCCCCCC">

 

</endpoint>

</client>

</system.serviceModel>

 

A、B、C、D、如上面所述。

FFFFFFFFF:终结点行为的名称。

GGGGGGGGG:AIF服务中,"命名空间+数据契约"的名字。本例为,MessageInterceptor.ICalculate

 

 

11结束

至此,WCF中间层的转发,已经设置好了。

在客户端,可以以context.LogonAsUser=aaa@bbb.com;的方法,用AX中的账户进行操作,系统会检查该账户的数据权限。在表中,记录的createdBy等系统字段,会显示为该AD用户的别名。

如果使用客户端的人员为一线工人,可能没有AX账户,所以此处建议不设置。表中的createdBy等系统字段,记录是BC账户的别名,使用的是BC的数据权限。

posted @ 2014-06-09 17:43  Reinhard_Hsu  阅读(587)  评论(0编辑  收藏  举报