Thursday, December 20, 2007

How to download and save email activity attachments from CRM 3.0

Ever want to get an attachment from an Email in CRM 3.0?
Well you are in luck, here is how to do it.

1) Use fetchxml to get all attachments from a specific email or for any other emails matching your query, for simplicity sake, the fetch xml will be for a single email.

2) Email attachments will be returned in the result set complete with the contents of the original file in Base64 format, you will have to decode it.

Use the following method to get a nodelist for any query

static XmlNodeList GetFetchResult(string fetchXML)

{

CrmService service = new CrmService();

service.Credentials = new System.Net.NetworkCredential("CRMAdmin", "Pa$$w0rd", "ADVWORKS");

string fetch = fetchXML;

string ret = service.Fetch(fetch);

System.Xml.XmlDocument xdoc = new System.Xml.XmlDocument();

xdoc.LoadXml(ret);

System.Xml.XmlNodeList list = xdoc.SelectNodes("resultset/result");

xdoc = null;

return list;

}

All email attachments are kept in the entity "ActivityMimeAttachment", here is the xml for the query.

Replace the "activityID" value with any email value or add your conditions.

<fetch mapping='logical'>
<entity name='activitymimeattachment'>
<all-attributes/>
<filter type='and'>
<condition attribute='activityid' operator='eq' value='{A9B5DD63-D59E-DC11-920F-0003FF612152}'/>
</filter>
</entity>
</fetch>
Call the GetFetchResult into a new Nodelist and loop through it.
XmlNodeList list = GetFetchResult("");

if (list.Count > 0)

{

foreach (XmlNode n in list)

{
string body = n["body"].InnerText;
string decoded = base64Decode(body);

}

}

Use this method to convert the body of the email into it's original format.

static string base64Decode(string data)

{

try

{

System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding();

System.Text.Decoder utf8Decode = encoder.GetDecoder();

byte[] todecode_byte = Convert.FromBase64String(data);

int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length);

char[] decoded_char = new char[charCount];

utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0);

string result = new String(decoded_char);

return result;

}

catch (Exception e)

{

throw new Exception("Error in base64Decode" + e.Message);

}

}
From here you can save the file, the filename is in the "filename" attribute of the result set.
Have a great day.
Oshri Cohen

Labels: , , , , , , , ,

Wednesday, December 19, 2007

Get started using FetchXML

Ever want to get started using FetchXML?

Well use this simple method to retrieve a node list with the results.
By providing this method with any fetchxml query it will return the appropriate nodelist.

   static XmlNodeList GetFetchResult(string fetchXML)
{
CrmService service = new CrmService();
service.Credentials = new System.Net.NetworkCredential("CRMAdmin", "Pa$$w0rd", "ADVWORKS");
//DataSet ds = GetData("select * from systemuser ");
string fetch = fetchXML;
string ret = service.Fetch(fetch);
System.Xml.XmlDocument xdoc = new System.Xml.XmlDocument();
xdoc.LoadXml(ret);
System.Xml.XmlNodeList list = xdoc.SelectNodes("resultset/result");
xdoc = null;
return list;

}


Have a great day!

Oshri Cohen


Labels: , , , , ,

Tuesday, December 11, 2007

Do you find these posts helpful?

If you find these posts helpful, can you please write a quick recommendation on my linkedIn account if you have one.

It would be greatly appreciated.

Thank you in advance
Oshri Cohen

Monday, December 10, 2007

How to get picklist values for a specific entity

Have you ever needed to get Entity information from CRM 3.0.
I needed to import data into CRM 3.0 some of the data needed to be imported into picklist fields.

Essentially what you need to do is:
1) connect to the metadata service.
2) request the entity metadata.

The following block of code calls the RetrieveEntityMetadata method.
passing the entity schema name and the EntityFlags enum.
The code than iterates through all the attributes and inserts them into a HashTable.

/*
EntityFlags.All; 
EntityFlags.EntityOnly; (gets only entity info)
EntityFlags.IncludeAttributes; (includes the attributes)
EntityFlags.IncludePrivileges; (includes security)
EntityFlags.IncludeRelationships; (includes relationship)
*/

Hashtable _h = new Hashtable();
MetadataService s = new MetadataService();
s.Credentials = System.Net.CredentialCache.DefaultCredentials;

EntityMetadata em = s.RetrieveEntityMetadata("contact", EntityFlags.IncludeAttributes);

foreach (AttributeMetadata am in em.Attributes)
{
    _h.Add(am.Name, am);
}

Each attribute has it's own data type class that is inherits the AttributeMetaData base class.

To retrieve the picklist I needed to convert the attribute property of the entity to a PicklistAttributeMetadata.

The PickListAttributeMetaData class has an array property called options.

for (int i = 0; i < picklistData.Options.Length; i++)
{
     if (picklistData.Options[i].Description == displayValue)
     {
          // Value has been found!!!
          // optionValue = picklistData.Options[i].OptionValue;
          break;
      }
}


Oshri Cohen

Monday, December 3, 2007

How to create a Task in CRM 3.0

Introduction

I was recently assigned to transfer data from an ACT! to Microsoft CRM 3.0.
The data included importing all of the present and historical activities.
In CRM 3.0 all tasks, emails, appointments... are classified as activities in order to import the data effectively I needed to understand what each field in the task object of the CRM 3.0 web service represented.
I will explain each field in this Article.

Background

To get the most out of this article you need to understand how the CRM 3.0 web services work.
Take a look at the Microsoft CRM 3.0 documentation on MSDN, it is very detailed and will help you get started.
In the article I am assuming that you imported the web service.

Using the code

First of all what you need to do is create the task object.

task t = new task();

When creating any CRM object programmatically you will need to create a new instance of the non string property types.
CRM provides its own custom objects, in the case of a DateTime object create a new CrmDateTime instance and then access the value of the property.
*** Please note that CRM will modify the date to a different standard time zone for storage in the database, this is only done if the data is inserted using the API or the user interface.
so don't be alarmed if your dates different on the database.
If your task has a priority than use the following code.

t.prioritycode = new PickList();
t.prioritycode.Value = Convert.ToInt32("0");


Picklists are handled using the PickList object.
Make sure that you have the corresponding value CRM.
You must do the mapping between your lists and crm picklists.
If the data is not there add it through the CRM interface and don't forget to publish your changes otherwise the API will throw an exception.
Most activity objects require a subject.
Please be aware of the length of the subject data.
The task subject is limited to 150 characters, it will also truncate at the first line break instance.
If you don't do this test it will be hard to debug because the only error reported by the API is "a general SQL error".
In ACT! there is no subject line, so i needed to import the trailing text into the description property.

t.subject = "my first task";
t.description = "my first task description"

You can of course create tasks in the past, present or the future.
This is handled by the following lines of code.

t.scheduledstart = new CrmDateTime();
t.scheduledstart.Value = DateTime.Now.ToString();


Use the "scheduledend" property to set the end of the activity, this is not required.

t.scheduledend = new CrmDateTime();
t.scheduledend.Value = DateTime.Now.ToString();


The following properties are required and are explained in greater detail.
The regardingobjectid are of type Lookup.

t.regardingobjectid = new Lookup();
t.regardingobjectid.name = "my name";
t.regardingobjectid.type = "type of object";
t.regardingobjectid.Value = new Guid("");

The name property is self explanatory; it is the name of the user that created the object.
The type is more complex, you need to provide the name of the entity as it is specified in CRM.
The type for a user is "systemuser".
The authors of the API provided us with an Enum class with all the entity names and their type codes.
This would return the string "systemuser".

EntityName.systemuser.ToString();

This would return the type code (you don't really need this in most cases).

Convert.ToInt32(EntityName.systemuser).ToString();

The Value property is of type GUID; all primary keys are stored as unique identifiers.
The RegardingobjectID is very important but not required, this is the property that needs to be set if you want to associate the activity with a client, order... or any entity that is configured to accept Activities.
The ownerid property is similar to the Lookup where they share the same fields.
Substitute the Lookup object for the Owner object.

t.ownerid = new Owner();
t.ownerid.name = "my name";
t.ownerid.type = "type of object";
t.ownerid.Value = new Guid("");


The owner is usually of type User but it can also be a Queue.
If you need to set the duration for the task use the following property.
The duration is stored as an integer in minutes.

t.scheduleddurationminutes = new CrmNumber();
t.scheduleddurationminutes.Value = 45;


Actually creating the task on the server is simple.
An instance of the CrmService object is needed.
***This is for .net 2.0***

CrmService service = new CrmService();
service.Credentials = System.Net.CredentialCache.DefaultCredentials;


When you create a new entity CRM will return a newly created GUID.
If for whatever reason the creation fails the API will throw a SoapException and the actual error message will be located in the Detail.InnerText property.

Guid newTaskID = new Guid();
try
{
newTaskID = service.Create(t);
}
catch(System.Web.Services.Protocols.SoapException ex)
{
string strMessage = ex.Detail.InnerText;
}

If the task is a historical activity than you need to set it as completed.
Using the SetStateTaskRequest object to ask CRM to close the activity,
each activity type has its own SetState class.

SetStateTaskRequest tr = new SetStateTaskRequest();
tr.EntityId = newTaskID;
tr.TaskState = new TaskState();
tr.TaskState = TaskState.Completed (or Canceled,Open);
tr.TaskStatus = -1 (set this to -1 so that CRM can decide the appropriate status.)
service.Execute(tr);

How to attach a file to any CRM 3.0 entity

Introduction

Microsoft CRM 3.0 has the ability to store files as attachments for any entity that supports notes.
This article is a guide to how an article would be attached to a note (otherwise known as an annotation within CRM 3.0).
To understand how to attach a file in CRM 3.0, you need to understand CRM's file attachment architecture.
Almost all entities in the system can have notes, when manually attaching a file through the UI a reference to the file shows up in the notes section.
That is because CRM holds all file attachments that are not for activities as notes.
You will have to do the following:
1) Create or retrieve the entity that you want the file to be attached to.
2) Read the file steam and encode it to a Base64 string.
3) Create the UploadFromBase64DataAnnotationRequest object and execute it.

Using the code

I will first explain the steps and code required to attach a file to any entity.
Let's assume you want to attach a PDF file to a client's contact record in CRM programmatically.
First you will need the contactID guid of the target contact record.
Secondly, you will need to create a new note.
CRM attaches all files that are NOT activity attachments as notes.
To create a note use the following block of code:

CrmService myCRMService = new CrmService();
myCRMService.Credentials = System.Net.CredentialCache.DefaultCredentials;

//#1
annotation note = new annotation();
note.subject = "my attachment";

//#2
note.ownerid = new Owner();
note.ownerid.type = "systemuser";
note.ownerid.Value = new Guid("000-000-0000"); <-- don't use this value
note.ownerid.name = "Administrator";

//#3
note.objectid = new Lookup();
note.objectid.name = "contact name";
note.objectid.type = EntityName.contact.ToString(); <-- use the correct type
note.objectid.Value = new Guid("000-000-0000-000"); <-- use the contact's guid

//#4
note.objecttypecode = new EntityNameReference();
note.objecttypecode.Value = EntityName.contact.ToString();

Guid newNoteID = new Guid();
newNoteID = myCRMService.Create(t);

#1
A note entity is known in CRM as an Annotation.

#2
Like all other user owned entities in CRM you need to assign an owner, in this example i used the system administrator.

#3
The annotation class has what is known as the objectid it is a property of type Lookup().
You need to tell CRM where you want the note attached, by providing the name, ID and type of the target entity.

In this case I used a contact as my target.
The note.objectid.type is very important it tells CRM the type of the target entity.
In order to help reduce human error the CRM API developers included a reference Enum class called EntityName, this enum is updated if you create new custom entities and publish them.

#4
The objecttypecode property is a required field, without it CRM will throw an exception.
You have to give the target entity's type, i recommend using the EntityName Enum to prevent any errors.
This may seem a bit redundant, I am not sure why we have to set the type again, if anyone knows please let me know.

Finally you will need to create the note to get the a Guid.
The next step is to actually upload a file:

//#1
FileInfo pointer = new FileInfo( "c:/test.pdf");
FileStream fileStream = pointer.OpenRead();
byte[] byteData = new byte[(int)fileStream.Length];
fileStream.Read(byteData, 0, (int)fileStream.Length);
string encodedData = System.Convert.ToBase64String(byteData);

//#2
fileStream.Flush();
fileStream.Close();

//#3
UploadFromBase64DataAnnotationRequest upload = new UploadFromBase64DataAnnotationRequest();
upload.AnnotationId = newNoteID;
upload.FileName = "test.pdf";

//#4
upload.MimeType = "application/pdf";

//#5
upload.Base64Data = encodedData;

//#6
UploadFromBase64DataAnnotationResponse uploaded = (UploadFromBase64DataAnnotationResponse)myCRMService.Execute(upload);

#1
You will need two classes the FileInfo class and the FileStream class.

The FileInfo, because you can easily get the size of the file and the extension which will be needed at point #4 to identify the mimetype.
The FileStream class because you will need to convert the file to a base64 string (not really sure why).

#2
I don't need to state this but make sure to close the filestream.

#3
If you want to upload a file to an annotation you will need to use the UploadFromBase64DataAnnotationRequest class, this class is used ONLY for annotation attachments.

The class will accept the ID of the annotation, you will also have to provide the filename.

#4
The sticky point here is specifying the MimeType, this took me forever because you have to be very specific otherwise CRM will not deliver the file correctly when you want to upload it.
I used the following MimeType list to search for the correct value based on the file extension.

#5
In this step you provide the upload request with the encoded Base64 string.

#6
Finally you upload the file by calling the crmService.Execute method and passing it the upload request.

If you have a large file to upload and many other tasks to perform afterwards, i suggest uploading the file Asynchroniously by calling the crmService.ExecuteAsync method.

I hope you this article has helped you.

Labels: , , , , ,