CRM2011 Support reference numbers

Support reference numbers are now provided to the user in CRM2011 when an exception is thrown where the details may contain sensitive information. Initially we thought that they could be standard codes with corresponding meanings, however the code is a random code that is used to cross reference against the trace or application log. The message displayed is usually in the format: "Reference number for administrators or support: #xxxxxx" The section contains the Exceptions we have found using this method along with the resolution.


System.Data.SqlClient.SqlException at Microsoft.Crm.Application.Platform.ServiceCommands.PlatformCommand.XrmExecuteInternal() Cause: Occurred when executing a SDK Request during import/publish of Customisations. Resolution: Wait until the import has finished and re-try. (V 5.0.9688.53)


System.TypeInitializationException at Microsoft.Crm.Application.Platform.ServiceCommands.PlatformCommand.XrmExecuteInternal() Cause: Occurred when a Plug In was executed after it had just been re-imported via a solution. The early bound proxy types were updated and I'm guessing the Proxy Types Behaviour was trying to load the old assembly. Resolution: iisreset fixed the issue. (V 5.0.9688.53)


System.Web.HttpUnhandledException Entity role must be specified for reflexive relationship <relationship name> Cause: Occurred when trying to delete a N:N association where the relationship was defined as being between two entities of the same type. Resolution: This is as yet unresolved (V 5.0.9688.53). See http://social.microsoft.com/Forums/en/crm2011beta/thread/71ceaec4-a43b-494c-8518-a00e27f553cb for more information.


System.Runtime.InteropServices.COMException (0x800703FA): Retrieving the COM class factory for component with CLSID {E5CB7A31-7512-11D2-89CE-0080C792E5D8} failed due to the following error: 800703fa Illegal operation attempted on a registry key that has been marked for deletion. Cause: Occurred after a Plug In was re-registered using the Plug In Registration Tool. Resolution: Re-booted fixed (V 5.0.9688.34)


Microsoft.Xrm.Sdk.OrganizationServiceFault Plug-in assembly does not contain the required types or assembly content cannot be updated. Cause: Plugin being registered does not have a public constructor with no parameters Resolution: Add a public constructor without any parameters in addition to the constructor public MyPlugin(string unsecure, string secure)

View un-encrypted SOAP xml when calling CRM2011 WCF end point

If you've ever used an HTTP Proxy (like Fiddler) to debug web service calls to ASMX endpoints, you'll probably try the same with the CRM2011 WCF web services only to be greeted with an encrypted soap packet. If you want to see the plain soap xml calls your client application or web application is making you can use WCF logging. 1) Add the following to the configuration section of your web.config or app.config:

section--> <trace autoflush="true" />

/ section-->

    &lt;source name ="System.ServiceModel.MessageLogging"
          switchValue="Verbose, ActivityTracing">
        &lt;listeners>
            &lt;add name="xml" />
        &lt;/listeners>
    &lt;/source>

section-->

  &lt;add name="xml" type="System.Diagnostics.XmlWriterTraceListener"
       traceOutputOptions="LogicalOperationStack"
       initializeData="C:\CrmLogFile.svclog" />

-->

<system.serviceModel> <diagnostics> <messageLogging logEntireMessage="true" logMalformedMessages="false" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="false"/> </diagnostics> </system.serviceModel>

  2) Now make your call and look for the Log File generated (defined via the initializeData attribute). 3) If you've got Visual Studio installed, you should be able to double click on the file and open the Microsoft Service Trace Viewer and examine the un-encrypted soap messages – both sent and received.

See http://msdn.microsoft.com/en-us/library/aa702726.aspx for more information.

Automatically Generate CRM2011 Entity Data Model Documentation

I am a firm believer in using a tool to generate documentation; it makes it more accurate and ultimately more useful. With CRM 4.0 we used a tool that parsed the Customizations.xml to produce the documentation. A significant difference between CRM2011 and CRM4.0 is the managed solutions approach to customisations, making it much easier to move customisations between environments. However as a result of this change, the way that entity metadata is imported/exported has changed so that managed (out of the box) attributes are no longer included in the customizations.xml. This means that it is no longer possible to generate documentation from the customizations.xml and so any such tool must call the Organizations.svc web service to retrieve the metadata. With this in mind, I spent some time this Christmas (in between eating turkey left overs) writing a Microsoft Word 2010 Add-In to aid the documentation process. Download CRM2011WordDocumentor.zip (29.45 kb) Merry Christmas and a Happy New Year!

Speed up JavaScript Web Resource development time

Don't waste time re-publishing JavaScript web resources during development The new solution framework in Dynamics CRM 2011 is a fantastic addition to the Xrm platform – especially with the new Web Resources functionality. The ability to include JavaScript files rather than including the script in the form customisations makes the management of source code much easier. This post shows a method that allows rapid changes to be made to the JavaScript code without the lengthy process of uploading the updated file and then re-publishing it. The key here is that whilst we are developing, we are going to use a 'stub' JavaScript web resource that simply bootstraps the actual JavaScript that we are working on from the file system. When you are ready to publish your solution, you will replace the 'Stub' with the actual JavaScript file you have developed. In this example we are creating JavaScript for the Contact Form. 1) Create 'ContactForm.js' file

/// if (typeof (Develop1) == "undefined") { Develop1= {}; } Develop1.EntityForm = { onLoad: function () { var attribute = Xrm.Page.data.entity.attributes.get('salutation'); var delegate = function () { Develop1.EntityForm.salutation_onchange(attribute); }; attribute.addOnChange(delegate);

},

salutation_onchange: function(sender) {
    alert(sender.getName());
}

}

 This script simply provides an onload function that will be called from the form, and 'wires up' an event handler to the salutation onchange event. Notice that we don't need to add any messy script to the form event handlers in the CRM form designer anymore, we can do this programmatically. I am going to store my script in the Dynamics CRM ISV folder whilst I work on it – however you can use any location accessible via a URL. The ISV folder is now deprecated and will be removed in future versions, but it is convenient since we can use relative paths to access it. Correspondingly save this file in a folder with the same name as your organisation underneath the ISV folder. E.g. ISV\<YourOrgName>\ContactForm.js. To ease development, you can add this to a Visual Studio project using a File Link. 2) Create the 'ContactForm_stub.js'

var scriptHtml = '<script language="javascript" src="\/ISV\/' + Xrm.Page.context.getOrgUniqueName() + '\/contactForm.js"><\/script>'; var scriptElement = document.createElement(scriptHtml); document.documentElement.appendChild(scriptElement);

 3) Upload the 'ContactFormstub.js' as a web resource Inside your solution, upload the ContactFormstub.js. Important: Name it as though you are uploading the actual JavaScript file since you will replace it before you export your solution.

    4) Add reference to the contact form

Within the CRM Contact form designer, add a reference to the ContactForm.js web resource and add an onload event handler that calls the function: Develop1.EntityForm.onLoad

  5) Publish once, then test, edit, test, edit… Publish your web resource and form customisations, and you'll find that you can edit the ContactForm.js file in the ISV folder with the effects happening immediately you refresh your contact form. 6) When you're done, before you export your solution Now that you've got your JavaScript working, before you export your solution, you want to remove the dependency on that external file, so you can simply edit your ContactForm web resource and upload the external file, thus replacing the stub. You will find that this greatly reduces the time it takes to author and debug your form JavaScript.

Things Dynamics CRM 4.0 Developers must know about CRM 2011 #1:

Nulls are handled differently by the SDK Web services: Using the CRM 4.0 SDK webservice and pipeline context, you could always exclude an attribute from an update by setting it to null. This has now changed: CRM 4.0: entity.attributename = null; CRM 2011: entity.Remove("attributename");   Data loss when using the RESTfull endpoint When using the CRM 4.0 SDK  Webservices, you could choose to only return certain attribute values and then when updating only update those values provided whilst leaving the rest the same as they were on the server. This is very desirable to facilitate concurrency (we don't want to send any update to an attribute if we are not actually updating it) and to minimise the traffic between the client and server (don't send/return every attribute with each call). In CRM2011 however using the OData endpoint this is no longer possible. All values must be retrieved and re-set when updating. For example, we can use projection onto an Entity to manipulate the $select on the OrganisationData.svc: var queryVar = from c in context.ContactSet where c.FullName.Contains(criteria) select new Contact { FullName = c.FullName, Telephone1 = c.Telephone1, EMailAddress1 = c.EMailAddress1, FirstName = c.FirstName, LastName = c.LastName, Address1Line1 = c.Address1Line1, Address1City = c.Address1City, Address1StateOrProvince = c.Address1StateOrProvince, Address1PostalCode = c.Address1_PostalCode };

This code uses the following GET: http://{server}:5555/{orgname}/xrmservices/2011/organizationdata.svc/ContactSet()?$filter=FullName%20eq%20'{crtieria}'&$select=FullName,Telephone1,EMailAddress1,etc...

This will only pull down the attributes we need to display. All good so far. However, when using the DataContext to update the record on the server using: _context.BeginSaveChanges(OnSaveContactsComplete, TheMainViewModel.Contacts); ...

We find that all the data that we didn't include in the $select is now nulled out. This is a common problem with ADO.Net data services and is indeed detailed as "Data loss might occur in the data service when you save updates that were made to projected types." in http://msdn.microsoft.com/en-us/library/ee473425.aspx This seems like a big flaw to the OData endpoint - in that it forces us to retrieve all values if we want to make any updates at all via the Client OData API. This might be a good reason to avoid the RESTful endpoint unless you know up front that you will never be updating data. The overhead having to retrieving all values before performing an update is not something that you want.   There is always an exception... Since JScript RESTful operations use the JSON notation to send data to the server rather than the OData framework, we can perform partial updates as shown by this JScript in the SDK example 'JScriptRestDataOperations:

function updateAccountRecord(Id) {

var updateAccountReq = new XMLHttpRequest(); var changes = new Object(); changes.Name = "Updated Sample"; changes.Telephone1 = "555-0123"; changes.AccountNumber = "ABCDEFGHIJ"; changes.EMailAddress1 = "someone1@example.com";

updateAccountReq.open("POST", ODataPath + "/AccountSet(guid'" + Id + "')", true); updateAccountReq.setRequestHeader("Accept", "application/json"); updateAccountReq.setRequestHeader("Content-Type", "application/json; charset=utf-8"); updateAccountReq.setRequestHeader("X-HTTP-Method", "MERGE"); updateAccountReq.onreadystatechange = function () { updateAccountReqCallBack(this, Id); }; updateAccountReq.send(JSON.stringify(changes)); }

This code will only update the Name, Telephone1, AccountNumber and EmailAddress1 attributes, and leave the rest unchanged. Note that the attribute names are Case Sensitive.

Microsoft Dynamics CRM 2011 Release Candidate is available for download

The next major milestone in the release schedule for Dynamics CRM 2011 has been reached with the Release Candidate being available for download:

Download Dynamics CRM 2011 Release Candidate

This is said to be the last externally available release before RTM.

It seems that although the Team blog states that "On-premises Beta databases can be upgraded to this new RC release which can then be upgraded to the upcoming RTM release.", what that actually means is that you must un-install your Beta 1, install RC1, and then import the organisation database using the deployment manager.

Read more on the Dynamics Team Blog

Cloud CRM for Less: $200 Cash back per seat

Microsoft have announced a new promotion called 'Cloud CRM for Less' that targets Oracle and Salesforce.com customers offering USD200 per seat cash back: http://www.microsoft.com/Presspass/press/2010/dec10/12-06OpenLetterPR.mspx http://www.cloudCRMforLess.com

Unit Testing Dynamics CRM 2011 Pipeline Plugins using Rhino Mocks

It’s been a while since Dynamics CRM 2011 Beta 1 was released (surely we are due Beta 2 soon!) so I thought it was about time I set up a Unit Test framework for PlugIns. I’ve been using Rhino Mocks for a while to great effect, so here we go! This example aims to unit test the SDK sample Plugins, and demonstrates the following:

Mocking the pipeline context, target and output property bags. Mocking the Organisation Service. How to assert that exceptions are raised by a plugin How to assert that the correct Organisation Service method was called with the desired values.

To build the examples, you’ll need the CRM2011 SDK example plugins and Rhino Mocks 3.6 (http://ayende.com/Blog/archive/2009/09/01/rhino-mocks-3.6.aspx).  The key principle of mocking is that we can exercise and examine the code that we need to test without executing the bits that are not being tested. By mocking we are fixing behaviour and return values of the dependant code so that we can assert if the results are what we expect.  This approach supports Test Driven Development (TDD), where the test is written first and then the desired functionality is added in order that the test passes. We can then say we are ‘Done’ when all tests pass.  So in our example, the Followup Plugin should create a task with the regarding id set to the id of the account. So by mocking the pipeline context, we can specify the account id, and check that the resulting task that is created is regarding the same account. Using Rhino Mocks allows us to create a mock Organisation Service and assert that the Create method was called passing a task with the desired attributes set.   [TestMethod] public void FollowupPlugin_CheckFollowupCreated() { RhinoMocks.Logger = new TextWriterExpectationLogger(Console.Out);

// Setup Pipeline Execution Context Stub
var serviceProvider = MockRepository.GenerateStub();
var pipelineContext = MockRepository.GenerateStub();
Microsoft.Xrm.Sdk.IPluginExecutionContext context = (Microsoft.Xrm.Sdk.IPluginExecutionContext)
    serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext));
serviceProvider.Stub(x =&gt; x.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext))).Return(pipelineContext);

// Add the target entity
ParameterCollection inputParameters = new ParameterCollection();
inputParameters.Add("Target", new Entity("account"));
pipelineContext.Stub(x =&gt; x.InputParameters).Return(inputParameters);

// Add the output parameters
ParameterCollection outputParameters = new ParameterCollection();
Guid entityId= Guid.NewGuid();

outputParameters.Add("id", entityId);
pipelineContext.Stub(x =&gt; x.OutputParameters).Return(outputParameters);

// Create mock OrganisationService
var organizationServiceFactory = MockRepository.GenerateStub();
serviceProvider.Stub(x =&gt; x.GetService(typeof(Microsoft.Xrm.Sdk.IOrganizationServiceFactory))).Return(organizationServiceFactory);
var organizationService = MockRepository.GenerateMock();
organizationServiceFactory.Stub(x =&gt; x.CreateOrganizationService(Guid.Empty)).Return(organizationService);


// Execute Plugin
FollowupPlugin plugin = new FollowupPlugin();
plugin.Execute(serviceProvider);

// Assert the task was created
organizationService.AssertWasCalled(x =&gt; x.Create(Arg.Is.NotNull));

organizationService.AssertWasCalled(x =&gt; x.Create(Arg.Matches(s =&gt; 
        ((EntityReference)s.Attributes["regardingobjectid"]).Id.ToString() == entityId.ToString()
        &amp;&amp;
        s.Attributes["subject"].ToString() == "Send e-mail to the new customer."
        )));

}

The key thing to notice is that the only mock object here is the OrganisationService - all others are stubs. The difference between a stub and a mock is that the mock records the calls that are made so that they can be verified after the test has been run. In this case we are verifying that the Create method was called with the correct properties set on the task entity. It’s worth noting the RhinoMocks.Logger assignment. This gives the Rhino logging output in the VS2010 test results; most helpful during debugging asserts that don’t do as you expect. Looking at the sample Account Plugin, it throws an exception when the account number is already set - so what testing that plugin’s throw exceptions under some conditions. Unfortunately, the standard VS2011 ExpectedExceptionAttribute doesn’t provide the functionality we need here since we can’t check exception attributes, nor can we run the tests in Debug without the Debugger breaking into the code even though the exception is marked as being expected. In order to get around this this I use a class written by Keith E. Burnell: using System; using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Kb.Research.RhinoMocks.UnitTests.CusomAssertions { /// /// Custom assertion class for unit testing expected exceptions.
/// A replacement for the ExpectedException attribute in MSTest /// public static class AssertException { #region Methods

    /// 
    /// Validates that the supplied delegate throws an exception of the supplied type
    /// 
    /// Type of exception that is expected to be thrown
    /// Delegate that is expected to throw an exception of type 
    public static void Throws&lt;TExpectedExceptionType&gt;(Action actionThatThrows) where TExpectedExceptionType : Exception
    {
        try
        {
            actionThatThrows();
        }
        catch (Exception ex)
        {
            Assert.IsInstanceOfType(ex, typeof(TExpectedExceptionType), String.Format("Expected exception of type {0} but exception of type {1} was thrown.", typeof(TExpectedExceptionType), ex.GetType()));
            return;
        }
        Assert.Fail(String.Format("Expected exception of type {0} but no exception was thrown.", typeof(TExpectedExceptionType)));
    }

    /// 
    /// Validates that the supplied delegate throws an exception of the supplied type
    /// 
    /// Type of exception that is expected to be thrown
    /// Expected message that will be included in the thrown exception
    /// Delegate that is expected to throw an exception of type 
    public static void Throws&lt;TExpectedExceptionType&gt;(string expectedMessage, Action actionThatThrows) where TExpectedExceptionType : Exception
    {
        try
        {
            actionThatThrows();
        }
        catch (Exception ex)
        {
            Assert.IsInstanceOfType(ex, typeof(TExpectedExceptionType), String.Format("Expected exception of type {0} but exception of type {1} was thrown.", typeof(TExpectedExceptionType), ex.GetType()));
            Assert.AreEqual(expectedMessage, ex.Message, String.Format("Expected exception with message '{0}' but exception with message '{1}' was thrown.", ex.Message, expectedMessage));
            return;
        }
        Assert.Fail(String.Format("Expected exception of type {0} but no exception was thrown.", typeof(TExpectedExceptionType)));
    }

    #endregion

}

}

So, we want to test that if the account number is already set on execution of the AccountNumberPlugin, then an exception is raised: [TestMethod] public void AccountNumberPlugin_CheckExceptionIfAccountNumberSetAllready() { // Setup Pipeline Execution Context Stub var serviceProvider = MockRepository.GenerateStub(); var pipelineContext = MockRepository.GenerateStub(); Microsoft.Xrm.Sdk.IPluginExecutionContext context = (Microsoft.Xrm.Sdk.IPluginExecutionContext) serviceProvider.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext)); serviceProvider.Stub(x => x.GetService(typeof(Microsoft.Xrm.Sdk.IPluginExecutionContext))).Return(pipelineContext);

// Add the target entity
ParameterCollection inputParameters = new ParameterCollection();
inputParameters.Add("Target",new Entity("account") { Attributes = new AttributeCollection  { 
        new KeyValuePair("accountnumber", "123")
    }});

pipelineContext.Stub(x =&gt; x.InputParameters).Return(inputParameters);

// Test that an exception is thrown if the account number already exists
AccountNumberPlugin plugin = new AccountNumberPlugin();
AssertException.Throws &lt; InvalidPluginExecutionException&gt; ("The account number can only be set by the system.",(Action) delegate {
        plugin.Execute(serviceProvider);
        });

}

The examples above show how to test plugins that don’t call any external services or code - where all dependencies are discovered via the execution context. Next time I’ll provide an example of how to mock an external service using inversion of control.    

CRM 2011 Document Management in SharePoint

Integration between Dynamics CRM and SharePoint has been possible since version 3.0 but involved custom extensions that often proved tricky to maintain. Dynamics CRM 2011 now provides SharePoint integration out of the box. This post provides a summary of this new integration with SharePoint 2010. Essentially the integration allows you to create a folder for each entity type (where document management is enabled), and sub folders to store documents relating to a specific record. Using SharePoint 2010, Dynamics CRM 2011 will automatically create these folders for your having the same name as a particular record. To utilise this automatic folder creation, a SharePoint site is set as the default location, and then for each record folder a 'Document Location' record is automatically created. These Document Locations can also be created manually on a per record basis to point to a folder by a absolute URL in any SharePoint site, or by using a relative URL relating to an existing Document Location. To Set up Document Management in Dynamics CRM 2011: 1) Select System->Document Management->Document Management Settings. 2) In the Document Management Settings Dialogue, select the entities that you wish to enable Document Locations on. 3) Select the Url of the SharePoint site that you want to store documents in by default. Users have the option of adding more locations manually on a per record basis, but this site is the default that will be used.  The default site can be changed later if it becomes full or changed if it has moved - if a new default location is created, new document locations will use the new site, but existing document locations will still be linked to the old site.

4) Click Next 5) In the next dialogue, the SharePoint site is validated. If you select 'Based on entity' you can select the sub folder to store documents underneath. For example, if you create a document location for a contact 'Joe Bloggs', and then create a case for Joe, and then select 'Documents', a document location will be created underneath the Joe Bloggs document folder. So the folder structure will be: SharePoint Site/contact/Joe Bloggs/incident/Broken Printer

Interestingly, if you don't have a folder for the contact before you create the case and select 'Documents', the contact folder will not be used, and you'll have:

SharePointSite/incident/Broken Printer

6) By Clicking 'Next' you'll see a message box asking you to confirm that you want to create the root document locations for each entity.

7) Clicking Ok, will give you a progress window, where each folder is created in SharePoint. You must have permissions to the given SharePoint site to create folders otherwise the process will fail.

At the core of the integration there are two new entities: SharePoint Site - Provides a link to a SharePoint site collection or site. It has the following attributes:

Name - The Name of the SharePoint site. This is set automatically to 'Default Site Collection', or 'Default Site'. Description Url Type - Picklist - Absolute/Relative Parent Site - Lookup - If the Url Type is Relative, this references the parent Site or Site collection Relative Url - Text - If the Url Type is Relative, this provides the sub site location.

Document Location - Provides a link to a document library and folder for a given record. E.g. If you click on 'Documents' in the navigation bar, a folder is created with the same name as the record. It has the following attributes:

Name - The Name of the Location. By default this is of the structure 'Documents on <Site Name> #' Where # is an incremental counter. This can be renamed once created. Description Url Type - Picklist - Absolute/Relative Absolute Url - The Parent Site Relative Url - The folder name where documents are stored. It cannot be more than one folder, so if you have nested folders, you must create a parent Document Location, and reference it in a sub Document Location. Regarding - Empty for root document folders (contact, account, incident etc), and for specific record folders, the lookup field references the specific record that the document folder is associated with. Essentially this is the same as the 'Regarding' field on activities.

So if you enabled document management on contacts and incidents, you would end up with the following Document Libraries:

Name

Parent Library /Location

Relative Url

Regarding

Documents on Default Site Collection 1

Default Site Collection

contact

 

Documents on Default Site Collection 2

Default Site Collection

Incident

 

Scenario 1 - A contact with 1 case with the 'Based on entity' unchecked.

Name

Parent Library/Location

Relative Url

Regarding

Documents on Default Site Collection 1

Default Site Collection

contact

 

Documents on Default Site Collection 2

Default Site Collection

Incident

 

Documents on Default Site Collection 3

Documents on Default Site Collection 1

Joe Bloggs

Joe Bloggs

Documents on Default Site Collection 4

Documents on Default Site Collection 2

Broken Printer

Broken Printer

Notice that the parent library/location for the contact and case is set to the root document location that was created by the Document Management Settings wizard. Scenario 2 - A contact with 1 case with the 'Based on entity' is checked.

Name

Parent Library/Location

Relative Url

Regarding

Documents on Default Site Collection 1

Default Site Collection

contact

 

Documents on Default Site Collection 2

Default Site Collection

Incident

 

Documents on Default Site Collection 3

Documents on Default Site Collection 1

Joe Bloggs

Joe Bloggs

Documents on Default Site Collection 4

Documents on Default Site Collection 3

incident

 

Documents on Default Site Collection 5

Documents on Default Site Collection 4

Broken Printer

Broken Printer

Notice that the broken printer case document folder is created underneath the contact folder: Contact/Joe Bloggs/incident/Broker Printer

Figure 1 - UML representation of CRM 2011 SharePoint concepts The actual interface to SharePoint is provided by the CrmGrid page that must be installed on the SharePoint server. The installation notes provide good instructions on how to get it installed: http://go.microsoft.com/fwlink/?LinkID=200050. Because the SharePoint integration is an Iframe, the client security context of the current user is used and so you must have access to the SharePoint site to perform operations from within Crm. There is no linkage between CRM and SharePoint Security. When you click on 'Documents' inside an entity form, you will be prompted with:

Clicking Ok, will create the document folder, and document location record. The document library view inside CRM looks like:

You can select from multiple locations associated with the same location using the Document Locations drop down. If you fill up your default site, you can add a new SharePoint Site through the Document Management Settings->SharePoint Sites list, and select 'Make Default'

  Notes: 1) Since the folder in SharePoint is named using the display name of the record, if you subsequently change the name in CRM (e.g. change the name of the contact, or rename the title of the case) you will receive a 'library not found' error when clicking on the 'Documents' navigation bar item. CRM 2011 then gives you the option to re-associate the record with a new location. This usually would mean renaming the folder in SharePoint and then updating the document location record in CRM. 2) There is no synchronisation between the security roles in CRM and those in SharePoint. For this reason you must secure your SharePoint site manually. If a user attempts to use the automatic folder creation feature, but they don't have access to the SharePoint document library, the operation will fail. 3) Some CRM 4.0 integrations to SharePoint allowed you to create a sub-site per entity record. This would still be possible with the CRM 2011 integration with some coding, since you can create your own document locations and relate them to the given record. A Plug-in could be created to automatically create a sub-site and create a corresponding SharePoint Site and Document Location when a record is created. Since the default site folder creation only runs if there are no Document Locations for the current record, you would then simply be given the custom document location rather than a default one created. 4) Note attachments and email attachments are still stored in the CRM database, and currently there is no easy way of removing the ability for uses to create attachments in CRM without removing notes all together. Email attachment support is tightly coupled to the CRM database storage of documents, and so this is always going to be needed. A custom plugin could be written to 'harvest' note/email attachments and automatically add them to the associated document location. 5) If you enable Content Types on your document libraries, when you add a document via CRM, since the interface is essentially just SharePoint, you are still promoted to select the Content Type and provide additional metadata. 6) The automatic folder creation functionality is only available when you are connecting to SharePoint 2010, although Document locations will work with SharePoint 2007. Conclusions The out of the box support for SharePoint in Dynamics CRM2011 does a good job of providing a simple and low maintenance option for users to store and retrieve documents in SharePoint  from within Dynamics CRM records. Users can then benefit from SharePoint's searching and document management features. More importantly, it is designed in such a way that it can be extended by Developers to support more complex integration scenarios.

Export Dynamics CRM Plugin Assembly

Have you ever uploaded a Dynamics CRM Plug in and then later wanted to get the assembly that was uploaded? The following steps are to modify the Plugin Registration Tool found in the CRM SDK to allow export of assemblies once they are uploaded. 1. In the file OrganisationHelper.cs

Modify the GetColumnSet method case PluginAssembly.EntityLogicalName: // Develop1:Download Assembly Support cols.AddColumns("name", "createdon", "modifiedon", "customizationlevel", "pluginassemblyid", "sourcetype", "path", "version", "publickeytoken", "culture", "isolationmode", "description","content");

  1. In the file CrmPluginAssembly.cs Add the following code to the class:

// Develop1 private string content; public void ExportAssembly(string path) {   byte[] assembly = Convert.FromBase64String(this.content);   File.WriteAllBytes(path, assembly); }

  1. In the file CrmPluginAssembly.cs Add the following code at the end of the RefreshFromPluginAssembly method:

// Develop1:Download Assembly Support if (assembly.Content != null) {     this._content = assembly.Content; }

  1. On the PluginRegistrationForm Form Add a button next to the Load Assembly command button with the following properties: Text: Export Assembly Name: cmdExport

  2. Double click on the button to get to the event code:

        // Develop1:Download Assembly Support         private void cmdExportClick(object sender, EventArgs e)         {             this.mcurrentAssembly.ExportAssembly(txtAssemblyPath.Text);           MessageBox.Show("Assembly exported.");         }

To use, you'll need to double click on a plugin assembly to bring up the properties page. Enter a file path (e.g. C:\ExportedAssembly.dll) Ddon't use the browse button since this wlll load an assembly. Click 'Export Assembly' to export your assembly.