Microsoft Dynamics CRM 2011

Microsoft Dynamics CRM 2011
Showing posts with label Attributes. Show all posts
Showing posts with label Attributes. Show all posts

Wednesday, April 30, 2014

"The given key was not present in the dictionary" KeyNotFoundException Exception in CRM 2013 Plugin or custom Workflow

by Carmel Shvartzman

In this walkthrough we will learn how to take care of the "The given key was not present in the dictionary" - "KeyNotFoundException" error in a Dynamics CRM 2013 (2011) Plug-in or custom Workflow.
  1. A disadvantage of using late-binding when coding a CRM 2013 Plugin is, you shoud be very careful with the attributes names, since there is no compile time checking. 
  2. Specially when you have added a new field to an entity, and you get the "The given key was not present in the dictionary" Exception, you'll be prone to think something is not good with the new field definitions, or worse , the Organization Web Service is not recognizing the new field, or even worse, the Web Service needs to be reset to render the field, or may be you'd be desperately thinking to restart the CRM server to refresh the Web Service.
  3. That's because the error message by CRM may be misleading : you may be expecting that, in case your new field contains a null value, CRM Retrieve() or RetrieveMultiple() or Execute() will locate the field attribute, and fetch a NULL value for it. But CRM wouldn't : it WILL NOT INCLUDE THE KEY IN THE ATTRIBUTE'S DICTIONARY UNLESS IT CONTAINS A VALUE : if your field is not a REQUIRED field, you must code a checking for the presence or absence of the field.
  4. For instance, let's say you've added a new field named "new_test", and inside your Plug In you fetch it as follows:
  5. If the present entity has a NULL value in it , you'll be faced to the "The given Key..." exception:
  6. First think you'll want to check, is whether you requested for the field in the ColumnsSet:
  7. But that's not the problem. You did indeed. However, the "new_test" field attribute IS NOT THERE:

  8. That's because CRM WILL NOT INCLUDE THE KEY IN THE ATTRIBUTE'S DICTIONARY UNLESS IT CONTAINS A VALUE (meaning it was SET or UPDATED).
  9. You MUST perform a check for the field existence, but not a NULL check as this:
  10. You should instead use the ContainsKey(string) method from the Collection, for EVERY NO REQUIRED FIELD on the Entity:



    Hoping it helped you.
    That's all...Enjoy Dynamics CRM


    כתב: כרמל שוורצמן

Tuesday, November 19, 2013

How to Extend the Dynamics CRM Entity Class to enable Indexing for Late Binding

by Carmel Schvartzman

  1. In this walkthrough we will learn Step-By-Step  How to Extend the CRM2011 Entity class enabling Indexing while calling a Web Service in Dynamics CRM 2011. We'll call the CRM 2011 WCF Web Service and perform CRUD operations using Indexing on the generic Entity class.  We'll consume the CRM 2011 Organization (SOAP) services from a class which can also be used by a console application, or a Windows Service, or a Windows Form application.
    We'll not use the Microsoft CRM SDK designed for Early Binding; instead we'll reach directly the CRM 2011 WCF SOAP endpoint to perform the CRUD operations for Late Binding.
  2. By enabling Indexing we mean that we could access an Entity property getting/setting its value, by using an " entity["field_name"]  "  sintax, while reaching Crm2011 from a WIN app via its Organization Web Service. Therefore, a code like the following....:
    How to Extend the Dynamics CRM Entity Class to enable Indexing for Late Binding

    ... will allows us to create a new Contact, for example:
  3. In order to create a client for the CRM2011 Organization Service, we'll create a .dll which will send requests to the CRM2011 Organization Web Service Endpoint. Then, we'll create a WIN project to use that assembly and fetch the CRM data, and also create new entities.
  4. So let's create a new Class Project in Visual Studio 2010 or 2008, selecting the target framework to be version 3.5:
  5. Next, add the CRM 2011 Web Service reference to the project:
  6. Remember that we'll be using the Organization CRM Web Service, so type the address:
  7. After adding the Web Service, rename the Class :
  8. Code an static method returning an IOrganizationService object. This function will receive as parameters the host, organization and user logon data:
  9. Now add the following security code for reaching the CRM Web Service :





    SymmetricSecurityBindingElement security = new SymmetricSecurityBindingElement();
    security.ProtectionTokenParameters = new SspiSecurityTokenParameters();
    HttpTransportBindingElement httpTransport = new HttpTransportBindingElement();
    httpTransport.MaxReceivedMessageSize = Int32.MaxValue ;
    CustomBinding binding = new CustomBinding();
    binding.Elements.Add(security);
    TextMessageEncodingBindingElement encoding =
    new TextMessageEncodingBindingElement(MessageVersion.Soap12WSAddressing10, Encoding.UTF8);
    binding.Elements.Add(encoding);
    binding.Elements.Add(httpTransport);

     
  10. Now add the WCF Endpoint code:



    EndpointAddress endpoint =
    new EndpointAddress(new Uri(string.Format("{0}/{1}/XRMServices/2011/Organization.svc", host, organization)),
    EndpointIdentity.CreateUpnIdentity(string.Format("{0}@{1}", user, "")), new AddressHeader[] { });

     
  11. And finally add the code to create the Client:





    OrganizationServiceClient client = new OrganizationServiceClient(binding, endpoint);
    client.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(user, password, "");

     
  12. Check that we have all the "usings" we need:
  13. Also, check whether all the references are listed:
  14. Now, in order to perform the testings on our class, create a new console Project , which will call our CRM assembly:
  15. Add the following references:
  16. First, add the Runtime.Serialization assembly:
  17. Second, add our class assembly:
  18. Now, code a call to the IOrganizationService:
  19. Add the corresponding using:
  20. ... and type the relevant logon data, for security reasons:
  21. Next, select some columns that you want to retrieve, and instantiate the entity object:
  22. Check in the QuickWatch window that you get an account with the required attributes:


  23. Now, try to use the  "account["field_name"]" sintax, that means to use Indexing on the Entity class:
  24. It seems that,  if we want to use the "account["field_name"]" sintax, we'll be confronted with an error:
  25. Same thing will happen if we try to create a new record, let's say a new Contact:
  26. According to the error message, Indexing cannot be applied to an Entity.
  27. To solve the problem, let's add Indexing support to the Entity class. We'll extend the Entity class by using two collections: the FormattedValueCollection and the RelatedEntityCollection:
  28. Loockup also the RelatedEntityCollection documentation on MSDN:
  29. Both collections are defined as OptionalFieldAttribute on the Serialization assembly, meaning that the formatters will not require such fields while serializing the Entity class:
  30. Take a look at the FormattedValuesField in the "References" .cs file on your project:


    As you can see, the Entity class is partial, can be extended, and the FormattedValuesField is an optional collection.
  31. Therefore, the first thing we'll do is instantiate those two fields in the partial Entity constructor:
  32. Next, we'll add the Indexing feature to allow a "entity["field_name"] " sintax:
  33. Then, code the get/set functions of the indexing using the AttributeCollection class:
  34. Press "F12" on the AttributeCollection to see that object:


    It's a generic List<> of  KeyValuePair<string,object>, therefore let's code according to that.
  35. Create a static class to hold the Extensions we need to interact with that List<>. Inside the static class, type the get - set extension methods for that List<>:
  36. There are two simple cases that can happen while getting-setting a value from-to a collection. Let's code against them first. In the case of the "get" extension method, prepare to throw an exception if the key does not exists:
  37. In the case of the "set" extension method, the value does not exists in the collection, so just add it to the List.
  38. Next, the two important cases are when the value is in the collection , and we need to know the index it is in, in order to fetch it....:


    ...and when the value is in the collection, and we must override it with the new value. In both cases we'll use a method to get the index of the cell holding the value: GetIndex<K,V>(IList<> col,K key, out i).
  39. Create the  GetIndex<K,V>(IList<> col,K key, out i) method:
  40. Code the basic case in which the collection is null:
  41. .. Next add the code for the Index search:
  42. We're done. Compile and run the WIN Startup Project which uses the assembly:

    We can see that the Indexing is now working, and we can fetch the Account name, and also create a new Contact record.
  43. Finally, let's check our CRM 2011 Workplace to see the new Contact added:




    That's all...Enjoy Dynamics CRM


    כתב: כרמל שוורצמן




Tuesday, November 12, 2013

How to Integrate Dynamics CRM with SSIS

by Carmel Schvartzman

  1. In this walkthrough we will learn Step-By-Step How to integrate CRM2011 with SSIS through a Web Service in Dynamics CRM 2011 and Sql Server Integration Services.
    We'll call the CRM 2011 WCF Web Service and perform CRUD operations using Indexing on the generic Entity class. We'll consume the CRM WCF Organization (SOAP) service from a class which we will use from inside an SSIS project, but the same logic can also be used by a console application, or a Windows Service, or a Windows Form application.
    We'll not use the early binding Microsoft CRM SDK 2011; instead we'll reach directly the CRM 2011 WCF SOAP endpoint using Late Binding to perform the required CRUD operations.
  2. Sql Server Integration Services as implemented through Visual Studio 2008, support only the Framework 3.5 libraries at most. Therefore, we cannot use the CRM SDK and its framework 4.0  assemblies while coding from SSIS 2008. Therefore , we'll reach Crm2011 from SSIS via its Organization Web Service.
  3. First, we'll create a .dll which will send requests to the CRM2011 Organization Web Service . Then, we'll create an SSIS project to use that assembly and fetch the CRM data, and also create new entities from SSIS.
  4. So let's create a new Class Project in Visual Studio 2010 or 2008, selecting the target framework to be version 3.5:
    How to Integrate Dynamics CRM with SSIS
  5. Next, add the CRM 2011 Web Service to the project:
  6. Remember that we'll be using the Organization CRM Web Service:
  7. After adding the Web Service, rename the Class to CrmInit:
  8. Create an static method returning an IOrganizationService object. This function will get the host, organization and user logon data:
  9. Now add the following security standard code for reaching the CRM Web Service :





    SymmetricSecurityBindingElement security = new SymmetricSecurityBindingElement();
    security.ProtectionTokenParameters = new SspiSecurityTokenParameters();
    HttpTransportBindingElement httpTransport = new HttpTransportBindingElement();
    httpTransport.MaxReceivedMessageSize = Int32.MaxValue ;
    CustomBinding binding = new CustomBinding();
    binding.Elements.Add(security);
    TextMessageEncodingBindingElement encoding =
    new TextMessageEncodingBindingElement(MessageVersion.Soap12WSAddressing10, Encoding.UTF8);
    binding.Elements.Add(encoding);
    binding.Elements.Add(httpTransport);
  10. Now append the WCF Endpoint code:



    EndpointAddress endpoint =
    new EndpointAddress(new Uri(string.Format("{0}/{1}/XRMServices/2011/Organization.svc", host, organization)),
    EndpointIdentity.CreateUpnIdentity(string.Format("{0}@{1}", user, "")), new AddressHeader[] { });



  11. And finally add the code to craete the Client:





    OrganizationServiceClient client = new OrganizationServiceClient(binding, endpoint);
    client.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential(user, password, "");
  12. Check that you have all the "usings" you need:
  13. Also, check the references:
  14. Now, in order to test our class, create a new console Project , which will call our CRM assembly:
  15. Add the following references:
  16. First, add the Serialization assembly:
  17. Second, add our assembly:
  18. Now, code a call to the IOrganizationService:
  19. Add the corresponding using:
  20. ... and type the relevant logon data:
  21. Next, select some columns you want to retrieve, and get the entity object:
  22. Check in the QuickWatch window that you get an account with the required attributes:


  23. Now we want to use the dll, so get to the BIN folder, and get it:
  24. .. and paste it to some relevant folder where you'll keep it:


    Also, paste it to the SSIS folder (if you decide to put the assembly inside the GAC, you could skip this step):
    C:\Program Files (x86)\Microsoft SQL Server\100\DTS\Binn
  25. Now it's time to create the SSIS project. Open Visual Studio 2008, and create the project:
  26. Search in the toolbox for an Script Task:
  27. Drop the task and rename it:
  28. Change the project properties:
  29. .. in order to run the debbugger in 32 bits mode:


  30. Now open the editor of the task:
  31. Go to the Properties window:
  32. ... and change the target Framework to 3.5:
  33. Now go to the references tab:
  34. ... and add a reference to the Serialization assembly:
  35. ... and to our CRM assembly:
  36. Add the "using" directive to make visible the assembly's classes:
  37. Now go to the CRMProxy project, and copy the code we tested inside our Console project:
  38. Return to the SSIS project and paste the code inside the Main function:
  39. .. and add a breakpoint:
  40. Build the Script Task :
  41. Close the VisualStudio Tool For Applications (VSTA) project, and save the Script Task:
  42. Debug the SSIS project until the breakpoint is reached:
  43. When the debugger reaches the breakpoint, check for the account data returned from the Web Service:
  44. We got the CRM data. Let's now retrieve the Account name:
  45. Run again the debugger, and check for the returned Account name:
  46. It seems OK. We fetch CRM2011 data from the SSIS. Now let's create a new Entity record from the SSIS. Add the following code to create a new Competitor record:
  47. Run the SSIS, and open your CRM2011 to see the new added Competitor:






    This late Entity creation using the "Attributes" ( [""] ) feature, is the result of extending the Entity class to allow for string indexing using the field names. We extended the Entity class in this walkthrough.

    That's all...Enjoy Dynamics CRM


    כתב: כרמל שוורצמן