This is the first in a series of posts that give some examples of getting Lead data from an external source such as a Website into your internal CRM system.

This situation is the simplest. This one would work when either your Webserver has direct access to Read/Write on the CRM server via an External Connector License, or (unusual but still occurs occasionally) both your External Facing Website & your CRM system are on the same server.

Just to show you there is nothing up my sleeve & I am using a pretty straight forward setup, I have included below the Specs of the VPC I used to build this demo:

  • Windows Server 2003 Enterprise R2 SP2
  • Dynamics CRM 4.0
  • SQL Server 2005
  • Visual Studio 2005

Note: I am using as the basis for this example the code that was included in the Events management Accelerator code. I have previously setup the free Business Portal available with many of the CRM accelerators available on Codeplex on this server (see details on that here) and am adding a Request More Info page to this portal. Keep in mind however you can just as easily add your own Form to an existing website for this. One of the benefits of using this portal is that a theme has already been created (so thankfully I don’t have to worry too much about layout/design), the WebConfig is already setup to store the CRM server connection details and the Global.asax does all the talking to the CRM Webservice, so all I need to worry about is adding my fields, then calling the CRM functions I need. I will go through each step individually below anyway – but using this portal setup definitely saved me time!

 

1. Add a CRM Marketing Campaign

We are going to begin by setting up a marketing campaign inside Dynamics CRM so we can track the responses we receive against this specific campaign.

Integrate 1 - Campaign

We need to know the unique GUID this campaign has been allocated so we are going to hit CTRL-N on this window to get a new Window with the address bar showing:

Integrate 1 - Campaign ID

We need the last part of this path (after the ID= bit) as we want to add it to our Banner image on our website so that we know when users arrive on our Request More Info Page, which campaign the request came from.

Copy the ID & paste it into Notepad as we will need it a little bit later…

2. Add the Page that Receives the Request for More Info & calls the CRM Webservice

1. Add new Form to the Web project

Integrate 1 - Add form

This form will contain fields that the end user will populate to provide their details and then these will be saved directly to CRM 4.0 by calling the CRM webservice.

There are 2 sides to this project, the aspx Form, and the cs code behind file.

Aspx Form

Our aspx form inherits from a Master Page, so excluding that information, has the following code within its main content area:

<input type="hidden" id='campaignid' runat="server" />
    <div id='content-container-three-column'>
        <div id='content-side1-three-column'>
            <br />
            <div>
                &nbsp;</div>
        </div>
        <div id='content-main-three-column'>
            <table width='95%' cellpadding='2' cellspacing='2'>
                <col width="50%" />
                <col width="50%" />
                <tr>
                    <td class="fieldLabel">
                        <span id='firstname_label' runat="server">First Name:</span></td>
                    <td class="fieldLabel">
                        <span id='lastname_label' runat="server">Last Name:</span></td>
                </tr>
                <tr>
                    <td>
                        <input class="inputField" id='firstname' runat="server" /></td>
                    <td>
                        <input class="inputField" id='lastName' runat="server" /></td>
                </tr>
                <tr>
                    <td class="fieldLabel">
                        <span id='middlename_label' runat="server">Middle Name/Initial:</span></td>
                    <td class="fieldLabel">
                        <span id='prefix_label' runat="server">Email Address:</span></td>
                </tr>
                <tr>
                    <td>
                        <input class="inputField" id='middlename' runat="server" /></td>
                    <td>
                        <input class="inputField" id='emailaddress' runat="server" /></td>
                </tr>
                <tr>
                    <td class="fieldLabel">
                        <span id='companyname_label' runat="server">Company/Organization Name:</span></td>
                    <td class="fieldLabel">
                        <span id='jobtitle_label' runat="server">Job Title:</span></td>
                </tr>
                <tr>
                    <td>
                        <input class="inputField" id='companyname' runat="server" /></td>
                    <td>
                        <input class="inputField" id='jobtitle' runat="server" /></td>
                </tr>
                <tr>
                    <td class="fieldLabel">
                        <span id='street1_label' runat="server">Street Address 1:</span></td>
                    <td class="fieldLabel">
                        <span id='street2_label' runat="server">Street Address 2:</span></td>
                </tr>
                <tr>
                    <td>
                        <input class="inputField" id='street1' runat="server" /></td>
                    <td>
                        <input class="inputField" id='street2' runat="server" /></td>
                </tr>
                <tr>
                    <td class="fieldLabel">
                        <span id='street3_label' runat="server">Street Address 3:</span></td>
                    <td class="fieldLabel">
                        <span id='city_label' runat="server">City:</span></td>
                </tr>
                <tr>
                    <td>
                        <input class="inputField" id='street3' runat="server" /></td>
                    <td>
                        <input class="inputField" id='city' runat="server" /></td>
                </tr>
                <tr>
                    <td class="fieldLabel">
                        <span id='stateorprovince_label' runat="server">State/Province:</span></td>
                    <td class="fieldLabel">
                        <span id='postalcode_label' runat="server">Postal Code:</span></td>
                </tr>
                <tr>
                    <td>
                        <input class="inputField" id='stateorprovince' runat="server" /></td>
                    <td>
                        <input class="inputField" id='postalcode' runat="server" /></td>
                </tr>
                <tr>
                    <td class="fieldLabel">
                        <span id='country_label' runat="server">Country:</span></td>
                    <td class="fieldLabel">
                        <span id='phonenumber_label' runat="server">Phone Number:</span></td>
                </tr>
                <tr>
                    <td>
                        <input class="inputField" id='country' runat="server" /></td>
                    <td>
                        <input class="inputField" id='phonenumber' runat="server" /></td>
                </tr>
            </table>
            <asp:Button ID="btnSendRequest" runat="server" OnClick="btnSendRequest_Click" Text="Send Request" /><br />
            <br />
            <br />
            <div id='div_formsaved' runat="server" style="display: none;" class="notification">
                &nbsp;</div>
        </div>
        <hr style='display: block; clear: left; margin: -0.66em 0; visibility: hidden;' />
    </div>

 

This is a very straight forward entry form. The only thing you really need to note here is the Hidden CampaignID Field at the top which tracks the Campaign ID passed to this form in the query String (You’ll see this a bit later)

C# Code Behind Page

Our code behind page has been largely based on the Events Management Registration page, and contains the following code:

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using CrmSdk;
using System.Web.Services;
using System.Text;

public partial class RequestMoreInfo : System.Web.UI.Page
{
    private Guid _id = Guid.Empty;


    protected override void OnInit(EventArgs e)
    {
        //retrieve the campaign guid from the querystring
        if (!string.IsNullOrEmpty(Request.QueryString["id"]))
        {
            campaignid.Value = Request.QueryString["id"];
            _id = new Guid(Request.QueryString["id"]);
        }
        base.OnInit(e);
    }


      protected void btnSendRequest_Click(object sender, EventArgs e)
    {
        //in order to do this accurately it retrieves the reference code for the web channel
        int channelWebCode = int.Parse(Cache["msa.EventManagement.ResponseWebChannelCode"].ToString());
        try
        {
            //creates the campaign response in MSCRM
            CreateCampaignResponse(_id, this.firstname.Value, this.lastName.Value,
                    middlename.Value,
                    emailaddress.Value, companyname.Value, jobtitle.Value, street1.Value, street2.Value,
                    street3.Value, city.Value, stateorprovince.Value, postalcode.Value, country.Value,
                    phonenumber.Value,  channelWebCode);
        }
        catch (System.Web.Services.Protocols.SoapException ex)
        {
            throw new Exception(ex.Detail.InnerText);
        }   
}


public static Guid? CreateCampaignResponse(Guid campaignId, string firstName, string lastName, string middleName,
         string emailAddress, string companyName, string jobTitle,
        string street1, string street2, string street3, string city, string stateOrProvince,
        string postalCode, string country, string phoneNumber, int channelWebCode)
    {
        Guid lead_id = Guid.Empty;
        campaignresponse response = new campaignresponse();
        response.regardingobjectid = new Lookup();
        response.regardingobjectid.name = EntityName.campaign.ToString();
        response.regardingobjectid.Value = campaignId;

        response.subject = "Web Site Request";
        response.firstname = firstName;
        response.lastname = lastName;
        response.msa_middlenameinitial = middleName;
        response.emailaddress = emailAddress;
        response.companyname = companyName;
        response.msa_jobtitle = jobTitle;
        response.msa_streetaddress1 = street1;
        response.msa_streetaddress2 = street2;
        response.msa_streetaddress3 = street3;
        response.msa_city = city;
        response.msa_state = stateOrProvince;
        response.msa_postalcode = postalCode;
        response.msa_country = country;
        response.telephone = phoneNumber;
        response.receivedon = new CrmDateTime();
        response.receivedon.Value = DateTime.Now.Date.ToString();

        //Default to the response channel to be web
        response.channeltypecode = new Picklist();
        response.channeltypecode.Value = channelWebCode;

        BusinessEntityCollection contacts = FindExisitingContacts(firstName, lastName, emailAddress);

        //If there is 1 or more matching contacts then associate with the most recently updated one
        if (contacts.BusinessEntities.Length > 0)
        {
            contact crmContact = (contact)contacts.BusinessEntities[0];

            activityparty crmCustomer = new activityparty();
            crmCustomer.partyid = new Lookup();
            crmCustomer.partyid.Value = crmContact.contactid.Value;
            crmCustomer.partyid.type = "contact";

            response.customer = new activityparty[] { crmCustomer };
        }
        else
        {
            //create a new Lead record and associate this
            lead_id = CreateLead(campaignId, firstName, lastName, emailAddress, companyName, jobTitle,
                                    street1, street2, street3, city, stateOrProvince, postalCode, country,
                                    phoneNumber, "Web Site Request");
            activityparty crmCustomer = new activityparty();
            crmCustomer.partyid = new Lookup();
            crmCustomer.partyid.Value = lead_id;
            crmCustomer.partyid.type = "lead";

            response.customer = new activityparty[] { crmCustomer };
        }
        return Global.GetCrmService().Create(response);
    }

    private static BusinessEntityCollection FindExisitingContacts(string firstname, string lastname, string emailaddress)
    {
        ColumnSet cols = new ColumnSet();
        cols.Attributes = new string[] { "contactid" };

        //Uncomment these lines if you want to compare several attributes apart from emailaddress
        ConditionExpression firstNameCondition = new ConditionExpression();
        firstNameCondition.AttributeName = "firstname";
        firstNameCondition.Operator = ConditionOperator.Equal;
        firstNameCondition.Values = new object[] { firstname };

        //Uncomment these lines if you want to compare several attributes apart from emailaddress
        ConditionExpression lastNameCondition = new ConditionExpression();
        lastNameCondition.AttributeName = "lastname";
        lastNameCondition.Operator = ConditionOperator.Equal;
        lastNameCondition.Values = new object[] { lastname };

        ConditionExpression emailAddressCondition = new ConditionExpression();
        emailAddressCondition.AttributeName = "emailaddress1";
        emailAddressCondition.Operator = ConditionOperator.Equal;
        emailAddressCondition.Values = new object[] { emailaddress };

        ConditionExpression entityIsActive = new ConditionExpression();
        entityIsActive.AttributeName = "statecode";
        entityIsActive.Operator = ConditionOperator.Equal;
        entityIsActive.Values = new object[] { 0 };

        FilterExpression filter = new FilterExpression();
        filter.FilterOperator = LogicalOperator.And;
        filter.Conditions = new ConditionExpression[] { emailAddressCondition, entityIsActive };
        //filter.Conditions = new ConditionExpression[] { firstNameCondition, lastNameCondition, emailAddressCondition, entityIsActive };

        PagingInfo paging = new PagingInfo();
        paging.Count = 2;
        paging.PageNumber = 1;

        QueryExpression query = new QueryExpression();
        query.EntityName = EntityName.contact.ToString();
        query.ColumnSet = cols;
        query.Criteria = filter;
        query.PageInfo = paging;

        // Set the order of returned contacts.
        OrderExpression oe = new OrderExpression();
        oe.AttributeName = "modifiedon";
        oe.OrderType = OrderType.Descending;
        query.Orders = new OrderExpression[] { oe };

        return Global.GetCrmService().RetrieveMultiple(query);
    }

    public static Guid CreateLead(Guid campaignId, string firstName, string lastName, string emailAddress, string companyName, string jobTitle,
        string street1, string street2, string street3, string city, string stateOrProvince,
        string postalCode, string country, string phoneNumber, string subject)
    {
        lead lead = new lead();

        lead.campaignid = new Lookup();
        lead.campaignid.name = EntityName.campaign.ToString();
        lead.campaignid.Value = campaignId;

        lead.subject = subject;
        lead.firstname = firstName;
        lead.lastname = lastName;
        lead.emailaddress1 = emailAddress;
        if (companyName.Length > 0)
            lead.companyname = companyName;
        else
            lead.companyname = firstName + " " + lastName;
        lead.jobtitle = jobTitle;
        lead.address1_line1 = street1;
        lead.address1_line2 = street2;
        lead.address1_line3 = street3;
        lead.address1_city = city;
        lead.address1_stateorprovince = stateOrProvince;
        lead.address1_postalcode = postalCode;
        lead.address1_country = country;
        lead.telephone1 = phoneNumber;
        return Global.GetCrmService().Create(lead);
    }
}

So, what does this code do?

First there is an onInit function that grabs the ID passed in the Query string & populates the Hidden CampaignID field & the _ID

Then we have the btnSendRequest_Click event handler, that parses all the fields from the form, and passes them to the CreateCampaignResponse function, catching any soap errors that may occur.

The CreateCampaignResponse creates the actual campaign response, then checks to see if the passed in details match an existing contact, if they do, it ties the campaign response to the existing contact, otherwise it adds a new Lead, and ties the Lead & Contact together.

CreateLead & FindExistingContacts are two helper functions that pull out the code to Create a Lead & to Search the contact database respectively.

But something’s missing? How does this code actually connect to & write to the CRM Webservices?

Well this is where the code already provided in the Business portal comes in handy – there is a Global.asax file, and a Web.config file that contain this magic. As i haven’t touched them in this example, I have included their contents verbatim below:

Global.asax

<%@ Import NameSpace="System.IO" %>
<%@ Import NameSpace="System.Xml" %>
<%@ Import NameSpace="System.Web" %>

<script language="C#" runat="server">
    static Cache _cache = null;
    void Application_Start ()
    {
        _cache = Context.Cache;
        Cache_EventManagement_FullConfig();
    }

    public static void Cache_EventManagement_FullConfig()
    {
        //establish a crmservice to read the necessary config settings from crm custom entities
        Microsoft.Crm.SdkTypeProxy.CrmService crmService = CrmServiceManager.GetCrmService();

        //-----------------------------------------------------------------------------------
        //This section deals with reading and setting up the configuration for the Event
        //Management Accelerator. All configuration data for this accelerator is stored
        //in a custom entity called msa_eserviceconfiguration.
        //-----------------------------------------------------------------------------------     
        Microsoft.Crm.Sdk.Query.QueryByAttribute eventManagementQuery = new Microsoft.Crm.Sdk.Query.QueryByAttribute();
        eventManagementQuery.EntityName = "msa_eventmanagementconfiguration";
        eventManagementQuery.ColumnSet = new Microsoft.Crm.Sdk.Query.ColumnSet(new String[] { "msa_eventisopenstatuscode", "msa_eventissoldoutstatuscode", "msa_eventiswaitlistingstatuscode", "msa_registeredresponsecode", "msa_waitlistresponsecode", "msa_cancelledresponsecode", "msa_webregistrationchannelcode" });
        eventManagementQuery.Attributes = new String[] { "statecode" };
        eventManagementQuery.Values = new Object[] { 0 };
        System.Collections.Generic.List<Microsoft.Crm.Sdk.DynamicEntity> eventManagementConfigSettings = RetrieveConfiguration(crmService, eventManagementQuery);

        Microsoft.Crm.Sdk.CrmNumber crmnumEventIsOpenCode = (Microsoft.Crm.Sdk.CrmNumber)eventManagementConfigSettings[0].Properties["msa_eventisopenstatuscode"];
        int intEventIsOpenCode = crmnumEventIsOpenCode.Value;
        _cache.Insert("msa.EventManagement.OpenStatusCode", intEventIsOpenCode);

        Microsoft.Crm.Sdk.CrmNumber crmnumEventIsSoldOutCode = (Microsoft.Crm.Sdk.CrmNumber)eventManagementConfigSettings[0].Properties["msa_eventissoldoutstatuscode"];
        int intEventIsSoldOutCode = crmnumEventIsSoldOutCode.Value;
        _cache.Insert("msa.EventManagement.SoldOutStatusCode", intEventIsSoldOutCode);

        Microsoft.Crm.Sdk.CrmNumber crmnumEventIsWaitlistingCode = (Microsoft.Crm.Sdk.CrmNumber)eventManagementConfigSettings[0].Properties["msa_eventiswaitlistingstatuscode"];
        int intEventIsWaitlistingCode = crmnumEventIsWaitlistingCode.Value;
        _cache.Insert("msa.EventManagement.WaitlistStatusCode", intEventIsWaitlistingCode);

        Microsoft.Crm.Sdk.CrmNumber crmnumRegisteredResponseCode = (Microsoft.Crm.Sdk.CrmNumber)eventManagementConfigSettings[0].Properties["msa_registeredresponsecode"];
        int intRegisteredResponseCode = crmnumRegisteredResponseCode.Value;
        _cache.Insert("msa.EventManagement.ResponseRegisteredCode", intRegisteredResponseCode);

        Microsoft.Crm.Sdk.CrmNumber crmnumWaitlistResponseCode = (Microsoft.Crm.Sdk.CrmNumber)eventManagementConfigSettings[0].Properties["msa_waitlistresponsecode"];
        int intWaitlistResponseCode = crmnumWaitlistResponseCode.Value;
        _cache.Insert("msa.EventManagement.ResponseWaitlistCode", intWaitlistResponseCode);

        Microsoft.Crm.Sdk.CrmNumber crmnumCancelledResponseCode = (Microsoft.Crm.Sdk.CrmNumber)eventManagementConfigSettings[0].Properties["msa_cancelledresponsecode"];
        int intCancelledResponseCode = crmnumCancelledResponseCode.Value;
        _cache.Insert("msa.EventManagement.ResponseCancelCode", intCancelledResponseCode);

        Microsoft.Crm.Sdk.CrmNumber crmnumWebRegistrationChannelCode = (Microsoft.Crm.Sdk.CrmNumber)eventManagementConfigSettings[0].Properties["msa_webregistrationchannelcode"];
        int intWebRegistrationChannelCode = crmnumWebRegistrationChannelCode.Value;
        _cache.Insert("msa.EventManagement.ResponseWebChannelCode", intWebRegistrationChannelCode);
    }

    public static System.Collections.Generic.List<Microsoft.Crm.Sdk.DynamicEntity> RetrieveConfiguration(Microsoft.Crm.SdkTypeProxy.CrmService service, Microsoft.Crm.Sdk.Query.QueryBase query)
    {
        Microsoft.Crm.SdkTypeProxy.RetrieveMultipleRequest retrieveRequest = new Microsoft.Crm.SdkTypeProxy.RetrieveMultipleRequest();
        retrieveRequest.Query = query;
        retrieveRequest.ReturnDynamicEntities = true;

        System.Collections.Generic.List<Microsoft.Crm.Sdk.DynamicEntity> results = new System.Collections.Generic.List<Microsoft.Crm.Sdk.DynamicEntity>();

        try
        {
        Microsoft.Crm.SdkTypeProxy.RetrieveMultipleResponse retrieveResponse = (Microsoft.Crm.SdkTypeProxy.RetrieveMultipleResponse)service.Execute(retrieveRequest);
        foreach (Microsoft.Crm.Sdk.BusinessEntity entity in retrieveResponse.BusinessEntityCollection.BusinessEntities)
        {
            results.Add((Microsoft.Crm.Sdk.DynamicEntity)entity);
        }
        }
        catch (System.Web.Services.Protocols.SoapException ex)
        {
            string errorMsg = ex.InnerException.ToString();
        }
        return results;
    }

    public static Microsoft.Crm.Sdk.DynamicEntity RetrieveDynamicEntity(Microsoft.Crm.SdkTypeProxy.CrmService service, String entityName, Guid entityId, params String[] attributes)
    {
        Microsoft.Crm.SdkTypeProxy.TargetRetrieveDynamic retrieveTarget = new Microsoft.Crm.SdkTypeProxy.TargetRetrieveDynamic();
        retrieveTarget.EntityName = entityName;
        retrieveTarget.EntityId = entityId;

        Microsoft.Crm.SdkTypeProxy.RetrieveRequest retrieveRequest = new Microsoft.Crm.SdkTypeProxy.RetrieveRequest();
        retrieveRequest.Target = retrieveTarget;
        retrieveRequest.ColumnSet = new Microsoft.Crm.Sdk.Query.ColumnSet(attributes);
        retrieveRequest.ReturnDynamicEntities = true;

        return (Microsoft.Crm.Sdk.DynamicEntity)((Microsoft.Crm.SdkTypeProxy.RetrieveResponse)service.Execute(retrieveRequest)).BusinessEntity;
    }   
</script>

Web.Config

Web.config (only included the <appsettings> section as everything else in the file is unrelated to this post)

<appSettings>
        <!--
        These settings are for the portal pages and controls to call the
        crm web services. If you are setting this up on the stndard vpc image then
        set the domain value to "." if you are having problems.
        -->
        <add key="msa.CRMOrganizationName" value="MicrosoftCRM"/>
        <add key="msa.CRMServerUrl" value="http://localhost:5555/MSCRMServices"/>
        <add key="msa.CRMWebServicesUser" value="administrator"/>
        <add key="msa.CRMWebServicesPassword" value="pass@word1"/>
        <add key="msa.CRMDomain" value="."/>
    </appSettings>

Save your changes, and then build your Web Application!

3. Link to your New Page & See it in Action

Add the Banner to your Website (or an external site linking back to yours) with the Link pointing to your new RequestMoreInfo Page, and the additional querystring parameter of ID={insert your CampaignID here copied in step 1 above} so for my example, it would be something along the lines of:

<a href="requestmoreinfo.aspx?id=e0d05869-87f9-dd11-aaed-0003ff4bd8e9#"><img src="images/EGI_Button_critter.jpg" /></a>

 

Which looks like this, where I have added the banner in the right header panel (framed in Red below):

Integrate 1 - Website banner

When I click the Banner, I am taken to the Request More Info Page:

Integrate 1 - Request More Info Page Dets

After completing the details and clicking Send Request, our page does its work in the background. It connects to CRM,

Adds a response to the marketing Campaign:

Integrate 1 - Campaign Response

Adds the Lead Details:

Integrate 1 - New Lead

And it includes the link between the Lead & the Campaign, so when reporting on your Campaigns success, you can see which leads originated from this Web Campaign.