Microsoft Dynamics CRM 2011 (5.0) : C# - Add file to an account

April 18, 2011 by C#  

Microsoft CRM 2011 Logo

In one of my previous posts we had a look at how to add a file to a CRM 4.0 account using the webservices that ship with CRM - in this post we're going to look at how to accomplish this using CRM 2011.

In CRM 4.0 I used the webservice "/MSCRMServices/2007/CrmService.asmx", which is also available in CRM 2011 - which in theory should provide us with some compatibility, allowing us to still use our older code. (in my example I didn't use the CRM 4.0 SDK)

But unfortunately (at the moment I wrote this, perhaps Microsoft fixed it by the time you read this) this is not the case at all, if we attempt to add a file using the 2007 service provided by CRM 2011 you will get an exception that looks something like this:

Server was unable to process request

If we look at the detail of our SoapException, we'll find a message looking something like this:

<error>
  <code>0x80040216</code>
  <description>Attribute objectidtypecode must have the same value as attribute objecttypecode</description>
  <type>Platform</type>
</error>

Now this is obviously a new field added to CRM 2011, one thats not being exposed by the service - not sure if this is neglect or on purpose, but there is an easy workaround - try to avoid this workaround if possible (will show you a better alternative in this post).

Assuming you're using a web reference (not to be confused with the newer service reference WCF), you will need to find reference.cs in your web references folder, search for the annotation class and add the following property within the annotation class:
public EntityNameReference objectidtypecode  {
	get {
		return this.objecttypecodeField;
	}
	set {
		this.objecttypecodeField = value;
	}
}

If you're using a service reference (WCF), you will need to add something like this:

[System.Xml.Serialization.XmlElementAttribute(Order=24)]
public EntityNameReference objecttypecode {
	get {
		return this.objecttypecodeField;
	}
	set {
		this.objecttypecodeField = value;
		this.RaisePropertyChanged("objecttypecode");
	}
}

Even though this will probably solve the problem, we must rather try to use the new webservice "/CSTruter/XRMServices/2011/Organization.svc", because how many other underlying issues will we have without knowing about them?

Instead of consuming the above mentioned webservice like we generally consume services, we can use the CRM service utility (located in the CRM 2011 SDK bin folder) which will generate us some early-bound entity classes via the following command:
crmsvcutil /url:"http://dionysis:5555/CSTruter/XRMServices/2011/Organization.svc" /out:Xrm.cs

Note: chances are that you might receive the following exception if you run this tool:
Exiting program with exception: Could not load file or assembly 'Microsoft.IdentityModel, 
Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
This means that you need to install the WIF (Windows Identity Foundation) on your machine, available from here


To make all of this work you'll need to include the generated code along with references to the following .NET 4.0 assemblies:

microsoft.xrm.sdk assembly (also located within the CRM 2011 SDK bin folder)
System.Runtime.Serialization
System.ServiceModel

In the following snippet we consume the webservice:
using System;
using System.Linq;
using System.IO;
using Microsoft.Xrm.Sdk.Client;
using System.ServiceModel.Description;

class Program
{
    static OrganizationServiceContext context;

    static void Main(string[] args)
    {
        ClientCredentials credentials = new ClientCredentials();
        credentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
        //credentials.Windows.ClientCredential = new System.Net.NetworkCredential("username", "password", "domain");
        Uri uri = new Uri("http://dionysis:5555/UIT/XRMServices/2011/Organization.svc");
        OrganizationServiceProxy proxy = new OrganizationServiceProxy(uri, null, credentials, null);
        proxy.EnableProxyTypes(); // enable support for early-bound entities

        context = new OrganizationServiceContext(proxy);

Notice classes OrganizationServiceProxy and OrganizationServiceContext:
In the snippet we're passing the proxy to the service context class, now we don't need to do this, we can use the proxy class directly and skip the use of the context class, but apparently the context class attempts to manage entity relationships etc.

We're going to attempt to add a file to the following CRM account:

CRM 2011 Account

To add the file to this account (or pretty much any entity for that matter) will look something like this:
static void AddDocument(string filename, Guid id, string logicalName)
{
    Annotation annotation = new Annotation();
    FileInfo fileInfo = new FileInfo(filename);
    annotation.FileName = fileInfo.Name;
    annotation.DocumentBody = Convert.ToBase64String(File.ReadAllBytes(filename));
    annotation.IsDocument = true;
	
	// rather pass the actual type
    annotation.MimeType = "application/octet-stream";
    annotation.ObjectId = new Microsoft.Xrm.Sdk.EntityReference(logicalName, id);
    context.AddObject(annotation);
    context.SaveChanges();
}

We need to pass a filename (full path), Guid (reference to the specific account to which we want to add the file) and the logicalName of the entity (which is "account" in this instance).

In order to retrieve the reference needed for the preceding snippet, you can write a simple LINQ query like seen in the following snippet:

Account account = (from p in context.CreateQuery<Account>()
                    where p.Name == "test"
                    select p).SingleOrDefault();

if (account != null)
{
    AddDocument(@"c:\test.txt", account.Id, "account");
}


Leave a Comment


August 5, 2011 by Anonymous

THANK YOU. I didnt realize Documentbody was the actual contents of the file, your guide helped alot.