Inside Microsoft CRM

6.30.2004

Programming models

Some days I really wonder what's right for the ISV / VAR community when it comes to programming models. I know what I'd like to see if I were writing code against MS-CRM. The problem is that I don't know what you want.

We have this great infrastructure at MSFT that allows us to stay in touch with the community. It works well when the contacts are asked the right questions and when the PM knows what to listen for in an answer. It breaks down when the questions are too generic, too specific, or flat out wrong. That's why I don't really know what you guys want in a programming model.

In MS-CRM there's only one way to go after the platform and that's using the SOAP proxy. Sure, it's possible to use the WSDL and generate client-side code directly, but it's a pain because of name collisions and all the other goo that happens when you add a web reference. There are also the well-known problems with the interfaces as they stand today which I've commented on before.

We've spent a lot of time talking about the ideal interfaces, programming model, and interaction model for the next releases. The problem is that we're talking about it but your voices aren't being represented anywhere. So, I'd like to see if 1) anyone's interested, 2) anyone's listening and 3) if anyone wants to comment.

Option 1 - we leave things the way they are. I won't go into this because everyone understands how things work. You get to keep Intellisense on the API signatures but everything else is a string.

Option 2 - clean up the interfaces by choosing a better naming convention, getting rid of the XML strings, and removing some of the 'extra' parameters. You get IntelliSense on the API signatures here to, but no more strings. (But no IntelliSense on the entities themselves). This one saving grace here is that the "objects" are really extensible. Because they're nothing more than a property bag it's very easy to add new attributes without breaking things (this is one of the reasons we have XML strings today).

Option 3 - get really radical and move to a type-safe, SOA-based model with only 6 methods but with a pile of messages. You get full IntelliSense here including type-safe entities and interfaces. The price you pay is dealing with the extensibility problem (i.e. what happens to your entities when another customer modifies the entity schema - this might be a recompilation or a property bag over the extra attributes).

Option 2 might look a lot like the interfaces do today with the exception that they'll take an "object" instead of XML. This object simply wraps a property bag of strings. You'd get run-time type checking like you do today but with the expense of either having a client-side copy of the metadata or by round-tripping to the server.

The code might be something like this (after adding a reference to the metadata, entity, and service assemblies):


AccountWebService accountService = new AccountWebService();
Account account = new Account(); // or BusinessEntity

account["ownerid"] = ownerid;
account["owneridtype"] = 8;
account["name"] = 64;
account["customertypecode"] = "21"; // is this a string?

string accountId = accountService.Create(account);

Principal p = new Principal();
p.Type = sptUser;
p.Id = someOtherUserId;

uint rights = 1; // READ?

accountService.GrantAccess(accountId, p, rights);


As you can see there's an account "object" on the client, but it's really just a name. The real class is BusinessEntity which is a thin wrapper around a NameValueCollection.

The third option would look structurally the same, but there would be a few key differences. This is after adding a web reference to the CRMWebService (which would get the interfaces and schemas).


CRMWebService crmService = new CRMWebService();

// get a new account instance with default values
Account account = crmService.CreateNew(typeof(Account));

account.name = "account name";
account.customertypecode = 21; // this is an int

Guid accountId = crmService.Save(account);

GrantAccessMessage grantAccess = new GrantAccessMessage();
grantAccess.Moniker.Type = typeof(Account);
grantAccess.Moniker.Id = accountId;
grantAccess.Principal.Type = sptUser;
grantAccess.Principal.Id = otherUserId; // Guid
grantAccess.GrantRead = true;

crmService.Exectue(grantAccess);


I won't cover the V1.x flavor because I don't feel like typing < and > all over the place. That's just too much work.

I'd like to hear from you which option is prefered (and option 1 is still an option). If there's another model you'd prefer, I'm listening. Just don't ask for DataSets everywhere, please.