29 November 2010

CRM 2011 field names as constants

Hi to everyone!
I have started my development against Dynamics CRM 2011 using beta version of SDK. I used CrmSvcUtil.exe command line utility to generate classes which represents the entity data model. Now we are not going to use the WSDL.
Short exploration of generated classes allow us to write first impressions.

All the classes now inherit Entity class, which replaces DynamicEntity from CRM 4.0. It means that now we are able to use strongly typed classes in plug ins. EntityName class is disregarded in CRM 2011. But each from generated classes contain property EntityLogicalName. We may use it in next way:

Contact theContact = service.Retrieve(Contact.EntityLogicalName, guid, new Microsoft.Xrm.Sdk.Query.ColumnSet(true)).ToEntity<Contact>();


Line new Microsoft.Xrm.Sdk.Query.ColumnSet(true) is equal to new AllColumns() in CRM 4.0. The parameter true means that we will use all available for the entity properties. But we are also able to choice only few properties. In this case we need to point out names of the properties. I don't found the way how to retrieve logical names of  properties. In case if we are talking about entity names we might use EntityLogicalName. In case of logical names of properties, we don't have similar property. And I don't like to use string constants like "Telephone1" as property name. It could cause further errors and it decreases maintainability. Therefore I prefer to have a class with string constants as logical names of fields. Below I provide the code which generates such class. We might use OrganizationService to retrieve list of available entities and its properties names. But I used for this purpose generated classes with its EntityLogicalNameAttribute attribute and its properties with AttributeLogicalNameAttribute attributes.



class Program
{
  /// <summary>
  /// Method to populate a list with all the class
  /// in the assembly where located Contact class
  /// with all properties logical names
  /// </summary>
  /// <returns></returns>
  static IList<KeyValuePair<string, IList<KeyValuePair<string, string>>>> GetAllClasses()
  {
    //create an Assembly
    Assembly asm = Assembly.GetAssembly(typeof(Contact));


    //create a list that will hold all the classes
    IList<KeyValuePair<string, IList<KeyValuePair<string, string>>>> returnList = new List<KeyValuePair<string, IList<KeyValuePair<string, string>>>>();


    //loop through all the "Types" in the Assembly
    foreach (Type type in asm.GetTypes())
    {
      System.Reflection.MemberInfo info = type;
      
      //Retrive EntityLogicalNameAttribute custom attributes
      object[] attributes = info.GetCustomAttributes(typeof(Microsoft.Xrm.Sdk.Client.EntityLogicalNameAttribute), true);


      //if class has EntityLogicalNameAttribute add it to our list
      if (attributes.Length > 0)
      {
        //Retrieve array of properties PropertyInfo
        PropertyInfo[] propertyInfos = type.GetProperties();
        IList<KeyValuePair<string, string>> propertyList = new List<KeyValuePair<string, string>>(propertyInfos.Length);


        //Iterate through array of PropertyInfo[]
        foreach (var propertyInfo in propertyInfos)
        {
          //Retrieve attribute AttributeLogicalNameAttribute
          object[] propertyAttributes = propertyInfo.GetCustomAttributes(typeof(Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute), true);
          //If property has attribute AttributeLogicalNameAttribute
          //add it to our list of properties
          if (propertyAttributes.Length > 0)
          {
            Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute attribute = (Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute)propertyAttributes[0];
            propertyList.Add(new KeyValuePair<string, string>(propertyInfo.Name, attribute.LogicalName));
          }
        }
        returnList.Add(new KeyValuePair<string,IList<KeyValuePair<string, string>>>(info.Name, propertyList));
      }
    }


    //return the list
    return returnList;
  }


  static void Main(string[] args)
  {
    const string CLASS_NAME = "CustomEntities";
    const string NAMESPACE_NAME = "DataModelNamespace";


    IList<KeyValuePair<string, IList<KeyValuePair<string, string>>>> classesList = GetAllClasses();


    // Write the string to a file.
    System.IO.StreamWriter file = new System.IO.StreamWriter(CLASS_NAME + ".cs");
    
    file.WriteLine("namespace " + NAMESPACE_NAME);
    file.WriteLine("{");
    file.WriteLine("    public static class " + CLASS_NAME);
    file.WriteLine("    {");
    foreach (var cls in classesList)
    {
      file.WriteLine("        public static class " + cls.Key);
      file.WriteLine("        {");
      foreach (var ppt in cls.Value)
      {
        file.WriteLine("            public const string " + ppt.Key + " = \"" + ppt.Value + "\";");
      }
      file.WriteLine("        }");
      file.WriteLine("");
    }
    file.WriteLine("    }");
    file.WriteLine("}");


    file.Close();
  }
}


GetAllClasses() method return list of classes with EntityLogicalNameAttribute and with list of names of properties and AttributeLogicalNameAttribute values. Generated class allow us to use it in next way:

Contact theContact = service.Retrieve(Contact.EntityLogicalName, guid, new Microsoft.Xrm.Sdk.Query.ColumnSet(CustomEntities.Contact.EMailAddress1)).ToEntity<Contact>();

3 comments:

  1. Contact theContact = service.Retrieve(Contact.EntityLogicalName, guid, new Microsoft.Xrm.Sdk.Query.ColumnSet(CustomEntities.Contact.EMailAddress1)).ToEntity();

    What is "guid" used for? When i try to run this code i get an error: "Expected non-empty Guid."

    ReplyDelete
  2. Variable 'guid' is 'id' of existing in the CRM database entity instance. For 'contact' entity it should be 'contactid'. For testing you could extract it from URL of contact form.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete