Question

I'm currently working on a system with a very complex communication between its tiers, there are a few classes reponsible for querying the database, each one of these classes have a DbContext instance, these classes are invoked by one class responsible for committing data to the database and one class responsible for querying read only data from the database. What is happening now is that the "writer" class must pass this Context for every call within it, let's say:

public class Writer {
    private DbContextClass _context;
    public DbContextClass Context
    {
        get
        {
            if (_context == null)
            {
                _context = new DbContextClass();
            }
            return _context;
        }
        set
        {
            _context = value;
        }
    }

    public void WriteToDatabase() {
        MethodCallA();
        MethodCallB();
        MethodCallC();
        Context.SaveChanges();
    }

    public void MethodCallA() {
        var entityA = (from c in Context.EntitiesA where c.id == 1).FirstOrDefault();
        entityA.FieldA = "changed";
        Context.SaveChanges();
    }

    public void MethodCallB() {
        //very long task here, probably creating a pdf and uploading to another place
        var entityB = new EntitiesB();
        entityB.FieldB = "new";
        Context.AddToEntitiesB(entityB);
    }

    public void MethodCallC() {
        var changedEntityA = (from c in Context.EntitiesA where c.id == 1).FirstOrDefault();
        var newEntityB = (from c in Context.EntitiesB where c.FieldB == "new").FirstOrDefault();
        var newEntityC = new EntitiesC();
        newEntityC.FieldC = newEntityB.FieldB + changedEntityA.FieldA;
        Context.AddToEntitiesC(newEntityC);
    }
}

So the reason why they share the same context over methods is to be able to do what I did on MethodCallC(), they wanted to be able to get the entities inserted in the context before sending them to the Database.

I want to know if it's possible to refactor theses methods, so every method have its own context, and still be capable of achieving the same results, in other words, still be able to read the data that has not been sent to the database yet.

My first thought was to remove the "Context" property, and inside each method create a new context, the only problem is that these operations might or might not be atomic, that is, there are methods where they simply update data into the database without worrying about what else is happening thus calling SaveChanges, and there are methods that depend upon the changes made to the context, so I thought this should call for a TransactionScope inside the "WriteToDatabase" method, surrounding all other methods, the reasoning behind this is that I wanted to make sure that every call to the database is taking the least time possible, since today I can't really tell when will the database connection close, right?

In short, will a TransactionScope guarantee that every new DbContext created within its scope read the data not yet committed into the database (before calling SaveChanges) so other methods can create their own context and do whatever they need closing the connection as soon as possible? And another question, since the connection will open inside the transaction at some point, will it be kept open until the transaction ends? Or will it close as soon as the query is executed?

My biggest challenge right is to be able to make sure that this code takes the least time communicating with the database as possible.

Was it helpful?

Solution

I think you use the wrong patters to your problem. You should take a look at Repository and UnitOfWork patterns and also to use Dependency Injection to make sure the same DbContext is used in all the components during one unit of work. This is a starting point.

You will have to understand the patterns and adjust them to your application.

OTHER TIPS

It sounds like a CQRS type set-up - I guess without denormalized read model etc.

I would look at Dependency Injection. It can control lifetime scope etc. Your code sounds really hard to maintain - passing around the context is never a good sign.

This article talks about session management which is equivalent to EF Context

You may want to look at Command Pattern and Handler using DI. Something like this article

You can use Decorator Pattern to mange transaction scope of command.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top