3

Apparently (and quite possibly) there's a flaw in my current UnitOfWork implementation, because I have connection errors when doing many calls at once.

Exception:

The underlying provider failed on Open.

Inner Exception:

The connection was not closed. The connection's current state is connecting.

This results in a HTTP 500 response on the client side.

UnitOfWork implementation

public class ScopedUnitOfWork : IUnitOfWork
{
    public Entities Context { get; set; }
    public UnitOfWorkState State { get; set; }

    public ScopedUnitOfWork(IEnvironmentInformationProvider environmentInformationProvider)
    {
        this.Context = new Entities(environmentInformationProvider.ConnectionString);
        this.State = UnitOfWorkState.Initialized;
    }

    public UowScope GetScope()
    {
        this.State = UnitOfWorkState.Working;

        return new UowScope(this);
    }

    public SaveResult Save()
    {
        if (this.State != UnitOfWorkState.Working)
            throw new InvalidOperationException("Not allowed to save out of Scope. Request an UowScope instance by calling method GetScope().");

        this.Context.SaveChanges();

        this.State = UnitOfWorkState.Finished;

        return new SaveResult(ResultCodes.Ok);
    }
}

Working on a single UowScope would solve the issue but that's not possible given the current circumstance, because each request is completely separate. De facto each request IS using an UoWScope, but apparently it goes wrong when the UoW receives many calls at once.

The UoW is injected through Unity IoC, so I suppose it's a singleton in effect.

The question

Is there a way to adapt the UoW so that separate high-frequency requests are not an issue?

Preferably I'd solve this server side, not client side, any tips? Thanks!

Disclaimer

I don't claim I fully understand UoW, so my implementation may need improvement, be gentle :). Any improvements on that are certainly welcome!

UPDATE

I -know- the EF Context is an UoW, I use mine at Domain level to enable transactional processing of data that is functionality related. And it's also by customer demand, I have no choice.

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Spikee
  • 3,967
  • 7
  • 35
  • 68
  • 4
    [Entity Framework's DbContext already implements UoW](http://stackoverflow.com/a/29972845/14357). What you have here is the UoUoW antipattern, – spender Oct 22 '15 at 10:09
  • 1
    Echo what spender has said... generally speaking all you really need is a using(DBContext ctx = new DbContext()) statement around each unit of work, with perhaps a manual ctx.Connection.Open() and ctx.Connection.Close(). – Paul Zahra Oct 22 '15 at 10:11
  • @Spikee this should interest you greatly... http://www.asp.net/mvc/overview/older-versions/getting-started-with-ef-5-using-mvc-4/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application – Paul Zahra Oct 22 '15 at 10:21
  • Are you saying that the UoW is a singleton which in effect makes the context also a singleton? That absolutely will not work. – DavidG Oct 22 '15 at 10:24
  • Read the answer by Chris Pratt http://stackoverflow.com/questions/29972433/uow-and-repository-pattern-in-ef5/29972845#29972845 Read the answer by Phil Soady http://stackoverflow.com/questions/17944837/ef-entity-framework-usage-of-using-statement – Paul Zahra Oct 22 '15 at 10:30
  • All aboard the "Entity Framework already is a UoW/Repository" bandwagon! Seriously, that's been chewed out long ago. There _are_ reasons to wrap EF operations in your own UoW/Repository patterns, easiest case being `Include()` reuse or generic CRUD of items sharing a common interface or base entity type. Not saying this is a good implementation or good question, but you really don't have to all chime in to blame OP for wanting to experiment. – CodeCaster Oct 22 '15 at 10:35
  • @CodeCaster Hardly bashing him, more informing him... read Phil Soady's answer (which I already linked to) and the link he gives is excellent for a Tom Dykstra article on creating a Generic Repository / UoW pattern... http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application – Paul Zahra Oct 22 '15 at 10:40
  • @spender: I know EF implements UoW, don't just assume please. – Spikee Oct 22 '15 at 11:05
  • @DavidG: I mean the UoW becomes a singleton through Unity, because it's kept-alive by the IoC mechanism. Maybe there's a better term for it. – Spikee Oct 22 '15 at 11:20
  • @Spikee I get that. So your UoW object stays alive for the entire duration of the app, that means the context must also do that. So you have multiple threads calling the context at the same time which will throw exceptions. – DavidG Oct 22 '15 at 11:24
  • @DavidG: Correct. Do I understand you correctly that I should keep the Context out of it? – Spikee Oct 22 '15 at 11:30
  • Meh, Abstraction over abstraction ? – Fabjan Oct 22 '15 at 11:48
  • What's to stop me calling `GetScope` several times for the same `ScopedUnitOfWork`? And then a call to `Save` would only succeed for *one* of the scopes, because of the status check... – Gary McGill Oct 22 '15 at 11:52

1 Answers1

3

The issue you have is that the unit of work object is effectively a singleton as your IoC framework is keeping it around for the duration of your application. This means that your context is also being kept as a singleton as it's inside the UoW. So you will almost certainly get multiple concurrent calls to your context which will throw exceptions.

However, I think you are misusing the concept of what a UoW supposed to do. A UoW is there to provide a container for a group of transactions. For example lets say you have an eCommerce platform. When you create an order, you will insert a row in the orders table, then as part of the same transaction you will also insert rows into the order items table, update a users loyalty points etc. So you should do all this inside a single unit of work, commit it, then destroy it. Let the IoC framework (Unity in this case) create your unit of work for each session.

DavidG
  • 113,891
  • 12
  • 217
  • 223