CRMContext is a special object that is available to you in the code expressions, and that provides some core properties / methods you can use to work with Dynamics.
Here is an example of a code expression that’s using CRMContext:
CRMContext.Target[“name”] = “First Account”;
This expression is going to set “name” parameter of the target record to “First Account”.
Let’s see what’s available there:
1. CRMContext.Target
For the Create/Update/Delete/Retrieve expressions, this is the actual entity record for which your expression is running. If you are familiar with plugins development, it’s what you would get in the plugin if you accessed context.InputParameters[“Target”]
CRMContext.Target[“name”] = “Test Account”;
For the RetrieveMultiple expressions, this is, actually, an EntityCollection. Which is the same as context.InputParameters[“BusinessEntityCollection”] in the plugins.
foreach(a in CRMContext.Target.Entities)
{
a[“name”] = “Test”;
}
2. CRMContext.PreImage
For the Update/Delete expressions, this is how the entity looked like before the operation started.
if(CRMContext.PreImage[“name”] == CRMContext.Target[“name”]){
…
}
3. CRMContext.UserId
This is GUID of the user performing the operation
4. CRMContext.CreateRecord(string entityName)
You can use this function to create a new entity record:
newRecord = CRMContext.CreateRecord(“account”);
5. CRMContext.SaveRecord(Entity entity)
You can use this function to update an entity record. If you used CreateRecord to create that record in the first place, this call will be translated into a “Create” call to the organization service. Otherwise, it’ll be transalted into an “Update” call:
CRMContext.SaveRecord(updatedAccount);
6. CRMContext.DeleteRecord(Entity entity)
You can use this function to delete an entity record:
CRMContext.DeleteRecord(account);
7. CRMContext.DeleteRecord(Entity entity)
You can use this function to delete an entity record:
CRMContext.DeleteRecord(account);
8. CRMContext.LookupRecord(entityName, attributeName, attributeValue)
You can use this function to lookup a record:
contact = CRMContext.LookupRecord(“contact”, “fullname”, “CRM Admin”);
9. CRMContext.QueryFetchXml(fetchXml)
You can use QueryFetchXml to run a fetchXml query:
contacts = CRMContext.QueryFetchXml(<YOUR FETCH XML>);
foreach(c in contacts)
{
…
}
12. CRMContext.IsTeamMember(TeamId, UserId)
You can use IsTeamMember function to verify user’s membership in a team:
if(CRMContext.IsTeamMember(teamId, userId))
{
…
}
13. There is, also, a Sequence(sequenceName, minimumCharacters, leadingCharacters) function that you can use to generate sequential id-s. This function is not in the CRMContext, though:
CRMContext.Target[“name”] = Sequence(“Account”, 5, “*”);
In the example above, “name” attribute of the current entity record will be populated with a new sequential id. That id will be generated for the “Account” sequence (it will be created if it does not exist), it will have at least 5 characters, and, if the number is not big enough, it will use “*” for the leading characters (so, for example, the first time you call it you’ll get *****1 as a result)
I try to use CRMContext.QueryFetchXml(fetchXml) on Retrieve salesorderdetail with code:
prop = CRMContext.QueryFetchXml(“”);
and get error:
[TreeCatSoftware.Dynamics.TCSTools.Plugins.Expressions: TreeCatSoftware.Dynamics.TCSTools.Plugins.Expressions.ExpressionsPlugin]
[935942d8-f56d-e711-80d9-005056b37a5c: TreeCatSoftware.Dynamics.TCSTools.Plugins.Expressions.ExpressionsPlugin: Retrieve of salesorderdetail]
Where I’m wrong? Please clarify or provide any examples.
Thanks!
Firat Vakiliev
My TCS Expression code:
prop = CRMContext.QueryFetchXml(“”);
Hi Firat,
here is an example (on the retrieve of the account, it will get “firstname” attribute fro a lead and put it into the account name field). QueryFetchXml is trying to run that fetch, so there has to be a fetch in the first place(not an empty string):
___________________________________________
leads = CRMContext.QueryFetchXml("
<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false' top='1' >
<entity name='lead' >
<all-attributes/>
</entity>
</fetch>");
foreach(l in leads)
{
CRMContext.Target["name"] = l["firstname"];
}
Hi Alex!
How to get PrimaryEntityId (aka IExecutionContext.PrimaryEntityId property)?
CRMContext.Target[“salesorderdetailid”] does not work 🙁
Thanks!
Firat Vakiliev
Sorry all found. CRMContext.Target is Microsoft.Xrm.Sdk.Entity with all attributes!
Hi Firat,
Yes, that’s exactly it:)
Hi Alex!
My next questions bundle:
1. How to register new TCS Expression on dynamicpropertyinstance entity? On save raised error! I need to update text field (with json value) in salesorderdetail when any property is changed.
2. How to update dynamicpropertyinstance object when my json field is changed. In shortly – synch between salesorderdetail and dynamicpropertyinstance.
May be you can provide any suggestions and/or examples?
Regards,
Firat
PS: Your solution looks like very powerful and usable for me!
Hi Firat,
It seems dynamicpropertyinstance is one of those few entities for which you can’t register a plugin. TCS Expressions are using plugins behind the scene – those plugins will interpret the expressions and run the code.. However, they are still plugins, and they have to be registered (even if the solution does it for you automaticaly).
If you tried registering a plugin step for DynamicsPropertyInstance in the PluginRegistration tool, you wouldn’t be able to even specify that entity for the plugin step.. In other words, that’s a platform limitation.
Hi Firat,
I also need to update a field of an entity when any property of that entity is changed. Has you found any solution to that?
Hi Alex!
I found that the TCS expression (Update) only fires when the is fields changed on form (manually), but it does not run when the entity fields are changed in the workflow or when importing.
Why is that?
Regards!
Firat
Hi Firat,
I am wondering if I need to verify the depth conditions there.. Will take a look tonight, and, if it’s there, will probably republish the solution (will let you know)
OK. I’m not sleeping and waiting for a new version.
Hi Firat,
here is a link to the “hotfix”
http://www.itaintboring.com/downloads/v1_0_10/TCSTools_managed.zip
there will be no ‘depth’ testing there, so, if you actually update the same record from the expression (not if you just set an attribute – that’s fine), this version may start running into infinite loops. Dynamics will prevent infinite recursions, so you’ll see an error message if this happens. Anyway, I will do more testing and publish this version at the regular download location in a few days.
Hi Alex!
I’ll try it tonight. In any case, for the time being, I’m afraid to let this decision go into production. We need to test everything well.
Thanks!
Hi Alex!
At first glance, everything looks great. I checked the work of the Update step through the workflow and the import. I think to try to use the solution in production.
Thanks,
Firat
Often there are errors associated with looping the plug-in during execution.
————————
This workflow job was canceled because the workflow that started it included an infinite loop. Correct the workflow logic and try again. For information about workflow logic, see Help. If you contact support, please provide the technical details.
————————-
I try to add field name (who is need to me by logic) to “Track Fields for Updates”, but those error appear again. Please clarify how I must use the “Track Fields for Updates”.
Thanks,
Firat
PS: Also, I really need detailed documentation and examples of use.
Hi Firat,
the purpose of the “track fields for updates” attribute was to ensure those expressions are registered on the specific attributes only. That part has not been implemented so far, though, so that’s why that field is not even on the expression form. Could you post your expression “code” here (or, if you prefer, just send it through the contact page – will get right to me)? That kind of recursion normally happens if you save the same record for which the plugin is running(so, in this case, if you have an expression and you are saving updated record from within the expression, that create a recursion.. technically, you don’t need to save it.. Create/Update expressions will run in the pre-operation, so, if you just update the Target directly, it will be save to the database automatically after that)
Hi Alex!
Yes. In My program I need to change several fields in case of changing one field. In this case, I do not touch the trigger field. However, I thought that “Track Fields for Updates” with my trigger field should prevent recursion.
Entity: salesorder
event: update
Source:
————————————–
pre = CRMContext.PreImage;
mustApply = pre.Contains(“cp_agreementtemplate”) && entity.Contains(“cp_agreementtemplate”) && pre[“cp_agreementtemplate”].Id.ToString() != entity[“cp_agreementtemplate”].Id.ToString();
if ( mustApply || pre.Contains(“cp_agreementtemplate”) == false && entity.Contains(“cp_agreementtemplate”)) {
template = CRMContext.LookupRecord(“cp_agreementtemplate”, “cp_agreementtemplateid”, entity[“cp_agreementtemplate”].Id.ToString());
entity[“cp_directionofsale”] = template[“cp_directionofsale”];
entity[“cp_numberofparties”] = template[“cp_numberofparties”];
entity[“pricelevelid”] = template[“cp_pricelist”];
entity[“cp_signer”] = template[“cp_signer”];
entity[“cp_2ndpartysituation_1033”] = template[“cp_2ndpartysituation_1033”];
entity[“cp_2ndpartysituation_1049”] = template[“cp_2ndpartysituation_1049”];
entity[“cp_3rdpartysituation_1033”] = null;
entity[“cp_3rdpartysituation_1049”] = null;
entity[“cp_3rdpartyentity”] = null;
entity[“cp_3rdpartyaccount”] = null;
entity[“cp_3rdpartybank”] = null;
entity[“cp_3rdpartycontact”] = null;
entity[“cp_3rdpartyuser”] = null;
if (template[“cp_numberofparties”].Value == 3) {
entity[“cp_3rdpartysituation_1033”] = template[“cp_3rdpartysituation_1033”];
entity[“cp_3rdpartysituation_1049”] = template[“cp_3rdpartysituation_1049”];
entity[“cp_3rdpartyentity”] = template[“cp_3rdpartyentity”];
if (template.Contains(“cp_3rdpartyaccount”)) {
entity[“cp_3rdpartyaccount”] = template[“cp_3rdpartyaccount”];
}
if (template.Contains(“cp_3rdpartybank”)) {
entity[“cp_3rdpartybank”] = template[“cp_3rdpartybank”];
}
if (template.Contains(“cp_3rdpartycontact”)) {
entity[“cp_3rdpartycontact”] = template[“cp_3rdpartycontact”];
}
if (template.Contains(“cp_3rdpartyuser”)) {
entity[“cp_3rdpartyuser”] = template[“cp_3rdpartyuser”];
}
}
}
————————————
Firat
Hi Firat,
this looks ok to me – you are just adding fields to the “target” record, it’s happening in pre-retrieve.. should not be causing the recursion. Is it possible you have another workflow or plugin that’s responding to the update of those fields somehow (and, maybe, that’s where the recursion happens then?)
Hi Alex!
There are no other plugins working together or within this. This is a test program on a clean DB. I’m trying to assess how much the TCS can serve as a quick substitute for classic plugins.
Firat
Hi Firat,
realistically, I think it’s a “quick fix” solution for when you need to do something quickly and you don’t have a developer around to create a classic plugin.
Not sure why it would be running into a recursion (I think it’s not, actually, the expression.. not on its own, at least.. Otherwise, it would be doing it every time. There should be something else involved)
Hi Alex,
Another awesome tool thank you! I just have one problem with my fetchXml.
Using your simple fetchXml above, I got this very simple fetch below working well….
The problem is I need to refine my fetchXml query with filters, and ideally only pull the attributes (fields) I need rather than all fields in the record. I want my fetch to be based on an Advanced Find output, so it would look more like this:
However when I run a fetch (using a workflow to run the code), I get this error:
“Input string was not in a correct format.”
Any ideas on what the problem might be – I was hoping I can use pretty much any valid Fetch query?
Many thanks in advance!
Hi James,
you should be able to use pretty much any fetch. Unfortunately, WordPress tends to hide all those xml tags in the comments, but, if you could send me your fetch by email, I’ll have a look ([email protected])
Thanks Alex – have dropped you a mail – you’re a legend!
Hi all – just to share Alex’s solution to the problem above, I’d wrapped the fetch statement in double quotes, but then also used double-quotes in the fetchxml itself (because that’s how it comes out of advanced find fetchxml in Dynamics). Nested ‘quotes’ are only possible if you use both kinds of quotes to make it clear which is which.
I replaced all my fetch command ” with ‘ and all was good from there.
Hope this helps someone. Thanks Alex – you were very helpful as always.
Thanks, James! Good to hear it’s working:)
Hi Alex,
I wonder how I could use Expressions to retrieve a single value from a related entity.
For example I’d like to retrieve the city of an account to the contact I’m creating OnCreate. Is that possible?
Thanks,
Jeroen