Microsoft Dynamics CRM 4.0 - A Platform for Packaged Software?
Since its introduction in 2008, Microsoft Dynamics CRM 4.0 has been touted as a platform for rapid application development. We’ve always been hard core C# and ASP.NET snobs, but we were intrigued by the value that Dynamics CRM promised. We set out to build our next generation software product on that platform, and learned a lot about what’s real and what’s not so real. In this article, we will share the rollercoaster ride of our experience developing our software product on this new CRM “platform.”
When we were first introduced to Dynamics CRM 4.0, we were not looking for a software package to manage our customer relationships. We were looking for a new platform to build our next generation suite of applications. Most of the developers on our side scoffed at the thought of using someone else’s platform; they all wanted to build it from scratch. But the more we learned about Dynamics CRM, the more we liked it. The value of having much of the infrastructure already built was very attractive, but there were constant hints of missing elements here and there that we were just not sure about.
We started with a small “proof of concept” task that gave us a good initial feel for how Dynamics CRM 4 works, and how to leverage it. We then turned our attention to the much larger project that we were tasked with. Everyone on the team was excited to work on this new platform and learn its nuances. The project was fairly sizeable, and we had to push Dynamics CRM to its limits, and at times step a little outside of its intended use. Our project was to develop a software package that was to be installed and customized by our own enterprise clients. It was not to be used by our own organization. The difference between developing an application for in-house consumption and one that can be packaged and sold to your clients makes a huge difference in whether Dynamics CRM is a good fit for your goals or not.
The next few months were very interesting, and we certainly learned a lot about Dynamics CRM 4 along the way. In this article, we will share our discoveries about the platform and explain how we solved some of the problems we ran into.
Dynamics CRM Development Fundamentals
Dynamics CRM is often marketed as an “xRM” development platform where the “x” can be substituted with anything. The idea is that while you can configure out of the box to handle general customer relationship management, you can extend the platform to manage a wide variety of business domains. To accomplish this, Dynamics CRM provides several different methods of customizing and developing on the platform.
The most basic means of customizing Dynamics CRM is through the entity data model. The platform itself uses SQL Server as its backend database. However, you will typically never access the database directly except in a limited manner when creating custom reports. Instead, you will work with a layer on top of the database that Dynamics CRM provides in the form of “entities.” We will expand on the data modeling process later, but for now you can think of an entity as a database table on steroids. In addition to the table schema and relationships, the metadata for an entity defines multi-lingual names for field labels, column sets for views of entity records, and the contents and layout of the entity’s data entry form. Data modeling is done through the Dynamics CRM client, but the data model can also be manipulated programmatically.
The programming API for Dynamics CRM consists primarily of two Web service endpoints: the “CrmService” which is used to interact with entity data, and the “MetadataService” which is used to read and manipulate the entity data model. Microsoft provides an SDK containing assemblies that can be referenced to access the services as an alternative to referencing the service endpoints directly. We will dive deeper into the Web services as we expand more on the different aspects of CRM development.
On the server side, your options consist of plug-ins and workflow activities. A plug-in is conceptually similar to a SQL trigger: you are writing code that will be fired automatically in response to some change occurring in the database. However the plug-in model is more robust since it allows you to respond to a wider range of events and you can execute your logic in a synchronous or asynchronous manner. For workflow, Dynamics CRM uses .NET Workflow Foundation as the base platform and provides a powerful and user-friendly editor in the CRM native client for creating workflows. You can create your own custom workflow activities that can be registered with the server and then used in any CRM workflow.
CRM Entities in C#
Dynamics CRM, when dissected to its simplest level, is essentially a data store mechanism. Obviously it can do a lot more than just store data, but everything you do in CRM is related to the Entity Model. Essentially, an entity is CRM’s abstracted version of a SQL Data Table. But rather than expose the massive amount of features that SQL allows for, Microsoft has limited their entity model to a specific subset of features that are easier for the layperson to manage.
One of the great strengths of CRM is that it isn’t necessarily just for developers. A lot of back office applications require data model changes and not every organization is going to want to have a professional developer on hand to make data model changes. In this respect, CRM excels, because it allows anyone (anyone who has permission, that is) to modify the data model and create new entities as they are needed, without needing to know the specifics of database programming.
Within the first ten minutes of playing around with CRM I was able to create a new entity and populate it with attributes (CRM’s term for columns). It’s incredibly simple and user-friendly.
The user-friendliness ultimately comes with a cost, however. Some of the more advanced SQL features aren’t really possible with CRM. For example, every entity must have a GUID primary key. You’re not even allowed to name the primary key column. You cannot have composite keys either, or unique columns. Your data type choice is limited to the types that CRM exposes (which means you won’t have access to any of the neat new types that SQL Server 2008 introduced).
All of the limitations and standard attributes are designed to streamline the CRM API. Since CRM puts these limitations in place, you can make certain assumptions, which allows the system to act in a very uniform manner and allows software built on top of CRM to be simplified by making those very same assumptions.
In addition to the limitations, CRM also adds a host of standard attributes to every entity. There’s a Name attribute, which holds the name of the record (as identifying records by GUIDs is not something humans are apt to do), the owner of the record (a CRM user), whom the record was created by, the last user who modified the record, the date of the change, and a state code that tracks whether the record is currently active or inactive.
The entity system in CRM provides more than what SQL does at a base level. Because of the unified entity system, CRM enables the use of file attachments on every single Entity as well as a comprehensive security system that allows administrators to choose who has access to which entity types and how much control each user has over that entity.
As a CRM developer, you will want to interact with entities using C# code. There are two primary methods of doing so. The first method is to interact with an entity within a CRM plug-in or workflow. Plug-ins and workflows are attached to a single entity type and executed when certain events happen (for example, creation or modification of an entity record). The entity that the event is being executed on will be attached to the plug-in or workflow context objects in the form of a DynamicEntity, which is CRM’s method of working with an entity without worrying about having a specific type representing the entity. These objects expose a collection of property objects which you are free to modify, where each property represents an attribute on the entity.
The other method of interacting with entities is to use the CRM Web Services, which allows generic access to entities using the DynamicEntity class, but also introduces typed entities. One of the neat things about CRM is that when the data model is updated, the CRM system actually generates a brand new WSDL definition, defining all of the entities and attributes in the system as datatypes that can be consumed by Web service clients. If you’ve ever used .NET’s svcutil.exe tool to generate a client wrapper, this is good news, because it means you’ll be able to interact with all of the entities on a CRM installation automatically in a fully typed manner.
Unfortunately the data definitions that CRM’s WSDL generates leaves a lot to be desired. Rather than generating types that contain attributes of int’s, bools, strings, and so on, CRM actually uses their own wrapper classes to wrap all of the basic datatypes. For example, the CrmNumber class represents a 32-bit integer and exposes a property named Value that allows you to retrieve the true value. Unfortunately this made working with entities somewhat unnatural and cumbersome because we couldn’t just pass the attributes around. Most code has no idea what a CrmNumber is, but most code does understand what an Int32 is. Instead we had to extract the .NET base types first and then we were able to use them.
An additional problem was that we had code we wanted to work on both the internal plug-in/workflow side and the external Web service client side. On the internal side, we could access and use the DynamicEntity object, which was a type defined in the CRM DLLs. On the external side, all of these classes are replicated via the svcutil.exe code generator and put into our own namespaces. So while we could access an external DynamicEntity, it was a brand new class that looked identical to the internal DynamicEntity.
In order to solve these problems we used custom code generation to build a strongly typed Object Relational Mapping (ORM) layer that would allow code written from any context to talk to CRM entities without caring where it’s being executed from.
There are several commercial products out there which will generate an ORM layer. Most products will do what most people need them to do and are pretty good solutions. Ultimately we decided to roll our own code generator so we could tailor it to our unique requirements. If you decide that your CRM project requires entity code generation to streamline the development process, I would definitely recommend looking at the commercial products out there first.
Dynamics CRM presents unique challenges to overcome in order to support a team of developers working concurrently on the same application. For one thing, it quickly became obvious to us that each developer needed their own CRM environment. CRM supports the concept of “multi-tenancy” in a general sense, which means that you can have multiple databases (CRM calls them “organizations”) hosted on the same CRM server. However, as we began to implement more advanced scenarios it became clear that the multi-tenancy support did not easily apply to all the different ways we needed to extend the platform. For example, if you are adding your own ASP.NET pages to the CRM client, there is no multi-tenancy support built-in for those extensions. Developers working on the same pages will step on each other’s toes. Furthermore, it was not uncommon during the course of development to find ourselves needing to recycle the CRM Web application pool or to stop and restart the Windows service that supports asynchronous operations in CRM. As you can imagine, that creates chaos when multiple developers are working in the same environment.
We also discovered early on that it was far easier to develop when Visual Studio was installed directly on the same machine as CRM. Remote debugging can certainly be a lifesaver when that is your only option, but in general it can be difficult to configure properly and the performance can be quite slow. Developing in Visual Studio on the same machine as CRM allows you to attach to the different CRM processes directly for debugging. It was also easier to automate the deployment of our build output to the CRM server when we were building the application on the same machine. We leveraged MSBuild extensively to automate deployment as part of our development build process. We also created several utilities that we plugged into Visual Studio to help automate some of the more tedious tasks. This included tools for registering our plug-ins and workflows as well as importing data into CRM, which are actions you will find yourself doing frequently during the course of development.
Another aspect of Dynamics CRM that added to the complexity is its requirement to be installed on a server OS (Windows Server 2003 or Windows Server 2008). Our developers were running desktop operating systems locally and it was not practical to have them switch to Windows Server. We used virtualization to overcome this issue, and each developer worked in their own virtual environment running Windows Server 2008. We sometimes struggled to keep adequate resources available to support this, even with server-based virtualization and reasonably powerful hardware. Each development environment needed enough resources to at least run CRM and Visual Studio, and we also virtualized the necessary SQL Server instances.
You can deploy Dynamics CRM in many different configurations with various components of the platform optionally installed on separate machines. For development, however, we found it simpler to install all components of CRM on a single server. We configured CRM and Windows consistently across our different environments. This greatly simplified the deployment of our application during the development cycle because we could engineer our build and deployment process for a consistent target environment. However, it was still necessary during the QA cycle to test thoroughly on every configuration of CRM and Windows Server that we planned to support in order to expose any unanticipated issues caused by different configurations.
The final piece of the puzzle for us was source control. In general, source control for a CRM application is no different than any typical .NET application. Tracking our changes to the data model, on the other hand, was a different story. CRM allows exporting the data model to an XML file, and this file is the most practical way to store model changes in source control. Here is a small snippet of the XML produced by CRM when customizations are exported:<ImportExportXml version="184.108.40.206"
This XML file can quickly become very complex and extremely large, and it is not practical to modify or even read manually as may be the case with a SQL script. Furthermore, the order of the contents in the file can easily become scrambled as it is imported and exported from different CRM environments. This made it very difficult to determine what changes were made to the file and by whom. It was also very easy for one developer to accidently overwrite or remove changes made to the model by others. We did our best to address these problems by documenting and adhering to a strict process for making model changes that required coordination across the team. We used utility programs to assist with comparisons with previous versions of the file, and to automate the export process to avoid human error. Finally, we split the components of our data model into several logical groupings and exported and maintained those groupings with separate XML files. This made the data modeling process manageable and allowed us to have some level of traceability, but it was still not ideal.