I have a plugin that needs to create a bunch of entities, and does so using;
service.Create(Entity);
At the end of the plugin (pre-operation on Update, synchronous) I sometimes need to cancel the save operation. The only way I know how is to throw an exception, but if I do that my service.Create(Entity) does not get executed.
How do I force the service to execute the operations inside the plugin, and then cancel the save by throwing an exception?
EDIT: The code is;
var id = service.Create(newEntity);
throw new Exception("Cancelled Save but created the new entity");
If I throw the exception, the entity does not get created. If I do not throw the exception, the entity does create. I need it to create and also throw the exception so that the save operation is cancelled.
Thanks for any pointers.
Nicknow's answer is correct, but there are still a few ways to accomplish what you want to do.
In your plugin, don't use the OrganizationService from the plugin context. Create your own new OrganizationService just like you would if you were creating a console app. This new OrganizationService won't be subject to the transaction scope of the original OrganizationService.
Create a web service that does the work. From your plugin, call this web service. The web service won't be subject to the transaction scope of the original OrganizationService.
Use ExecuteMultiple. I've never tried this but here's somebody who claims it works: http://crmtidbits.blogspot.com/2014/02/bypass-plug-in-transaction-to-log.html
Hope that helps!
If IPluginExecutionContext.IsInTransaction == true then when an Exception is thrown anything that has been written to the database will be rolled back. You can try registering in the Pre-Validation stage and sometimes that is outside the transaction, but not always.
Just be aware that there is no guarantee it'll always be outside of the transaction. The SDK documents make this very clear - so at anytime an update could occur that puts it in a transaction.
https://msdn.microsoft.com/en-us/library/gg327941.aspx#bkmk_DatabaseTransactions:
Plug-ins may or may not execute within the database transaction of the
Microsoft Dynamics CRM platform. Whether a plug-in is part of the
transaction is dependent on how the message request is processed by
the pipeline. You can check if the plug-in is executing in-transaction
by reading the IsInTransaction property inherited by
IPluginExecutionContext that is passed to the plug-in. If a plug-in is
executing in the database transaction and allows an exception to be
passed back to the platform, the entire transaction will be rolled
back. Stages 20 and 40 are guaranteed to be part of the database
transaction while stage 10 may be part of the transaction.
Any registered plug-in that executes during the database transaction
and that passes an exception back to the platform cancels the core
operation. This results in a rollback of the core operation. In
addition, any pre-event or post event registered plug-ins that have
not yet executed and any workflow that is triggered by the same event
that the plug-in was registered for will not execute.
Related
I am attempting to learn and apply the CQRS design approach (pattern and architecture) to a new project but seem to be missing a key piece.
My client application executes a query and retrieves a list of documents (with joins) from the read model. The user selects an item and clicks a button to initiate some action. The action is performed by creating and sending the corresponding command object to the write model (where the command handler carries out the action, updates the data store, etc. by read model). At some point, however, I need to update the UI to reflect changes to the state of the application resulting from the action.
How does the UI know when it is time to refresh the original list?
The operation succeeds, data has changed and the UI should be updated to reflect these changes.
The operation fails, data has not changed but the user should be notified of the failure and potential corrective actions.
Without completion of projection, I can't fetch inserted/updated data and send back to UI by a real-time sockets. One solution, I can send the state of the same aggregate and aggregateId but in case of joins, how could I send full updates back (data with joins) to UI?
You have a few options for updating the UI.
If possible, it's easier if your API synchronously returns success/failure. If you get a failure, you can immediately report to the user, and you don't need to update the UI. If you get a success, you have some options.
Include in the success response some entity version information. Include in your read model some version information, and poll the query API until the version is new enough.
Include in the success response some entity version information. Have your query API allow you to specify that you want the data as-of at least that version (in a header, query parameter, or whatever). Either you can have the query API immediately return failure if it isn't yet up to date, or you can have it block until it is up to date (with a timeout) then return the up-to-date data.
Use some kind of client notification system such as web sockets to notify the client whenever the read model it is 'subscribed' to changes. Either the notification can include the information the client needs to update, or it can make another query.
On top of those options, you can also employ the technique of optimistic updates. In this case, after making the appropriate client side checks before issuing a command, you assume instantly that the command succeeded (if it's the kind of command that usually does succeed), update the UI immediately using client-side code. Then:
If the command comes back with failure, undo the client-side update (you could do this by re-fetching the read model, or using client-side code).
If the command succeeds, consider re-fetching the read model, if there's any likelihood that the client-side optimistic update isn't quite right. This should use one of the strategies listed above to ensure the command's effect is present in the read model.
This also works quite well with offline support - you queue commands up persistently in the client, and optimistically assume they succeed (perhaps with a UI indication that you are offline and data is stale) using client-side code to update the read model, until you can actually send the commands through to the server and re-fetch the server-generated read model.
I have 2 plugins registered on the same pipeline stage (post operation) and triggers on the same entity Entity A (create event).
When plugin 1 triggers on Entity A (create event) and make changes to Entity B and another plugin 2 triggers on Entity A (create event) and while making changes to Entity A an exception was thrown.
Does the changes that plugin 1 made on Entity B get rolled back since both plugins triggers on the same entity and they have the same pipeline stage OR the changes plugin 1 made to Entity B remain and will not be rolled back?
Depends on the plugin transaction.
Event execution pipeline
Plug-ins may or may not execute within the database transaction of the
Microsoft Dynamics CRM platform. Whether a plug-in is part of the
transaction is dependent on how the message request is processed by
the pipeline. You can check if the plug-in is executing in-transaction
by reading the IsInTransaction property inherited by
IPluginExecutionContext that is passed to the plug-in. If a plug-in is
executing in the database transaction and allows an exception to be
passed back to the platform, the entire transaction will be rolled
back. Stages (pre-operation) 20 and (post-operation) 40 are guaranteed to be part of the database
transaction while stage 10 may be part of the transaction.
Any registered plug-in that executes during the database transaction
and that passes an exception back to the platform cancels the core
operation. This results in a rollback of the core operation. In
addition, any pre-event or post event registered plug-ins that have
not yet executed and any workflow that is triggered by the same event
that the plug-in was registered for will not execute.
In your case if the plugins are chained together, and the second one is synchronous the exception from plugin 2 will bubble up to plugin 1. If both plugins are inside a transaction the actions from both plugins are rolled back.
I remembered that it's can be done using the SDK and the API, for example:
I wanted to create Task and with the same request (using the same Proxy and Context objects) create a new Contact entity and link in to the Task (RegardingObjectId) – All in one execute / request message to the CRM and of cures with full support of transaction
10X
Itzik
If you want it all to be wrapped into a single transaction, you'll have to use a plugin registered either before or after Create, just not in the validation stage. Then if any action fails, it'll all be rolled back.
Given that the existing OrganizationServiceContext for a Plugin is performs updates in the current SQL transaction context (assuming pre or post validation event), is there an efficient way to update a separate entity outside this transaction/context?
The goal is to avoid rollback of updates on a different entity (other than the Plugin target entity) when an exception is thrown.
Just create a new IOrganizationService rather than use OrganizationServiceContext.
Check this example.
I have a question regarding the rollback mechanism in CRM 2011. I know that a synchronous plugin will rollback any changes it has performed if an error is thrown. But what if this plugin triggers another plugin during it's execution.
Consider the following plugins:
Plugin A: Triggers on pre-UPDATE of Task entity
Plugin B: Triggers on pre-UPDATE of Case(incident) entity
Scenario would be like this:
I update a case and Plugin B get's triggered.
Fields in the case are modified
One of the Tasks associated with the case is also updated with some information.
Plugin A is triggered
Fields related to the task are modified
Some other operations on the case record
Plugin B throws exception
My question is, would the operations performed in Plugin A get rolled back as well?
As long as the plugins are registered in the transaction (before or after, but still in), everything will get rolled back. If a plugin is registered for the Pre-Validation stage, it will not get rolled back.
This also assumes that you're retrieving the IOrganziationService from the PluginContext as well.
This is what happens on the server:
A request to update an entity comes in, are we currently in a transaction via the context? If not create a new Database Transaction and store it in the Plugin Context.
Request is made to update a different entity, transaction is passed to the new plugin's context, and step 1 is repeated for new plugin execution.