Thursday, July 21, 2011

Create a One-To-Many Relationship with Jscript or .Net in Microsoft Dynamics CRM 2011 With CreateOneToManyRequest

This illustration shows how to create a one-to-many relationship between two entities in Microsoft Dynamics CRM 2011 in code in jscript and also C#  using the CreateOneToManyRequest.   This example will be given in SOAP (JScript) and in C# (.NET).

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

 CreateOneToManyRequest req =
         new CreateOneToManyRequest
    {
        OneToManyRelationship =
        new OneToManyRelationshipMetadata
        {
            ReferencedEntity = "account",
            ReferencingEntity = "campaign",
            SchemaName = "new_account_campaign",
            AssociatedMenuConfiguration = new AssociatedMenuConfiguration
            {
                Behavior = AssociatedMenuBehavior.UseLabel,
                Group = AssociatedMenuGroup.Details,
                Label = new Label("Account", 1033),
                Order = 10000
            },
            CascadeConfiguration = new CascadeConfiguration
            {
                Assign = CascadeType.Cascade,
                Delete = CascadeType.Cascade,
                Merge = CascadeType.Cascade,
                Reparent = CascadeType.Cascade,
                Share = CascadeType.Cascade,
                Unshare = CascadeType.Cascade
            }
        },
        Lookup = new LookupAttributeMetadata
        {
            SchemaName = "new_parent_accountid",
            DisplayName = new Label("Account Lookup", 1033),
            RequiredLevel = new AttributeRequiredLevelManagedProperty(AttributeRequiredLevel.None),
            Description = new Label("Sample Lookup", 1033)
        }
    };


CreateOneToManyResponse resp =
    (CreateOneToManyResponse)slos.Execute(
    req);

//now you can work with the relationship values that come back.    

//_oneToManyRelationshipId =
//    createOneToManyRelationshipResponse.RelationshipId;
//_oneToManyRelationshipName =
//    createOneToManyRelationshipRequest.OneToManyRelationship.SchemaName;

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:


This is example is asynchronous, if you want to learn how to make JScript SOAP calls synchronously please visit this posthttp://mileyja.blogspot.com/2011/07/using-jscript-to-access-soap-web.html

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;
               }, 
           CreateOneToManyRequest: 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:CreateOneToManyRequest\" 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>Lookup</b:key>";
               requestMain += "            <b:value i:type=\"c:LookupAttributeMetadata\" xmlns:c=\"http://schemas.microsoft.com/xrm/2011/Metadata\">";
               requestMain += "              <c:MetadataId i:nil=\"true\" />";
               requestMain += "              <c:AttributeOf i:nil=\"true\" />";
               requestMain += "              <c:AttributeType>Lookup</c:AttributeType>";
               requestMain += "              <c:CanBeSecuredForCreate i:nil=\"true\" />";
               requestMain += "              <c:CanBeSecuredForRead i:nil=\"true\" />";
               requestMain += "              <c:CanBeSecuredForUpdate i:nil=\"true\" />";
               requestMain += "              <c:CanModifyAdditionalSettings i:nil=\"true\" />";
               requestMain += "              <c:ColumnNumber i:nil=\"true\" />";
               requestMain += "              <c:DeprecatedVersion i:nil=\"true\" />";
               requestMain += "              <c:Description>";
               requestMain += "                <a:LocalizedLabels>";
               requestMain += "                  <a:LocalizedLabel>";
               requestMain += "                    <a:IsManaged i:nil=\"true\" />";
               requestMain += "                    <a:Label>Sample Lookup</a:Label>";
               requestMain += "                    <a:LanguageCode>1033</a:LanguageCode>";
               requestMain += "                  </a:LocalizedLabel>";
               requestMain += "                </a:LocalizedLabels>";
               requestMain += "                <a:UserLocalizedLabel i:nil=\"true\" />";
               requestMain += "              </c:Description>";
               requestMain += "              <c:DisplayName>";
               requestMain += "                <a:LocalizedLabels>";
               requestMain += "                  <a:LocalizedLabel>";
               requestMain += "                    <a:IsManaged i:nil=\"true\" />";
               requestMain += "                    <a:Label>Account Lookup</a:Label>";
               requestMain += "                    <a:LanguageCode>1033</a:LanguageCode>";
               requestMain += "                  </a:LocalizedLabel>";
               requestMain += "                </a:LocalizedLabels>";
               requestMain += "                <a:UserLocalizedLabel i:nil=\"true\" />";
               requestMain += "              </c:DisplayName>";
               requestMain += "              <c:EntityLogicalName i:nil=\"true\" />";
               requestMain += "              <c:IsAuditEnabled i:nil=\"true\" />";
               requestMain += "              <c:IsCustomAttribute i:nil=\"true\" />";
               requestMain += "              <c:IsCustomizable i:nil=\"true\" />";
               requestMain += "              <c:IsManaged i:nil=\"true\" />";
               requestMain += "              <c:IsPrimaryId i:nil=\"true\" />";
               requestMain += "              <c:IsPrimaryName i:nil=\"true\" />";
               requestMain += "              <c:IsRenameable i:nil=\"true\" />";
               requestMain += "              <c:IsSecured i:nil=\"true\" />";
               requestMain += "              <c:IsValidForAdvancedFind i:nil=\"true\" />";
               requestMain += "              <c:IsValidForCreate i:nil=\"true\" />";
               requestMain += "              <c:IsValidForRead i:nil=\"true\" />";
               requestMain += "              <c:IsValidForUpdate i:nil=\"true\" />";
               requestMain += "              <c:LinkedAttributeId i:nil=\"true\" />";
               requestMain += "              <c:LogicalName i:nil=\"true\" />";
               requestMain += "              <c:RequiredLevel>";
               requestMain += "                <a:CanBeChanged>true</a:CanBeChanged>";
               requestMain += "                <a:ManagedPropertyLogicalName i:nil=\"true\" />";
               requestMain += "                <a:Value>None</a:Value>";
               requestMain += "              </c:RequiredLevel>";
               requestMain += "              <c:SchemaName>new_parent_accountid</c:SchemaName>";
               requestMain += "              <c:Targets i:nil=\"true\" xmlns:d=\"http://schemas.microsoft.com/2003/10/Serialization/Arrays\" />";
               requestMain += "            </b:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "          <a:KeyValuePairOfstringanyType>";
               requestMain += "            <b:key>OneToManyRelationship</b:key>";
               requestMain += "            <b:value i:type=\"c:OneToManyRelationshipMetadata\" xmlns:c=\"http://schemas.microsoft.com/xrm/2011/Metadata\">";
               requestMain += "              <c:MetadataId i:nil=\"true\" />";
               requestMain += "              <c:IsCustomRelationship i:nil=\"true\" />";
               requestMain += "              <c:IsCustomizable i:nil=\"true\" />";
               requestMain += "              <c:IsManaged i:nil=\"true\" />";
               requestMain += "              <c:IsValidForAdvancedFind i:nil=\"true\" />";
               requestMain += "              <c:SchemaName>new_account_campaign</c:SchemaName>";
               requestMain += "              <c:SecurityTypes i:nil=\"true\" />";
               requestMain += "              <c:AssociatedMenuConfiguration>";
               requestMain += "                <c:Behavior>UseLabel</c:Behavior>";
               requestMain += "                <c:Group>Details</c:Group>";
               requestMain += "                <c:Label>";
               requestMain += "                  <a:LocalizedLabels>";
               requestMain += "                    <a:LocalizedLabel>";
               requestMain += "                      <a:IsManaged i:nil=\"true\" />";
               requestMain += "                      <a:Label>Account</a:Label>";
               requestMain += "                      <a:LanguageCode>1033</a:LanguageCode>";
               requestMain += "                    </a:LocalizedLabel>";
               requestMain += "                  </a:LocalizedLabels>";
               requestMain += "                  <a:UserLocalizedLabel i:nil=\"true\" />";
               requestMain += "                </c:Label>";
               requestMain += "                <c:Order>10000</c:Order>";
               requestMain += "              </c:AssociatedMenuConfiguration>";
               requestMain += "              <c:CascadeConfiguration>";
               requestMain += "                <c:Assign>Cascade</c:Assign>";
               requestMain += "                <c:Delete>Cascade</c:Delete>";
               requestMain += "                <c:Merge>Cascade</c:Merge>";
               requestMain += "                <c:Reparent>Cascade</c:Reparent>";
               requestMain += "                <c:Share>Cascade</c:Share>";
               requestMain += "                <c:Unshare>Cascade</c:Unshare>";
               requestMain += "              </c:CascadeConfiguration>";
               requestMain += "              <c:ReferencedAttribute i:nil=\"true\" />";
               requestMain += "              <c:ReferencedEntity>account</c:ReferencedEntity>";
               requestMain += "              <c:ReferencingAttribute i:nil=\"true\" />";
               requestMain += "              <c:ReferencingEntity>campaign</c:ReferencingEntity>";
               requestMain += "            </b:value>";
               requestMain += "          </a:KeyValuePairOfstringanyType>";
               requestMain += "        </a:Parameters>";
               requestMain += "        <a:RequestId i:nil=\"true\" />";
               requestMain += "        <a:RequestName>CreateOneToMany</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.CreateOneToManyResponse(req, successCallback, errorCallback); };
               req.send(requestMain);
           },
       CreateOneToManyResponse: 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.CreateOneToManyRequest function from your form jscript handler.
Thats all there is to it!

I hope this helps!

7 comments:

  1. Hello,

    My requirement is to create a ManyToOne Relationship with JvavaScript in Microsoft Dynamics CRM 2011.
    I reviewed "organizationservice_schemas_
    microsoft_com_xrm_2011_metadata.xsd" but didn't find metadata for N:1 relationshio (like "OneToManyRelationshipMetadata").

    Please tell me if it is possible to fulfill my requirement directly or I have to do it using 1:N.

    Please reply.

    Thanks.

    ReplyDelete
    Replies
    1. I am still confused as to why the JavaScript example provided in this blog-post does not work for you. Did you try it?

      Delete
    2. Sorry for unclear post. Your example is working without any error.
      I know that 1:N relationship for Entity1 is N:1 for Entity2 and if I want to create N:1 relationship I can surely use above example.

      But I was just asking for that does Microsoft provide anything similar to "OneToManyRelationshipMetadata" for N:1 relationship (e.g. ManyToOneRelationshipMetadata).

      Delete
    3. They are really the same thing but switched around. There isn't a separate call for this, but there is a separate call for many to many. You just switch your ReferencedEntity and ReferencingEntity to get the effect of a many-to-one instead of a one-to-many.

      I hope this helps!

      Delete
  2. Hi Jamie,

    I have already tried that and its working fine. I was just making my self clear that I am not missing anything.

    Thanks for reply.

    ReplyDelete
    Replies
    1. You shouldn't be missing anything. You might not want to have all the relationship settings set to cascading like I have them in this example though. I would maybe think about picking a different enum value for that one depending on your scenario.

      Delete
    2. That will be great.

      Thanks again.

      Delete