How to modify transaction behavior in EF Code First - entity-framework

In Code-First Entity Framework, is there a way to modify the transaction behavior so that it does not discard all changes and instead keep all the changes up to the point of failure?
For example:
foreach {var objectToSave in ArrayOfEntityObjects)
{
MyContext.Insert(objectToSave);
}
try
{
MyContext.SaveChanges();
}
catch (Exception x)
{
//handling code
}
In the above example, assuming the array is an array of 100 objects and that an error occurred on item 50, I want to keep the ones that were successful, up to the point of failure at least. Right know we are executing the MyContext.SaveChanges() command during each iteration of the foreach loop to do this, but we would like the performance boost of committing to the database in one commit (Our understanding is that EF sends all the commands at once for the transaction, over a single connection, thus only using one round trip).

No, there is no way to do this automatically. EF commits all changes in a single transaction, which means it's an all or nothing thing.
If you want this behavior, then you must save changes after each and every record you add.
The reason is that EF doesn't know what constitutes a "transaction" in your data. It might be one row, or it might be several rows in several tables. EF doesn't even try to understand what your transactional requirements might be. You have to do it manually if you want it.

Related

How to assign fetchedObjects to a variable independently

In my case i 'm getting my nsmanagedobjects into an array with line of
let rows = self.fetchedResultsController.fetchedObjects as [Row]
after this line i rollback changes as my needs
managedObjectContext?.rollback()
and i am supposed to use the values of the rows variable. But due to rollback() function changes in rows are also deleted.
How can i keep my data for after rollback() function call?
"But due to rollback() function changes in rows are also deleted."
That's because this is specifically what that method is documented to do. The documentation says:
Removes everything from the undo stack, discards all insertions and deletions, and restores updated objects to their last committed values.
The entire purpose of calling rollback is to get rid of any unsaved changes in the context, so what you describe is the expected behavior.
If you want to keep changes and call rollback for some reason, you would have to copy those changes to someplace not affected by the managed object context, then I guess copy them back later on. That will be a pain in the ass, but it's possible.
The real question is why you are calling rollback-- a method specifically designed to undo unsaved changes-- if you have changes you don't want to undo.

Could I save Postgres transaction and continue work with db within it later

I know about prepared transaction in Postgres, but seems you can just commit or rollback it later. You cannot even view the transaction's db state before you've committed it. Is any way to save transaction for later use?
What I want to achieve actually is a preview (and correcting) of some changes in db (changes are imports from csv file, so user need to see preview before apply it). I want to make changes, add some changes later, see full state of db and apply it (certainly, commit transaction)
I cannot find a very good reference in docs, but I have a very strong feeling that the answer is: No, you cannot do that.
It would mean that when you "save" the transaction, the database would basically have to maintain all of its locks in place for an indefinite amount of time. Even if it was possible, it would mean horrible failure modes and trouble on all fronts.
For the pattern that you are describing, I would use two separate transactions. Import to a staging table and show that to user (or import to the main table but mark rows as "unapproved"). If user approves, in another transactions move or update these rows.
You can always end up in a situation where user can simply leave or crash without clicking "OK" or "Cancel". If what you're describing was possible, you would end up with a hung transaction holding all these resources. In my proposed solution you end up with wasteful rows in "staging" table that you may still show to user later or remove.
You may want to read up on persistence saga. This is actually a very simple example of a well known and researched problem.
To make the long story short, this pattern breaks down a long-running process like yours into smaller operations that are applied and persisted in some way in separate transactions. If any of them happens to fail (or does not occur as expected), you have compensating actions that usually undo what the steps executed so far have done (e.g. by throwing away stale/irrelevant data).
Here's a decent introduction:
https://blog.couchbase.com/saga-pattern-implement-business-transactions-using-microservices-part/#:~:text=The%20SAGA%20Pattern,completion%20of%20the%20previous%20one.
http://vasters.com/clemensv/2012/09/01/Sagas.aspx
This concept was formally introduced in the 80s, but is well alive and relevant today.

EF Code First and Saving Multiple Records

Just getting to grips with Entity Framework and I can save, add, delete etc a single entity like so:
db.Entry(client).State = EntityState.Modified;
db.SaveChanges();
My question is if I wanted to change several records how should I do this, for example I want to select all Jobs with a Type of 'new' and set the Type to 'complete'. I can select all the jobs easily enough with Linq but do I have to loop through, change them, set the state to modified, save changes, next one etc? I'm sure there is a straightforward way that I just don't know about or managed to find yet.
Are you sure you need to set the EntityState? SaveChanges will DetectChanges before saving. You can't update multiple records at once as you are requesting, but you can loop through, update the value and call save changes after the loop. This will cause 1 connection to the database where all of your updated records will be saved at once.
Yes, you would have to loop through each object, but you don't have to save changes after each one. You can save changes after all the changes have been made and update in a single go. Unless there is some reason you need to save before editing the next record.
However, if you have a simple operation like that, you can also just issue a SQL statement to do it.
context.Table.SqlQuery("UPDATE table set column = 1 where column2 = 3");
Obviously, that more or less bypasses the object graph, but for a simple batch statement there's nothing wrong with doing that.

Salesforce.com: UNABLE_TO_LOCK_ROW, unable to obtain exclusive access to this record

In our production org, we have a system of uploading sales data into Salesforce using command line data loader. This data is loaded into a temporary object Temp. We have created a formula field (which combines three fields) to form a unique key. The purpose of the object is to reduce user efforts for creating the key manually.
There is an after insert trigger on Temp which calls an asynchronous method which upserts the data to another object SalesData using the key. The insert/update trigger on SalesData checks the various fields and creates/updates the records in another object SalesRecords. After the insertion/updation is complete, all the records in temp object Temp are deleted. The SalesRecords object does not have any trigger on it and is a child of another object Sales. The Sales object has some rollup fields which are summing up fields from SalesRecords object.
Lately, we are getting the below error for some of the records which are updated.
UNABLE_TO_LOCK_ROW, unable to obtain exclusive access to this record
Please provide some pointers to resolve the issue
this could either be caused by conflicting DML operations in the various trigger execution or some recursive trigger execution. i would assume that the async executions cause multiple subsequent updates on the same records, probably on the SalesRecords object. I would recommend to try to simplify the process to avoid too many related trigger executions.
I'm a little surprised you were able to get this to work in the first place. After triggers should be used with caution and only when before triggers can't be. One reason for this is that you don't need to perform additional DML to make changes to records, since in before triggers you simply change the values and the insert/update commit happens automatically. But recursive trigger firings is the main problem with after triggers.
One quick way to avoid trigger re-entry is to use a public static Boolean in a class that states whether you're already in this trigger from the same thread of execution.
Something like:
public static Boolean isExecuting = false;
Once set to true, any trigger code that is a re-fire can be avoided with:
if(Class.isExecuting == false)
{
Class.isExecuting = true;
// Perform trigger logic
// ...
}
Additionally, since the order of trigger execution cannot be determined up front, you might be seeing an issue with deletions or other data changes that depend on other parts of your flow to finish first.
Also, without knowing the details of your custom unique 3-part key, I'd wonder if there's a problem there too such as whether it's truly unique or not. Case insensitivity is a common mistake and it's the reason there are 15 AND 18 character Ids in Salesforce. For example, when people export to Excel (a case-insensitive environment) and do VLOOKUPs, they would occasionally find the wrong record. The 3-digit calculated suffix was added to disambiguate for case-insensitive environments.
Googling for this same error lead me to this post:
http://boards.developerforce.com/t5/General-Development/Unable-to-obtain-exclusive-access-to-this-record/td-p/345319
Which points out some common causes for this to happen:
Sharing Rules are being calculated.
A picklist value has been replaced and replacement is in progress.
A custom index creation/removal is in progress.
Most unlikely one - someone else is already editing the same record that you are trying to access at the same time.
Posting here in case somebody else needs it.
I got this error multiple times today. Turned out one of our vendors was updating their installed package during that time in the same org. All kinds of things were going wrong also - some object validation exceptions were being thrown on DMLs, without any error message content.
Resolution
The error is shown when a field update such as a roll-up summary field is being attempted on a parent object that already had a field update to cause the roll-up summary field to calculate. This could also occur if a trigger or another apex job running on the master object and it also attempting to do an update.
You can either reduce the batch size and try again or create separate smaller files to be imported if this issue occurs.

How to safely increment a counter in Entity Framework

Let's say I have a table that tracks the number of times a file was downloaded, and I expose that table to my code via EF. When the file is downloaded I want to update the count by one. At first, I wrote something like this:
var fileRecord = (from r in context.Files where r.FileId == 3 select r).Single();
fileRecord.Count++;
context.SaveChanges();
But then when I examined the actual SQL that is generated by these statements I noticed that the incrementing isn't happening on the DB side but instead in my memory. So my program reads the value of the counter in the database (say 2003), performs the calculation (new value is 2004) and then explicitly updates the row with the new Count value of 2004. Clearly this isn't safe from a concurrency perspective.
I was hoping the query would end up looking instead like:
UPDATE Files SET Count = Count + 1 WHERE FileId=3
Can anyone suggest how I might accomplish this? I'd prefer not to lock the row before the read and then unlock after the update because I'm afraid of blocking reads by other users (unless there is someway to lock a row only for writes but not block reads).
I also looked at doing a Entity SQL command but it appears Entity SQL doesn't support updates.
Thanks
You're certainly welcome to call a stored procedure with EF. Write a sproc with the SQL you show then create a function import in your EF model mapped to said sproc.
You will need to do some locking in order to get this to work. But you can minimise the amount of locking.
When you read the count and you want to update it, you must lock it, this can be done by placing the read and the update inside a transaction scope. This will protect you from race conditions.
When you read the value and you just want to read it, you can do this with a transaction isolation level of ReadUncommited, this read will then not be locked by the read/write lock above.