Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fflib_SObjectUnitOfWork order dependency #129

Open
cropredy opened this issue Jul 18, 2016 · 12 comments
Open

fflib_SObjectUnitOfWork order dependency #129

cropredy opened this issue Jul 18, 2016 · 12 comments

Comments

@cropredy
Copy link

Andy (as you can imagine, I'm been retrofitting existing orgs to the SoC pattern and running into interesting use cases..appreciate the forum)

Use Case: Sync Quote + Quote Items to Opportunity + OLIs

The preferred pattern for me is to

  1. Delete the OLIs in the target
  2. Update the Opportunity
  3. Insert new OLIs from the Quote Items

But the UoW framework won't support this if you have SFDC Duplicate Management in place to prevent duplicate SKUs (ProductCode). Why?

The fflib_SObjectUnitOfWork.commitWork() method first does inserts, then does updates, then does deletes, and finally registered workers.

Hence, the inserts are likely to have the same ProductCodes as in the OLIs to be deleted and the insert fails. Order of Execution says that Duplicate Checks run immediately after VRs.

IDoWork doesn't directly help because it gets called long after the inserts. The only workaround is to use an IDoWork handler for all three steps above - but then the uow pattern isn't helping much (only a savepoint-rollback wrapper).

One possible enhancement to the framework could simply be an argument to commitWork(Boolean deletesFirst) .

@o-lexi
Copy link

o-lexi commented Sep 7, 2016

I also ran into a similar issue when my custom Child object record was using updated field value from the parent record, but because of IUD order of operations in UoW it was failing as all inserts are done before updates. I agree with the 'preferred pattern' of DUI (Delete, Update, Insert) you provided as in this case all operation are done in the order from the least dependant to the most

@cropredy
Copy link
Author

@o-lexi - Funny, I ran into your use case myself today. Here it is:

Use Case: Construct in one transaction an updated Opportunity (new pricebook2Id) + insert OLI

Let's say that the Opportunity starts with Pricebook2Id = IdA and we want to change the pricebook to IdB and insert some OLI using PBEs for pricebook IdB -- all in the same Unit of Work.

Unless the Opportunity is updated with the new Pricebook2Id IdB first, the insert of the OLI using PricebookEntries that reference IdB will fail with a Field Integrity Exception PricebookEntryId (pricebook entry is in a different pricebook than the one assigned to the opportunity

But, the code in onCommitWork() blindly does the inserts of the OLIs first before ever processing the registered-as-dirty Opportunity. Hence the Field Integrity Exception.

A step in the right direction would be to register the DML order as either I - U - D (default) or U - I - D or U - D - I, or D - I - U or D - U - I or I - D - U. This won't solve every issue but would mean less resorting to IDoWork.

@tmowbrey
Copy link
Contributor

tmowbrey commented May 3, 2017

@afawcett Any idea if there will be movement on this issue or should I plan on figuring out an IDoWork pattern?

@cropredy Any change of sharing a general pattern on how your IDoWork is setup? Might be helpful for me and others that come across this issue :)

Thanks!
Tyler

@cropredy
Copy link
Author

cropredy commented May 5, 2017 via email

@daveerickson
Copy link
Contributor

I'm working on an apex sharing project for community/partner access and trying to delete before inserting, as this is the recommended approach:

"For a sharing recalculation, we recommend that the execute method delete and then re-create all Apex managed sharing for the records in the batch. This process ensures that sharing is accurate and complete."

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_interface.htm#apex_batch_best_practices

Sorry, no PR to contribute, but wanted to add more rationale behind configurable order of dml.

@afawcett
Copy link
Contributor

@cropredy @ImJohnMDaniel there is some good evidence to support this enhancement here - agree?

@cropredy
Copy link
Author

Yes, I agree - IDoWork should be reserved for unsupported UoW use cases like partial success DML; the delete-before-insert use case is a common-enough pattern; especially when dealing with objects that can't have certain fields updated like PricebookEntryId on OpportunityLineItem, QuoteItem, OrderItem, ServiceContractLineItem - or the sharing rules as @daveerickson states

@marco10507
Copy link

Hi All,

It is great we can tailor the unit of work by adding peaces of work. I would need to first send emails, then update related objects. I see that the class SendEmailWork, which is located here, is set to private, I would like to add an instance of SendEmailWork using the method registerWork(IDoWork work). Can you set the class SendEmailWork to public?

@ImJohnMDaniel
Copy link
Contributor

@marco10507 If you would like to open a separate PR for that change please feel free to do so. One request that I will be asking is of you is to document why you would want to make that method public and what scenario/use case it supports. Cheers!

@sruthikattula-okta
Copy link

sruthikattula-okta commented Oct 15, 2020

Hi, Can I get some guidance on how to achieve partial success in DML as I am trying to extend FFLib framework for batches and other asynchronous processes.

@cropredyHelix
Copy link

You'll have to delegate the DML to a class that implements IDoWork and avoid using any of the registerXXX methods.

@hansds
Copy link

hansds commented Sep 16, 2021

Would a PR be accepted that added support for a delete-before-insert usecase?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants