Inna Agranov
Microsoft Corporation
June 2007
Applies to:
Microsoft Dynamics™ CRM 3.0
Requires:
Microsoft Dynamics CRM 3.0
Microsoft® Visual Studio® 2003
Summary
Learn how to programmatically manage marketing or sales events,
such as seminars, using Microsoft Dynamics CRM 3.0 Software Development Kit
(SDK). Also, learn how to create and integrate a .NET Framework 2.0 Web
application into Microsoft Dynamics CRM.
Download the Visual Studio 2003 Visual C# code sample for this
article:
MarketingAutomation.exe.
The Readme.doc that is included with the sample code contains
information about how to set up, build, and test the sample application.
Content
Introduction
Conducting a seminar involves a lot of planning, time and
resources. You have to determine the seminar objectives, the material to
present, who your desired audience is and how to let your customers know about
the upcoming event. Automating the marketing processes that are associated with
planning, organizing, and monitoring the results of the seminar is very
important in making you more efficient, competitive, and successful in meeting
the business goals.
This sample application shows you how to automate your marketing
activities related to managing a seminar. Specifically, it shows you how to use
Microsoft Dynamics CRM SDK to retrieve campaign responses that indicate whether
the customers will attend the seminar and to create a marketing list that
contains seminar attendees. It launches a campaign to evaluate the results
of the seminar and creates a campaign activity, such as sending e-mail messages
to the seminar attendees to ask for a feedback on the seminar.
The sample is implemented as an ASP.NET Web application that you
can use as a seminar automation add-in for the Microsoft Dynamics CRM Web
application. The customization data that is provided with the sample includes
two custom entities, seminar and attendee, that are used to track seminar
activities and seminar attendees. It also includes two custom toolbar buttons, Qualify Response and Solicit Feedback.
The Qualify Response button is used to qualify customer
responses to the seminar invitations. The Solicit Feedback
button is used to launch a post-seminar campaign to solicit a feedback from all
customers who attended the seminar.
An example of a Campaign Response record that contains a
customer response to the seminar invitation and illustrates the Qualify Response
button is shown here.
.gif)
Figure 1 – The customer response to the invitation to the seminar
The following is an example of a Seminar record that contains a
list of attendees and illustrates the Solicit Feedback button.
.gif)
Figure 2 – The request for a feedback on the seminar
Implementation
The MarketingAutomation Web application contains two ASP.NET Web
pages: ConfirmAttendance.aspx and SolicitFeedback.aspx.
The ConfirmAttendance.aspx page includes the Yes
and No buttons to confirm the seminar attendance. The
code-behind creates attendees from the customers who responded with the Yes using the customer information that is associated with the
campaign response.
Figure 3 – The Qualify Response ConfirmAttendance Web page dialog
The SolicitFeedback.aspx page contains the OK
button. When the OK button is clicked, the code-behind
creates a new campaign to solicit a feedback on the seminar. It also creates a
marketing list that contains seminar attendees and an e-mail campaign activity
to distribute the e-mail messages to all attendees on the marketing list.
The customization data for the sample is contained in the
AllCustomizations.xml and ISVCustomizations.xml files. You can easily add
customizations to Microsoft Dynamics CRM by using Import
Customizations feature that is available in the Microsoft Dynamics CRM
Web application. Another way of creating custom entities is by going to
Setting, Customizations, and Customize Entities
in the Microsoft Dynamics CRM Web application.
The AllCustomizations.xml file defines two new custom entities, seminar and attendee, their
attributes, and their relationships with other entities.
The seminar entity represents a
container for campaigns, marketing lists, attendees and various activities, such
as tasks, e-mails, appointments and other activities associated with the
seminar. Each of these entities is linked to a seminar for which it is created.
The attendee entity represents a contact
who attended a seminar. Each attendee is linked to a specific contact from which
it is created and to a seminar.
The following diagram shows the relationships among the seminar, attendee, campaign,
and list entities:
Note This sample uses the default
customization prefix "new_". To avoid possible conflicts with other
customizations that you may install in the future, it is recommended that you
modify the default prefix. To do this, in the Microsoft Dynamics CRM Web
application, in the Navigation Pane, click Settings. In the Settings area, click
Organization Settings, and then click System Settings. On the Customization tab,
modify the default prefix before you create custom entities and attributes.
Figure 4 – The entities relationship diagram
Defining Custom Toolbar Buttons
The ISVCustomizations.xml file contains the definitions of the
two custom toolbar buttons, Qualify Response and Solicit Feedback. The XML configuration code for the buttons
is shown here.
<Button Title="Qualify Response"
ToolTip="Qualify the campaign response."
Url="http://localhost:31040/MarketingAutomation/ConfirmAttendance.aspx"
PassParams="1"
WinParams="dialogWidth=400px;dialogHeight=200px;center:yes;status:no;help:no;"
WinMode="2" />
<Button Title="Solicit Feedback"
ToolTip="Solicit feedback for the seminar from its attendees."
Url="http://localhost:31040/MarketingAutomation/SolicitFeedback.aspx"
PassParams="1"
WinParams="dialogWidth=200px;dialogHeight=100px;" WinMode="1" />
The following table identifies the attributes used in the XML
configuration code.
| Attribute | Description |
| Title | Specifies the name that appears on the button. |
| Tooltip | Specifies the ToolTip that appears for the button. |
| Url | Specifies the URL to be opened when the button is clicked. |
| WinParams | Specifies the parameters to be passed to the window. The
format of this parameter is different depending on the value of the WinMode
parameter. In the example code, the dialog width and height in pixels are
specified. |
| PassParams | Specifies whether the entity type and entity instance ID
parameters are to be passed to the URL. A value of 1 indicates "true". |
| WinMode | Specifies the window mode. 0 = Window [default] 1 = Modal Dialog 2 = Modeless Dialog |
For more information about the configuration format for a
button, see the Button
topic in the Microsoft Dynamics CRM 3.0 SDK.
The ConfirmAttendance.aspx page qualifies a customer response to
an invitation to attend a seminar.
It performs the following functions:
- Validates that the campaign response is open and it is a
response to a seminar invitation.
- Displays the Yes/No
buttons to indicate if the customer will attend the seminar.
- If the answer is Yes, it creates an
attendee for the seminar using the customer information that is associated with
the campaign response.
- Closes the campaign response.
Page_Load Event Handler
When you click the Qualify Response
button that is located on the campaign response toolbar, you issue a request for
the ConfirmAttendance.aspx page. The ConfirmAttendance.aspx page is specified in
the Url attribute of the Qualify Response button in the ISVCustomizations.xml
file. The ConfirmAttendance code-behind contains the Page_Load event handler
that is invoked when a request for the ConfirmAttendance.aspx page is issued and
the code inside the handler is executed.
The Page_Load event handler validates
that the campaign response is open and it is a response to the seminar
invitation by verifying that the campaign response ID obtained from the query
string is valid. It also adds the event handlers for the Yes
and No buttons.
protected void Page_Load(object sender, EventArgs e)
{
// Set up the standard Microsoft Dynamics CRM 3.0 service.
_service = new CrmService.CrmService();
// Set credentials to avoid access denied errors.
_service.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Verify that the campaign response is open
// and a part of a seminar before continuing.
IsValidResponse = false;
// Obtain the campaign response GUID from the query string.
if (IsGuid(Request.QueryString["oId"]))
{
// Retrieve the campaign response in question
// from Microsoft Dynamics CRM 3.0.
_campaignResponseId = new Guid(Request.QueryString["oId"]);
campaignresponse response =
(campaignresponse) _service.Retrieve(EntityName.campaignresponse.ToString(),
_campaignResponseId, new AllColumns());
// Verify that the campaign response is open.
if (response.statecode.Value == CampaignResponseState.Open)
{
// Retrieve the campaign for the response from Microsoft Dynamics CRM 3.0.
campaign campaign =
(campaign)_service.Retrieve(EntityName.campaign.ToString(),
response.regardingobjectid.Value, new AllColumns());
// Verify that the campaign is for the seminar.
if ((campaign.new_seminarid != null) && (!campaign.new_seminarid.IsNull))
{
IsValidResponse = true;
}
}
}
// Add the event handlers for the Yes and No buttons.
this.confirmButton.ServerClick += new EventHandler(this.confirmButton_ServerClick);
this.noButton.ServerClick += new EventHandler(this.noButton_ServerClick);
this.cancelButton.ServerClick += new EventHandler(this.cancelButton_ServerClick);
}
The Yes Button Event Handler
The confirmButton_ServerClick event
handler is invoked when you click the Yes button inside
the ConfirmAttendance dialog box. It marks the response
as Attending, creates a new attendee from a contact, and closes the campaign
response.
protected void confirmButton_ServerClick(object sender, EventArgs e)
{
if (_campaignResponseId != Guid.Empty)
{
// Mark the response as Attending.
SetCampaignResponseAttending(_campaignResponseId, true);
}
// Close the dialog box.
Response.Write(@"<script language=""javascript"">window.close();</script>");
}
The SetAttending method creates a new
attendee from a contact. If you wish to create attendees from accounts or leads,
you can modify this code to support use of these entity types. After you do
this, you will have choice of entity types in the Customer lookup field of the
Campaign Response when you qualify a customer response. For more information,
see "Create a campaign response and qualify it" in the Testing section of the
Readme.doc file.
In the following code example, contactId
is the ID of a contact that is converted into an attendee, seminarId
is the ID of the seminar that the attendee is attending, and
isAttending specifies whether to convert a contact into an attendee.
private void SetAttending(Guid contactId, Guid seminarId)
{
// Retrieve the contact object from Microsoft Dynamics CRM 3.0.
contact contact =
(contact)_service.Retrieve(EntityName.contact.ToString(),
contactId, new AllColumns());
// Create a new attendee object.
new_attendee attendee = new new_attendee();
// Set the attendee's contact name to the contact's full name.
attendee.new_contactname = contact.fullname;
// Set the attendee's contact ID to the contact's ID.
attendee.new_contactid = new Lookup();
attendee.new_contactid.Value = contactId;
// Associate the attendee with the campaign's seminar.
attendee.new_seminarid = new Lookup();
attendee.new_seminarid.Value = seminarId;
// Create the attendee in Microsoft Dynamics CRM 3.0.
_service.Create(attendee);
}
The SetCampaignResponseAttending method
creates a new attendee from a contact and closes the campaign response.
In the following code example, campaignResponseId
is the ID of the campaign response, isAttending
specifies whether the contact in the response is attending the seminar.
private void SetCampaignResponseAttending(Guid campaignResponseId, bool isAttending)
{
// Retrieve the campaign response object from Microsoft Dynamics CRM 3.0.
campaignresponse campaignResponse =
(campaignresponse)_service.Retrieve(EntityName.campaignresponse.ToString(),
campaignResponseId, new AllColumns());
// Get the campaign ID and object associated with
// the campaign response from Microsoft Dynamics CRM 3.0.
Guid campaignId = campaignResponse.regardingobjectid.Value;
campaign campaign =
(campaign)_service.Retrieve(EntityName.campaign.ToString(),
campaignId, new AllColumns());
// Get the seminar ID associated with the campaign.
if (campaign.new_seminarid.IsNull == false)
{
Guid seminarId = campaign.new_seminarid.Value;
if (isAttending)
{
// Create an attendee for the seminar using the customer
// that is associated with the campaign response.
Guid customerId = campaignResponse.customer[0].partyid.Value;
SetAttending(customerId, seminarId);
}
// Close the campaign response.
CloseCampaignResponse(campaignResponseId);
}
}
The No Button Event Handler
The noButton_ServerClick event handler
is invoked when you click the No button inside the ConfirmAttendance dialog box. It marks the response as Not
Attending and does not create a new attendee.
protected void noButton_ServerClick(object sender, EventArgs e)
{
if (_campaignResponseId != Guid.Empty)
{
// Mark the response as Not Attending.
SetCampaignResponseAttending(_campaignResponseId, false);
}
// Close the dialog box.
Response.Write(@"<script language=""javascript"">window.close();</script>");
}
Processing the
SolicitFeedback.aspx Web Page
The SolicitFeedback.aspx page requests a feedback on a seminar
from the seminar attendees. It performs the following functions:
- Creates a campaign to solicit the feedback from the seminar
attendees.
- Creates a campaignactivity entity to distribute the feedback requests using e-mail.
- Retrieves the BusinessEntityCollection
class that contains the seminar attendees.
- Creates a marketing list and populates it with the attendees
contained in BusinessEntityCollection.
- Adds the marketing list to the campaignactivity.
- Distributes the campaignactivity to the marketing list members.
Page_Load Event Handler
When you click the Solicit Feedback
button that is located on the Marketing Automation seminar toolbar, you issue a
request for the SolicitFeedback.aspx page. The SolicitFeedback.aspx page is
specified in the Url attribute of the Solicit Feedback
button in the ISVCustomizations.xml file. The SolicitFeedback code-behind
contains the Page_Load event handler that is invoked when a request for the
SolicitFeedback.aspx page is issued and the code inside the handler is executed.
The Page_Load event handler retrieves
the seminar ID from the query string, creates a feedback request campaign, and
distributes it, as shown in the following code example.
protected void Page_Load(object sender, EventArgs e)
{
string seminarIdStr = Request.QueryString["oId"];
if ((seminarIdStr != null) && (seminarIdStr.Length > 0))
{
Guid seminarId = new Guid(seminarIdStr);
SendFeedbackRequest(seminarId);
}
}
The SendFeedbackRequest method creates a
feedback request campaign for a seminar and a campaign activity that distributes
the e-mail messages to the attendees, as shown in the following code example.
In the following code example, seminarId is an ID of the seminar
for which the feedback campaign is created.
public void SendFeedbackRequest(Guid seminarId)
{
// In a real-world implementation, you would set up a Web site
// to collect the attendee's feedback for the seminar,
// and send the feedback URL in the e-mail.
const string EMAIL_BODY = "Thank you for attending our seminar. "
+ "We'd like to hear from you. "
+ "Please visit us by clicking on the following link to share your feedback:\n\n"
+ "http://www.adventureworkscycle.com/SeminarFeedback?seminarID={0}";
// Set up the standard Microsoft Dynamics CRM service.
CrmService.CrmService service = new CrmService.CrmService();
// Set the credentials to avoid access denied errors.
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Retrieve the seminar for which to solicit feedback.
new_seminar seminar =
(new_seminar)service.Retrieve(EntityName.new_seminar.ToString(),
seminarId, new AllColumns());
// Check that feedback has not been already solicited for this seminar.
if ((seminar.new_hassolicitedfeedback == null) || (seminar.new_hassolicitedfeedback.Value == false))
{
#region Create a campaign to facilitate the feedback request distribution
campaign feedbackCampaign = new campaign();
feedbackCampaign.name = "Feedback for " + seminar.new_name;
feedbackCampaign.new_seminarid = new Lookup();
feedbackCampaign.new_seminarid.Value = seminarId;
Guid feedbackCampaignId = service.Create(feedbackCampaign);
#endregion
#region Create a campaignactivity to distribute the feedback requests using e-mail
campaignactivity distributeFeedback = new campaignactivity();
//Set the parent campaign.
distributeFeedback.regardingobjectid = new Lookup();
distributeFeedback.regardingobjectid.Value = feedbackCampaignId;
distributeFeedback.regardingobjectid.type = EntityName.campaign.ToString();
distributeFeedback.subject = "Feedback for " + seminar.new_name;
distributeFeedback.description =
String.Format(EMAIL_BODY, seminarId.ToString("B"));
//Set the channel to e-mail.
const int EMAIL = 7;
distributeFeedback.channeltypecode = new Picklist();
distributeFeedback.channeltypecode.Value = EMAIL;
Guid distributeFeedbackId = service.Create(distributeFeedback);
#endregion
#region Get a BusinessEntityCollection containing the seminar attendees
QueryByAttribute query = new QueryByAttribute();
query.ColumnSet = new AllColumns();
query.EntityName = EntityName.new_attendee.ToString();
query.Attributes = new string[] { "new_seminarid" };
query.Values = new object[] { seminarId };
BusinessEntityCollection attendees = service.RetrieveMultiple(query);
#endregion
#region Create a marketing list to contain the seminar attendees
list attendeeList = new list();
attendeeList.new_seminarid = new Lookup();
attendeeList.new_seminarid.Value = seminarId;
attendeeList.new_seminarid.type = EntityName.new_seminar.ToString();
attendeeList.listname = "Feedback Request Recipients for " + seminar.new_name;
// Set the type of list members to contact.
const int CONTACT = 2;
attendeeList.membertype = new CrmNumber();
attendeeList.membertype.Value = CONTACT;
// Set the allowed list member type to contact.
attendeeList.createdfromcode = new Picklist();
attendeeList.createdfromcode.Value = CONTACT;
Guid attendeeListId = service.Create(attendeeList);
#endregion
#region Add the attendees in the BusinessEntityCollection to the marketing list
foreach (BusinessEntity attendee in attendees.BusinessEntities)
{
Guid contactId = ((new_attendee)attendee).new_contactid.Value;
AddMemberListRequest addContact = new AddMemberListRequest();
addContact.ListId = attendeeListId;
addContact.EntityId = contactId;
service.Execute(addContact);
}
#endregion
#region Add the marketing list to the campaignactivity
// Create a Microsoft Dynamics CRM 3.0 request object
// to add the marketing list to the feedback campaign
AddItemCampaignRequest addMemberListToCampaign = new AddItemCampaignRequest();
addMemberListToCampaign.CampaignId = feedbackCampaignId;
addMemberListToCampaign.EntityId = attendeeListId;
addMemberListToCampaign.EntityName = EntityName.list;
// Execute the request.
AddItemCampaignResponse addMemberListToCampaignResponse =
(AddItemCampaignResponse)service.Execute(addMemberListToCampaign);
// Create a Microsoft Dynamics CRM 3.0 request object
// to add the marketing list to the feedback request campaignactivity.
AddItemCampaignActivityRequest addMemberListToCampaignActivity =
new AddItemCampaignActivityRequest();
// Specify the campaign activity to modify.
addMemberListToCampaignActivity.CampaignActivityId = distributeFeedbackId;
// Specify the item to be added.
addMemberListToCampaignActivity.ItemId = attendeeListId;
// Specify the type of the item to be added.
addMemberListToCampaignActivity.EntityName = EntityName.list;
// Execute the request.
AddItemCampaignActivityResponse addMemberListToCampaignActivityResponse =
(AddItemCampaignActivityResponse)service.Execute(addMemberListToCampaignActivity);
#endregion
#region Distribute the feedback request campaignactivity
// Create a Microsoft Dynamics CRM 3.0 request object
// to execute the feedback request distribution.
ExecuteCampaignActivityRequest executeCampaignRequest =
new ExecuteCampaignActivityRequest();
executeCampaignRequest.CampaignActivityId = distributeFeedbackId;
// Specify the campaign activity to be executed.
executeCampaignRequest.Activity = new email();
// Indicate that the campaign activity is to be executed,
// not propagated.
executeCampaignRequest.Propagate = false;
executeCampaignRequest.OwnershipOptions =
PropagationOwnershipOptions.ListMemberOwner;
// Execute the campaign activity.
ExecuteCampaignActivityResponse executeCampaignResponse =
(ExecuteCampaignActivityResponse)service.Execute(executeCampaignRequest);
// Mark that feedback has been solicited for the seminar.
if (seminar.new_hassolicitedfeedback == null)
{
seminar.new_hassolicitedfeedback = new CrmBoolean();
}
seminar.new_hassolicitedfeedback.Value = true;
service.Update(seminar);
#endregion
// Let the user know the operation was successful.
Response.Write(@"<script language=""javascript"" type=""text/javascript"">
alert(""Feedback has been solicited for this seminar."");
</script>");
Response.Flush();
}
else
{
// Let the user know feedback has already been solicited.
Response.Write(@"<script language=""javascript"" type=""text/javascript"">
alert(""Feedback has already been solicited for this seminar."");
</script>");
Response.Flush();
}
// Always close the window after alerting the user.
Response.Clear();
Response.Write(@"
<script language=""javascript"" type=""text/javascript"">
window.close();
</script>");
Response.Flush();
}
Additional
Information
For more information, see the Microsoft Dynamics
CRM 3.0 Software Development Kit (SDK) documentation.