在CRM中,更多的時候我們為了便於部署和提高使用者體驗都會選擇用Javascript在用戶端調用Web Service.其實在伺服器端開發plugin我們仍然可以達到類似的效果,並且我們可以很方便的處理通過Web Service返回的結果,但用Javascript最主要的好處是很容易部署。眾所周知,在CRM裡的二次開發大多集中於定製,我們將寫的Javascript及在其用戶端事件中調用的Javascript方法都可以方便的Export到Customization檔案中,這樣在部署到生產機器上時我們無需做其他的事情。
和普通的Javascript Client Side調用Web Service一樣,過程都是構造一個請求的SOAP訊息,然後通過建立XMLHttp對象將請求發送給Web Service,然後通過XMLHttp帶回的結果解析XML從而重新整理介面。其實和伺服器端調用Web Service的過程相同,我們不妨來做個比較,這樣可能更容易協助大家理解Client-Side Calling的過程:
1). 構建QueryExpression對象來準備請求的對象和請求返回的對象屬性。
QueryExpression query = new QueryExpression();
query.ColumnSet = new AllColumns();
query.EntityName = EntityName.account.ToString();
2). 給構建的請求對象附加過濾條件
FilterExpression filter = new FilterExpression();
query.Criteria = filter;
ConditionExpression hasEmailAddress = new ConditionExpression();
filter.Conditions = new ConditionExpression[] { hasEmailAddress };
hasEmailAddress.AttributeName = "emailaddress1";
hasEmailAddress.Operator = ConditionOperator.NotNull;
3). 調用CrmService的RetrieveMultiple或者RetrieveSingle方法來發出請求。
CrmLogService service = new CrmLogService();
service.Url = "http://stunnwarecrm/mscrmservices/2006/crmservice.asmx";
service.Credentials = CredentialCache.DefaultCredentials;
service.SoapMessageSerializationFinished += new SoapMessageEventHandler(service_SoapMessageSerializationFinished);
service.RetrieveMultiple(query);
服務端調用Web Service的三個步驟其實和所有調用Web Service的方法也沒有什麼不同,除了需要按照CRM的寫法來聲明ColumnSet作為返回的對象屬性群組,用ConditionOperator 作為條件運算式的關係符號以及略顯繁瑣的ConditionExpression外無非就是聲明一個參數,並調用Service.RetrieveMultiple而已。理解了這個過程我們可以很輕鬆的把以上Managed 程式碼翻譯為Javascript代碼,所不同的是在javascript裡邊我們需要將其拼接為一個Evelop訊息(因為代理類幫我們在伺服器端處理了這個過程,而在javascript裡我們卻無法得到,所以必須通過手工來建立)。一下代碼示範了如何擷取一組Account對象:
//call web service to retrieve the account
var xml = "" +
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope//" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance/" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema/">" +
GenerateAuthenticationHeader() +
" <soap:Body>" +
" <RetrieveMultiple xmlns=\"http://schemas.microsoft.com/crm/2007/WebServices/">" +
" <query xmlns:q1=\"http://schemas.microsoft.com/crm/2006/Query/" xsi:type=\"q1:QueryExpression\">" +
" <q1:EntityName>account</q1:EntityName>" +
" <q1:ColumnSet xsi:type=\"q1:ColumnSet\">" +
" <q1:Attributes>" +
" <q1:Attribute>name</q1:Attribute>" +
" <q1:Attribute>accountid</q1:Attribute>" +
" </q1:Attributes>" +
" </q1:ColumnSet>" +
" <q1:Distinct>false</q1:Distinct>" +
" <q1:Criteria>" +
" <q1:FilterOperator>And</q1:FilterOperator>" +
" <q1:Conditions>" +
" <q1:Condition>" +
" <q1:AttributeName>firstname</q1:AttributeName>" +
" <q1:Operator>Like</q1:Operator>" +
" <q1:Values>" +
" <q1:Value xsi:type=\"xsd:string\">%A%</q1:Value>" +
" </q1:Values>" +
" </q1:Condition>" +
" </q1:Conditions>" +
" </q1:Criteria>" +
" </query>" +
" </RetrieveMultiple>" +
" </soap:Body>" +
"</soap:Envelope>" +
"";
需要注意的是必須加入對CRM提供的Global Methode GenerateAuthenticationHeader()的引用,它將給這段請求代碼加入有關驗證身份的代碼。我們仍然只需要產生Query,然後聲明需要返回哪些列(如果返回所有列可以選擇<q1:ColumnSet xsi:type=\"q1:AllColumn\">" ),然後給請求添加過濾條件。接下來便可以把信發給Web Service並等待通知了:
var xmlHttpRequest = new ActiveXObject("Msxml2.XMLHTTP");
xmlHttpRequest.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
xmlHttpRequest.setRequestHeader("SOAPAction","http://schemas.microsoft.com/crm/2007/WebServices/RetrieveMultiple");
xmlHttpRequest.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xmlHttpRequest.setRequestHeader("Content-Length", xml.length);
xmlHttpRequest.send(xml);
var resultXml = xmlHttpRequest.responseXML;
alert(resultXml.xml);
返回的結果同樣是以XML形式封裝的,接下來所要做的就是解析返回的resultXML了。
var entityNodes = resultXml.selectNodes("//RetrieveMultipleResult/BusinessEntities/BusinessEntity");
for (var i = 0; i < entityNodes.length; i++) {
// Access the current array element
var entityNode = entityNodes[i];
var accountidNode = entityNode.selectSingleNode("q1:accountid");
var nameNode = entityNode.selectSingleNode("q1:name");
var accountid = (accountidNode == null) ? null : accountidNode.text;
var name = (nameNode == null) ? null : nameNode.text;
// finally display the values.
alert(name + ", " + accountid);
}
有些不方便的是我們通常還需要用到多個對象(LinkEntity來關聯),多個過濾條件等,對於大小寫敏感且無自動感知的javascript來說很難寫。既然我們已經瞭解了如何用戶端調用CRM Web Service,這裡我們再給大家介紹一個工具用來簡化上述一系列問題:用FetchXmlWizard工具來產生請求的XML(用來檢索對象的文法稱為FetchXML),並將驗證過的FetchXML發給JavaScript Web Service Calls工具來產生標準的Client-side javascript代碼。這兩個工具你都可以很方便的在http://www.stunnware.com/網站下載。下邊簡單說說如何來用這兩個工具:
1. 首先開啟FetchXMLWizard.exe檔案,在彈出的連結CRM Server的視窗中輸入相關資訊,然後確定。
2. 在空白地區右擊並選擇Add Main Entity(only choice now),然後再彈出的選擇實體框中選擇一個實體,這裡我們選擇Account.
3. 右鍵點擊Account並選擇Select Attributes, 在彈出的對話方塊中選擇你想要選擇的屬性。
4,接下來我們可以通過右鍵來添加任意我們想要的東西,比如Link Entity, Add Filter等,都比較簡單,我們不一一概述。需要提及的一點是Add Filter,在Add Filter之後我們需要再右鍵點擊來給Filter加入Condition,因為一個Filter實際上是包含多個Condition的並且每個Condition之間還有關係。在Specify Condition表單中選擇相應的欄位和關係,並輸入你的條件值即可,在這裡我們可以看到很多關係(根據類型的不同,關係也會有所改變)
5. 一切做完之後,轉到Fetch XML tab頁我們就看到產生的FetchXML了,這裡我們已經成功了一大半(你仍然可以通過選擇(Query->Execute)或F5來驗證你的結果。)
6. 最後一步了,我們將產生的FetchXML交給Javascript Web Service Call tool去產生Javascript code. 這個工具是個.NET項目,你首先需要開啟解決方案並更改其對web service的正確引用,並在app.config中將CRM server的地址更改正確。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
。。。。。。
</configSections>
<applicationSettings>
<Stunnware.Tools.Properties.Settings>
<setting name="JavaScript_Web_Service_Calls_CrmService3_CrmService"
serializeAs="String">
<value>http://SERVER_NAME/MSCrmServices/2007/CrmService.asmx</value>
</setting>
</Stunnware.Tools.Properties.Settings>
</applicationSettings>
</configuration>
然後運行程式,將上邊產生的FetchXML拷貝到request欄裡,點擊Start按鈕
ok,大功告成, 看看你的Javascript code吧:)