Loading...

Making CrmWebApi Entity References Suck Less For Creates and Updates

Making CrmWebApi Entity References Suck Less For Creates and Updates
For those that “grew up” on the 2011 Rest endpoint for CRM, attempting to populate Entity Reference attributes for create or update calls feels rather painful in the new CrmWebApi.
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 {
    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 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.

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 {
    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;
}
This then is all used in the prepareForOData function:
/**
* 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;
}
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:
export function create(entityCollection: string, data: any): Promise<any> {
    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);
}
And now, these two calls, will result in the same exact request made to the CrmWebApi:

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 more
.Net Dust
.Net Dust

NULL

Share post:

Related posts

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...

16 days ago

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. ...

26 days ago

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...

27 days ago

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...

1 month ago

How to create an impactful fan experience in sports with Dynamics CRM?

Mohona Dutta By Mohona Dutta | Reading time 5 mins For a salesperson, every day is game day. Sports organizations are always looking to i...

1 month ago

Updating JavaScript code in Dynamics CRM Made Easy for Developers

Hema Shamala By Hema Shamala | Reading time 5 mins Why do we need JavaScript in D365 CRM? It allows us to implement custom logic by using...

1 month ago

How To Use Advanced Find in Dynamics CRM 365

Nikhil Rajendran By Nikhil Rajendran | Reading time 5 mins One of the most commonly used features in Dynamics 365 is Advanced Find. A d...

1 month ago

Security Model of Dynamics CRM

Business Unit – It is a way to group business activities.When an organization is created, a Root Business Unit is created by default. Thi...

2 months ago

I recreated Dynamics CRM with the Power Platform Plan designer

In January 2003 after many months of engineering and development, Microsoft released one of the first business solutions built-in house; Micro...

3 months ago
Stay up to date with latest Microsoft Dynamics 365 and Power Platform news!
* Yes, I agree to the privacy policy