Random Musings

June 25, 2009

How to: Add the CrmService Web Reference

Filed under: Dynamics CRM — haditeo @ 9:29 pm
Tags: ,

I seldom use CRM Service proxy Web Reference and i almost forgot that there is a difference in invoking the web service between CRM 3.0 and CRM 4.0

Here is the CRM 3.0 way:

http://<your server name>/mscrmservices/2006/crmservice.asmx

and here is the CRM 4.0 way:

http://<servername[:port]>/mscrmservices/2007/crmservice.asmx?WSDL&uniquename=organizationName

No wonder when i invoke using below code, i still can obtain the CrmService, but i cannot find any proxy entities there

http://<your server name>/mscrmservices/2007/crmservice.asmx

After i using the correct way, i can refer to the CRM entities through intellisense as usual
class_account

intellisense

I forgot that in the CRM 4.0, there is a multitenancy concept, so we can create multiple organization within one Dynamics CRM deployment, so to refer to the particular organization CRM entities, we can customize the organization name below

http://<servername[:port]>/mscrmservices/2007/crmservice.asmx?WSDL&uniquename=organizationName

Type.GetType() return null value

Filed under: Dynamics CRM — haditeo @ 9:06 pm

Here are my code snippet that returning null value when invoking Type.GetType()

        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;
            }

        }

The above codes are working well when i have tested by testing the dll using NUnit. But when i deployed it as a custom workflow activity, these code snippet returned null value. There is an IOException complained by the system. In the details of the Exception, it’s mentioned that it cannot find the microsoft.crm.sdk.dll at the ‘c:\windows\system32’. I have tried to copy the code manually to \windows\system32 but it still cannot find the correct dll, maybe i should register it through the GAC.

Type currentCrmTypeProperty = sdkDll.GetType("Microsoft.Crm.Sdk." + crmTypeProperty);

Anyway, i have another alternative by directly using Type.GetType as shown below. I was reluctant to use the Full Assembly Qualified Name, but somehow when referring to microsoft.crm.sdk.KeyProperty, it still cannot find, unless i specified Full Qualified Name

 Type currentCrmTypeProperty = Type.GetType("Microsoft.Crm.Sdk." + crmTypeProperty + ", Microsoft.Crm.Sdk, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); 

And here are the modified code

        public Opportunity ToOpportunity(DynamicEntity de)
        {
            try
            {
                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 = Type.GetType("Microsoft.Crm.Sdk." + crmTypeProperty + ", Microsoft.Crm.Sdk, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"); 
                                                 
                        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;
            }

        }

June 21, 2009

Using reflection to iterate over all the properties inside an object

Filed under: Dynamics CRM — haditeo @ 6:04 pm
Tags: ,

Here are the code snippet before it’s refactored using reflection :

        public DynamicEntity Convert(Project newProject)
        {
            try
            {
                DynamicEntity deProject = new DynamicEntity();
                deProject.Name = "new_project";


                if (newProject.IsProjectRefExist())
                    deProject.Properties.Add(newProject.ProjectRef);

                if (newProject.IsClientExist())
                    deProject.Properties.Add(newProject.Client);

                if (newProject.IsPrimaryContactExist())
                    deProject.Properties.Add(newProject.PrimaryContact);

                if (newProject.IsClientCategoryExist())
                    deProject.Properties.Add(newProject.ClientCategory);

                if (newProject.IsProjectTitleExist())
                    deProject.Properties.Add(newProject.ProjectTitle);

                if (newProject.IsProjectTitleInChineseExist())
                    deProject.Properties.Add(newProject.ProjectTitleInChinese);

                if (newProject.IsAccountManagerExist())
                    deProject.Properties.Add(newProject.AccountManager);

                if (newProject.IsKbaExist())
                    deProject.Properties.Add(newProject.Kba);

                if (newProject.IsServiceTypeExist())
                    deProject.Properties.Add(newProject.ServiceType);

                if (newProject.IsDevelopmentTypeExist())
                    deProject.Properties.Add(newProject.DevelopmentType);

                if (newProject.IsRegionExist())
                    deProject.Properties.Add(newProject.Region);

                if (newProject.IsLocationExist())
                    deProject.Properties.Add(newProject.Location);

                if (newProject.IsCountryExist())
                    deProject.Properties.Add(newProject.Country);

                if (newProject.IsDescriptionExist())
                    deProject.Properties.Add(newProject.Description);

                if (newProject.IsSourceExist())
                    deProject.Properties.Add(newProject.Source);

                if (newProject.IsEstimatedProjectCostExist())
                    deProject.Properties.Add(newProject.EstimatedProjectCost);

                if (newProject.IsEstimatedProjectFeesExist())
                    deProject.Properties.Add(newProject.EstimatedProjectFees);

                if (newProject.IsEstimatedFeesPercentageExist())
                    deProject.Properties.Add(newProject.EstimatedFeesPercentage);

                if (newProject.IsEstimatedProjectStartDateExist())
                    deProject.Properties.Add(newProject.EstimatedProjectStartDate);

                if (newProject.IsEstimatedProjectEndDateExist())
                    deProject.Properties.Add(newProject.EstimatedProjectEndDate);

                return deProject;
            }
            catch (System.Web.Services.Protocols.SoapException ex)
            {
                throw ex;
            }
            catch (Exception e)
            {
                throw e;
            }
        }

and here are the code after it’s refactored

        public DynamicEntity Convert(Project newProject)
        {
            try
            {
                DynamicEntity deProject = new DynamicEntity();
                deProject.Name = "new_project";

                foreach (PropertyInfo projectProperty in typeof(Project).GetProperties())
                {
                    // skip if the method is not exist
                    if ((typeof(Project).GetMethod("Is" + projectProperty.Name + "Exist")) == null)
                        continue;

                    bool isPropertyHasValue = (bool)(typeof(Project).GetMethod("Is" + projectProperty.Name + "Exist")).Invoke(newProject, null);

                    //don't add to the Project's dynamic entity with the null value of the current Project property
                    if (!isPropertyHasValue)
                        continue;

                    deProject.Properties.Add((Property)projectProperty.GetValue(newProject, null));
                }

                return deProject;
            }
            catch (System.Web.Services.Protocols.SoapException ex)
            {
                throw ex;
            }
            catch (Exception e)
            {
                throw e;
            }
        }

Reflection is cool. What reflection techniques that i am using it here ?

  • I am using these code snippet to iterate to all the properties inside an object. The advantage of this is i do not need to hard code additional lines when my business object gets added with additional attributes
    foreach(PropertyInfo projectProperty in typeof(Project).GetProperties())
    {}
    
  • I am using the GetMethod() to check whether a particular method name is indeed exist inside the object
    if ((typeof(Project).GetMethod("Is" + projectProperty.Name + "Exist")) == null)
    
  • I am using the Invoke() to invoke the particular method. Fortunately i do not need to pass the parameter
    (bool)(typeof(Project).GetMethod("Is" + projectProperty.Name + "Exist")).Invoke(newProject, null);
    
  • As usual, i always use the GetValue method to obtain the current property value for the particular object

    (Property)projectProperty.GetValue(newProject, null)
    

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 18, 2009

ILMerge : A tool to merge your multiple assemblies into one assembly

Filed under: Dynamics CRM — haditeo @ 11:48 pm
Tags:

This ILMerge has rescued me once more.

I was developing a Dynamics CRM Custom Workflow activity. As usual, the assembly needs to be signed correctly and there is one problem though. Only one assembly can be registered using the Plugin Registration Tool.

Currently i like to have a minimum 4 assemblies in my custom solution. Here are they :
1) Domain
2) Repository
3) Utilities or Helper
4) Either web, workflow or plugin dll anything
5) Usually i have a test library to be tested using NUnit

The ILMerge has a GUI tool to automate the merging of all your assemblies, but you can still use the command line. Anyway the GUI tool will execute the ILMerge command line to merge your assemblies

Here is the sample of using ILMerge

ILMerge /t:library /out:CustomLibrary.dll DomainLibrary.dll RepositoryLibrary.dll Utilities.dll Workflow.dll

/t option means we are emitting a dll library. We can also emit an executable file
/out option means we are specifying CustomLibrary.dll as the name of the one dll to be produced by the ILMerge
All the libraries that we would like to be merged should be specified individually with the space between them

June 16, 2009

Error 401 while using NetworkCredential

Filed under: Dynamics CRM — haditeo @ 10:02 am

While running these code snippet to invoke Dynamics CRM Web Service using NetworkCredential, i have encountered the famous HTTP Error 401.2 – Unauthorized Access is Denied

CrmAuthenticationToken token = new CrmAuthenticationToken();
token.AuthenticationType = 0;
token.OrganizationName = "123";

CrmService service = new CrmService();
service.Url = "http://SERVER123/mscrmservices/2007/crmservice.asmx";
service.CrmAuthenticationTokenValue = token;

NetworkCredential nc = new NetworkCredential();
nc.Domain = "STAFF";
nc.UserName = "Administrator";
nc.Password = "whatisthepassword";

service.Credentials = nc;

Fiddler

Notice the current domain name. It’s specified as “STAFF.COM”, but inside the code above, i have specified it as “STAFF”. After i changed it to “STAFF.COM”, it’s working well at the 1st HTTP request. In my assumption, i don’t need to include Full Qualified Domain Name (FQDN), but eventually the system must accept a FQDN

AD

Here are the things that i have tried before i found the current solution:

  • Downloaded Authentication Diagnostic to help pointing whether the current user (STAFF\Administrator) has proper rights and privileges to the current server.

    Found out that the current Dynamics CRM application pool is run with NetworkService user account, it needs to be changed to run under a valid user account.

  • Setspn command is also used to create SPN for the current user (STAFF\Administrator) to be authenticated properly in the current user
  • Tried to use DefaultCredentials and DefaultNetworkCredentials, turned out that it’s working well.

    service.Credentials = CredentialCache.DefaultCredentials;
    
    service.Credentials = CredentialCache.DefaultNetworkCredentials;
    

    At this point, i am very confuse, why it’s working on DefaultNetworkCredentials , and found out the domain name that i have specified is incorrect.

June 15, 2009

Open thread token failed error message

Filed under: Dynamics CRM — haditeo @ 9:57 pm
Tags: ,

While i was using ExtractCrmAuthenticationToken method, i have encountered this error message “Open thread token failed with hr = 1008” while running the solution. The compilation is working well.

OpenThreadToken

Why the error message is appeared ?
1) I placed the .ASPX custom solution outside of Dynamics CRM website.
2) In order to extract CRM authentication token, the custom solution must be placed under the Dynamics CRM website so that it can extract the authentication token which is utilized by CRM in the web.config . Here is the web.config definition taken from Dynamics CRM solution

<httpModules>
    <add name ="MapOrg" type="Microsoft.Crm.MapOrgEngine, Microsoft.Crm, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    <add name ="CrmAuthentication" type="Microsoft.Crm.Authentication.AuthenticationEngine, Microsoft.Crm, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</httpModules>

The solution is :
1) Place the custom ASPX solution under the Dynamics CRM website in order to utilize the ExtractCrmAuthenticationToken method, so that this method can use the CrmAuthentication module.

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 13, 2009

nHibernate SchemaExport

Filed under: nhibernate — haditeo @ 3:02 pm
Tags:

Today i spent some time to learn nHibernate for the first time. This article is really fabolous. Credit to Gabriel Schenker as the author.

When i followed the article, somehow i noticed that the ‘Category’ records that have been added to the database was wiped out. Very very strange indeed, i cannot find any exception araised, the nUnit tests are working properly.

My first investigation is to turn on the profiler and these results come up :

table is dropped

Please be aware that i have created the Category table in the database, so why this nHibernate drop my ‘Category’ table? That means somewhere inside the code, it re-creates the necessary table for me automatically.

Finally i found the culprit, after careful debugging and googling, this line is the culprit

[SetUp]
public void SetupContext()
{
    new SchemaExport(_configuration).Execute(false, true, false, false);
}

The SchemaExport method automatically create the necessary tables for me based on the Domain that i have been created. This is cool ! I noticed that in the article, i didn’t even have to create a single table in the database. Fabolous

I have also found this article NHibernate:How to Build Great Mappings and use SchemaExport while googling for the SchemaExport method

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

« Previous PageNext Page »

Create a free website or blog at WordPress.com.