Geeks With Blogs

News
Elton Stoneman (@EltonStoneman) IT Consultant, integration specialist, @Microsoft MVP and @Pluralsight author.

Sometimes you need to switch on behaviour in your code for short period that you want to ensure gets switched back off again afterwards. I had this recently with an app we were performance tuning.

We found a hotspot with our EF code where we were adding a batch of entities to a collection. We were adding a few dozen new entities, with some new nested entities of their own, and it was running slowly, compared to the rest of the stack.

There are two flags you can switch off in DbContext.Configuration in this scenario – AutoDetectChangesEnabled and ValidateOnSaveEnabled, which gave us a good performance boost and got rid of the hotspot. I added methods to our repository to let us switch those flags on and off, named to make it clear why we were using them:

public interface IRepository 
{ 
     void EnableFastInserts(); 
     void DiableFastInserts(); 
}

public class Repository : IRepository 
{ 

     protected DbContext Context { get; set; }

     public void EnableFastInserts() 
     { 
          Context.Configuration.AutoDetectChangesEnabled = false; 
          Context.Configuration.ValidateOnSaveEnabled = false; 
     }

     public void DisableFastInserts() 
     { 
          Context.Configuration.AutoDetectChangesEnabled = true; 
          Context.Configuration.ValidateOnSaveEnabled = true; 
     } 
}

For the duration of the code, it’s fine to have those settings disabled – we’re only adding new objects, so we don’t need to track changes to existing  object state, and we’re not saving changes at that point so we don’t need to enable validation.

But it’s dangerous behaviour, and we certainly want the settings enabled again after we’ve finished building up our collection, and before we save changes. The implementation on IRepository works fine, but it doesn’t tell you the switch is dangerous and should only be used for a limited scope.

There’s nothing to stop someone enabling the fast insert switch and leaving it on, thinking it’s a permanent performance boost. Or switching the switch in a try block but forgetting to switch it off in a finally, so you’d only see strange behaviour in some circumstances.

It’s better to make it obvious that the switch belongs in a fixed scope, by isolating it in a scope class which implements IDisposable:

public class FastInsertScope : IDisposable 
{ 
    private readonly ISupportFastInserts _supporter;

    public FastInsertScope(IRepository repository) 
    { 
        _supporter = repository as ISupportFastInserts; 
        if (_supporter != null) 
        { 
            _supporter.EnableFastInserts(); 
        } 
    }

    public void Dispose() 
    { 
        if (_supporter != null) 
        { 
            _supporter.DisableFastInserts(); 
        } 
    } 
}

Then you can make the switch behaviour internal to the infrastructure assembly where your repository is, so I’ve split it out into an secondary ISupportFastInserts interface:

public interface IRepository 
{ 
}

internal interface ISupportFastInserts 
{ 
    void EnableFastInserts(); 
    void DisableFastInserts(); 
}

public class Repository : IRepository, ISupportFastInserts 
{ 
    protected DbContext Context { get; set; }

    void ISupportFastInserts.EnableFastInserts() 
    { 
        Context.Configuration.AutoDetectChangesEnabled = false; 
        Context.Configuration.ValidateOnSaveEnabled = false; 
    }

    void ISupportFastInserts.DisableFastInserts() 
    { 
        Context.Configuration.AutoDetectChangesEnabled = true; 
        Context.Configuration.ValidateOnSaveEnabled = true; 
    }
}

The  FastInsertScope is public, so that’s the only way you can enable the switch, and then it’s clear in your code what you’re doing:

using (new FastInsertScope(repository))
{ 
     //dangerous 
} 
//not dangerous

The switch will get turned off whenever the object goes out of scope, whether there’s an exception or not, and it’s obvious from the usage that the behaviour should be limited to blocks where it’s explicitly needed. Making the actual implementation internal means you can’t turn the switch on or off explicitly, you have to go through the scope, so you can’t explicitly enable the behaviour and then neglect to disable it.

Not the intended purpose of IDisposable, but a very useful pattern.

Posted on Friday, November 15, 2013 1:01 PM Code Snippet , .NET 4.0 , EF | Back to top


Comments on this post: Using IDisposable to scope dangerous behaviour

# re: Using IDisposable to scope dangerous behaviour
Requesting Gravatar...
AMAZING POST!
Left by Hans-Peter Gummihals on Nov 16, 2013 4:44 PM

# re: Using IDisposable to scope dangerous behaviour
Requesting Gravatar...
Agreed.
Small sidenote: if the switch is really dangerous and should be reverted anyway, I'd prefer to modify API to state it explicitly.

E.g. if EnableFastInserts() will return IDisposable, there will be no easy way to ignore it (assuming you have static analysis turned on).
Also it'll be fine to rename the method to something alike BeginFastInserts(), to highlight it will end somedays:)
Left by Sinix on Nov 18, 2013 12:47 PM

# re: Using IDisposable to scope dangerous behaviour
Requesting Gravatar...
Just wondering what would stop someone to call the method on the repository? Since its public.
Wouldnt it be better design if your class FastInsertScope inherits Repository and uses the Context to set it properly

public class FastInsertScope : Repository, IDisposable

Left by Taswar Bhatti on Nov 18, 2013 5:49 PM

# re: Using IDisposable to scope dangerous behaviour
Requesting Gravatar...
I thought this was a pretty awesome post. Simple and straight to the point. There are many cases I come across where I feel like leveraging a "using" block could take care of things for me, and you've put it together with a pretty solid example.

Thanks for sharing!
Left by Nick Cosentino on Nov 18, 2013 6:39 PM

# re: Using IDisposable to scope dangerous behaviour
Requesting Gravatar...
Thanks for the feedback guys.

@Taswar - you're right, the repository should implement the interface explicitly, so you can't access the methods. I've updated the code to show that (it wasn't an issue for our codebase as we always work against the IRepository interfaces).

Have to say I don't like the idea of a scope inheriting from a repository - they have fundamentally different meanings...
Left by Elton on Nov 19, 2013 12:59 PM

# re: Using IDisposable to scope dangerous behaviour
Requesting Gravatar...
Is there a danger that an old repository object going out of scope might 'reset the switch' for other, valid repository objects? Is that switch shared between all repository instances?
Left by Chris on Nov 22, 2013 9:02 AM

# re: Using IDisposable to scope dangerous behaviour
Requesting Gravatar...
amazing post! this one will surely help a lot of people including me, you dont see this kind of information every day.
Left by isabel crest on Nov 29, 2013 5:34 PM

Your comment:
 (will show your gravatar)


Copyright © Elton Stoneman | Powered by: GeeksWithBlogs.net | Join free