HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Enabling discard pending changes on DbContext

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
discardpendingchangesdbcontextenabling

Problem

This code review request is tightly coupled with this SO question, this is the solution I implemented to solve the problem being asked about there.

All my ViewModels get constructor-injected with a Model that itself gets constructor-injected with a DbContext-derived class, and that works well in all cases, except when the View has a command to allow the user to discard pending changes, in which case the DbContext should be disposed and then reinstantiated.

Obviously since the DbContext is created by an IoC container it would be a very bad idea to just dispose and reinstantiate the context, so I came up with a solution involving a factory.

public interface IContextFactory where TContext : DbContext
{
    TContext Create();
}

public interface IDiscardModelChanges
{
    void DiscardChanges();
}


Now IDiscardModelChanges is implemented by the model, so to facilitate this I've created a base class that will ensure I have a context factory at hand:

public abstract class DiscardableModelBase : IDiscardModelChanges, IDisposable
    where TContext : DbContext
{
    private readonly IContextFactory _factory;

    protected TContext Context { get; private set; }

    protected DiscardableModelBase(IContextFactory factory)
    {
        _factory = factory;
        Context = _factory.Create();
    }

    public virtual void DiscardChanges()
    {
        Context.Dispose();
        Context = _factory.Create();
    }

    public void Dispose()
    {
        Context.Dispose();
    }
}


The model that needs to control its DbContext then derives from this class:

public class SomeModel : DiscardableModelBase, ISomeModel
{
    public SomeModel(IContextFactory factory) 
        : base(factory) 
    { }

    /* methods that act upon protected Context property */
}


And then the ViewModel that needs to discard pending changes can do it like this (given a private readonly ISomeModel _model):

```
var model = _model as IDiscardModelChanges;
if (mode

Solution

As stated by previous comments, there is no need to throw away the context.
But since this is a code review, and not a concept review I'll leave that part aside.

All your TContext generics are to be inheriting from DbContext, but nowhere in any class do you use any of DbContext's properties or methods. You are only using the .Dispose method, which comes from the IDisposable interface. I've refactored your code to have this result:

public interface IContextFactory where TContext : IDisposable
{
    TContext Create();
}

public interface IDiscardModelChanges
{
    void DiscardChanges();
}

public abstract class DiscardableModelBase : IDiscardModelChanges, IDisposable
where TContext : IDisposable
{
    private readonly IContextFactory _factory;

    protected TContext Context { get; private set; }

    protected DiscardableModelBase(IContextFactory factory)
    {
        _factory = factory;
        Context = _factory.Create();
    }

    public virtual void DiscardChanges()
    {
        Context.Dispose();
        Context = _factory.Create();
    }

    public void Dispose()
    {
        Context.Dispose();
    }
}


The changes might seem small, but the impact is big. In your example, you are going to pull in Entity Framework everywhere you are using this application, which isn't needed at all.
You are not doing anything entity framework related, you are doing something with disposable objects. This could be a StreamWriter, etc...

PS: I know this post is old. But people should really be aware of the issue with this code.

Code Snippets

public interface IContextFactory<out TContext> where TContext : IDisposable
{
    TContext Create();
}

public interface IDiscardModelChanges
{
    void DiscardChanges();
}

public abstract class DiscardableModelBase<TContext> : IDiscardModelChanges, IDisposable
where TContext : IDisposable
{
    private readonly IContextFactory<TContext> _factory;

    protected TContext Context { get; private set; }

    protected DiscardableModelBase(IContextFactory<TContext> factory)
    {
        _factory = factory;
        Context = _factory.Create();
    }

    public virtual void DiscardChanges()
    {
        Context.Dispose();
        Context = _factory.Create();
    }

    public void Dispose()
    {
        Context.Dispose();
    }
}

Context

StackExchange Code Review Q#32156, answer score: 5

Revisions (0)

No revisions yet.