Random Musings

July 6, 2009

Create simple domain generator by reading from CRM Metadata

Filed under: c#,Dynamics CRM — haditeo @ 9:30 pm
Tags: ,

Objective
I would like to create my own domain (class) from the custom CRM entity. The advantage of this is to simplify domain persistence and retrieval between application layer and database layer (CRM Web Service)

How to achieve this ?

  • First of all we utilize RetrieveEntityRequest message to retrieve all attributes of the specific entity.
                    RetrieveEntityRequest request = new RetrieveEntityRequest();
                    request.LogicalName = entityName;
                    request.EntityItems = EntityItems.IncludeAttributes;
    
                    RetrieveEntityResponse response = (RetrieveEntityResponse)metadataService.Execute(request);
    
                    EntityMetadata entityMetadata = response.EntityMetadata;
    
                    foreach (AttributeMetadata attributeMetadata in entityMetadata.Attributes)
                    {
                        //loop through all attributes here.
                    }
    
  • There are some interesting attributes of the AttributeMetadata. Such as LogicalName attribute, DisplayName attribute and AttributeType attribute
  • By looping through each of the attributes, i read the relevant template file consisting of the code templates and replace the necessary placeholder with the actual values

    private LookupProperty _#internalVariableName#;
    [CrmAttributeMapping("#crmAttributeMapping#")]
    public LookupProperty #publicPropertyName#
    {
        get { return _#internalVariableName#; }
        set { _#internalVariableName# = value; }
    }
    
    public void Set#publicPropertyName#(Guid value)
    {
        _#internalVariableName# = new LookupProperty("#crmAttributeMapping#", new Lookup("#lookupAttributeMapping#", value));
    }
    
    public bool Is#publicPropertyName#Null()
    {
        if (_#internalVariableName# == null)
            return true;
    
        if (_#internalVariableName#.Value == null)
            return true;
    
        if (_#internalVariableName#.Value.Value == Guid.Empty)
            return true;
            
        return false;
    }
    

    Here are the result of the placeholder value replacement

    private LookupProperty _owninguser;
    [CrmAttributeMapping("owninguser")]
    public LookupProperty owninguser
    {
        get { return _owninguser; }
        set { _owninguser = value; }
    }
    
    public void Setowninguser(Guid value)
    {
        _owninguser = new LookupProperty("owninguser", new Lookup("#lookupAttributeMapping#", value));
    }
    
    public bool IsowninguserNull()
    {
        if (_owninguser == null)
            return true;
    
        if (_owninguser.Value == null)
            return true;
    
        if (_owninguser.Value.Value == Guid.Empty)
            return true;
            
        return false;
    }
    
  • Some clean up needs to be performed to tidy up the public Property name, but this code generator has helped me tremendeously to eliminate boring and repititive tasks of creating custom domain objects by hand.

June 19, 2009

Reflection in C#

Filed under: Dynamics CRM — haditeo @ 10:00 pm
Tags: , ,

These are the code before it’s refactored

         private Opportunity ToOpportunity(DynamicEntity de)
         {
             try
             {
                 Opportunity opportunity = new Opportunity();

                 foreach (string attribute in opportunity.AttributesList)
                 {
                     if (!de.Properties.Contains("opportunityid"))
                         throw new PropertiesNotExistException("The Dynamic Entity type Opportunity does not have id property");

                     opportunity.Id = new KeyProperty("opportunityid", ((Key)de["opportunityid"]));

                     switch (attribute)
                     {
                         case "customerid":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetPotentialClient(((Customer)de[attribute]).Value);
                             break;
                         case "new_contactpersonid":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetContactPerson(((Lookup)de[attribute]).Value);
                             break;
                         case "new_clientcategory":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetClientCategory(((Picklist)de[attribute]).Value);
                             break;
                         case "name":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetPotentialProjectTitle(de[attribute].ToString());
                             break;
                         case "new_potentialprojecttitlechinese":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetPotentialProjectTitleInChinese(de[attribute].ToString());
                             break;
                         case "new_accountmanager":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetAccountManager(de[attribute].ToString());
                             break;
                         case "new_kba":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetKba(((Picklist)de[attribute]).Value);
                             break;
                         case "new_servicetype":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetServiceType(((Picklist)de[attribute]).Value);
                             break;
                         case "new_developmenttype":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetProjectDevelopmentType(((Picklist)de[attribute]).Value);
                             break;
                         case "new_regionopportunityid":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetRegion(((Lookup)de[attribute]).Value);
                             break;
                         case "new_location":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetLocation(de[attribute].ToString());
                             break;
                         case "new_countryopportunityid":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetCountry(((Lookup)de[attribute]).Value);
                             break;
                         case "description":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetDescription(de[attribute].ToString());
                             break;
                         case "new_source":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetSource(((Picklist)de[attribute]).Value);
                             break;
                         case "new_estimatedprojectcost":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetEstimatedProjectCost(((CrmMoney)de[attribute]).Value);
                             break;
                         case "estimatedvalue":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetEstimatedProjectFees(((CrmMoney)de[attribute]).Value);
                             break;
                         case "new_estimatedfeespercentage":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetEstimatedFeesPercentage(((CrmDecimal)de[attribute]).Value);
                             break;
                         case "new_estimatedprojectstartdate":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetEstimatedProjectStartDate(((CrmDateTime)de[attribute]).UserTime);
                             break;
                         case "new_estimatedprojectenddate":
                             if (de.Properties.Contains(attribute))
                                 opportunity.SetEstimatedProjectEndDate(((CrmDateTime)de[attribute]).UserTime);
                             break;
                     }
                 }
                 return opportunity;
             }
             catch (SoapException soapExc)
             {
                 throw soapExc;
             }
             catch (Exception exc)
             {
                 throw exc;
             }

         }

and these are the code after it’s refactored

        public Opportunity ToOpportunity(DynamicEntity de)
        {
            try
            {
                Assembly sdkDll = Assembly.LoadFrom("microsoft.crm.sdk.dll");

                Opportunity newOpportunity = new Opportunity();

                foreach(PropertyInfo pi in  typeof(Opportunity).GetProperties())
                {
                    string crmFieldMapping = ((CrmFieldMapping)(pi.GetCustomAttributes(typeof(CrmFieldMapping), false))[0]).GetCrmFieldMapping();
                    string crmTypeProperty = ((CrmTypeProperty)(pi.GetCustomAttributes(typeof(CrmTypeProperty), false))[0]).GetCrmTypeProperty();

                    if (de.Properties.Contains(crmFieldMapping))
                    {
                        Type currentCrmTypeProperty = sdkDll.GetType("Microsoft.Crm.Sdk." + crmTypeProperty);

                        object obj = Activator.CreateInstance(currentCrmTypeProperty, crmFieldMapping, de[crmFieldMapping]);

                        pi.SetValue(newOpportunity, obj, null);
                    }
                }

                return newOpportunity;
            }
            catch (SoapException soapExc)
            {
                throw soapExc;
            }
            catch (Exception exc)
            {
                throw exc;
            }

        }

Currently i’m closely applying Domain Driven Development by creating domain objects first. The previous source code is created without using reflection. Yesterday, i was wondering and got the idea of decorating the domain object with custom attributes as metadata, so that i can easily refer to it when reflecting the object at runtime.

These are the sample of the custom attribute :

    public class Opportunity
    {
        private KeyProperty opportunityId;
        [CrmTypeProperty("KeyProperty")]
        [CrmFieldMapping("opportunityid")]
        public KeyProperty Id
        {
            get { return opportunityId; }
            set { opportunityId = value; }
        }

        private StringProperty _projectTitle;
        [CrmTypeProperty("StringProperty")]
        [CrmFieldMapping("name")]
        public StringProperty PotentialProjectTitle
        {
            get { return _projectTitle; }
            set { _projectTitle = value; }
        }
   }

What are the advantages of using reflection ? Now, let’s say i am adding another property at the domain object, i just need to update the domain object and i don’t need to change the code at the data layer.

June 14, 2009

Interface example in the real world

Filed under: Book Review — haditeo @ 4:05 pm
Tags: ,

Today, i discovered a book titled “Interface-Oriented Design” published by The Pragmatic-Programmers series, when i made a trip to the local library here.

The sample PizzaOrdering interface at chapter 1 made me interesting. The interface concept is described clearly in day-to-day daily scenario.

Here are the sample discussion when you’re ordering Pizza :

Waiter : “Yo”
Me : “I would like to order order large pizza”
Waiter : “What topping do you want ?”
Me : “Cheese with pepperoni”
Waiter : “Where is your address ?”
Me : “1 Oak Street”
Waiter : “Please wait 30 minutes for your order. Thanks”

Imagine that this conversation is actually similar to most of the pizza restaurant. When it’s translated to the interface, it looks like this

public interface IPizzaOrder
{
  public interface enum Size {Small, Medium, Large};
  public interface string Topping {get; set;}
  public interface string Address {get; set;}
  public interface DateTime RetrieveEstimatedTimeCompletion();
}

Imagine that restaurant A just need to implement the IPizzaOrder interface in order to “open” a pizza restaurant. Restaurant B also. Clients / customers just need to “set” the proper attributes, such as “Size”, “Topping”, “Address” and finally invoked the RetrieveEstimatedTimeCompletion method in order to know how long is it taking to receive the pizza.

June 12, 2009

Using App.Config in C# dll library

Filed under: Dynamics CRM — haditeo @ 5:03 pm
Tags: ,

Here are the steps to use App.Config in dll;
1) Make sure that the build action of the App.Config is ‘Content’ and Copy to Output Directory = ‘Copy always’
2) Make sure that the App.Config file is placed in the \bin\Debug folder
3) Read the App.Config by using XmlDocument.Load() method

            XmlDocument doc = new XmlDocument();
            doc.Load(AppDomain.CurrentDomain.BaseDirectory + "\\" + "App.Config");

            XmlNode appSettingsNode = doc.GetElementsByTagName("appSettings")[0];

            foreach(XmlNode node in appSettingsNode.ChildNodes)
            {
                switch (node.Attributes["key"].Value)
                { 
                    case "Domain":
                        nc.Domain = node.Attributes["value"].Value;
                        break;
                    case "UserName":
                        nc.UserName = node.Attributes["value"].Value;
                        break;
                    case "Password":
                        nc.Password = node.Attributes["value"].Value;
                        break;
                    case "AuthenticationType":
                        token.AuthenticationType = Convert.ToInt32(node.Attributes["value"].Value);
                        break;
                    case "OrgName":
                        token.OrganizationName = node.Attributes["value"].Value;
                        break;
                    case "CrmUrl":
                        crmService.Url = node.Attributes["value"].Value;
                        break;
                }
            }

The purpose of the AppDomain.CurrentDomain.BaseDirectory property is to locate the actual path of the \bin\Debug folder

June 1, 2009

File Property – Build Action To Compile

Filed under: Uncategorized — haditeo @ 8:53 pm
Tags:

BuildActionToCompile

Today, i really scratched my head. The class file has been included properly into a dll, it has been given public so that it can be seen.

But when i browsed it from the dll browser, the public properties and methods are not seen, and then i carefully see that the build action is set to ‘content’ . No wonder it cannot be seen. It should be set to ‘compile’

Blog at WordPress.com.