Pages

Monday, December 19, 2011

Using XML-based Plug-in Configuration

For more complicated and feature-rich Plug-in customizations for CRM, it can be appealing to use highly dynamic configurations.  The Plug-in model offers simple string parameters to allow for the open interpretation by Plug-in developers.  One of the more efficient and flexible configuration mechanisms is XML.

With XML configuration, there are three methods that can be used to varying degrees of efficacy:

  1. Hard-parsing; dissecting XML with String manipulation (useful in dire situations)
  2. XmlDocument parsing and node traversal; similar to JavaScript DOM manipulation and perhaps easier for web developers to grasp
  3. XML-serialized classes; deserializing XML into one or more strongly-typed classes that represent the configuration

For the purposes of this post, I’m going to focus on the method that I prefer and find superior to the others:  XML-serialized classes.  The reason I believe this is superior is for the following reasons:

  • Serialized classes can be versioned with namespace declarations; meaning that updating code and providing for new configuration specifications can preserve older versions
  • Hard-parsing and XmlDocument traversal requires significantly more code to determine what was passed
  • Serialization and deserialization work both ways; Plug-in configuration can be performed by a utility (think Silverlight!) that understands the .Net class representing the configuration, and easily construct the XML; this ability is simplified to a great extent with WCF Data Contracts (which we’ll get to in a moment)
  • Hard-parsing and XmlDocument traversal code requires more effort to update or alter the configuration schema
  • Hard-parsing and XmlDocument traversal work better in situations when the XML document and its contents are either
    1. Very simple and statically defined
    2. Totally unknown and dynamic
  • Serialized classes define a rigid standard and schema for the configuration, but also offer a significant amount of flexibility while simplifying the interpretation of the configuration

As a caveat, I should state that I’m extremely comfortable with XmlDocument use.  I’ve used it many times, and found it a natural fit for my long experience with JavaScript DOM programming.  However, I recently implemented a project that needed a highly structured and very robust configuration model, and decided to look for ways to reduce the amount of code I needed to write.  Additionally, I needed the process to be flexible, as I might change my mind about the operational needs from the configuration as the code evolved.

Starting with the concept of deserializing .Net classes from XML, I searched for the best method of implementation for CRM that I could identify.  I found that there are a few ways within the .Net framework to serialize and deserialize classes into and from XML.  Some are more flexible than others, but the CRM platform offers unique challenges that pretty much made my answer for me:  WCF Data Contracts.

Why WCF Data Contracts?  Well, I like to deploy my assemblies to the database.  This is a practice that helps assure the interoperability of my code with CRM Online.

The problem with traditional, old-school XmlSerializer methods is that .Net requires the original assembly to be file-system accessible in order to reference for compilation into a “[foo].XmlSerializers.dll” assembly.  If you really didn’t know what these assemblies were (as I didn’t) before now, allow me to explain.  The XmlSerializer routines require strongly-typed interpretive classes that are Reflected out of the original assembly and built “on-the-fly” by the .Net CLR.  It cannot achieve this with assemblies that exist only in memory, as database-deployed assemblies are.  Therefore when CRM attempts to deserialize by calling these interpretive classes, they don’t exist—causing an “Object Is Not An Instance” exception.

Experienced developers know that these XmlSerializer assemblies don’t have to be built “on-the-fly”, and they can certainly be assembled and distributed in conjunction with the original assembly.  However, CRM doesn’t support a shared-assembly model for database deployment.  The assemblies could be deployed to disk or GAC, but again this isn’t compatible with CRM Online.

If you’re thinking, as I did, that perhaps the assemblies can be merged together with ILmerge and deployed together, think again.  ILmerge generates a new AssemblyId attribute for the combined assembly metadata—which is only a problem for a the “[foo].XmlSerializers.dll” assembly because it strictly validates the AssemblyId of the calling code; and it imprints on the original assembly.

Before I discovered WCF Data Contracts, I was desperate enough to output the generated code for the XmlSerializer assembly and inject it into my project.  This is as ugly and highly ill-advised as it is time-consuming.  So, if you’re considering XML configuration for CRM Plug-ins, I strongly recommend using WCF Data Contracts.

For more reading, see the MSDN Article on WCF Data Contracts.

Some tricky “gotchas” with using WCF Data Contracts for newbies:

  • The order of XML nodes must be alphabetically ordered by their declared Data Contract name; this is a deserialization optimization requirement, and can be massaged to a certain extent by using advanced Data Contract directives.
  • There are no deserialized “attributes” for XML nodes.  The only supported XML-node attributes are namespace declarations.  Every Data Member is its own node, and its contents become the deserialized value.

Tuesday, October 18, 2011

Upgrade CRM 4 to 2011: For ITdiots

Q: Who’s an ITdiot?  A: Me.

I’ve been involved in the IT world since my early teen years.  It started as the family computer expert, and evolved into advanced circuit configuration for ISPs and eventually a fulltime network administrator gig.  All the while, I nurtured and evolved my passion for development.

Ordinarily, this blog is dedicated to the later talent and my trials and tribulations therewith.  However, I cannot deny that the foundations of my career and experience have solid roots in the IT realm.  Having never acquired any formal education for either talent is what makes me, your friendly (mostly) neighborhood CRM MVP, an ITdiot.

If it weren’t for Google, I wouldn’t have much of a career.  While some things in the IT world have become intrinsically intuitive, I would be remiss to ignore the fact that my first real job at an Internet Helpdesk left me puzzled for longer than I care to admit as to why “.net” or “.org” worked to execute things on the computer, and “.com” was so much more prevalent than “.exe”.

At any rate, I recently gave a presentation for CRMUG geared toward the IT Crowd—not the British comedy (although if you’re in IT and haven’t yet seen it, you should), but my people.  So, the following guide complements that presentation and the material I covered, and all that I had wished to cover but ran out of time for.  This is a basic list of the considerations I make when considering the move from CRM 4 to CRM 2011.

CRM 2011 On-Premise Upgrade Requirements

What about Online?

Pros:
  • Reduced IT overhead; from infrastructure to support
  • Reduced deployment timeframe
  • Automatic/Scheduled updating
    • “Scheduled” becomes “Automatic” within a year
  • Aligns with Office 365 and SharePoint Online
  • Provides an easy to use Internet Lead Capture feature
  • Can be redeployed as 2011 On-Premise
  • Federated authentication is just around the corner (R7; Q4 2011 Service Update)
Cons:
  • May not be ideal for integrated systems (ERP, Accounting, etc)
    • Limited functionality connecting from CRM to external resource
  • No support for custom Workflow actions
  • Limits on number of Workflows and custom entities (200?)
  • Externally hosted email router (on-premise, hosting provider)
  • FetchXML reports only

Helpful Overviews

Upgrade Methods

Social Experiences

Personal Upgrade Process

  • Avoiding more than one direct upgrade
    • Never upgrade from CRM 3 to CRM 4 and then to CRM 2011; always make a clean break somewhere in the chain to avoid esoteric issues caused by upgrades daisy-chained this way
  • Wait for first adopters to uncover problems (sorry first adopters!)
  • Virtual Machines!
    • VMs are your IT friend.  If you don’t think you can afford it, go grab VMWare ESXi—it’s free, man
  • [IRAD] unsupported customizations
    • Identify: know where you’re currently using them, and what they do
    • Remove: eliminate them to avoid upgrade issues
    • Analyze: determine whether or not new functionality allows you to replace them with supported customizations/features
    • Deploy: after the upgrade, update and redeploy any unsupported customizations deemed necessary for business

Saturday, October 8, 2011

Farewell, Jim Glass

With Jim’s recent announcement of his retirement from Microsoft, I thought I’d make a little space here to immortalize my thoughts of a man I never got to meet in person, but dearly wished to.

There’s something to be said about the Dynamics CRM community: chiefly that it is the most vibrant and active product community around any single Microsoft product.  Why? Two reasons:

  1. As I have long believed and been rewarded for such faith, it is simply the best product Microsoft has ever brought to market (that isn’t an Operating System); and
  2. Fantastic community leadership, fostering an open, engaging dialog between the users of the product and the experts that know it.

Jim Glass is directly responsible for both.  There’s no objective way for me to quantify that, however, so take that declaration as the biased opinion of one happy CRM MVP.

I’ve never known a CRM world without Jim, and long before I entered the MVP space, I met Jim on the battlegrounds we call the public CRM forums.  Jim was always watching, carefully moderating and steering the forum to productive ends.  He encouraged the MVPs, on a regular basis, to contribute and participate with the community at large—and the forums were just that.

There is no mistaking the emptiness that I feel now that I know he’s gone, and I wait patiently for his successor to fill his space as best as possible (daunting though that may be).  Since I never had the opportunity to pay my respects and give my appreciation to Jim in person, I find myself obligated by duty to honor his legacy by applying myself to the forums in a way I know he would wish.

Those who knew Jim understand the impact and the gravity his absence will generate.  Those who didn’t, may never understand what he did to improve their experience with CRM, the product or the community.  In some small way, I wish I could impart that feeling; but I doubt I have the raw ability to put in words what Jim has done, for me—for us.  I will miss him, and follow his social publications with great interest.

Farewell, Jim. To have known you from afar, is better than to never have known you at all.

“To leave, or not to leave--that is the question.
Whether ‘tis nobler in the mind to suffer
The pings and queries of MVP misfortune
Or to take charms against a sea of blogs
And by composing, mend them. To pry, to seek—
No more—and by seek to say we end
The madness, and the thousand natural questions
An MVP is heir to.”

Tuesday, September 27, 2011

60k Page Views!

When I first started this blog, I really didn’t expect it to take me anywhere.  I just wanted a neat little place to post my code, a place that would follow me wherever I went and be a sounding board for whatever I thought (about CRM).

This evening, while following up on my social activities (forum contributions, blog reading, and that Twitter thing), I discovered that the ticker on my page-view counter rolled past 60 thousand.  A number I would never have expected.  This comes on the tail of passing my first Microsoft Certification Exam.  (I’m finally an MCP, too!)

Now, I’m not one to relish in popularity.  I’m a pretty shy and reserved person.  So, the only thing I hope to take from this statistic is that I have hopefully helped many Dynamics CRM implementers and developers who have come here looking for help (or free code *wink*).

If you have found value in this space, I’d love to hear about it.  That kind of thing is what encourages me to contribute—and with more frequency.

On a positive note, I may start posting more frequently in this space (and others) regarding some very exciting learning opportunities that are coming my way in the near future.  There will be unique challenges that I’ll get to address with CRM, and my company, JourneyTEAM, has given me motivation to contribute the value from my experiences with you, my audience.

So, thank you for your readership.  I hope I have given you useful and helpful information, and a better idea of how to squeeze great things from Dynamics CRM.

September 2011 Stats

Saturday, September 17, 2011

Review: CRM 2011 Administration Bible

Publisher: Wiley
Authors: Matt Wittemann, Geoff Ables
CRM Entropy Rating (in Baked Goods): 3.1415926535897

Matt, a fellow CRM MVP, sent me a copy of this book a long time ago, and I’ve pretty much only used it for reference material here and there until recently.  I’ve read a handful of CRM 4 books in my day, and found a great deal of helpful information from them.  The Microsoft Dynamics CRM 2011 Administration Bible marks my first in-depth read relating to CRM 2011.

The title of this book is both accurate and misleading—and I don’t mean from a theological standpoint.  (Though I imagine, given the apologist forewords by Matt and Geoff, the word “God” was uttered on multiple occasions with various modifiers during its authoring.)  This book is indeed a Bible in the sense that it combines a complete picture of CRM, from URL to SQL, from Form to Filter, and from Dialog to Migration.  It really has it all.

However, don’t let the word “Administration” throw you for a loop.  This book isn’t geared specifically for administrators of any kind, and it doesn’t just cover administration.  It’s so thoroughly educational on the core processes of CRM, I dare say that it is the first and last book you’ll need for Microsoft Dynamics CRM 2011 (see my “Bible” comment above).  Matt and Geoff take special care to make every reader an Administrator.  And that’s why the damn thing is so thick!  (Caution: Ordering more than 3 of these books at a time involves freight charges.*)

The machines with which this Bible was written have long surrendered their keyboards to a higher power.  The material is dense and enriching not only for newcomers to the CRM experience, but for those arriving to CRM 2011 looking for what changed from the previous version—like myself.  As with a religious text, you’ll find yourself engaged in the index or table of contents quite a bit, if you’re working with CRM already.  But, you will find value throughout the areas you don’t think you need to read.

This book will teach you all of the standard features that come with Dynamics CRM out-of-the-box, and then open your mind to the possibilities of custom enrichment.  It wouldn’t be a book to buy if you’re only investigating Dynamics CRM (though it wouldn’t hurt to read); but it is an essential part of any CRM implementation, and its material is useful to every deployment and use of CRM that I can think of.

So, from this digital pulpit, I preach from one Bible, and that is the Microsoft Dynamics CRM 2011 Administration Bible!  If you have Dynamics CRM, do yourself a favor and have this book, too.

* I’m kidding!  But really, you do get more words for your dollar… and the most helpful ones to boot!

Tuesday, September 13, 2011

CRM 2011 and WSDL

CRM 2011 makes a dramatic change with the WSDL implementation compared to CRM 4.0.  The biggest difference?  The WSDL file you download is not a full definition of the service (for either Discovery or Organization). 

Instead it’s a very small and self-conscious (“lightweight”) WSDL that points back to the same service (and host) with an import directive for the full WSDL.  What does this accomplish?  Well, WSDL-imports are supposed to allow a WSDL definition to be modular with reusable and multi-authored definitions.  We really don’t see that being applied in CRM’s case in any sensible fashion (like importing out-of-the-box entity/attribute definitions separately from custom ones, or importing WSDLs with Solution-based scopes).  All it seems to do is complicate the retrieval of information we need for our application.

So, if you prefer or require the late-bound Entity class, and you don’t have local access to a CRM system.  Having a “full” WSDL file could help when you want to avoid…

  1. …deployment of the CRM DLLs.  This may be important when single-assembly functionality is imperative and assembly dependency deployment is a serious headache, such as CLR operations from SQL for integration projects.  Or maybe the target system doesn’t use or support .Net.
  2. …the code-generation tool.  Let’s face it, the code-generation tool is nice for simple and quick developments, there’s no argument about the excellence of many of its Utility methods.  However, it produces a lot more code than many will ever use—artificially inflating the assembly size unnecessarily.  And the code it produces isn’t without its own bugs.  Sometimes keeping things simple is its own reward.

So, when you need to have the “full” WSDL file, for your services, use links formatted like so:

Discovery Service

http[s]://<crmServer>/<crmOrg>/XRMServices/2011/Discovery.svc?wsdl=wsdl0

Organization Service

http[s]://<crmServer>/<crmOrg>/XRMServices/2011/Organization.svc?wsdl=wsdl0

Monday, September 12, 2011

DOM Events and CRM 2011

[UPDATED: 2013.04.11 I've learned a lot since this premature post, and have struck out all the incorrect information.]

Recently, another MVP asked in a private forum how one might connect to the “onclick” event of a CRM field.  With the new Xrm.Page namespace object model, it seemed that all references to the actual DOM element of the control were lost (or at least, very well hidden).  Well, I did some poking around, and I discovered two one things:
  1. Xrm.Page lives only within the scope of web-resources and their execution (wherever and however they are scripted to occur); this means that the old days of firing up the IE Developer Toolbar to hack CRM on-the-fly are gone.
  2. The DOM elements are buried in undocumented members of the control object (as opposed to the attribute object).  As a consequence, the following hack is unsupported, but also much easier to implement than the supported method of using a Web Resource to present your own custom controls that drive, in the background, CRM’s native controls.
So, here’s the method I found to achieve access to the DOM element control container, and how to assign a new “onclick” handler to it:
var controlname = "ownerid"; // The "Owner" field, as an example

function myFunction ()
{
alert("I have been clicked");
}

Xrm.Page.getControl(controlName)[“_control”][“_element”].attachEvent(‘onclick’, myFunction);

Wasn’t that easy?

Wednesday, September 7, 2011

Ask the MVP (CRMUG)

There’s an upcoming roundtable event, hosted by CRMUG, where many CRM MVPs have agreed to come together and address questions submitted to the “Ask the MVP” event site.  After the bulk of the event, I understand there will be time afterward for free-form, open floor discussion and Q&A.  However, if you want to make sure your question is asked, drop an email to Janet Lampert or Donna Edwards well in advance!  As with most CRMUG events, attendance is limited to CRMUG members—become a member today, and reap the benefits.

I’m personally excited to get involved in this event, since this is the second CRMUG event to which I’ve been invited to participate.  Hopefully, I am more useful to the discussion than a fly-on-the-wall.  Many long-time MVP greats will be coming to the table, and I’m honored to find myself among them.  We haven’t seen the question list yet, so I’m anxious to find out what you, the audience, want to know!

Thursday, August 25, 2011

Copying Lookup Values

Very recently a poster in the CRM Development forum asked why she could not copy the value from a Contact Lookup field into the “To” field of a Phone Call entity.  On the surface, her code was very simple and would largely be expected to work.  What ended up working was basically creating a new value for the PartyList that recreated values from the Contact Lookup.

Why was this necessary?  I don’t really know, and haven’t looked any deeper into CRM 2011 yet, but what I know from CRM 4 is that certain expressions of the DataValue for a Lookup field can contain extra, undocumented data elements.  I suspect that CRM 2011 has expanded the use and perhaps number of these undocumented data elements, and their presence from one Lookup field prevents the direct assignment of the value to another Lookup field.

Perhaps this is limited to Single-to-PartyList copies, but perhaps not.  Either way, I’ve whipped up a handy function that can help easily copy or append values into Lookups when those values are taken from other Lookups.  Also, it allows you to provide a delegate function to which it will pass each entity reference and expect a boolean indication as to whether or not the value should be accepted for inclusion in the target attribute.

function CopyLookupValue(sourceAttribute, targetAttribute, boolAppendSource, funcDelegate)
{
var sourceValue = sourceAttribute.getValue();
var newValue = [];

if (typeof boolAppendSource != "undefined" && boolAppendSource)
{
sourceValue = targetAttribute.getValue().concat(sourceValue);
}

foreach(var valueIndex in sourceValue)
{
var copyValue = true;

if (typeof funcDelegate == "Function")
{
copyValue = funcDelegate(sourceValue[valueIndex]);
}

if (copyValue)
{
newValue.push({
id: sourceValue[valueIndex].id,
name: sourceValue[valueIndex].name,
entityType: sourceValue[valueIndex].entityType});
}
}

targetAttribute.setValue(newValue);
}

Using the thread that initiated this code as an example, here is how the above code would be used to copy the value of a Contact field on a parent form window to a To field on the child form window:

var remoteAttribute = window.parent.opener.Xrm.Page.data.entity.attributes.get("bc_contact");

CopyLookupValue(remoteAttribute, Xrm.Page.getAttribute("to"));

Monday, August 22, 2011

Changing Product Unit Groups

I ran into a situation where a large list of Products had been imported into CRM 2011, but the Unit Groups for many of them needed to be changed after the fact.  As many have discovered, even though the form customization options for the Product entity professes that the field is not flagged as “read only”, the field is in fact “read only” when Products are opened.

The workaround is fairly simple, but comes with some caveats:  export the records to an Excel spreadsheet with the option “Make this available for re-importing by including required column headings” selected.  You must export both the Unit Group field and the Default Unit field.  In spreadsheet form, you can alter the Unit Group and the Default Unit.  Now, here’s the caveats:

  1. The Default Unit must be uniquely named.  Multiple Units with identical names (in any Unit Group) will cause the import to fail, because it does not look for the Unit in a specific Unit Group.  The failure is because CRM cannot properly resolve the Unit, due to name duplication.
  2. Beware of any Price List Items configured to use the old Units configured for this Product.  I don’t know what CRM will do to you if you try to use the Product in a Quote/Order/Invoice after you change its Unit Group in this way.

When you import the changed data, you should notice that the modifications to the Unit Group (and Default Unit) values have been properly changed.

[Update:  Astute reader, Jevgenij, notes that the following Microsoft KB will also allow you to work around the problem using the Bulk Edit feature: http://support.microsoft.com/kb/949941  This is particularly useful for CRM administrators.  The process above, however, will work for anyone with access Product catalog maintenance.]

Friday, July 8, 2011

Silverlight and CRM 4 (continued, again)

When I delivered the code example in my last post on the subject, I knew that it would probably be worthwhile to pick apart the individual code elements in order to provide a better understanding of what’s going on underneath it all.  This marks the final chapter of the saga which started as a 10-minute presentation, and culminates about 40-hours of work.

Again, this subject matter all stems from my participation in CRMUG’s ongoing 10@10of10 series.  To date, I’ve slept in too long to catch any of the other presentations live—for that I apologize to the other presenters, because I really am interested in their content matter.  Problem is, I’ve been up until 4 A.M. most mornings doing freelance work, or dealing with other things.  Thankfully, CRMUG will be posting the session recordings to their site—or so I’m told.

The code I will present will be in snippets from the Simple CRM 4 Silverlight Application project that I’ve uploaded to CodePlex.  If you didn’t already download it to build the application for yourself using my last post’s instructions, you’ll probably want to do so now for what follows.  (You will also need the Silverlight 4 Tools for VS 2010 and the Silverlight 4 for Developers Runtime to work with the project files.)

What follows: A Silverlight primer

Ok, so… first thing’s first:  If you’re inexperienced with Silverlight, I recommend the Silverlight Jumpstart book by David Yack (downloadable as a PDF).  It serves as a pretty good primer for working with Silverlight in general.  I used it, and found it very informative and useful. 

Mr. Yack also wrote another book, CRM as a Rapid Development Platform that contains a chapter on Silverlight development for CRM.  I also found that chapter useful in my project, but diverged from his examples by using the WSDL for web-service interaction.  His point about WSDL bloat upon the final XAP file are valid, and should be considered when developing smaller, single-purpose products.  However, for richer and fuller Silverlight apps, I find the WSDL to be integral to the speed of development.

I’m not going to get into the ins-and-outs of Silverlight development.  There’s too much to cover for this space, and I will simply proceed under the assumption that you, the reader, will find this knowledge for yourself and use it to obtain an understanding from what follows.

What follows: Connecting to CRM

There are two things necessary to connect Silverlight to CRM:

  1. knowing the service endpoint and authentication method1
  2. having an asynchronous interface to the endpoint methods3

For ease of use within the context of the ISV folder, I split these requirements across two projects, respectively:

  1. an ASPX host page for the Silverlight control
  2. the WSDL in the Silverlight application

Because Silverlight must ultimately satisfy both requirements internally, there is a third, intermediary requirement caused by my division of responsibility:  passing service endpoint and authentication information from the host page into Silverlight2.

Knowing the service endpoint and authentication method

Because I decided to use an ASPX page and code-behind file to meet this requirement, the process is decidedly simple, and is explained best in these snippets of code:

SimpleCrmApp.aspx.cs (line 35):
serviceUrl = this.Request.Url.Scheme + "://" + this.Request.Url.Host + "/MSCRMServices/2007/CrmService.asmx";

The snippet above uses the Page.Request member to assemble relevant components of the scheme used to view the page (“http” or “https”), and the hostname.

SimpleCrmApp.aspx.cs (lines 19 and 38):
orgName = Request.QueryString["orgname"];
//... jump over code
CrmAuthenticationToken token = CrmAuthenticationToken.ExtractCrmAuthenticationToken(Context, orgName);

The snippet above shows how the code-behind grabs the value of “orgname” from the same Page.Request to complete our ExtractCrmAuthenticationToken() method call.  This parameter must be passed to the host page somehow.  In our example, we accomplish this in this part of the SiteMap.xml configuration:

SiteMap.xml:
<SubArea Id="SimpleCrmApp" PassParams="1" Url="/../ISV/SCA/SimpleCrmApp.aspx" AvailableOffline="false">

Here, we rely on PassParams to do our dirty work for us.

Now, you might be misled into thinking that the token would be sufficient for Silverlight to authenticate its own SOAP messages.  Many in the forums have.  The reason you would be wrong is that the ExtractCrmAuthenticationToken() method provides you with the CrmAuthenticationToken instance used by CRM’s platform to communicate with the web service endpoints.

As I previously explained, this poses a problem because this token is designed to always operate under two conditions:

  1. Use to communicate with SOAP under a CrmImpersonator() call, which removes impersonation from the thread, thereby bringing “SYSTEM” user network credentials for use with…
  2. Integrated Authentication

It’s important to note that the reason token won’t work for Silverlight in strictly Integrated Authentication environments is because of the presence of the CallerId value. Any time this value is not Guid.Empty, CRM assumes impersonation is taking place, and checks the credentials for membership in the PrivUserGroup.  Because CRM does not communicate with itself over IFD, the CrmAuthenticationToken provided with always have an AuthenticationType of “0” and will never contain a CrmTicket.

When authenticating via IFD, you must supply a value for CallerId.  That’s what explains this bit:

SimpleCrmApp.aspx.cs (line 40):
callerId = token.CallerId.ToString();

Finally, I need a sensible way to determine if CRM is being used in an IFD scenario or not.  I could go the complicated way, parsing the URL to find out if the hostname has the orgname in it—but that’s a lot of code.  A more elegant way, is to look for the browser-cookie “MSCRMSession”:

SimpleCrmApp.aspx.cs (line 28):
// A broadly applicable mechanism for detecting IFD is the presence of the MSCRMSession cookie
if (Request.Cookies["MSCRMSession"] is HttpCookie)
    authType = "2";
else
    authType = "0";

Fun, huh?

Here’s why “MSCRMSession” is also important: it contains the value of the CrmTicket.  See, whenever the browser is authenticated via IFD, it uses a browser-cookie (which is inaccessible through JavaScript or Silverlight) to hold this ticket value.  The nice thing is, that it’s passed in every HTTP request header that originates from the browser (including Silverlight) to its domain of origin.  Therefore, we do not need to worry about finding it or passing it through the headers of our own SOAP requests.

Notice how every value I’m taking is ending up in string format?  Well that leads into the next requirement.

Passing service endpoint and authentication information from the host page into Silverlight

There are a handful of ways to obtain data from a Silverlight application’s hosting page:

  • Inspect the DOM.
  • Call a JavaScript function.
  • Use InitParams.

I’m sure there are others I’m unfamiliar with, but let me tell you why I choose the InitParams: it’s easy, and it’s a one-way stream into Silverlight that doesn’t require any backward movement by the Silverlight app into its execution context.  It simply doesn’t care.  In fact, if I had some other way that I would like to instantiate my Silverlight app in a wholly different context, I could rely on data passed into InitParams to define its operation.

To achieve this, there are two code-snippets on the server side, and one on the Silverlight side to illustrate how this works:

SimpleCrmApp.aspx (line 69):
<param name="initParams" value="<%= BuildInitValue() %>" />

The snippet above, from the <object> container for the Silverlight control, instructs Silverlight to receive the value and pass it into the Startup event arguments.  To construct the value, I have this function:

SimpleCrmApp.aspx.cs (lines 43 – 47):
protected string BuildInitValue()
{
    // build a series of parameters to be piped into the Silverlight app from the hosting control page
    return "callerId=" + callerId + ",orgName=" + orgName + ",serviceUrl=" + serviceUrl + ",authType=" + authType;
}

Since the values are being embedded into the host page by this function, I need to work with strings—which flows from the code in the first requirement.  Then, it’s a matter of examining the values from the Startup event arguments in Silverlight:

App.xaml.cs (lines 30 – 34):
if (e.InitParams == null ||
    e.InitParams["callerId"] == String.Empty ||
    e.InitParams["orgName"] == String.Empty ||
    e.InitParams["serviceUrl"] == String.Empty)
    throw new ArgumentException("This Silverlight application requires values for callerId, orgName, urlScheme, and serviceUrl parameters.");

The snippet above occurs as the first statement in the handler Application_Startup(), which has been attached to the Startup event of the Application class in my custom constructor:

App.xaml.cs (line 20):
this.Startup += this.Application_Startup;

As you can see, the type StartupEventArgs (and its instance, e) expose the member InitParams, from which I obtain the values I submitted from the hosting page via its implementation of IDictionary.

So, now what?  Well, since we have all of the necessary information to connect to CRM, it’s time to start working with the code from my first post.

Having an asynchronous interface to the endpoint methods

Since I have all the information I need to connect to CRM within the Application_Startup() call, I refrain from storing the information and instead use it immediately to construct an instance of my CrmServiceInstance class:

App.xaml.cs (lines 36 – 51):
// Construct a new CrmAuthenticationToken from some of the InitParams elements
CrmAuthenticationToken authToken = new CrmAuthenticationToken();
 
authToken.AuthenticationType = Convert.ToInt32(e.InitParams["authType"]);
 
// This is an important piece for determining proper IFD communication
if (authToken.AuthenticationType != 0)
    authToken.CallerId = new Guid(e.InitParams["callerId"]);
else
    authToken.CallerId = Guid.Empty;
 
authToken.OrganizationName = e.InitParams["orgName"];
 
// Create a new CrmServiceInstance and assign a new CrmServiceConnectionParams object to its ConnectionParams member
CrmServiceInstance crmService = new CrmServiceInstance();
crmService.ConnectionParams = new CrmServiceConnectionParams(e.InitParams["serviceUrl"], authToken);

In the snippet above, you can see that I’m constructing a new CrmAuthenticationToken object, with all the various input received through InitParams.  I then pass this and the endpoint URL (also taken from InitParams) to the ConnectionParams member of crmService

Because I perform these immediately upon the application startup, I have no need to examine the IsCrmServiceReady member or attach any handlers to the OnCrmServiceReady event—it is simply ready for me to proceed.  However, I put an example of how this might be performed in this code snippet from the MainPage_Loaded() event handler:

MainPage.xaml.cs (lines 42 – 46):
// Validate the readiness of the crmServiceInstance before proceeding further; use an event handler to work out the kinks
if (!crmServiceInstance.IsCrmServiceReady)
    crmServiceInstance.OnCrmServiceReady += new EventHandler<EventArgs>(crmService_OnCrmServiceReady);
else
    crmService_OnCrmServiceReady(this, new EventArgs());

Where this event is handy, is if I had created my CrmServiceInstance in XAML—rather than directly in the code, as with this example.

From here, I pass crmService into the customized constructor for my MainPage class.  Once this is done, a connection to CRM has been adequately defined and is now available for my Silverlight page, MainPage, to use for what follows.

What follows: Using CrmServiceSoapClient

Because Silverlight requires web-service interaction to operate in an asynchronous way (for non-interfering performance reasons), all of the traditional methods from [I]CrmService are implemented in CrmServiceSoapClient with an “Async” suffix.  Execute() becomes ExecuteAsync(), and RetrieveMultiple() becomes RetrieveMultipleAsync(), for example.  Working with these asynchronous counterparts can be perplexing, considering that they all have no return.

The returns are provided through events.  Each traditional method not only has an “Async” representation in Silverlight, but also a “Completed” event; ExecuteCompleted and RetrieveMultipleCompleted, for example.  This means that every method call to the web-services is handled by every active event handler registered to these events.  This can complicate the design of your Silverlight application, to a degree—given that:

  1. You should always have the event handler in place before the method is called; and
  2. Every event handler will catch every execution of the method for the same CrmServiceSoapClient instance

For our example, however, all I need is a simple, one-time query for all active Account names.  I achieve that with the following code in the crmService_OnCrmServiceReady() method (again, this method is an event handler for the CrmServiceInstance.OnCrmServiceReady event, and is not needed by the code, but provided for example purposes):

MainPage.xaml.cs (lines 51 – 66):
// Query for active accounts
QueryByAttribute query = new QueryByAttribute();
query.EntityName = EntityName.account.ToString();
query.Attributes = new string[] { "statecode" };
query.Values = new object[] { 0 };
 
// Query three specific string columns
ColumnSet columns = new ColumnSet();
columns.Attributes = new string[] { "name" };
 
query.ColumnSet = columns;
 
// Assign a handler to deal with the results, before triggering the execution of the query
crmServiceInstance.CrmService.RetrieveMultipleCompleted += new EventHandler<RetrieveMultipleCompletedEventArgs>(CrmService_RetrieveMultipleCompleted);
 
crmServiceInstance.CrmService.RetrieveMultipleAsync(query);

Note that one statement before I call RetrieveMultipleAsync(), I attach the handler CrmServce_RetrieveMultipleCompleted() to the RetrieveMultipleCompleted event.  I do this to prevent any possible—though unlikely—race condition by which the thread processing the “RetrieveMultiple” message might finish and trigger the event, before the event handler is assigned.

So, interpreting the return is an important function of CrmService_RetrieveMultipleCompleted().  Here’s the body of that method:

MainPage.xaml.cs (lines 69 – 82):
void CrmService_RetrieveMultipleCompleted(object sender, RetrieveMultipleCompletedEventArgs e)
{
    // Instantiate a new List<account> for our results
    List<account> retrievedAccounts = new List<account>();
 
    // Assign the results of our query to the new List
    foreach (account a in e.Result.BusinessEntities)
    {
        retrievedAccounts.Add(a);
    }
 
    // Push the results into our RetrievedRecords data context
    RetrievedRecords.AccountRecords = retrievedAccounts;
}

As you can see, RetrieveMultipleCompletedEventArgs (as an instance, e) has a Result member that contains our retrieved records, much the same way as the traditional RetrieveMultipleResponse would.

The rest of my code operates the way a basic Silverlight application should when pushing data to the view.  To make a long story short, I use a “ViewModel” for the data, represented by a collection of DisplayAccount.  These are handled by the class CrmRecords, which serves as the type for the RetrievedRecords member of MainPage.  These serve as abstractions of the account type to limit the amount of columns automatically generated by the DataGrid.

What follows:  Bed Time

Ok, I’ve been up late working on this post.  Time for bed.  Hopefully, it all makes sense.  Comment below if you have additional inquiries.

Thursday, July 7, 2011

New Twitter Feed

As you may notice, on the right-side of this blog is now a small reproduction of a twitter feed I setup for this blog, and for other CRM related goodness.  Many MVPs engage in Twitter, and a few have asked me to use it to broadcast blog updates.  Apparently RSS and Atom up and died since last I checked.

Anyway, new posts will be dropped in that space as well, if you’re keen to follow that kind of thing.  Another post will be coming tonight to complete the “Silverlight and CRM 4” trilogy.  Stay tuned.

Wednesday, July 6, 2011

Silverlight and CRM 4 (continued)

[UPDATE: The conclusion of this series is up, and in it I discuss the code and break-down how I developed it and why it works the way it does.]

I promised in my last post on the subject that I would include an example of the code I presented during my CRMUG 10@10of10 segment.  Then, I went to delay that example for a couple of weeks while I was looking for a good time to produce it.  Well, today was that day.

For starters, the example code is hosted at CodePlex.  I put the project together in Visual Studio 2010, so the solution and project files will all reflect that.  Also, you will need the Silverlight 4 Tools for VS 2010 and the Silverlight 4 for Developers Runtime to open the project files.  Head on over there to download it, decompress it to your favorite demonstration code directory, and come back here for the instructions on putting it together.

Putting It Together

First, download the WSDL from a CRM deployment you would like to compile the project against.  I personally prefer a “vanilla” deployment of CRM to keep the WSDL small, and rely on DynamicEntity to perform all the dirty work for me—however that is not necessary for this sample project (in fact, I don’t use DynamicEntity at all in it, though my special “NamedProperties” extension is available to the project for your own tinkering amusement).

1 - Customization

2 - Download WSDL

3 - Download WSDL for CrmService

4 - Download WSDL for CrmService - Save As

Save the resultant XML file somewhere easy to locate, such as the root folder for our project.

5 - Save WSDL as Xml File

Open the solution in Visual Studio, and add a “Service Reference” to the Simple CRM App project.

6 - Add Service Reference

Be sure to specify the XML file location as the “Address” and then click Go.  Upon a successful load, the “Services” frame will contain an item titled “CrmService”.  Specify the “Namespace” value as CrmSDK.  Click OK when finished.

7 - Add Service Reference - Location and Namespace

Next, add a “Reference” to the Simple CRM App.Web project.

8 - Add Reference

Locate and select the assembly microsoft.crm.sdk.dll from the CRM 4 SDK.  Click OK.

9 - Add Reference - CRM SDK

Now, build the whole solution.

To deploy the project to CRM, create this new folder:

<crm web root>/ISV/SCA

In this folder place the following files from the Simple CRM App.Web project folder:

Silverlight.js
SimpleCrmApp.aspx
ClientBin/Simple CRM App.xap  (copy the folder)

Deploy this file:

Simple CRM App.Web/bin/SimpleCRMApp.Web.dll

Into the following location:

<crm web root>/bin/

Now, in order to view the project, export your SiteMap.xml from the customizations, and insert this code into some reasonable area

<SubArea Id="SimpleCrmApp" PassParams="1" Url="/../ISV/SCA/SimpleCrmApp.aspx" AvailableOffline="false">
  <Titles>
    <Title LCID="1033" Title="Simple CRM App" />
  </Titles>
  <Descriptions>
    <Description LCID="1033" Description="A simple Silverlight demonstration." />
  </Descriptions>
</SubArea>

Import the altered SiteMap.xml, and refresh your CRM window to view the changes.  Upon clicking on the new navigation item, you should be treated to a very basic Silverlight page that shows a list of all active accounts by name.

I’ll get into the details of the code a little more with another post to follow within a day or two.

Friday, July 1, 2011

I’m a Microsoft MVP! (Still)

It’s surprising to me to think that a year ago, I was welcomed graciously into the arms of the MVP program.  Mostly, because this year seems to have just flown by.  Awaiting news of my renewal status caused even more anxiety than I had last year, because my daughter’s illness dropped my activity level in the community quite a bit.

I tried desperately to find time to contribute as often as I could, so that I might secure my MVP renewal—and it appears those efforts paid off.  Soon, our daughter’s treatments will be largely over, and things brought to a more stable and relaxed atmosphere around the home.  This is exactly the situation I need to move forward with CRM 2011, and really make a return to the forums a stronger developer for it.

Thanks to Microsoft, Melissa (my MVP team lead), and the many MVPs whom I’ve come to know on a personal level over the last year.  I promise to make the MVP Global Summit this year to make up for last year.

Wednesday, June 22, 2011

Silverlight and CRM 4

[UPDATE:  A demonstration of this code can be found in a follow-up post.]
My presentation this morning at CRMUG’s 10@10of10 went pretty well, I thought.  There weren’t too many attendees, though.  I can’t say I blame anyone for that.  In my experience, very few jump with glee for a chance to look at code.
Since the format was limited to 10 minutes, I obviously had to truncate a great deal of the information I wanted to provide.  It’s funny, because at first I wondered what I’d fill 10 minutes with.  Those who attended will likely visit this space to retrieve what I promised:  the code.  So, without further ado:
The Code <heavenly chorus>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Runtime.Serialization;
using System.Xml;

namespace CrmSDK
{
public partial class DynamicEntity
{
private Dictionary<string, Property> namedProperties;

[System.Xml.Serialization.XmlIgnore]
public Dictionary<string, Property> NamedProperties
{
get { return namedProperties; }
set
{
namedProperties = value;
this.RaisePropertyChanged("NamedProperties");
}
}

public DynamicEntity()
{
this.PropertyChanged += new PropertyChangedEventHandler(dynamicEntity_PropertyChanged);
}

private void dynamicEntity_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Properties")
{
NamedProperties = convertPropertyArray(this.Properties);
}
}

private static Dictionary<string, Property> convertPropertyArray(Property[] properties)
{
Dictionary<string, Property> propertyDictionary = new Dictionary<string, Property>();

for (int propertyIndex = 0; propertyIndex < properties.Length; propertyIndex++)
propertyDictionary.Add(properties[propertyIndex].Name, properties[propertyIndex]);

return propertyDictionary;
}
}

public class CrmServiceConnectionParams
{
public String Scheme { get; set; }

public String Url { get; set; }

public CrmAuthenticationToken AuthenticationToken { get; set; }

public BasicHttpSecurityMode SecurityMode { get; set; }

private void setSecurityModeFromScheme()
{
switch (Scheme)
{
case "https":
SecurityMode = BasicHttpSecurityMode.Transport;
break;
default:
SecurityMode = BasicHttpSecurityMode.None;
break;
}
}

public CrmServiceConnectionParams(String url, CrmAuthenticationToken token)
{
if (url.Contains("://"))
{
string[] urlSplit = url.Split(new string[] { "://" }, StringSplitOptions.None);

Scheme = urlSplit[0];
Url = urlSplit[1];
}
else
throw new ArgumentException("Failure creating CrmServiceConnectionParams instance. Invalid or missing URL scheme (e.g. 'http://').");

AuthenticationToken = token;

setSecurityModeFromScheme();
}

public CrmServiceConnectionParams(String scheme, String url, CrmAuthenticationToken token)
{
Scheme = scheme;
Url = url;
AuthenticationToken = token;

setSecurityModeFromScheme();
}

public CrmServiceConnectionParams(String scheme, String url, CrmAuthenticationToken token, BasicHttpSecurityMode securityMode)
{
Scheme = scheme;
Url = url;
AuthenticationToken = token;
SecurityMode = securityMode;
}
}

public class CrmServiceInstance
{
private CrmServiceConnectionParams connectionParams;
public CrmServiceConnectionParams ConnectionParams
{
get { return connectionParams; }
set
{
connectionParams = value;
spawnCrmService();
}
}

private CrmServiceSoapClient crmService;
public CrmServiceSoapClient CrmService
{
get { return crmService; }
set
{
crmService = value;
isCrmServiceReady = true;

if (OnCrmServiceReady != null)
OnCrmServiceReady(this, new EventArgs());
}
}

#region OnCrmServiceReady Event

public event EventHandler<EventArgs> OnCrmServiceReady;

private bool isCrmServiceReady;
public bool IsCrmServiceReady
{
get { return isCrmServiceReady; }
}

#endregion

private static CrmServiceSoapClient CreateCrmService(String crmServiceUrl, CrmAuthenticationToken authToken, BasicHttpSecurityMode securityMode)
{
BasicHttpBinding httpBinding = new BasicHttpBinding(securityMode);
httpBinding.MaxReceivedMessageSize = Int32.MaxValue;

EndpointAddress crmEndpoint = new EndpointAddress(crmServiceUrl);

CrmServiceSoapClient crmService = new CrmServiceSoapClient(httpBinding, crmEndpoint);

MessageHeader authTokenHeader = MessageHeader.CreateHeader("CrmAuthenticationToken",
"http://schemas.microsoft.com/crm/2007/WebServices", string.Empty, new CrmAuthenticationTokenSerializer(authToken));

crmService.ChannelFactory.Endpoint.Behaviors.Add(new CrmServiceBehavior(new CrmServiceMessageInspector(authTokenHeader)));

return crmService;
}

private void spawnCrmService()
{
CrmService = CreateCrmService(
ConnectionParams.Scheme + "://" + ConnectionParams.Url,
ConnectionParams.AuthenticationToken,
ConnectionParams.SecurityMode);
}

public CrmServiceInstance()
{
isCrmServiceReady = false;
}

private class CrmServiceMessageInspector : IClientMessageInspector
{
public MessageHeader ServiceHeader;

#region IClientMessageInspector Members

public void AfterReceiveReply(ref Message reply, object correlationState) { }

public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
request.Headers.Add(ServiceHeader);
return null;
}

#endregion 

public CrmServiceMessageInspector(MessageHeader header)
{
ServiceHeader = header;
}
}

private class CrmServiceBehavior : IEndpointBehavior
{
public CrmServiceMessageInspector ServiceInspector;

#region IEndpointBehavior Members

public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { }

public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(ServiceInspector);
}

public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher dispatcher) 
{
throw new NotImplementedException(); // Silverlight does not invoke this method.
}

public void Validate(ServiceEndpoint endpoint) { }

#endregion

public CrmServiceBehavior(CrmServiceMessageInspector inspector)
{
ServiceInspector = inspector;
}
}
}

public class CrmAuthenticationTokenSerializer : XmlObjectSerializer
{
#region CrmAuthenticationTokenSerializer Members

private readonly string authType;
private readonly string organizationName;
private readonly string callerId;
private readonly string crmTicket;

public CrmAuthenticationTokenSerializer(CrmAuthenticationToken authToken)
{
callerId = Guid.Empty.ToString();
authType = authToken.AuthenticationType.ToString();
organizationName = authToken.OrganizationName;
crmTicket = authToken.CrmTicket;
}

#endregion

#region XmlObjectSerializer Members

public override bool IsStartObject(XmlDictionaryReader reader)
{
return true;
}

public override object ReadObject(XmlDictionaryReader reader, bool verifyObjectName)
{
return null;
}

public override void WriteEndObject(XmlDictionaryWriter writer)
{
return;
}

public override void WriteObjectContent(XmlDictionaryWriter writer, object graph)
{
string tokenXmlLiteral = String.Empty;

tokenXmlLiteral += "<AuthenticationType xmlns='http://schemas.microsoft.com/crm/2007/CoreTypes'>"
+ authType
+ "</AuthenticationType>";

if (crmTicket != null && crmTicket != String.Empty)
tokenXmlLiteral += "<CrmTicket xmlns='http://schemas.microsoft.com/crm/2007/CoreTypes'>"
+ crmTicket
+ "</CrmTicket>";

tokenXmlLiteral += "<OrganizationName xmlns='http://schemas.microsoft.com/crm/2007/CoreTypes'>"
+ organizationName
+ "</OrganizationName>"
+ "<CallerId xmlns='http://schemas.microsoft.com/crm/2007/CoreTypes'>"
+ callerId
+ "</CallerId>";

writer.WriteRaw(tokenXmlLiteral);
}

public override void WriteStartObject(XmlDictionaryWriter writer, object graph)
{
return;
}

#endregion
}
}
</heavenly chorus>
So, here’s how this works:
  1. Copy the code into a .cs file of your choosing (I use CrmServiceHelpers.cs);
  2. Change the “namespace” declaration to match the declared namespace for your CRM Web Service Reference;
  3. Start using CrmServiceInstance in conjunction with CrmServiceConnectionParams to get the job done.
CrmServiceInstance exposes the member CrmService, which is the interface you’ll use with calling the Web Service API.  Remember, in Silverlight everything must be done asynchronously, so instead of Execute(), you call ExecuteAsync().  To catch the results, you attach a new handler to the event ExecuteCompleted.  You can find examples of this in Humberto Lezama’s Silverlight code.  As I mentioned in the presentation, this code served as the foundation upon which I built the code above.
CrmServiceInstance is designed to accept implementation as a XAML element.  This is not required, of course, but in my implementation I’ve successfully used CrmServiceInstance as a XAML element, declared as an Application resource, to allow connecting to a data-context element through binding as a {StaticResource}.  Why?  Because I like the way it looks.  Conceivably, I could switch the CrmServiceInstance context on-the-fly for a Silverlight control, thereby allowing that control to communicate with a dynamically defined CRM deployment.  Interesting stuff.
CrmServiceConnectionParams is a simple class that abstracts the parameters required to establish a connection with CRM. It takes, at a minimum, the Web Service URL and a CrmAuthenticationToken. Other constructors are provided to accept more parameters for customization, but the basic constructor requires these two. By assigning a new instance of this class to CrmServiceInstance.ConnectionParams, you call the underlying code within CrmServiceInstance to create a new connection model for the CRM deployment you have targeted.  It’s important to note that this must be done in the code-behind for the XAML.  CrmServiceInstance does not implement DependencyObject, and therefore does not suffer bindings to be made to ConnectionParams.
Because CrmServiceInstance can be configured as a XAML element, it provides two other features: an IsCrmServiceReady member, as a boolean indicator of whether the CrmService member is fully constructed; and a OnCrmServiceReady event, to which you can attach handlers for waiting for the service to become ready.  These are not necessary to use if you will not use CrmServiceInstance as a XAML element—because the construction of CrmService itself is synchronously performed.
Speaking of all this XAML power, there was a presentation link I wanted to provide—but forgot to place in the PowerPoint slides—and that’s a presentation by Daniel Roth and Rob Relyea, several years ago, about XAML.  Otherwise, you now have all the materials to complement the presentation I gave.
Cheers!

Wednesday, June 15, 2011

Slowly Getting Back

For those who have been concerned about the well-being of my daughter, please know that things are doing tremendously well.  Her treatment is far from over, but the chemotherapy portion is nearly complete and a recent surgery was successful at removing 90-99% of her existing mass.  Other treatments remain, but we remain very hopeful due to her resilience thus far.

I’ve been working at night on freelance work for George Doubinski, and have been picking up Silverlight as quickly as possible.  One thing that disappointed me was the dearth of information regarding Silverlight development in CRM 4.  Considering the maturity of both product lines, I would have thought them to have been married much sooner than CRM 2011.  To that end, I’ve decided to participate in CRMUG’s 10@10of10 presentation series and bring what I’ve learned to those of us who haven’t quite moved into CRM 2011 yet.  CRMUG membership is required to attend.

I’m still working up the presentation content, but I’m hoping to have a sensible development example for producing Silverlight code that works for both CRM 4 and CRM 2011.  At any rate, I expect to reproduce the contents and code from the presentation upon this blog; so if you can’t attend the Live Meeting, be sure to check back in the following weeks.

Aside from that, I’ve decided to apply some theme changes to the blog to encourage me to post a little more often.  I’m certainly looking forward to producing more CRM 2011 resources (while bringing forward as many of the CRM 4 developments as are suitable).  However, the pace will be crawling for a while.  Some of you may have seen more recent involvements from me in the CRM Development forum.  Indeed, that’s a pretty good indicator of my free time, because that’s where I spend it.

Monday, March 21, 2011

A Call-To-Arms For CRM Experts!

The CRM Community needs your help!  A new wiki has been established at TechNet Wiki for CRM.  While the principal pages have been setup, Neil Benson, George Doubinski, and I need your help in filling it in with content.  To that end, I’ve created a CRM Article Recommendations page with a list of topics I’d personally like to see deployed to the wiki.

Even if you’re not an author, or contributor, we’d be happy to find your article recommendations within that space.  Afraid to edit a wiki?  Don’t fret.  You can tweet your recommendations @crmwiki for good measure! 

Since activity within the wiki counts as community contribution, it may be a great way to not only secure the renewal of MVP status, but to obtain it for aspiring experts!  While many CRM MVPs are birthed from the flames of the CRM Forums, I’m excited at the prospect that some may yet come from the CRM Wiki.

I can’t enumerate how many times I’ve posted responses in the CRM Forum that refer to older threads.  If that practice could be replaced by references to a live and active wiki, I believe the whole community stands to benefit.  So, I openly encourage existing MVPs and CRM experts alike to join in welcoming and contributing to the new wiki.  And for those who become involved: thank you!

Thursday, March 17, 2011

CRM Wiki Moves to TechNet!

Several months ago, I was encouraged by George Doubinski to contribute to his blossoming CRM Wiki project, crmwiki.net.  At the time, the only thing I had to contribute was a list of CRM Development Resources which I had compiled into Microsoft’s public CRM forums.  The content was moved rapidly, and in the spirit of encouraging more utilization of the Wiki by the community, I removed my listings from the thread.

Now, George has taken the Wiki a step further by contacting the TechNet Wiki Advisory Board about making some space within their Wiki for CRM.  They seemed altogether pleased with the idea, and George then enlisted the help of myself and Neil Benson, to kick-start the Wiki space.  This seems to be a fantastic endeavor upon which the whole community can build knowledge surrounding CRM, and I wholeheartedly support it.

My first project within the new Dynamics CRM Wiki, will be to post the first revisions to the Dynamics CRM Survival Guide.  Hopefully, the community will be able to help me fill it in.  At the very least, however, it should serve as a fantastic starting point for people who are newcomers to Microsoft CRM!

Tuesday, March 8, 2011

Patrick Smacchia on Code Partitioning

While I’m still trying to find time to contribute to the CRM community with the often overwhelming personal time requirements, I found a lot of helpful information in an article by Patrick Smacchia, whom I know from the NDepend project.  He has written a very thorough and enlightening article on the principles and practices of segmenting code through the use of solutions, projects, and assemblies in Visual Studio.  I intend to use this information to make myself a more efficient developer in my CRM projects.  Thank you, Patrick!

Monday, February 28, 2011

Brad Wilson on Partners in CRM Online

Again, Software Advice continues their series of interview videos with Dynamics CRM GM, Brad Wilson:

Brad Wilson talks about the change in Microsoft Partner involvement with the advent of Cloud-based CRM.

Source: http://www.softwareadvice.com/articles/crm/can-dynamics-crm-partners-survive-in-the-cloud-era-microsoft-executive-interview-1022411/

Touching on the complaints I made in my last post about the absence of the Partner-centric CRM deployment option, Brad Wilson suggests rightly that the relationship between Partners and Microsoft have changed in the CRM arena.  Unfortunately, it seems that the only “out” for a Partner is to:

  1. become an ISV-development shop (subsisting on the new Dynamics Marketplace for revenue), and/or
  2. become consultants for business and data-management processes in the CRM space

As a point of topic, the improved turn-around for “trial” deployments of CRM Online seems to be ungracious at best, as Partners appear to have been reduced to glorified CRM Online sales agents; the knife twisting a full 360° when both the interviewer Don Fornes and Brad conclude: “change is hard.”

At this point, I should state that I’m uncertain if the Partner-hosted CRM option still exists with CRM 2011.  I don’t know why it wouldn’t, but it appears completely absent from these interviews.  I always assumed that “On-Premise” was a simple moniker, and that it didn’t actually necessitate on-site visits from a Partner to “drop in a CD”.  Granted, I’m certain the deployment of a CRM Online organization would be dramatically faster than an On-Premise installation, but haven’t we improved that process in the age of virtualization?

I understand that Microsoft is investing heavily in CRM Online, and for smaller organizations with immediate needs, it can hardly be beat as a robust, capable, and prompt solution.  What I fear, however, is that Microsoft is calling all chickens home to roost, and arbitrarily discarding the value Partners have established apart from CRM Online, and the differences in the capabilities, customization or otherwise, with On-Premise.

Nowhere is it apparent that the customer will have the ability to protect, optimize, or archive their data in CRM Online in ways that are intrinsic to CRM On-Premise, and it seems that such is readily sacrificed for the ability to move faster.  I don’t care how fast your car can go, if you have no way to stop it or correct your course without a fiery spectacle, you won’t find me in the passenger seat.

P.S.  I would love to be soundly debated and corrected if anything I have said is incorrect.