Microsoft.Xrm.Tooling.Connector library has been made available by the Dynamics CRM SDK team to make it easy to perform common operations, using direct functions that have been exposed for these.
Exploring the library in came in with a learning experience with subtle differences in how we were used to doing it earlier using API libs available.
Establishing Connection:
First things first, establishing connection. I had created a sample project a few months back that was perfectly working back then. However, when we tried to execute the same package again, we received the following error.
“Unable to Login to Dynamics CRMOrganizationServiceProxy is nullOrganizationServiceProxy is nullOrganizationWebProxyClient is nullOrganizationServiceProxy is nullOrganizationWebProxyClient is nullOrganizationServiceProxy is null”
We found an article by Aileen at, however make the requested change did not resolve the problem in our case.
Then to make sure we were working on the latest version of the libraries, we deleted the references to the following libraries
- Xrm.Sdk.dll
- Crm.Sdk.Proxy.dll
- Xrm.Sdk.Deployment.dll
- Xrm.Tooling.Connector.dll
- IdentityModel.Clients.ActiveDirectory.dll
When you compile, it works just fine, but upon execution, you receive an error about missing reference to Microsoft.IdentityModel
You need to add references to the following to get this to work
- Xrm.Tooling.Connector.dll
- Xrm.Sdk.Deployment.dll
- IdentityModel.Clients.ActiveDirectory.dll
Creating Records:
Traditionally, service.create has always required an object of Entity class as a parameter to be passed. XRM.Tooling, provides an additional way to pass the details of the record. You can now pass them as a dictionary of key/value pair
//create the dictionary array Dictionary<string, CrmDataTypeWrapper> data = new Dictionary<string, CrmDataTypeWrapper>(); //name data.Add("name", new CrmDataTypeWrapper("Test data " + DateTime.Now.ToString(), CrmFieldType.String)); data.Add("address1_city", new CrmDataTypeWrapper("Mumbai", CrmFieldType.String)); data.Add("telephone1", new CrmDataTypeWrapper("905-555-5555", CrmFieldType.String)); //create the record Guid id = client.CreateNewRecord("account", data);
Setting field values for CrmDataTypeWrapper
In CRM 4 days we used to have the wrapper classes for data type that we needed to create object of set the values for. So a Boolean value could be set by creating an object of CrmBoolean() and then set the value of that object to true/false as desired. Looks like this system is back with the tooling connector.
Here is how to set the values for the various data types
1. Customer:
//Set the customerid CrmDataTypeWrapper cust = new CrmDataTypeWrapper(); cust.ReferencedEntity = "account"; cust.Value = accId; cust.Type = CrmFieldType.Customer;
2. String:
//Set the name - String Field CrmDataTypeWrapper name = new CrmDataTypeWrapper("Sample quote " + DateTime.Now.ToString(), CrmFieldType.String);
3. Boolean:
//Set write-in CrmDataTypeWrapper isproductoverridden = new CrmDataTypeWrapper(true, CrmFieldType.CrmBoolean);
4. PickList:
//Set the status field quoteDataArray["statuscode"] = new CrmDataTypeWrapper((int)(4), CrmFieldType.Picklist);
5. Numeric:
//Qty detailArray["quantity"] = new CrmDataTypeWrapper((decimal)(10), CrmFieldType.CrmDecimal);
6. Money:
//Set the price detailArray["priceperunit"] = new CrmDataTypeWrapper((decimal)(15.5), CrmFieldType.CrmMoney);
7. Guid/Unique Identifier:
Guid quoteID = Guid.NewGuid(); CrmDataTypeWrapper id = new CrmDataTypeWrapper(quoteID, CrmFieldType.UniqueIdentifier);
8. Lookup:
//Set the pricelist CrmDataTypeWrapper pricelevel = new CrmDataTypeWrapper(); pricelevel.ReferencedEntity = "pricelevel"; pricelevel.Value = new Guid("XXXBE2B-B44F-E711-80DE-000D3AF32500"); pricelevel.Type = CrmFieldType.Lookup;
Batch Processing:
To ensure that all the requests are executed in a transaction so that even if a single request fails, the entire transaction rolls back, it provides a way for Batch Execution of requests as well.
The first step is to generate a Batch Id or Name
//Create a batch for commit/rollback scenario Guid batchID = client.CreateBatchOperationRequest("quotebatch", true, false);
Next with every request submitted, please provide the batchid along
//Create the Quote record client.CreateNewRecord("quote", quoteArray, batchId: batchID); //Create quote line record client.CreateNewRecord("quotedetail", detailArray, batchId: batchID);
Though these requests get added to the batch, they have not executed right away. You need to make the call to execute the batch for the requests in the batch to be processed.
//Execute batch ExecuteMultipleResponse response = client.ExecuteBatch(batchID);
If the response.IsFaulted == false that means the records have been successfully created successfully.
You get the results of the executed requests in the response object as can be seen below
Special requests like CloseQuote that earlier had to be executed through ExecuteRequest are now directly available as a function in the library that can be invoked directly
//Connect to CRM CrmServiceClient client = new Microsoft.Xrm.Tooling.Connector.CrmServiceClient(connString); //Create a batch for commit/rollback scenario Guid quotebatchID = client.CreateBatchOperationRequest("quotebatch", true, false); //Create the dictionary to define the list of fields that needs to be updated Dictionary<string, CrmDataTypeWrapper> quoteDataArray = new Dictionary<string, CrmDataTypeWrapper>(); //Set the status field as Active quoteDataArray["statuscode"] = new CrmDataTypeWrapper((int)(4), CrmFieldType.Picklist);
Note: The status must be greater than 3 but less than or equal to 7. You can also refer to the Microsoft’s blog for more info on this.
//quoteDataArray is the object of the Dictionary used to update the status to activate //So basically we are closing the Quote as Active client.CloseQuote(new Guid("7ECAC454-DA27-42F3-B168-A1E0C11FA792"), quoteDataArray, batchId: quotebatchID); //Get the response of the CloseQuote request ExecuteMultipleResponse quoteResponse = client.ExecuteBatch(quotebatchID);