ASP.NET Design Patterns – Using Entity Framework in ASP.NET Projects

asp.netcdesign-patternsentity-frameworkwebforms

I'm building a website in ASP.NET (Web Forms) on top of an engine with business rules (which basically resides in a separate DLL), connected to a database mapped with Entity Framework (in a 3rd, separate project).

I designed the Engine first, which has an Entity Framework context, and then went on to work on the website, which presents various reports. I believe I made a terrible design mistake in that the website has its own context (which sounded normal at first).

I present this mockup of the engine and a report page's code behind:

Engine (in separate DLL):

public Engine
{
    DatabaseEntities _engineContext;

    public Engine()
    {
       // Connection string and procedure managed in DB layer
        _engineContext = DatabaseEntities.Connect();
    }

    public ChangeSomeEntity(SomeEntity someEntity, int newValue)
    {
        //Suppose there's some validation too, non trivial stuff
        SomeEntity.Value = newValue;
        _engineContext.SaveChanges();
    }
}

And report:

public partial class MyReport : Page
{
    Engine _engine;
    DatabaseEntities _webpageContext;

    public MyReport()
    {
        _engine = new Engine();
        _databaseContext = DatabaseEntities.Connect();
    }

    public void ChangeSomeEntityButton_Clicked(object sender, EventArgs e)
    {
        SomeEntity someEntity;
        //Wrong way:
        //Get the entity from the webpage context
        someEntity = _webpageContext.SomeEntities.Single(s => s.Id == SomeEntityId);
        //Send the entity from _webpageContext to the engine
        _engine.ChangeSomeEntity(someEntity, SomeEntityNewValue); // <- oops, conflict of context

        //Right(?) way:
        //Get the entity from the engine context
        someEntity = _engine.GetSomeEntity(SomeEntityId); //undefined above
        //Send the entity from the engine's context to the engine
        _engine.ChangeSomeEntity(someEntity, SomeEntityNewValue); // <- oops, conflict of context
     }
}

Because the webpage has its own context, giving the Engine an entity from a different context will cause an error. I happen to know not to do that, to only give the Engine entities from its own context. But this is a very error-prone design. I see the error of my ways now. I just don't know the right path.

I'm considering:

  • Creating the connection in the Engine and passing it off to the webpage. Always instantiate an Engine, make its context accessible from a property, sharing it. Possible problems: other conflicts? Slow? Concurrency issues if I want to expand to AJAX?
  • Creating the connection from the webpage and passing it off to the Engine (I believe that's dependency injection?)
  • Only talking through ID's. Creates redundancy, not always practical, sounds archaic. But at the same time, I already recuperate stuff from the page as ID's that I need to fetch anyways.

What would be best compromise here for safety, ease-of-use and understanding, stability, and speed?

Best Answer

I don't think you are a million miles away from where you need to be.

What you need to do really depends on the level of abstraction that you need and what you foresee the future of this application to be.

You definitely do not want to be using multiple contexts in a single http request. That will cause you a lot of pain.

As a bare minimum I would alter your Engine to have the context dependency injected in (through the constructor or setter). That will allow you to isolate the engine for testing purposes by injecting in a mocked context.

I would then consider creating a BasePage class that new ups an Engine Instance in the constructor (passing in a new Context). Each subsequent page would then inherit from the BasePage and would therefore have an Engine instance by default.

I would then work on the Engine class to provide all the functionality that your pages require (possibly sub-classing if it starts to get a bit monolithic). You then make sure that all interaction between the UI and the underlying database is channelled through the Engine. This way you could extend the Engine as required say for instance Engine.Ajax.Get... or something along those lines.

Like I say the level of abstraction you require may reveal other design considerations that you will need to make. You may wish to implement a repository and unit of work pattern on top of the context (technically an EF context already gives you this but if you are not sure about sticking with EF or know that you will need to introduce alternative data access options then you will need to consider it).

If you are injecting dependencies and those dependencies are 'volatile' then you may look to use an Inversion of Control Container such as AutoFac or Windsor.