Making CrmWebApi Entity References Suck Less For Creates and Updates
account["[email protected]"] = "/contacts(E15C03BA-10EC-E511-80E2-C4346BAD87C8)";Was it “"@odata.bind” or “@bind.odata”? Was it a forward slash or backward slash? Did the Guid have curly braces?
Yes it’s a small pain, but it is bigger if you normally use field accessors (“entity.field” rather than array accessors: “entity[‘field’]”) because "[email protected]" isn't a valid field name. It’s probably because of my C# background, but I prefer not to use the object array accessor method when possible. So the question is, how to make this syntax better and help me remember it.
On my current project I use David Yack’s CRMWebAPI. It’s simple, and uses standard Promises, so no need for a new library, just polyfill Promises (if you’re using IE 11) and you’re all set. The calls are wrapped by a custom TypeScript library (CrmWebApiLib) to allow for some custom changes, of which, this implementation is one. First, the library defines an Entity Reference class (*Note, this is TypeScript, get it, use it, love it)
export class EntityReference implements ODataFormattable {The class has two public properties, “collectionName” and “id”, and implements the two functions of the ODataFormattable interface, “toODataFromat” and “getODataPropertyName”. The toODataFormat adds the forward slash and formats the guid correctly, and the getODataPropertyName appends the “@data.bind” to the property name parameter.
constructor(public collectionName: string, public id: string) { }
toODataFormat = (): string => {
return `/${this.collectionName}(${CrmWebApiLib.removeCurlyBraces(this.id)})`;
}
getODataPropertyName = (propertyName: string): string => {
return `${propertyName}@odata.bind`;
}
}
The ODataFormattable interface just defines the two functions. Then there is also a User Defined Type Guard to determine if any given object implements the ODataFormattable interface:
export interface ODataFormattable {This then is all used in the prepareForOData function:
toODataFormat(): string;
getODataPropertyName(propertyName: string): string;
}
export function isODataFormattable(arg: any): arg is ODataFormattable {
const formattable = arg as ODataFormattable;
return formattable && formattable.toODataFormat !== undefined && formattable.getODataPropertyName !== undefined;
}
/**It creates a new object, and basically loops through all properties of the data object, copying it over to the new object. If the value of the property is a ODataFormatable, it will update the value as well as the property name. There is then a recursive map call to handle arrays as well (think party lists). prepareForOData is then called from within the create and update methods:
* Loops through properties, searching for any ODataFormattable properties or arrays with ODataFormattable, and updates the format to be OData Friendly
* @param data
*/
function prepareForOData(data: any): any {
const oData = {};
for (const propName in data) {
if (!data.hasOwnProperty(propName)) {
continue;
}
const value = data[propName];
if (isODataFormattable(value)) {
oData[value.getODataPropertyName(propName)] = value.toODataFormat();
} else if (value instanceof Array) {
oData[propName] = value.map(prepareForOData);
} else {
oData[propName] = value;
}
}
return oData;
}
export function create(entityCollection: string, data: any): Promise<any> {And now, these two calls, will result in the same exact request made to the CrmWebApi:
return instance().Create(entityCollection, prepareForOData(data));
}
export function update(entityCollection: string, key: string, data: any, upsert?: boolean): Promise<any> {
if (key.indexOf("{") >= 0 || key.indexOf("}") >= 0) {
key = CrmWebApiLib.removeCurlyBraces(key);
}
return instance().Update(entityCollection, key, prepareForOData(data), upsert);
}
No Bueno
const note = {};
note["notetext"] = CommonLib.getValue(fields.description);
note["[email protected]"] = `/allgnt_locations(${CommonLib.getSelectedLookupId(fields.location)})`;
CrmWebApiLib.create("annotations", note);
Muy Bueno
const note = {
notetext: CommonLib.getValue(fields.description),
objectid_allgent_location: new CrmWebApiLib.EntityReference("allgnt_locations", CommonLib.getSelectedLookupId(fields.location))
};
CrmWebApiLib.create("annotations", note);
Published on:
Learn moreRelated posts
Microsoft Copilot in Dynamics 365 Customer Engagement: Where Teams See the Most Value
Artificial intelligence, particularly Microsoft Copilot in Dynamics 365 Customer Engagement, is quickly becoming part of everyday work across ...
Microsoft Power Platform 2026 Release Wave 1: What Copilot and Agents Mean for Dynamics 365 Customer Engagement
In conversations with organizations over the past several months, a consistent question has started to surface: how do we actually use these A...
Azure Data Factory Tips for Reliable Microsoft Dynamics 365 CE and Dataverse Integrations
Reliable integrations between Microsoft Dynamics 365 Customer Engagement and external systems can become challenging. This is especially true ...
Dynamics 365 CE: Known Issues Creating Word Templates
A classic feature worth revisiting, today’s focus is Word Templates in Dynamics 365 CE/CRM. This tool remains a powerful option available for ...
Architecting Scalable Business Logic in Dynamics CRM Using Plugin Life Cycle
Dynamics CRM Plugin Life Cycle: Optimizing for Scalability means designing plugins in a way that keeps the system fast, stable, and easy to ma...
Avoiding Currency Mismatch Errors in Dynamics 365 CE
When working with Dynamics 365 Sales, it’s important to understand how currency behaves across related entities like Opportunity, Quote, Order...
Sales Collaboration: How Sales Teams Work in Dynamics 365 CE
A Sales Team in Microsoft Dynamics 365 Sales represents a group of users who collaborate to manage and close sales opportunities efficiently. ...
Environment Variables vs Configuration Tables vs Hardcoding in Dynamics 365 Customer Engagement (CE)
In Dynamics 365 Customer Engagement (CE), managing configuration values effectively is key to building scalable and maintainable solutions. En...
Ticket sales management with Dynamics CRM in the Sports Industry
Mohona Dutta By Mohona Dutta | Reading time 5 mins So, how do you prospect? Pulling names out of lists on your laptop? Repeatedly calling...