-
Notifications
You must be signed in to change notification settings - Fork 1
Home
This project provides a Fluent-style interface to Microsoft Dynamics CRM using the Microsoft.Xrm.Sdk web interface.
This project arose from the frustrations of writing code that worked with the existing SDK interface and then dealing with various issues that arose from this: -
-
A programming model where we have to specify the names of the attributes we want to retrieve, and then having to check that they have actually been retrieved (and are not null) and dealing with the issues and potential errors around this.
Having to specify the attribute name in one or more places means we have the same information in multiple places, leading to mismatches and errors. On top of this all of the boilerplate code around retrieving attributes and then checking for null is very standard (but frustratingly error prone) The aim was to elimitate this code, so removing a common soure of error in CRM programming.
Fetching data with FluentCRM -
The issues around what I call "phantom" updates to CRM attributes, where a poorly written client updates an attribute with its existing value and writes it back to the CRM server.
This raises issues with clutter in the audit log, but more importantly can lead to workflows and plugins running even when there has been no change to an attribute. (Plugins and worflows run on "update" to an attribute, which does not necessarily mean that the attribute has"changed". A subtle point that is not always understood by every CRM developer 8-) )
Updating data with FluentCRM -
Writing for the CRM SDK interface produces very "noisy" code with a lot of fairly standard yet error prone boilerplate. So another aim was to take advantage generics and a Fluent-style interface to produce code that elimitates all of this excess code and produces something that is easy to write and, just as important, easy to read.
-
I wanted to try to discourage what I call the "retrieve of death" where a developer gives up trying to track which attributes they actually need and just pulls all the columns back - just in case they have forgotten to include a field they might need.
On large entities this can mean literally hundreds of attributes being read unnecessarily, with a predictable effect on your CRM server. -
I wanted to design a library that could be easily extended to cover the multiple different custom entity types found in a CRM installation. Extending the FluentCRM interface
-
Another problem I was trying to address: have you ever been asked to return a phone number from CRM, and the requirement says use the work number, or if that is missing use the mobile number. Oh and lets throw in the home number just in case the first two are missing? I had that problem, so FluentCRM can help there too.
-
Then I started thinking about how to do joins... Joins
A taste of Fluent: -
FluentContact.Contact(contact.Id, orgService)
.UseAttribute( (string e) => myStruct.emailAddress = e, "emailaddress1")
.Execute();
This fetches a contact by its ID, and if found it will call the closure to set myStruct.emailAddress to the value. Note that you have to only specify the attribute name once. Only this attribute will be retrieved from CRM. Also observe that nothing happens before the final "Execute()". (much in the same way as LINQ does nothing till you try to use the results of the query)
FluentContact.Contact(contact.Id, service)
.UseAttribute( (string e) => myStruct.emailAddress = e, "emailaddress1")
.UseAttribute<string>( num => myStruct.phoneNumber = num, "mobilephone", "telephone1" )
.Execute();
This adds a clause to extract a phone number from either the "mobilephone" or "telephone1" fields (checking in the specified order for a field that is not empty and then calling the closure only once with the found value. If no value is found then the closure will not be called)
FluentContact.Contact(contact1.Id, service)
.UseAttribute((string e) => myStruct.emailAddress = e, "emailaddress1")
.UseAttribute<string>(num => myStruct.phoneNumber = num, "mobilephone", "telephone1")
.UseEntity((EntityWrapper contact) =>
{
myStruct.name = string.Join(" ", contact["firstname"], contact["lastname"]);
},
"firstname", "lastname")
.Execute();
Adds a UseEntity clause. This will call the closue with an "entity" fetched with the listed attributes.
Another example with multiple levels of join
var permittedImageIds = new List<Guid>();
// Add Application attachments
FluentPortalUser.PortalUser(userid, CRMObjectSingleton.ztCRM.Service)
.Trace(s => Debug.WriteLine(s))
.Join<FluentAccount>(c =>
c.Join<FluentApplication>(a =>
a.Join<FluentAnnotation>(
an => an.UseAttribute((Guid noteId) => permittedImageIds.Add(noteId), "annotationid"))))
.Execute();
Return a list of the ids notes on the "Application" associated with a "PortalUser" entity via associated accounts, and Application entities
FluentCRM - Microsoft.Xrm programming made easy