0

I am attempting to encrypt some data where my table has half a million records. Inspired by the answer here - Fastest Way of Inserting in Entity Framework - I attempted to chunk up my update by calling a recursive function:

IDBContext context;
try{
    context = new MyDbContext();
    context.Configuration.AutoDetectChangesEnabled = false;
    EncryptFields(context, 500, (count / 500));
}
finally{
   if (context != null)
   {
       context.Dispose();
    }
}

Recursive function here:

private static void EncryptFields(IDBContext context, int batchSize, int maxRetries)
    {
        Debug.WriteLine(maxRetries.ToString());
        if (maxRetries == 0)
        {
            return;
        }
        var phones = context.Phones
                            .Where(p => !(p.Number == null 
                                       || p.Number.Trim() == String.Empty))
                            .Take(batchSize)
                            .ToList();

        if (phones.Count() > 0)
        {
            foreach (var phone in phones)
            {
                phone.Enc_Number = Encrypt(phone.Number);
            }
            context.SaveChanges();
            context.Dispose();
            context = new MyDBContext();
            context.Configuration.AutoDetectChangesEnabled = false;

            EncryptFields(context, batchSize, --maxRetries);
        }
    }

I started off with a maxRetry value of 1270, but when it dropped to 360, I got a StackOverflow exception on the line:

var phones = context.Phones...

Given I dispose of the context and re-create it after updating every 500 records, I'm unsure why I am getting this exception.

Community
  • 1
  • 1
Mister Epic
  • 16,295
  • 13
  • 76
  • 147
  • 3
    This exception is not caused by EF. It is caused by your recursive function calling itself too deeply. Instead of recursion use a loop. – usr Apr 16 '15 at 15:21
  • Would the downvoter care to tell me how I should have asked a better question? – Mister Epic Apr 16 '15 at 15:44

1 Answers1

0

The depth of the calls to EncryptFields in the stack over repeated runs looks something like this (where, for example, the first call to it is at depth 10):

10, 11, 12, ...

The depth of the calls to your EF query then look something like this:

11, 12, 13, ...

Suppose the maximum depth you're able to go to is N. Then when your call to EncryptFields is at depth N, it will try to call the EF query at depth N + 1. That is why you are getting the StackOverflowException inside of the EF query instead of directly in your recursive method EncryptFields.

This isn't an EF problem; the exception just happens to manifest itself there because your EF query leads your EncryptFields method in depth. The way to fix this is simply to eliminate the recursion.

private static void EncryptFields(IDBContext context, int batchSize, int maxRetries)
{
    while (true)
    {
        Debug.WriteLine(maxRetries.ToString());
        if (maxRetries == 0)
        {
            return;
        }
        var phones = context.Phones
            .Where(p => !(p.Number == null || p.Number.Trim() == ""))
            .Take(batchSize)
            .ToList();

        if (phones.Count() > 0)
        {
            foreach (var phone in phones)
            {
                phone.Enc_Number = Encrypt(phone.Number);
            }
            context.SaveChanges();
            context.Dispose();
            context = new MyDBContext();
            context.Configuration.AutoDetectChangesEnabled = false;

            maxRetries--;
            continue;
        }
        else
        {
            break;
        }
    }
}
Timothy Shields
  • 75,459
  • 18
  • 120
  • 173