I've been trying to re-deploy a CRM 4.0 installation from a client's site into our development domain so that I can work with some of the data in the system. Using the provided CRM 4.0 Deployment Manager is pretty easy (instructions found here), but wasn't working. The deployment manager would run for a good 30+ minutes and eventually come back to me with a SQL Server Timeout error. The details of this error follow: Import Organization (Name={Name}, Id={ID} failed. The changes made during Import Organization could not be rolled back. Please manually delete the Organization database from the 'SBCRM01' SQL Server if it was not successfully deleted. System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) … at Microsoft.Crm.Tools.Admin.ImportOrganizationInstaller.Import(Guid organizationId, String organizationUniqueName, String organizationFriendlyName, String sqlServerName, String databaseName, Uri reportServerUrl, String privilegedUserGroupName, String sqlAccessGroupName, String userGroupName, String reportingGroupName, String privilegedReportingGroupName, ICollection`1 users, MultipleTenancy multipleTenancy) I Googled, hooked up SQL Server Profiler and searched Microsoft’s knowledge base – desperate for some sort of answer to this problem. I finally found a forum post online that described a similar scenario being resolved by installing Microsoft Dynamics CRM 4.0 Update Rollup 1. It wasn’t until I looked at the deployment log file that I realized that the timeout expired error was really just a symptom of another problem. Turns out that below the Timeout exception I found another exception message that describes what’s really going on. Import Organization (Name={Name}, Id={ID}) failed with Exception: Microsoft.Crm.CrmException: Unable to find metadata information for attribute {attribute_name} at Microsoft.Crm.Workflow.ObjectModel.MetadataProvider.GetAttributeMetadata(EntityMetadata entityMetadata, String attributeName) … at Microsoft.Crm.Tools.Admin.ImportOrganizationInstaller.Import(Guid organizationId, String organizationUniqueName, String organizationFriendlyName, String sqlServerName, String databaseName, Uri reportServerUrl, String privilegedUserGroupName, String sqlAccessGroupName, String userGroupName, String reportingGroupName, String privilegedReportingGroupName, ICollection`1 users, MultipleTenancy multipleTenancy) What the heck is this? Turns out, the issue is caused by having workflow in CRM that references an attribute that is subsequently deleted. Per Joel CustomerEffective (forum post), here’s some things you can do to resolve the issue: 1. Recreate the missing attribute (you can delete after import). 2. Delete any rows that reference that attribute from the workflowdependencybase table 3. Import the organization into an environment that is pre-rollup 1 4. Wait for KB958571 to be released--they are testing it now. If you search for it, you will find the KB article for it, but the hotfix has been redacted pending further testing. Of course, KB958571 talks about a hotfix that isn’t publically available. Sad – really sad, but have no fear! After wasting 6 hours today working on this problem, I’m still waiting on getting an answer from Microsoft about the availability of the patch. I’ve attempted solution #2 (removing the dependencies from the source database before performing the import) without success. Options #1 and #3 aren’t available to me at the moment. If you know of a better solution, please post a comment to this blog! Technorati Tags: CRM 4.0,Troubleshooting,Tips and Tricks
One of the best features and biggest drawbacks to CRM 4.0's entity model is the ability to modify forms in CRM using Javascript. While this level of customization is something any sizable implementation will need, it's been reduced to pasting a bunch of Javascript code into a text box in CRM. Not that I've got anything against pasting code in a textbox in CRM, but this isn't exactly an integrated development experience. In this post, I'll demonstrate how to have the onLoad() script in a CRM entity dynamically load one or more Javascript files (externally) and then run a load script once all the files have completely loaded. First, let's talk about a little housekeeping. The image to your left (click to enlarge) represents the solution explorer view of a web project I've created that contains an ISV solution for CRM. There's some important things to note here. First, I've got a folder called "Javascript". This folder houses my site-wide Javascript, and the basis for the OnLoad() handler that I'll bind to each entity. Let's take a look at some of the files in this folder. CrmFormHelper.js Houses functions that help handle form events easily. After loaded by our dynamic scripting engine, the form helper is available to any form in CRM via the document.FormHelper object. CrmFormIFrame.js Houses functions that allow CRM to manipulate the IFRAME instance where a page appears CrmWebService.js Houses functions that easily allow CRM web services to be consumed through Javascript. DiagnosticsWindow.js When enabled, provides a trace window that shows events as they fire in CRM using the Javascript scaffolding in this article OnLoad.js Provides a stock OnLoad() implementation for your entities OnSave.js Provides a stock OnSave() implementation for your entities. For the purposes of this post, I'm going to focus on CrmFormHelper and OnLoad.js. What's in these fancy scripts, anyhow? Let's take a look:CrmFormHelper.js 1: CrmFormHelper = function() {
2:
3: //Trace - Start
4: var TraceWindowHandle = null; var TraceLastTrace = new Date(); function TraceClear() { if (TraceWindowHandle == null) return; alert(TraceWindowHandle.document.body.innerHTML); TraceWindowHandle.document.body.innerHTML = '<body>'; TraceWindowHandle.document.writeln('<a href="javascript:opener.TraceClear();">Clear Window</a>'); } function Trace(cat, message) { if (typeof TraceWindowEnabled == 'undefined') return; message = message.split('<').join('<'); message = message.split('>').join('>'); if (TraceWindowHandle == null) { TraceWindowHandle = open('', 'TraceWindow', 'width=500,height=500,location=no,menubar=no,resizable=yes,left=200,top=20,directories=no,status=no,scrollbars=yes'); TraceWindowHandle.document.writeln('<a href="javascript:opener.TraceClear();">Clear Window</a>'); } try { date_now = new Date(); elapsedMS = date_now.getTime() - TraceLastTrace.getTime(); TraceLastTrace = date_now; TraceWindowHandle.document.writeln('<br>' + date_now + '(' + elapsedMS + ') ' + ':' + cat + ':' + message); TraceWindowHandle.window.scroll(0, TraceWindowHandle.document.body.scrollHeight + 10); } catch (err) { TraceWindowHandle = null; } } function TraceWriteRaw(markup) { if (typeof TraceWindowEnabled == 'undefined') return; if (TraceWindowHandle == null) { TraceWindowHandle = open('', 'TraceWindow', 'width=500,height=500,location=no,menubar=no,resizable=yes,left=200,top=20,directories=no,status=no,scrollbars=yes'); TraceWindowHandle.document.writeln('<a href="javascript:opener.TraceClear();">Clear Window</a>'); } try { TraceWindowHandle.document.writeln(markup); } catch (err) { TraceWindowHandle = null; } }
5: //Trace - End
6:
7: CrmFormHelper.prototype.IsEditForm = function() {
8: var CRM_Form_TYPE_EDIT = 2;
9:
10: if (crmForm.FormType == CRM_Form_TYPE_EDIT)
11: return true;
12: else
13: return false;
14: }
15:
16: CrmFormHelper.prototype.HandleFormOnLoad = function() {
17: Trace('HandleFormOnLoad', 'Starting');
18:
19: var CRM_Form_TYPE_CREATE = 1;
20: var CRM_Form_TYPE_EDIT = 2;
21:
22: switch (crmForm.FormType) {
23: case CRM_Form_TYPE_CREATE:
24: if (this.OnFormLoadCreate != null) {
25: Trace('HandleFormOnLoad', 'Calling OnFormLoadCreate');
26:
27: try {
28: this.OnFormLoadCreate();
29: }
30: catch (e) {
31: Trace("HandleFormOnLoad", "Error while calling OnFormLoadCreate: " + e.Description);
32:
33: debugger;
34: }
35:
36: Trace('HandleFormOnLoad', 'Done Calling OnFormLoadCreate');
37: }
38:
39: break;
40: case CRM_Form_TYPE_EDIT:
41: if (this.OnFormLoadEdit != null) {
42: Trace('HandleFormOnLoad', 'Calling OnFormLoadEdit');
43:
44: try {
45: this.OnFormLoadEdit();
46: }
47: catch (e) {
48: Trace("HandleFormOnLoad", "Error while calling OnFormLoadEdit: " + e.Description);
49:
50: debugger;
51: }
52:
53: Trace('HandleFormOnLoad', 'Done Calling OnFormLoadEdit');
54: }
55:
56: break;
57: }
58:
59: Trace('HandleFormOnLoad', 'Ending');
60: }
61:
62: CrmFormHelper.prototype.OnFormLoadCreate = function() {
63: Trace('OnFormLoadCreate', 'Default implementation called - no action taken');
64: }
65:
66: CrmFormHelper.prototype.OnFormLoadEdit = function() {
67: Trace('OnFormLoadEdit', 'Default implementation called - no action taken');
68: }
69:
70:
71: CrmFormHelper.prototype.HandleFormOnSave = function() {
72: Trace('HandleFormOnSave', 'Starting');
73: if (crmForm.IsDirty) {
74: Trace('HandleFormOnSave', 'Calling OnFormSaveDirty');
75: if (!this.OnFormSaveDirty()) {
76: Trace('HandleFormOnSave', 'OnFormSaveDirty setting return value to false, since onsavedirty returned false');
77: // Cancel the save operation.
78: event.returnValue = false;
79: }
80: Trace('HandleFormOnSave', 'Done Calling OnFormSaveDirty');
81: }
82: else {
83: Trace('HandleFormOnSave', 'Calling OnFormSaveClean');
84: this.OnFormSaveClean();
85: Trace('HandleFormOnSave', 'Done Calling OnFormSaveClean');
86: }
87: Trace('HandleFormOnSave', 'Ending');
88: }
89:
90: CrmFormHelper.prototype.OnFormSaveDirty = function() {
91: Trace('OnFormSaveDirty', 'Default implementation called - no action taken');
92: }
93:
94: CrmFormHelper.prototype.OnFormSaveClean = function() {
95: Trace('OnFormSaveClean', 'Default implementation called - no action taken');
96: }
97:
98: CrmFormHelper.prototype.GetDefaultLookupValue = function(guid, type, label) {
99: var lookupItem = new Array();
100: lookupItem[0] = new LookupControlItem(guid, type, label);
101: return lookupItem;
102: }
103: }
104:
105: document.FormHelper = new CrmFormHelper();
What does all this code do, anyhow? Well, basically, we're creating a Javascript class that can responsibly handle form events (load and save). This means that a different function can be called on Create or Edit mode for a form, and a different function can be called based on the form's state (clean or dirty) upon save. You'll also notice a bit of error handling code on each call, where we'll dump failures to the Trace window and launch the debugger. In our setup, all of the code above lives in an external Javascript file. This means, using Visual Studio 2008, you can set a breakpoint in any portion of this code and get full debugging support.
But how do I get this external code to load when my entity's form is displayed? Basically, you can copy and paste the following 40 lines of code in your entity's onLoad handler. Let's take a closer look. 1: function onLoad() {
2: window.DynamicScripts = new Array();
3: window.DynamicScriptsLoaded = 0;
4:
5: // *** BEGIN EDITS HERE *** Add any scripts you want to be loaded when the page begins, CrmFormHelper.js is required.
6: window.DynamicScripts[0] = "/ISV/Custom/Javascript/CrmFormHelper.js";
7: window.DynamicScripts[1] = "/ISV/Custom/Javascript/CrmWebService.js";
8: window.DynamicScripts[2] = "/ISV/Custom/Price List/Javascript/PriceListForm.js";
9: // *** END EDITS HERE ***
10:
11: //Trace - Start
12: var TraceWindowHandle = null; var TraceLastTrace = new Date(); function TraceClear() { if (TraceWindowHandle == null) return; alert(TraceWindowHandle.document.body.innerHTML); TraceWindowHandle.document.body.innerHTML = '<body>'; TraceWindowHandle.document.writeln('<a href="javascript:opener.TraceClear();">Clear Window</a>'); } function Trace(cat, message) { if (typeof TraceWindowEnabled == 'undefined') return; message = message.split('<').join('<'); message = message.split('>').join('>'); if (TraceWindowHandle == null) { TraceWindowHandle = open('', 'TraceWindow', 'width=500,height=500,location=no,menubar=no,resizable=yes,left=200,top=20,directories=no,status=no,scrollbars=yes'); TraceWindowHandle.document.writeln('<a href="javascript:opener.TraceClear();">Clear Window</a>'); } try { date_now = new Date(); elapsedMS = date_now.getTime() - TraceLastTrace.getTime(); TraceLastTrace = date_now; TraceWindowHandle.document.writeln('<br>' + date_now + '(' + elapsedMS + ') ' + ':' + cat + ':' + message); TraceWindowHandle.window.scroll(0, TraceWindowHandle.document.body.scrollHeight + 10); } catch (err) { TraceWindowHandle = null; } } function TraceWriteRaw(markup) { if (typeof TraceWindowEnabled == 'undefined') return; if (TraceWindowHandle == null) { TraceWindowHandle = open('', 'TraceWindow', 'width=500,height=500,location=no,menubar=no,resizable=yes,left=200,top=20,directories=no,status=no,scrollbars=yes'); TraceWindowHandle.document.writeln('<a href="javascript:opener.TraceClear();">Clear Window</a>'); } try { TraceWindowHandle.document.writeln(markup); } catch (err) { TraceWindowHandle = null; } }
13: //Trace - End
14:
15: TraceWindowEnabled = true;
16: Trace("Dynamic Scripts", "Script loading started");
17:
18: for (i = 0; i < window.DynamicScripts.length; i++) {
19: var script = document.createElement("SCRIPT");
20:
21: script.language = "javascript";
22: script.src = window.DynamicScripts[i] + "?nocache=" + Math.random();
23: script.onreadystatechange = function() {
24: if (this.readyState == "loaded" || this.readyState == "complete") {
25: window.DynamicScriptsLoaded += 1;
26:
27: Trace("Dynamic Scripts", this.src + " " + this.readyState);
28:
29: if (window.DynamicScriptsLoaded == window.DynamicScripts.length) {
30: Trace("Dynamic Scripts", "Script loading complete");
31:
32: if (document.FormHelper != null)
33: document.FormHelper.HandleFormOnLoad();
34: }
35: }
36: }
37:
38: document.getElementsByTagName("HEAD")[0].appendChild(script);
39: }
40: }
41:
Basically, lines 5-9 of the above snippet specify which external script files should be loaded before the page's CrmFormHelper's load methods are called. You can add any number of Javascript files here. Lines 18-36 dynamically build <SCRIPT> tags and add them to the header. At this point when all the scripts are loaded, then (and only then) is the FormLoad() fired (line 33). This guarantees that all of your script libraries are available to your OnLoad script.
So, what happens when the page loads? Looking at line 9 of the above code snippet, we see that one of the external javascript files is "/ISV/Custom/Price List/Javascript/PriceListForm.js". This external Javascript file is real small and houses 2 different functions that fire when my page loads (one for edit mode, one for creation). 1: /// <reference path="../../Javascript/CrmFormHelper.js" />
2: /// <reference path="../../Javascript/CrmWebService.js" />
3: /// <reference path="../../Javascript/Intellisense/new_custompricelist.js" />
4:
5: document.FormHelper.OnFormLoadCreate = function() {
6: alert("Form create was called");
7: }
8:
9: document.FormHelper.OnFormLoadEdit = function() {
10: alert("Form edit was called");
11: }
What's most important about this file is the first 3 lines. Those comments actually tell Visual Studio 2008 to load the specified Javascript files (which are loaded using our DynamicScript above) for interpretation in Intellisense. Yes, you got the right -- by referencing those external scripts in the first 3 lines of this file, you'll get full Intellisense support.
That concludes this post. In summary, using external Javascript files gets us a first class debugging experience in Visual Studio. Try it!
Technorati Tags: VSTS,CRM 4.0,Javascript
Presented by Andrew Bybee (Principal Program Manager, Microsoft) and Nirav Shah (Senior Development Lead, Microsoft) This presentation is all about code. Objectives in the session include: Demonstration of the rapid development of business applications with Dynamics CRM. Introduce new capabilities for extending business logic in Dynamics CRM "v.Next" Think "XRM" instead of CRM -- building on CRM as an application platform instead of just customer relationship management. What's the value of CRM? Well, it's really about simplifying the buy vs. build decision-making process. Specifically, CRM's value is that it can: Quickly build prototype solutions to help approve the project Meet customer requirements with foundation for future releases Build credibility and trust with predictable delivery Today, writing enterprise line of business applications requires interaction with many different components, including considerations for: Workflow Security Reporting Outlook /; Office Communications Configuration Configuration Deployment Scalability Manageability Extensibility Upgrade If you're building your applications in .NET, all the aspects mentioned above will take away from the budget you may have to deliver a solution to your customer. So, what's new in CRM 5?Before PDC, Microsoft has been really tight-lipped on what's new in CRM 5 (or v.Next). Nirav Shah previously worked on the CRM Online team, and now he's a Lead Developer on the CRM 5 product team. Here's the short list of future items for CRM 5. Partial-trust code in hosted environments (a.k.a. "The Sandbox") - Basically, this functionality offers developers the capability to have a fully baked business layer in their CRM solutions. Transaction support Integration with Internet Service Bus and Cloud services (Azure) Single-sign-on support, impersonation and delegation via Geneva STS The SandboxHow does this work anyway? Well, basically, the CRM SDK will keep the Pipeline Processing model from CRM 4, but will have a special partial-trust calling mode on the Sandbox server (a new server role in CRM). The Sandbox host service (reached through a WCF channel) receives information from the CRM SDK about what code needs to be executed, what it needs to be executed for and what inputs are required. Next, the Sandbox server will check the assembly cache for the code to be executed. If a new execution is run, it's done in a separate application domain, locked down to only allow code execution by the hosted code from the Sandbox server. The result? A highly scalable, flexible extensibility model which allows for richer and richer application support inside CRM. SolutionsSolutions allow a way to package a number of customizations in CRM together to provide a single solution (e.g. "Product Management Solution"). Solutions can be combinations of customizations from ISV.config changes, entity definitions, custom CRM configuration options and more. Ribbon SupportCRM 5.0 will feature a new Ribbon-based UI in many places. Customizable Roles and LevelsAn enhanced security subsystem now allows for new types of role definitions (not just permission definitions for user accounts). Isolated ExecutionNew hosted plug-ins are managed through a new IIsolatedPlugin interface, which is identical to the CRM 4.0 plug-in interface. In addition, plug-ins can now be registered for execution inside or outside a transaction context. In the event that a medium-trust plug-in fails, TransactionsInitiated by the server, managed by the server but completely administratable by the Plug-in through it's execution context. DiagnosticsNew tracing service lets developers track what's going on in a plug-in's execution and report on it later. In addition, CRM itself monitors for plug-in and code health when hosing code in the CRM environment. Want to find out more about CRM 5? I'm sure more will be released over the next few weeks, but for now, I'd check out this great page on Channel 9. Technorati Tags: Dynamics CRM 5.0
If you're like me, you're certain that the best new feature (and most anticipated) in VS2008 is Javascript intellisense. Wish you could get that great feature available to you when working with custom CRM entities? You can. Check out the video here.
Technorati Tags: Dynamics CRM,Tips and Tricks
Many people don't realize just how extensible Dynamics CRM 4.0 is as a platform for building line of business applications. David Yack (MVP) has self-published a great book (available in eBook or print) for developers about building Line of Business applications on top of Dynamics CRM. It's a bit pricey, but I downloaded it this morning and I'm super-psyched about many of the chapters. It's also been vetted by a number of Microsoft MVP's in CRM, which should help alleviate some of the subjective-ness of other technical books.
About the Book
Technorati Tags: Dynamics CRM
Tired of manually using the web service to execute queries on custom CRM entities? Wish you could write queries with complex joins, select, where, grouping and ordering clauses without having to go to your FetchXML reference?
I've been a LINQ evangalist for a while, mostly because it provides a consistent query expression syntax for just about anything you can imagine, now including Dynamics CRM 4.0 entities :)
http://www.codeplex.com/LinqtoCRM
Curious about some other things you cna query using LINQ? Here's a short (and incomplete) list:
SQL Server databases, which was a bit obvious (LINQ to SQL)
XML documents (LINQ to XML)
The Amazon.com storefront (LINQ to Amazon)
SharePoint lists (LINQ to SharePoint) -- no more CAML queries!
Flickr.com web services (LINQ to Flickr)
Any web service you can imagine (LINQ to Web Services)
ADO.Net DataSets (LINQ to DataSets)
Active Directory stores (no more LDAP queries)
Google services
Anything that implements IEnumerable
Anything else really, through the creation of your own provider that implements IQueryable.
Want a complete list? No problem, here's one.
Technorati Tags: Dynamics CRM,LINQ,LINQ Provider
Here's the start of my journey into Dynamics CRM 4.0.
Blogs and Community
A Freky Microsoft Dynamics CRM blog (previously hosted at MSDN, now moved to i's own server)
Janne Matilla's Blog - mostly Sharepoint and CRM from a developer's point of view
Dynamics CRM blog from polish BI team
Microsoft Dynamics CRM team blog on MSDN
Stunware's CRM blog - Contributed by Seth Kircher; CRM blog that looks like CRM 3.0
Unitek Microsoft CRM team blog
David Yack's blog - He blogs about all things App Dev, but there's quite a few posts about CRM. Dave's also put a book together on building applications on top of Dyanmics CRM.
A more complete list of blogs is available here.
Video demos and Install bits
Developer Ramp Up kit for Dynamics CRM 4.0 - Basically this 12 module series is for those familiar with C#, .Net 2.0+ and ASP.Net and want to learn the ins and outs of CRM customizations.
Dynamics 4.0 SDK - If you don't have this and are doing any CRM 4.0 development, get it!
Dynamics 4.0 Virtual Machine - Full evaluation copy with data for CRM 4.0. Great for demos or proof of concept work. Expires April 2009.
Tools and must-haves
Dynamics CRM JScript Export Tool
Dynamics 4.0 Performance Toolkit - To perform load testing on CRM 4.0 installations
The Stunware Tools for Microsoft Dynamics CRM 4.0
I'll be focusing on posting tips, tricks and links to CRM resources on this blog for many of my future posts. I hope you all enjoy!
Technorati Tags: Dynamics CRM