Delegates as callback functions in csharp

callback functions in c#

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.

Tags: