[AX]AX2012 AIF(四):文档服务应用实例
在这篇文章我们将通过文档服务来创建、读取销售订单等来演示如何使用文档服务。
实例一:在这个例子中我们使用文件系统适配器端口,通过XML文档创建销售订单。首先在Inboud ports创建一个文件适配器的增强型端口,选择一个网络共享目录作为URI(我的配置\\dax\AIFData\AIFIn),注意不要使用本地目录,否则会提示找不到相应目录的错误,此外设置目录的权限允许AOS服务账号读写。在适配器“config”窗口中选择一个用户作为“User default for Administration group”,文件系统适配器将文件的owner作为发送消息的用户,在启用UAC的系统上文件的Owner可能会被设置为管理员组因而无法判断具体是谁创建了XML文档,因此这里选择一个用户作为这些文档的默认Owner。另外我们勾选“Response address”,同样设置为文件系统适配器,选择一个网络共享目录为输出目录(我的配置\\dax\AIFData\AIFOut)。在Service operations我们选择 SalesSalesOrderService.create、SalesSalesOrderService.read、SalesSalesOrderService.findKeys操作,在“Data policies”中激活所有的字段。其他配置看自己的需求配置,最后激活这个端口。
下面要做的就是准备好创建销售订单的消息XML,下面是一个样例:
<?xml version="1.0" encoding="utf-8" ?> <Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"> <Header> <MessageId>{5603D03A-4380-404D-9F27-738BE0FEA13E}</MessageId> <Action>http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/create</Action> </Header> <Body> <MessageParts> <SalesOrder xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder"> <SalesTable class="entity"> <CustAccount>9001</CustAccount> <DeliveryDate>2013-03-11</DeliveryDate> <PurchOrderFormNum>PO</PurchOrderFormNum> <ReceiptDateRequested>2013-02-11</ReceiptDateRequested> <SalesLine class="entity"> <ItemId>1206</ItemId> <SalesQty>123</SalesQty> <SalesUnit>ea</SalesUnit> </SalesLine> </SalesTable> </SalesOrder> </MessageParts> </Body> </Envelope>
注意消息头中<Action>中我们用的SalesOrderService/create请求,将这个XML保存(文件名so_create_01.xml)到入站目录\\dax\AIFData\AIFIn,这个时候什么都不会发生,我们还缺少一个Batch job来运行AifGatewayReceiveService, AifGatewaySendService, AifInboundProcessingService, 和AifOutboundProcessingService这个几个服务,如果觉得麻烦用下面的Job手工执行一次吧:
static void AifProcessingClasses(Args _args) { AifGatewayReceiveService receive = new AifGatewayReceiveService(); AifInboundProcessingService inbound = new AifinboundProcessingService(); AifOutboundProcessingService outbound = new AifOutboundProcessingService(); AifGatewaySendService send = new AifGatewaySendService(); receive.run(); inbound.run(); outbound.run(); send.run(); }
一切正常的话你会发现so_create_01.xml消失,在\\dax\AIFData\AIFOut会多出来一个xml,这就是新创建销售订单的响应结果,形如:
<?xml version="1.0" encoding="UTF-8"?> <Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"> <Header> <MessageId>{D630C246-3C62-42AE-BE69-4BFE609E8470}</MessageId> <Action>http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/create</Action> <RequestMessageId>{5603D03A-4380-404D-9F27-738BE0FEA13E}</RequestMessageId> </Header> <Body> <MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"> <EntityKeyList xmlns="http://schemas.microsoft.com/dynamics/2006/02/documents/EntityKeyList"> <EntityKey xmlns="http://schemas.microsoft.com/dynamics/2006/02/documents/EntityKey"> <KeyData> <KeyField> <Field>SalesId</Field> <Value>00000009_050</Value> </KeyField> </KeyData> </EntityKey> </EntityKeyList> </MessageParts> </Body> </Envelope>
在AR下的销售订单列表中你也能看到这个新创建的销售订单。
实例二:这个例子中我们通过销售订单号来读取订单的完整信息,使用例一中的端口配置。需要更改的是我们的XML消息请求:
<?xml version="1.0" encoding="UTF-8"?> <Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"> <Header> <MessageId>{D630C246-3C62-42AE-BE69-4BFE609E8470}</MessageId> <Action>http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/read</Action> <RequestMessageId>{5603D03A-4380-404D-9F27-738BE0FEA13E}</RequestMessageId> </Header> <Body> <MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"> <EntityKeyList xmlns="http://schemas.microsoft.com/dynamics/2006/02/documents/EntityKeyList"> <EntityKey xmlns="http://schemas.microsoft.com/dynamics/2006/02/documents/EntityKey"> <KeyData> <KeyField> <Field>SalesId</Field> <Value>00000009_050</Value> </KeyField> </KeyData> </EntityKey> </EntityKeyList> </MessageParts> </Body> </Envelope>
其实就是把例一中的响应消息中的Action换成read,同样保存到\\dax\AIFData\AIFIn,运行一次服务JOB,在响应目录中会找到订单详细信息的XML,包含订单头及订单行的所有信息,内容比较长就不列出了。
实例三:在这个例子中我们使用MSMQ适配器端口,如何部署AIF消息队列参见http://msdn.microsoft.com/EN-US/library/aa834340.aspx。创建MSMQ适配器一个入站端口,使用例一中销售订单服务的read和create两个操作,具体步骤不做介绍。消息队列不像文件夹那样可以直接放入XML文档,下面我们使用C#程序向队列投递消息来创建销售订单:
static void Main(string[] args) { // The queue name. // Replace this AOS name and queue name with the values from // the URI field in the Inbound ports form. // For this walkthrough, // only the server name should be different. String QName = "FormatName:DIRECT=OS:DAX\\CreateSO"; // The path to the sales order XML file String XMLPath = "c:\\so_create_01.xml"; // Create the XML document object. XmlDocument doc = new XmlDocument(); doc.Load(XMLPath); // Create a new message. Message msg = new Message(); // Add authentication. msg.AttachSenderId = true; msg.UseAuthentication = true; msg.HashAlgorithm = HashAlgorithm.Sha; // Attach the document to the message. msg.Body = doc; // Create the queue object. MessageQueue myQ = new MessageQueue(QName); try { // Send a message to the queue // Create a transaction. MessageQueueTransaction myTransaction = new MessageQueueTransaction(); // Begin the transaction myTransaction.Begin(); // Send the message myQ.Send(msg, "Sales Order", myTransaction); // End the transaction myTransaction.Commit(); } catch (Exception e) { // Display the description of the exception Console.WriteLine(e.Message); } Console.Write("Done. Press ENTER to continue."); Console.In.ReadLine(); }
程序比较简单,不多讨论。
实例四:在这个例子我们使用C#程序通过NetTcp适配器入站端口读取销售信息,如何配置NetTcp端口也从略,创建好一个C#工程,添加服务引用http://DAX:8101/DynamicsAx/Services/SalesOrderRead,主程序:
static void Main(string[] args) { // Create the sales order objects // Service client var cl = new ServiceReference1.SalesOrderServiceClient(); // Sales order object to receive response TCP_Client.ServiceReference1.AxdSalesOrder resp; // Create the entity key list for the request var readRespKeys = new ServiceReference1.EntityKey[1]; readRespKeys[0] = new ServiceReference1.EntityKey(); readRespKeys[0].KeyData = new ServiceReference1.KeyField[1]; readRespKeys[0].KeyData[0] = new ServiceReference1.KeyField(); readRespKeys[0].KeyData[0].Field = "SalesId"; readRespKeys[0].KeyData[0].Value = "00000003_050"; try { // Try to read the sales order resp = cl.read(null, readRespKeys); // Display the information from the first sales table Console.WriteLine("For sales order: " + resp.SalesTable[0].SalesId); Console.WriteLine("Customer Account is: " + resp.SalesTable[0].CustAccount); } catch (Exception e) { Console.WriteLine("Exception: " + e.Message); cl.Abort(); } cl.Close(); }
程序读取打印订单00000003_050的客户号,也是比较简单,不多讨论。需要讨论的是上面代码中没有用到的CallContext类,它有Company、LogonAsUser、Language、PropertyBag、MessageId、AX2012 R2引入的PartitionKey几个属性,默认情况下调用服务是以当前进程用户的默认公司执行,使用CallContext则可以设置这些选项,比如:
var context = new CallContext(); context.Company = "dat"; context.LogonAsUser = "yourdomain\userid"; context.Language = "en-us";
在调用服务时指定context,比如上面的resp = cl.read(context, readRespKeys),它产生的结果类似XML消息的消息头中指定的相关选项:
<Header> <MessageId>MessageId1</MessageId> <LogonAsUser>domain\user1</LogonAsUser> <PartitionKey>PartitionKey1</PartitionKey> <Company>Company1</Company> <Action>create</Action> </Header>
实例五:这个例子我们使用C#通过HTTP适配器入站端口读取销售订单,HTTP适配器需要将Web service部署到IIS。在C#中使用这类适配器和使用NetTcp适配器端口一样,WSDL地址可能是http://DAX/MicrosoftDynamicsAXAif60/WebSalesOrderRead/xppservice.svc,调用服务的代码和NetTcp一样不用改动,不同的是添加Service reference时app.config中WCF服务绑定端口不一样了,所以这里就不再贴出代码了。
实例六:这是一个使用C#通过NetTcp或者HTTP适配器端口创建销售订单的例子:
SalesOrderServiceClient proxy = new SalesOrderServiceClient(); CallContext context = new CallContext(); context.Company = "ceu"; AxdSalesOrder salesOrder = new AxdSalesOrder(); AxdEntity_SalesTable[] salesTables = new AxdEntity_SalesTable[1]; AxdEntity_SalesTable salesTable = new AxdEntity_SalesTable(); salesTable.CurrencyCode = "USD"; salesTable.CustAccount = "1103"; salesTable.ReceiptDateRequested = Convert.ToDateTime("2/1/2012"); salesTable.Payment = "N060"; salesTable.PurchOrderFormNum = "PO113"; AxdType_DimensionAttributeValue dimBusinessUnit = new AxdType_DimensionAttributeValue(); dimBusinessUnit.Name = "BusinessUnit"; dimBusinessUnit.Value = "20"; AxdType_DimensionAttributeValue dimCustomerGroup = new AxdType_DimensionAttributeValue(); dimCustomerGroup.Name = "CustomerGroup"; dimCustomerGroup.Value = "10"; AxdType_DimensionAttributeValue dimDepartment = new AxdType_DimensionAttributeValue(); dimDepartment.Name = "Department"; dimDepartment.Value = "500"; AxdType_DimensionAttributeValueSet valueSet = new AxdType_DimensionAttributeValueSet(); valueSet.Values = new AxdType_DimensionAttributeValue[3] { dimBusinessUnit, dimCustomerGroup, dimDepartment }; salesTable.DefaultDimension = valueSet; AxdEntity_SalesLine salesLine = new AxdEntity_SalesLine(); salesLine.ItemId = "1000"; salesLine.SalesQty = 1; salesLine.SalesUnit = "ea"; AxdEntity_SalesLine salesLine2 = new AxdEntity_SalesLine(); salesLine2.ItemId = "1000"; salesLine2.SalesQty = 55; salesLine2.SalesUnit = "ea"; AxdEntity_SalesLine salesLine3 = new AxdEntity_SalesLine(); salesLine3.ItemId = "10004"; salesLine3.SalesQty = 21; salesLine3.SalesUnit = "Pcs"; AxdEntity_InventDim inventDim = new AxdEntity_InventDim(); inventDim.InventSiteId = "1"; salesLine3.InventDim = new AxdEntity_InventDim[1] { inventDim }; salesTable.SalesLine = new AxdEntity_SalesLine[3] { salesLine, salesLine2, salesLine3 }; salesOrder.SalesTable = new AxdEntity_SalesTable[1] { salesTable }; try { proxy.create(context, salesOrder); Console.WriteLine("Worked"); } catch (Exception e) { throw e; }
实例七:使用findKeys操作查找销售订单文档键值,使用例一中的文件系统适配器入站端口,和创建销售订单的不同点就在于XML消息中的QueryCriteria部分:
<?xml version="1.0" encoding="UTF-8"?> <Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"> <Header> <MessageId></MessageId> <Action>http://schemas.microsoft.com/dynamics/2008/01/services/SalesOrderService/findKeys</Action> </Header> <Body> <MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"> <QueryCriteria xmlns = "http://schemas.microsoft.com/dynamics/2006/02/documents/QueryCriteria"> <CriteriaElement> <DataSourceName>SalesTable</DataSourceName> <FieldName>CustAccount</FieldName> <Operator>Equal</Operator> <Value1>80012</Value1> </CriteriaElement> </QueryCriteria> </MessageParts> </Body> </Envelope>
这里查找客户号为80012的所有销售订单,返回这些销售订单的订单号列表,结果就不再列出。如何使用QueryCriteria可以参见http://msdn.microsoft.com/EN-US/library/aa892862.aspx,更多如何创建服务操作XML消息参见http://msdn.microsoft.com/EN-US/library/jj191598.aspx。
以上例子中演示了创建、读取销售订单,其他的服务操作雷同不多讨论,使用update操作更新数据由于有它特殊的地方,会放在单独的一篇文章中讨论。