To update many records in entity framework, it is good to use the .Attach
method of the db context instead of looping through every record and fetching the data through EF and updating it.
So, I sat down and wrote the attaching part of the code to update the records.
But, the .Attach
method isn’t efficient when you are not turning off the entity tracker on the data context.
So, before updating the records I’ve to turn off AutoDetectChangesEnabled
property. Once completed, we will turn on AutoDetectChangesEnabled
.
Here’s the code, anyway.
public static void UpdateTodos(IDataContext context) { try { ((DbContext)context).Configuration.AutoDetectChangesEnabled = false; var items = context.TodoItems; foreach (var item in items) { context.TodoItems.Attach(item); item.TodoText = “updating with dummy content”; item.ModifiedDate = DateTime.Now; } } finally { ((DbContext)context).ChangeTracker.DetectChanges(); } } public static void UpdateDetails(IDataContext context) { try { ((DbContext)context).Configuration.AutoDetectChangesEnabled = false; var items = context.Details; foreach (var item in items) { context.Details.Attach(item); item.Notes = item.Notes + Convert.ToString(item.ID * 10); item.ModifiedDate = DateTime.Now; } } finally { ((DbContext)context).ChangeTracker.DetectChanges(); } }
We see the calls to change tracking are being turned off and on in both the methods.
To make the code look less lines of code, we could move the toggle part of detect changes to separate methods for turning on and off but that still leaves with duplicated method calls.
What if we could wrap the change tracking thing to a method, which will also execute our method after detect we turn detect changes off and after executing our method it turns off change detection?
This is something like the callback functions in javascript.
So, in JavaScript here’s how we write callback functions for the above scenario.
var config = { //assuming this as DataContext configuration detectChanges: true }; function SurroundWithDetectChanges (callback) { try { config.detectChanges = false; callback(); } catch () { } finally { config.detectChanges = true; } } SurroundWithDetectChanges(function() { //code to update the table records goes here. });
Similarly, we’ve to leverage the same thing in c# side with the callback functions.
Delegates as callback functions
There are several advantages of delegates. One of them is their callback ability.
Let’s write an extension method which turns the AutoDetectChangesEnabled
to off/on as needed and also takes a callback (similar to what we’ve done in JavaScript).
public static class Ext { public static void FastUpdate(IDataContext context, Action callBack) { try { ((DbContext)context).Configuration.AutoDetectChangesEnabled = false; callBack(context); } finally { ((DbContext)context).Configuration.AutoDetectChangesEnabled = true; ((DbContext)context).ChangeTracker.DetectChanges(); } } }
So, the above method turns OFF the detect changes mechanism and calls our callback method we’ll supply to this method. After completing the callback, then we’ll turn ON the detect changes in the finally
block of the FastUpdate
method.
Here’s how to call the above extension method.
Ext.FastUpdate(context, (ctx) => { var items = ctx.TodoItems; foreach (var item in items) { ctx.TodoItems.Attach(item); item.TodoText = “with auto detect changes”; } });
The second parameter to the FastUpdate
method is our callback method. To make it clear, let me rewrite the above call to FastUpdate
method.
//call the extension method with callback function Ext.FastUpdate(context, (ctx) => UpdateTodoItems(ctx)) //callback function private static void UpdateTodoItems(IDataContext ctx) { var items = ctx.TodoItems; foreach (var item in items) { ctx.TodoItems.Attach(item); item.TodoText = “with auto detect changes”; } }
This callback(UpdateTodoItems
) method, invoked from the FastUpdate
method return nothing. It just updates the items on the data context. This looks good but if we want to return something from the callback method, then we‘d have to go for Func
delegate.
Action delegate is for void callbacks, Func delegate is for returning something to the caller.
Let’s write a callback function that returns the result.
Func delegates to return values
Let me take a simple example to return data from the method. Say, we’ve to compute the sum of even numbers.
Here’s our sum of even numbers method.
public static int SumOfEvens(IEnumerable listOfItems) { return listOfItems.Where(a => a % 2 == 0).Sum(); }
This method takes a list of items as input and returns the even numbers sum.
Here’s the delegate to call the above method.
Func<IEnumerable, int> sum = new Func<IEnumerable, int>(SumOfEvens);
The sum
variable above is our delegate to which we must supply the list of items to get the sum of evens in the list of values.
var listItems = Enumerable.Range(1, 10); Console.WriteLine(sum(listItems));
That should print us the result.
If we were to log the information before calculating the sum of even number we must tweak the delegate assignment here.
Here’s the Func
delegate declaration with logging information before and after executing the SumOfEvens
method.
Func<IEnumerable<int>, int> sum = new Func<IEnumerable<int>, int>(a => { Log($"-- About to calculate the even sum for {a.Count()} items ---“); var result = SumOfEvens(a); Log($”-- sum of the even numbers is: {result} ---“); return result; }); public void Log(string message) { Console.WriteLine(message); }
In the above code, we’ll log the number of items to print before calculating the sum. Once we get the sum, we’ll print the sum using Log
extension method. And at the end, we’ll just return the result.
Karthik is a passionate Full Stack developer working primarily on .NET Core, microservices, distributed systems, VUE and JavaScript. He also loves NBA basketball so you might find some NBA examples in his posts and he owns this blog.