Click here to Skip to main content
Click here to Skip to main content

CRM Integration to Outlook (Exchange) Using WebDav

, 11 Sep 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
In this article, an integration between a CRM and an Exchange will be explained by using WebDav

Introduction

In this article, an integration between a CRM and an Exchange will be explained using WebDav.

Background

Database

Assume we have a database containing our customers. And, we want our system to update Outlook contacts when we add/update/delete a customer.

Here's a basic customer table we're going to use:

URL of a Contact / How Exchange Stores Contacts

In Outlook, a contact is displayed as follows:

When we look this contact from web, URL is shown like this:

As you see, the URL is:
https://mail.yourcompany.com/exchange/efe.erdogru@yourcompany.com/%C4%B0lgili%20Ki%C5%9Filer/Efe%20Erdogru.EML.

There are four parts in the URL:

  1. Base domain: https://mail.yourcompany.com/exchange/
  2. User alias: efe.erdogru@yourcompany.com
  3. Folder: %C4%B0lgili%20Ki%C5%9Filer.
  4. Contact file name: Efe%20Erdogru.EML

P.S.: Because my culture definition is Turkish, you see different characters in folder. Probably, you'll see the folder name as "Contacts", etc.

General structure of a URL is:
https://yourcompany.com/exchange/UserAlias/Folder/Contact.EML.

Difference Between Add and Update

We used to use different methods for adding and inserting. In WebDav, it won't work like this. For both adding and updating, we'll use the same method.

Therefore, how does Exchange know if the request is for an add or update?

In Exchange, there is no difference between an e-mail and a contact; both are EML files. So, when you add a new contact, Outlook creates a new EML file based on the contact name. When updating, that file will be updated.

Briefly, when you send a request, if there is no file, Exchange creates a new, if exists, Exchange updates.

How Will We Integrate?

By default, Exchange stores contact files based on entered names (e.g. first name + last name). However, this does not mean that we have to do so.

For an integration, we'll need a primary key to synchronize. In our example, "CustomerID" field will be our primary key.

The next step is creating an unique EML file name. For this, we can create a naming template. For example, we may say that, all contact EML file names must be in this format:

Customer-{XYZ}.EML

Yes, the "XYZ" field will be the ID of the customer. Because of the ID of a customer never changes, if we create the EML contact file with this ID, we can easily synchronize our contacts.

So, for a customer with ID 127, contact file name will be "Customer-127.EML".

XML Structure of a WebDav Request

<?xml version="1.0"?>
<g:propertyupdate xmlns:g="DAV:" xmlns:c="urn:schemas:contacts:" 
    xmlns:e="http://schemas.microsoft.com/exchange/" xmlns:h="urn:schemas:httpmail:">
	<g:set>
		<g:prop>
			<g:contentclass>urn:content-classes:person</g:contentclass>
			<e:outlookmessageclass>IPM.Contact</e:outlookmessageclass>
			<c:givenName>Efe</c:givenName>
			<c:sn>Erdogru</c:sn>
			<c:cn>Efe Erdogru</c:cn>
		</g:prop>
	</g:set>
</g:propertyupdate>

The tags contentclass and outlookmessageclass are necessary when adding new contacts. When updating a contact they won't be necessary, but it's not worth sending them on every request.

The namespace urn:schemas:httpmail: is necessary for upating "notes" in a contact. If you don't need to synchronize notes, you can remove it.

Possible Problems

In an integration system like this, we have to handle user operations. For example, when a user makes changes directly from Outlook, our CRM system will override the change.

We can provide different solutions based on our whole system architecture.

One way is, our CRM system manages changes. Periodically, system checks if any change has occurred. If yes, system will update its own database. And if a user makes changes from CRM, system will check if the record is changed and warns the user.

Another way is to prevent making changes directly from Outlook. In this solution, all changes must be made from CRM system. This is also the fastest and easiest way.

Using the Code

SSL Connections

First of all, for SSL connections, we'll need a dummy validator.

ServicePointManager.ServerCertificateValidationCallback = 
				(o, certificate, chain, errors) => true;

Update Structure

Let's assume that we have a database model containing customer table and we want to update all contacts. The variable contactUrl will be the customer's contact url in Exchange.

void BatchUpdate()
{
	using (var mc = new DatabaseModelContainer())
	{
		var customers = mc.Customers.ToList();

		foreach (var customer in customers)
		{
			var contactUrl = String.Format(
				"https://mail.yourcompany.com/exchange/
						{0}/{1}/Customer-{2}.EML",
				UserAlias,
				FolderName,
				customer.CustomerID);

			var updateCommand = GetUpdateCommand(customer);

			UpdateContact(contactUrl, updateCommand);
		}
	}
}

In method GetUpdateCommand, we'll create the request.

string GetUpdateCommand(Customer customer)
{
	var cn = ((customer.FirstName + " " + 
		customer.MiddleName).Trim() + " " + customer.LastName).Trim();

	//

	var sb = new StringBuilder();

	sb.AppendLine("<?xml version=\"1.0\"?>");
	sb.AppendLine("<g:propertyupdate xmlns:g=\"DAV:\" 
	xmlns:c=\"urn:schemas:contacts:\" 
	xmlns:e=\"http://schemas.microsoft.com/exchange/\" 
	xmlns:h=\"urn:schemas:httpmail:\">");
	sb.AppendLine("<g:set><g:prop>");
	sb.AppendLine("<g:contentclass>urn:content-classes:person</g:contentclass>");
	sb.AppendLine("<e:outlookmessageclass>IPM.Contact</e:outlookmessageclass>");

	sb.AppendLine(String.Format("<c:givenName>{0}
			</c:givenName>", customer.FirstName));
	sb.AppendLine(String.Format("<c:middlename>{0}
			</c:middlename>", customer.MiddleName));
	sb.AppendLine(String.Format("<c:sn>{0}</c:sn>", customer.LastName));
	sb.AppendLine(String.Format("<c:cn>{0}</c:cn>", cn));
	sb.AppendLine(String.Format("<c:fileas>{0}</c:fileas>", cn));
	sb.AppendLine(String.Format("<c:email1>\"{0}\" <{1}>
			</c:email1>", cn, customer.Email));
	sb.AppendLine(String.Format("<c:o>{0}</c:o>", customer.Company));
	sb.AppendLine(String.Format("<c:title>{0}</c:title>", customer.Title));
	sb.AppendLine(String.Format("<c:telephoneNumber>{0}
			</c:telephoneNumber>", customer.Phone));
	sb.AppendLine(String.Format
	("<c:facsimiletelephonenumber>{0}
			</c:facsimiletelephonenumber>", customer.Fax));
	sb.AppendLine(String.Format("<c:mobile>{0}</c:mobile>", customer.Mobile));
	sb.AppendLine(String.Format("<c:street>{0}</c:street>", customer.Street));
	sb.AppendLine(String.Format("<c:l>{0}</c:l>", customer.City));
	sb.AppendLine(String.Format("<c:st>{0}</c:st>", customer.State));
	sb.AppendLine(String.Format
			("<c:postalcode>{0}</c:postalcode>", customer.Zip));
	sb.AppendLine(String.Format("<c:co>{0}</c:co>", customer.Country));
	sb.AppendLine(String.Format("<h:textdescription>{0}
			</h:textdescription>", customer.Notes));

	sb.AppendLine("</g:prop></g:set>");
	sb.AppendLine("</g:propertyupdate>");

	return sb.ToString();
}

Variable cn [^] is the friendly name. Outlook uses this for EML file naming and also fileas [^] property. We'll use our custom naming for EML file.

Remember that the property workaddress [^] is a read-only property. For addresses, you have to fill street [^], l [^], st [^], postalcode [^] and co [^] fields.

The last part is UpdateContact method.

void UpdateContact(string contactUrl, string updateCommand)
{
	var buffer = Encoding.UTF8.GetBytes(updateCommand);

	var request = (HttpWebRequest)WebRequest.Create(contactUrl);
	request.Credentials = new NetworkCredential(UserName, Password);
	request.Method = "PROPPATCH";
	request.ContentLength = buffer.Length;
	request.ContentType = "text/xml";
	request.Headers.Add("Translate", "F");

	request.GetRequestStream().Write(buffer, 0, buffer.Length);
	request.GetRequestStream().Flush();
	request.GetResponse();
}

Deleting Contacts

To delete a contact, simply make a DELETE request.

private static void DeleteContact(string contactUrl)
{
	var request = (HttpWebRequest) WebRequest.Create(contactUrl);
	request.Credentials = new NetworkCredential(UserName, Password);
	request.Method = "DELETE";
	request.GetResponse();
}

Additional Information

If you want an Exchange account to do the synchronization process (on behalf of), you can create a new account for CRM system and give that account necessary rights.

To do this, go to contacts folder of the account which will hold the CRM data and enter to its properties. From "Permissions" tab, add the account which will manage the contacts, and then, give at least "Editor" permission.

References

History

This is the first published version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Efe Erdogru
Software Developer (Senior)
Turkey Turkey
No Biography provided
Follow on   Google+

Comments and Discussions

 
QuestionWebDAV .NET for Exchange PinmemberCarlLM30-Jul-12 1:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141216.1 | Last Updated 11 Sep 2011
Article Copyright 2011 by Efe Erdogru
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid