[AX]AX2012 AIF(二):文件服務程式設計模型

weixin_34037977發表於2013-01-30

一個完整的文件服務包含很多物件,以Customer服務為例,它包含以下物件:

  • 查詢AxdCustomer:這個query的頂層表為CustTable,其下Datasource包含表DirParty,DirParty下的Datasource則再包含表DirPersonName、表DirOrganizationName、檢視DirPartyContactInfoView、檢視DirPartyPostalAddressView。這個Query定義了服務所用的所有相關資料的資料模型。
  • 文件服務類CustCustomerService:這是直接提供服務的類,它包含Create、Delete、find、findKeys、getKeys、getChangedKeys等對外服務的方法,這些方法使用特定的特性標註,比如Create方法使用了[AifDocumentCreateAttribute, SysEntryPointAttribute(true)]標註,AifDocumentCreateAttribute指出這個方法是文件服務的建立方法,這個特性不是必須的,主要用途是在使用metadata服務時我們可以根據這個特性列舉出所有刪除記錄的服務操作;SysEntryPointAttribute特性指示是否進行授權檢查,所有的服務操作必須指定這個特性,其引數true表示要對呼叫使用者檢查是否有權操作該方法涉及到的資料表,false則不執行這個檢查。文件服務類服務的具體操作都交由其繼承的基類AifDocumentService具體操作,比如read()方法內部呼叫的是AifDocumentService.readList()方法。
  • 資料物件類CustCustomer、CustCustomer_CustTable、CustCustomer_DirParty、CustCustomer_DirParty_DirPerson等:這些類描述了查詢AxdCustomer定義的資料模型,CustCustomer直接對應Query AxdCustomer,它是資料物件類的最頂層,它依靠其他資料類CustCustomer_CustTable、CustCustomer_DirParty等。CustCustomer從AifDocument繼承,後者又從AfStronglyTypedDataContainer繼承,而AfStronglyTypedDataContainer又從AifXmlSerializable繼承,由它描述了XML訊息定義的一個文件,所以我們在read()服務操作方法中看到的返回值就是這個類的一個例項。其他的資料物件類都直接從AfStronglyTypedDataContainer繼承,所以資料物件類包括CustCustomer都實現一系列的existsXXX()方法,這些方法判斷某個欄位是否存在,呼叫基類的exists()方法;一系列parmXXX()方法,獲取某個欄位的值或者下層的資料物件例項;createXXX()方法,建立並返回其下層的資料物件類例項列表,比如CustCustomer.createCustTable()返回包含CustCustomer_CustTable類例項的列表。
  • 文件類AxdCustomer:文件類的作用是封裝涉及到的多個表業務邏輯,這樣外部呼叫應用不需要確切的知道如何操作底層資料庫表。Axd類實現AifServiceable介面,這裡的AxdCustomer不是直接實現這個介面,而是從AxBase擴充套件,後者實現AifServiceable介面。總體上來講Axd類實現到XML的序列化及反序列化,生成XSD資料Schema,控制內部表的生命週期等,由它間接的操作下面要講到的AxXXX表類。Axd類包含的方法很多,更詳細的介紹可以參見http://msdn.microsoft.com/EN-US/library/aa862063.aspx,這裡列舉幾個實現介面AifServiceable比較重要的方法:getName()返回文件的名稱,這是XML文件的根標籤的名稱;getSchema()返回資料Schema XSD;getActionList()返回文件支援的操作列表,比如AxdCustomer支援findList、read、readList等操作。
  • 表類AxCustTable、AxDirPartyTable等:這組類以Ax<Table>方式命名,和文件類協同工作,同樣封裝資料表業務邏輯,從AxInternalBase繼承,代表了AOT中的某個表。Ax<Table>類不是必須的,在使用AIF Document Wizard建立新的文件服務時,勾選了“Generate AxBC Classes”才會建立這些類。如果使用Ax<Table>類,可以在文件級別使用“Value mapping” form來對映資料表欄位。Ax<Table>內部使用類AxdBaseRead、AxdBaseCreate讀寫資料庫表,不使用Ax<Table>的情況則可以在Axd文件類中使用AxCommon讀寫資料庫表。需要注意的如果在服務的Query中新增了一個新的表,相應的Ax<Table>類不會自動生成,這時候可以使用Update document service”工具的“Regenerate data object classes”和“Update AxBC classes”選項重新生成或者更新Ax<Table>類。Ax<Table>類和AOT中的表是一一對應的,由它直接操作表資料,具體功能包括生成表欄位的預設值、按照正確的順序設定表欄位值、維護驗證關聯表資料的完整性、欄位值對映比如從供應商料名對映到內部料名、錯誤處理等,需要注意的是Ax<Table>不會驗證是誰在運算元據表,使用者驗證要放到前面提到的服務類。

看完相關的類和物件,在開始後續的問題前,我們先來看看Schema XSD是如何生成的。資料物件類繼承自AifDocument(頂級物件),也可能是AfStronglyTypedDataContainer,AfStronglyTypedDataContainer有一個方法叫做getSchema()返回Schema XSD;而AifDocument是繼承自AfStronglyTypedDataContainer,它過載了getSchema()方法,它會建立對應Axd類的例項,呼叫前面提及的Axd類的getSchema()返回Schema XSD。實際上無論是AifDocument.getSchema()還是AfStronglyTypedDataContainer.getSchema(),它們最終都使用AxdBaseGenerateXSD.generate()生成Schema XSD。這裡不深入討論是如何生成XSD的,需要知道的是以上方法會列舉文件定義的Query,從Query中查詢Datasource欄位生成相應的XML標記,文件Query必須只有一個根Datasource,隱藏或者禁止的欄位被排除在外,XML的根元素名稱來自於Axd<document>去掉Axd字首。我們可以用下面的Job從程式碼生成相應文件服務的XSD:

static void GenerateXSDSchema_Customer(Args _args)
{
    CustCustomer        customer;
    XML                 xml;
    XMLDocument         xmlDocument;
    FileName            fileName;
    ;

    // Instantiate the class.
    customer = new CustCustomer();

    // Get the document class schema.
    xml =customer.getSchema();
    xmlDocument = XMLDocument::newXML(xml);

    // Save the schema to a file.
    fileName = "c:\\XSDSchema_Customer.xsd";

    new FileIoPermission(fileName, 'rw').assert();
    xmlDocument.save(fileName);
    CodeAccessPermission::revertAssert();
}

在生成的Schema中我們可以看到AxdCustomer類被對映為complexType型別,其下包含的元素是從AxdCustomer的parmXXX方法去掉parm而來;Query中的表Custtable也對映為complexType型別,名稱為AxdEntity_CustTable,包含的元素來自於表欄位,只有那些包含在AxCustTable.parmXXX方法的欄位才會出現在XSD中。更詳盡的欄位型別到XSD的單元的對映關係參見http://msdn.microsoft.com/EN-US/library/aa636469.aspx。要說明的是上面得到的文件服務的完整XSD,而我們在埠配置視窗中“View schema”看到的XSD是完整XSD的子集,在“Document data policies”視窗我們可以手工使能或者禁止某個欄位,這只是對當期所配置的AIF埠有效。

XSD描述了XML訊息的格式,下面是CustCustomerService.read操作得到的XML序列化結果樣例(省略部分內容):

  <?xml version="1.0" encoding="UTF-8" ?> 
- <Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
- <Header>
   <MessageId>{93FE7B5F-99E6-45D6-BAA5-654699EFF0EA}</MessageId> 
   <Action>http://schemas.microsoft.com/dynamics/2008/01/services/CustomerService/read</Action> 
   <RequestMessageId>{E983D78F-0011-47B7-8716-F8B64D120EF6}</RequestMessageId> 
  </Header>
- <Body>
  - <MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
  - <Customer xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/Customer">
      <DocPurpose>Original</DocPurpose> 
      <SenderId>DMO</SenderId> 
      <ValidAsOfDateTime>2012-04-19T19:42:40Z</ValidAsOfDateTime> 
      <ValidTimeStateType>AsOf</ValidTimeStateType> 
    - <CustTable class="entity">
        <_DocumentHash>261dcc95694f19ee9010b1866237b4a2</_DocumentHash> 
        <AccountNum>4503</AccountNum> 
        <AccountStatement>Always</AccountStatement> 
        <Blocked>No</Blocked> 
        <CashDisc>14D1%</CashDisc> 
 ......
        
        <WebSalesOrderDisplay>WebEntered</WebSalesOrderDisplay> 
      - <DirParty xsi:type="AxdEntity_DirParty_DirOrganization" class="entity" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <LanguageId>En-us</LanguageId> 
           <Name>3 Company</Name> 
           <NameAlias>3</NameAlias> 
           <PartyNumber>1310</PartyNumber> 
           <RecId>5637145091</RecId> 
           <RecVersion>1</RecVersion> 
         - <DirPartyPostalAddressView class="entity">
             <Address>522 West 5th Street New York, NY 10032 US</Address> 
             <City>New York</City> 
  ....
             <ABC>None</ABC> 
           - <OrganizationName class="entity">
               <Name>3 Company</Name> 
               <RecId>5637144581</RecId> 
               <RecVersion>1</RecVersion> 
               <ValidFrom>2009-06-13T00:17:00Z</ValidFrom> 
               <ValidTo>2154-12-31T23:59:59Z</ValidTo> 
             </OrganizationName>
           </DirParty>
         </CustTable>
       </Customer>
     </MessageParts>
    </Body>
  </Envelope>

訊息包括封皮Envelope和Header段,Header段的Action指定操作的名稱。有幾點需要說明,注意到Axd類parmXXX被序列化到XML;Query中的表加上了class="entity"屬性;如果使用了Ax<Table>,只有Ax<Table>.parmXXX方法指定的內容被序列化,並且由它來讀取驗證資料,否則資料直接從資料庫表讀出Query中指定的欄位。

我們已經知道文件服務的具體操作是在文件服務類中實現,標準的文件服務操作包括create、delete、find、findKeys、read、update、getKeys、getChangedKeys。在AOT的Services節點下我們可以新建一個Service來引用這些操作,進而在出入站埠中使用。當然不是每一個文件都需要實現上述所有的標準服務,此外我們還可以新增自定義的服務操作,這些自定義服務操作方法必須定義為public,如果引數或者返回值是個物件類,那麼這個物件類必須實現AifXmlSerializable介面,如果不是物件類則只有以下幾種元型別被支援:str、 date、 utcdatetime、 guid、 int、 int64、 enum、real、void。如何建立一個自定義服務可以參見http://msdn.microsoft.com/EN-US/library/aa607052.aspx

在findKeys、update、read等方法中用到類AifEntityKeyList,它表示的是一個鍵值對,比如我們要讀取一個Customer的資訊,傳入的鍵值對可能是AccountNum=5407。如果我們在自定義的方法中需要返回大量資料,可以考慮只返回記錄的鍵值對,然後再用read方法根據鍵值對取出實際的記錄資料,這有助於提高效能。

後續還有更多關於文件服務的內容......

相關文章