Thursday, June 23, 2011

Using RetrieveMultiple From Silverlight to Retrieve Entities in Microsoft Dynamics CRM 2011

This tutorial will show you how to retrieve an entity records in Microsoft Dynamics CRM 2011 using C# in Silverlight using RetrieveMultiple.  This  particular call was put together by a colleague of mine named Stephen Walsh by starting from the other examples on my blog, his main interest is mobility so he told me I could use this code in my blog.

IMPORTANT: First things first.  You have to set up your Silverlight app to make a web services connection to CRM.   The best tutorial I have found for this is located here in the MSDN:
http://msdn.microsoft.com/en-us/library/gg594452.aspx
After you finish that setup you are ready to implement the rest of this post.

Now once that is done you can retrieve records in CRM using the following syntax.  In the following example I am retrieving a webresource record wit the name new_mywebresourcename.

Here is the call in C#:

ColumnSet Columns = new ColumnSet();
Columns.Columns = new System.Collections.ObjectModel.ObservableCollection<string>(new string[] { "content" });
QueryExpression Query = new QueryExpression();
Query.EntityName = "webresource";
Query.ColumnSet = Columns;
Query.Criteria = new FilterExpression
{
    FilterOperator = LogicalOperator.And,
    Conditions = 
    {
       new ConditionExpression
       {
           AttributeName = "name",
           Operator = ConditionOperator.Equal,
           Values = { "new_mywebresourcename" }
       }
    }
};
OrganizationRequest Request = new OrganizationRequest() { RequestName = "RetrieveMultiple" };
Request["Query"] = Query;
IOrganizationService Service = SilverlightUtility.GetSoapService();
Service.BeginExecute(Request, new AsyncCallback(GetCertificateResult), Service);

Now here is the call-back code:

private void GetCertificateResult(IAsyncResult Result)
{
    try
    {
        OrganizationResponse Response = ((IOrganizationService)Result.AsyncState).EndExecute(Result);
        EntityCollection EntityResult = (EntityCollection)Response["EntityCollection"];
        Entity wr = new Entity();
        wr = EntityResult.Entities[0];
        byte[] certbytes = Convert.FromBase64String(wr.Attributes[0].Value.ToString());
        X509Certificate MyCert = new X509Certificate(certbytes, "Certname");
        this.Dispatcher.BeginInvoke(DisplayCert);
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

You will need to use the this.Dispatcher.BeginInvoke to call a method to act on the UI or perform actions in the main thread.  I did not include the dispatcher invoked method called "DisplayCert" in this case because it could do anything in the UI.

You will notice that the biggest change in thinking and syntax from standard CRM SDK work will be that you have to specify your request and response properties as strings explicitly in code.

I hope this helps!

-

Wednesday, June 22, 2011

Delete an Entity in Microsoft Dynamics CRM 2011 Using Jscript or .NET With DeleteEntityRequest

This illustration shows how to delete an entity in Microsoft Dynamics CRM 2011 in code using jscript or also C#  using the DeleteEntityRequest.   This example will be given in JScript (SOAP) and in C# (.NET).

Ok, here is what the code look like!
First in C#:

DeleteEntityRequest request = new DeleteEntityRequest()
{
    //specify your  entitie's schema name
    LogicalName = "new_testdelete",
};
DeleteEntityResponse resp = (DeleteEntityResponse)service.Execute(request);

If you need help instantiating a service object in .NET within a plugin check out this post:
http://mileyja.blogspot.com/2011/04/instantiating-service-object-within.html

Now here is the Jscript nicely formatted by the CRM 2011 SOAP formatter. Available at: http://crm2011soap.codeplex.com/

Now in Jscript:

if (typeof (SDK) == "undefined")
   { SDK = { __namespace: true }; }
       //This will establish a more unique namespace for functions in this library. This will reduce the 
       // potential for functions to be overwritten due to a duplicate name when the library is loaded.
       SDK.SAMPLES = {
           _getServerUrl: function () {
               ///<summary>
               /// Returns the URL for the SOAP endpoint using the context information available in the form
               /// or HTML Web resource.
               ///</summary>
               var OrgServicePath = "/XRMServices/2011/Organization.svc/web";
               var serverUrl = "";
               if (typeof GetGlobalContext == "function") {
                   var context = GetGlobalContext();
                   serverUrl = context.getServerUrl();
               }
               else {
                   if (typeof Xrm.Page.context == "object") {
                         serverUrl = Xrm.Page.context.getServerUrl();
                   }
                   else
                   { throw new Error("Unable to access the server URL"); }
                   }
                  if (serverUrl.match(/\/$/)) {
                       serverUrl = serverUrl.substring(0, serverUrl.length - 1);
                   } 
                   return serverUrl + OrgServicePath;
               }, 
           DeleteEntityRequest: function () {
               var requestMain = ""
               requestMain += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
               requestMain += "  <s:Body>";
               requestMain += "    <Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
               requestMain += "      <request i:type=\"a:DeleteEntityRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\">";
               requestMain += "        <a:Parameters xmlns:b=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
               requestMain += "          <a:KeyValuePairOfstringanyType>";
               requestMain += "            <b:key>LogicalName</b:key>";
               requestMain += "            <b:value i:type=\"c:string\" xmlns:c=\"http://www.w3.org/2001/XMLSchema\">new_testdelete</b:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "        </a:Parameters>";
               requestMain += "        <a:RequestId i:nil=\"true\" />";
               requestMain += "        <a:RequestName>DeleteEntity</a:RequestName>";
               requestMain += "      </request>";
               requestMain += "    </Execute>";
               requestMain += "  </s:Body>";
               requestMain += "</s:Envelope>";
               var req = new XMLHttpRequest();
               req.open("POST", SDK.SAMPLES._getServerUrl(), true)
               // Responses will return XML. It isn't possible to return JSON.
               req.setRequestHeader("Accept", "application/xml, text/xml, */*");
               req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
               req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
               var successCallback = null;
               var errorCallback = null;
               req.onreadystatechange = function () { SDK.SAMPLES.DeleteEntityResponse(req, successCallback, errorCallback); };
               req.send(requestMain);
           },
       DeleteEntityResponse: function (req, successCallback, errorCallback) {
               ///<summary>
               /// Recieves the assign response
               ///</summary>
               ///<param name="req" Type="XMLHttpRequest">
               /// The XMLHttpRequest response
               ///</param>
               ///<param name="successCallback" Type="Function">
               /// The function to perform when an successfult response is returned.
               /// For this message no data is returned so a success callback is not really necessary.
               ///</param>
               ///<param name="errorCallback" Type="Function">
               /// The function to perform when an error is returned.
               /// This function accepts a JScript error returned by the _getError function
               ///</param>
               if (req.readyState == 4) {
               if (req.status == 200) {
               if (successCallback != null)
               { successCallback(); }
               }
               else {
                   errorCallback(SDK.SAMPLES._getError(req.responseXML));
               }
           }
       },
       _getError: function (faultXml) {
           ///<summary>
           /// Parses the WCF fault returned in the event of an error.
           ///</summary>
           ///<param name="faultXml" Type="XML">
           /// The responseXML property of the XMLHttpRequest response.
           ///</param>
           var errorMessage = "Unknown Error (Unable to parse the fault)";
           if (typeof faultXml == "object") {
               try {
                   var bodyNode = faultXml.firstChild.firstChild;
                   //Retrieve the fault node
                   for (var i = 0; i < bodyNode.childNodes.length; i++) {
                       var node = bodyNode.childNodes[i];
                       //NOTE: This comparison does not handle the case where the XML namespace changes
                       if ("s:Fault" == node.nodeName) {
                       for (var j = 0; j < node.childNodes.length; j++) {
                           var faultStringNode = node.childNodes[j];
                           if ("faultstring" == faultStringNode.nodeName) {
                               errorMessage = faultStringNode.text;
                               break;
                           }
                       }
                       break;
                   }
               }
           }
           catch (e) { };
        }
        return new Error(errorMessage);
     },
 __namespace: true
};




To understand how to parse the response please review my post on using the DOM parser.
Now you can call the SDK.SAMPLES.DeleteEntityRequest function from your form jscript handler.
Thats all there is to it!

I hope this helps!

Monday, June 20, 2011

Microsoft Dynamics CRM 2011 SDK Example Index

If you read my blog or are a CRM developer you will want to check this out.  I have taken all of my SDK examples and soon topic examples also and compiled them by SDK message.

I have also pinned this to my pages on my blog so you can always get to it easily.

Here it is and how to get to it:

Friday, June 17, 2011

Get the Version Number of a Microsoft Dynamics CRM 2011 Server Using .NET or Jscript

This illustration shows how to retrieve your complete CRM server version number in Microsoft Dynamics CRM 2011 in code using jscript and also C#  using the RetrieveVersionRequest.  This can be very useful if you need to programmatically determine what rollup version is installed on the server.   This example will be given in JScript (SOAP) and in C# (.NET).

Ok, here is what the code look like!
First in C#:

RetrieveVersionRequest req = new RetrieveVersionRequest();
RetrieveVersionResponse resp = (RetrieveVersionResponse)service.Execute(req);
//assigns the version to a string
string VersionNumber = resp.Version;

If you need help instantiating a service object in .NET within a plugin check out this post:
http://mileyja.blogspot.com/2011/04/instantiating-service-object-within.html

Now here is the Jscript nicely formatted by the CRM 2011 SOAP formatter. Available at: http://crm2011soap.codeplex.com/

Now in Jscript:


if (typeof (SDK) == "undefined")
   { SDK = { __namespace: true }; }
       //This will establish a more unique namespace for functions in this library. This will reduce the 
       // potential for functions to be overwritten due to a duplicate name when the library is loaded.
       SDK.SAMPLES = {
           _getServerUrl: function () {
               ///<summary>
               /// Returns the URL for the SOAP endpoint using the context information available in the form
               /// or HTML Web resource.
               ///</summary>
               var OrgServicePath = "/XRMServices/2011/Organization.svc/web";
               var serverUrl = "";
               if (typeof GetGlobalContext == "function") {
                   var context = GetGlobalContext();
                   serverUrl = context.getServerUrl();
               }
               else {
                   if (typeof Xrm.Page.context == "object") {
                         serverUrl = Xrm.Page.context.getServerUrl();
                   }
                   else
                   { throw new Error("Unable to access the server URL"); }
                   }
                  if (serverUrl.match(/\/$/)) {
                       serverUrl = serverUrl.substring(0, serverUrl.length - 1);
                   } 
                   return serverUrl + OrgServicePath;
               }, 
           RetrieveVersionRequest: function () {
               var requestMain = ""
               requestMain += "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
               requestMain += "  <s:Body>";
               requestMain += "    <Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
               requestMain += "      <request i:type=\"b:RetrieveVersionRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">";
               requestMain += "        <a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\" />";
               requestMain += "        <a:RequestId i:nil=\"true\" />";
               requestMain += "        <a:RequestName>RetrieveVersion</a:RequestName>";
               requestMain += "      </request>";
               requestMain += "    </Execute>";
               requestMain += "  </s:Body>";
               requestMain += "</s:Envelope>";
               var req = new XMLHttpRequest();
               req.open("POST", SDK.SAMPLES._getServerUrl(), true)
               // Responses will return XML. It isn't possible to return JSON.
               req.setRequestHeader("Accept", "application/xml, text/xml, */*");
               req.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
               req.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
               var successCallback = null;
               var errorCallback = null;
               req.onreadystatechange = function () { SDK.SAMPLES.RetrieveVersionResponse(req, successCallback, errorCallback); };
               req.send(requestMain);
           },
       RetrieveVersionResponse: function (req, successCallback, errorCallback) {
               ///<summary>
               /// Recieves the assign response
               ///</summary>
               ///<param name="req" Type="XMLHttpRequest">
               /// The XMLHttpRequest response
               ///</param>
               ///<param name="successCallback" Type="Function">
               /// The function to perform when an successfult response is returned.
               /// For this message no data is returned so a success callback is not really necessary.
               ///</param>
               ///<param name="errorCallback" Type="Function">
               /// The function to perform when an error is returned.
               /// This function accepts a JScript error returned by the _getError function
               ///</param>
               if (req.readyState == 4) {
               if (req.status == 200) {
               //if (successCallback != null)
               //{ successCallback(); }
               alert(req.responseXML.xml);
               }
               else {
                   errorCallback(SDK.SAMPLES._getError(req.responseXML));
               }
           }
       },
       _getError: function (faultXml) {
           ///<summary>
           /// Parses the WCF fault returned in the event of an error.
           ///</summary>
           ///<param name="faultXml" Type="XML">
           /// The responseXML property of the XMLHttpRequest response.
           ///</param>
           var errorMessage = "Unknown Error (Unable to parse the fault)";
           if (typeof faultXml == "object") {
               try {
                   var bodyNode = faultXml.firstChild.firstChild;
                   //Retrieve the fault node
                   for (var i = 0; i < bodyNode.childNodes.length; i++) {
                       var node = bodyNode.childNodes[i];
                       //NOTE: This comparison does not handle the case where the XML namespace changes
                       if ("s:Fault" == node.nodeName) {
                       for (var j = 0; j < node.childNodes.length; j++) {
                           var faultStringNode = node.childNodes[j];
                           if ("faultstring" == faultStringNode.nodeName) {
                               errorMessage = faultStringNode.text;
                               break;
                           }
                       }
                       break;
                   }
               }
           }
           catch (e) { };
        }
        return new Error(errorMessage);
     },
 __namespace: true
};




To understand how to parse the response please review my post on using the DOM parser.
Now you can call the SDK.SAMPLES.RetrieveVersionRequest function from your form jscript handler.
Thats all there is to it!

I hope this helps!

Thursday, June 16, 2011

Tanguy Created a Us a Nice Ribbon Browser For Microsoft Dynamics CRM 2011

Check this thing out!

He says,

"This is basically the same program that is provided in the SDK as a sample (exportribbon) but with a graphical user interface.
It will be helpful to retrieve Id’s and other attributes of ribbon controls when you want to update the system ribbon."

Check it out and get the tool here:




Thanks Tanguy!

Deprecated SDK Messages in Microsoft Dynamics CRM 2011

There are several SDK messages that are now deprecated in Microsoft Dynamics CRM 2011.  You are most likely to find these messages in use in customized CRM 4.0 systems that have been upgraded to CRM 2011.

If you are using these messages you should start to think about what alternatives exist in the new SDK and start to plan to replace these SDK calls as the messages probably will not exist in the next version of Microsoft Dynamics CRM.

The Deprecated calls are:


Deprecated. Establishes an association between a product and a substitute product.

Deprecated. Adds a link between two records in a many-to-many relationship.
Deprecated. Creates a compound entity (salesorder, invoice, quote, or duplicaterule) and its related entity (salesorderdetail, invoicedetail, quotedetail, or duplicaterulecondition).
Deprecated. Updates a compound record (salesorder, invoice, quote or duplicaterule) and its related detail record (salesorderdetail, invoicedetail, quotedetail or duplicaterulecondition).
Deprecated. Removes a link between two records in a many to many relationship.
Deprecated. Executes the specified Fetch XML query.  (Use Retrieve Multiple Instead)http://mileyja.blogspot.com/2011/06/use-fetchxml-queries-in-jscript-and-net.html
Deprecated. Checks if Microsoft Great Plains is installed.
Deprecated. Makes the report available to all users in the organization.
Deprecated. Makes the specified e-mail template available to the entire organization.
Deprecated. Makes the report unavailable to all users in the organization.
Deprecated. Makes the specified e-mail template no longer available to the entire organization.
Deprecated. Removes the relationship between two records as defined by the target classes listed below. For example, remove the relationship between an invoice and a contact.
Deprecated. Removes the association between a product and a substitute product.
Deprecated. Retrieves the members of a team.
Deprecated. Retrieves all the team information for child business units of the specified business unit.
Deprecated. Retrieves all system users for the child business units of the specified business unit.
Deprecated. Retrieves a collection of teams of which the specified system user is a member.
Deprecated. Retrieves the system user settings for the specified system user.
Deprecated. Creates a link between an opportunity and an account, contact, or competitor.
Deprecated. Updates the user settings for a system user.

Wednesday, June 15, 2011

Get Current User ID and Organization ID from Microsoft Dynamics CRM 2011 In SIlverlight Using WhoAmIRequest

This tutorial will show you how to get the currently logged in User ID and Organization ID from Microsoft Dynamics CRM 2011 using C# in Silverlight with WhoAmIRequest

IMPORTANT: First things first.  You have to set up your Silverlight app to make a web services connection to CRM.   The best tutorial I have found for this is located here in the MSDN:
http://msdn.microsoft.com/en-us/library/gg594452.aspx
After you finish that setup you are ready to implement the rest of this post.

Now once that is done you can set up your call to the CRM Organization service reference.

Here is the call in C#:

private void GetOrganizationandUserID()
{
    try
    {

        OrganizationRequest request = new OrganizationRequest() { RequestName = "WhoAmI" };
        //request["Query"] = query;

        IOrganizationService service = SilverlightUtility.GetSoapService();

        service.BeginExecute(request, new AsyncCallback(CrmGetUserOrgInfo_Callback), service);
    }
    catch (Exception ex)
    {
        this.ReportError(ex);
    }
}

Now here is the call-back code:

private void CrmGetUserOrgInfo_Callback(IAsyncResult result)
{
    try
    {
        OrganizationResponse response = ((IOrganizationService)result.AsyncState).EndExecute(result);
        Guid OrgID = (Guid)response["OrganizationId"];
        Guid UserID = (Guid)response["UserId"];
        guidOrganizationID = OrgID;
        guidUserID = UserID;
        this.Dispatcher.BeginInvoke(DisplayOrgAndUserInfo);


    }
    catch (Exception ex)
    {
        this.ReportError(ex);
    }
}

You will need to use the this.Dispatcher.BeginInvoke to call a method to act on the UI or perform actions in the main thread.  I did not include the dispatcher invoked method called "DisplayOrgAndUserInfo" in this case because it could do anything in the UI.  In my case I will tell you though it is setting a TextBox.Text property to the value of the global variables guidOrganizationID and guidUserID .

You will notice that the biggest change in thinking and syntax from standard CRM SDK work will be that you have to specify your request and response properties as strings explicitly in code.

I hope this helps!

-