So why this post? because I messed up around 3000 data records in production and caused most of the fields to update to null or default values.
It was ugly.
Fortunately we managed to recover from the damage but why did that happen.
So consider this scenario. You have a table which contains posts and each post has a HeaderPicture stored as a blob in it. Now what happens if you are receiving an update for let’s say 12000 posts in your database and you need to do an update using Entityframework.
First idea would be to load the records with the given IDs all together and after the database read you do the update on each entity because each entity gonna need its own update so you cannot have a sql statement which says set this field to this value for records with an specific condition.
Now what happens, at the beginning everything is fine the update is happening you are not even noticing it and life is good.
BUT it gets ugly when the posts pile up with images on them. Now I dare you to tell EntityFramework to load the posts for you to update. Well in our case we did not need to update the images so loading them did not make any sense. We had to tell
EntityFramework to just load some of the fields which we needed to update. There are some ways to do it but the are also some ways NOT to do it please. We tried these NOT To Dos which I will describe. First we get to the valid one.
The following code will load the posts based on the given IDs and will select only the needed fields for the update but because of the
new keyword in the
Select , EntityFramework will lose track of the entities and with the
AttachRange you will give the track back only to the selected fields.
Notice that the first time when you do this and if you are running UnitTests you may get an exception which says
Cannot track the entity because it is already being tracked
That exception is completely normal and you should reconsider your whole testing structure instead of changing this code, what I did not do 😔
So lesson learned. When this error happens only in your tests it means that you are loading the same entity with the same
DbContext which you are passing to the method where you are attaching the same entity to be tracked. If you really need to load that entity in your test and then do the test on it please use different
DbContexts which means different transactions.
But what happens when you freak out and ignore everything I described.
This happens 👇
The above code will cause your boss to get mad at you, why??
First you detach and tell entity framework that don’t follow it. Then you change it completely and say now here is the new entity and mark it as changed. What EntityFramework does is that it goes to database and check for this “Modified” entity and will replace it with the whole existing one. Now if you remember you have not loaded all the columns which makes the entity you have, to be incomplete and now EntityFramework REPLACES this abomination with the correct one and then you gonna get a heart attack.
Notice: With the first approach there is also something for you to take into account. If you are on saving the entities, checking for the Modified ones and are setting a
ModifiedOn then take care because since you are not loading the whole entity you may assume if you just always set one of the fields to a default value it will be fine, but it won’t. For example your Post has a property let’s say “State” and you are not loading it so the post object which you are getting has a default value for State. Now if you set the state even to an unchanged value than the one is stored in the database, EntityFramework will consider this as modified and will cause a minor mess which won’t get you a heart attack but it is as bad relatively.
I hope sharing this, helps a lot of developers.
Please do not hesitate to ask questions.
Have fun coding and be safe😉