2

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?

1

2 Answers 2

2

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.

    1

    I would guess you have a reason for using WebForms rather than MVC but my first tip would be this: If you possibly can, use ASP.Net MVC.

    Right, Got that off my chest, lets continue.

    You are right to suspect your architecture, having your entity store and then a second way of directly accessing your database when using Entity Framework is a big no-no. The simple rule to consider is this: Only one way to and from the database.

    Even if you're stuck with a tired technology like WebForms, you can still take a useful page out of the MVC book by using the MVC architecture. You're half way there with your Engine which sounds like it is doing most of the work of the Model layer. In fact it sounds to me like you want to go a little further and use a ViewModel type approach - a ViewModel behaves like a Model, but has extra things that make it easier to work with from the front end, so it effectively behaves as a DTO, but if there are any properties you don't want to be able to write on the front end or other view related logic it goes in the ViewModel class.

    Then you make sure your entity layer can take a ViewModel object ( or probably you create an interface for your entities so if you had a BookRecord object you would create an IBookRecord interface that the BookRecord entity would implement and the BookRecordViewModel would also implement. If possible it is always nicer to pass around interfaces and it really facilitates DI, mocking and all that good stuff that helps build resilience in general.

    The only real difference this makes is that you will have to handle logic to manage updates in your engine - when you push down a BookRecordViewModel from the interface you will need to check whether it has an Id field set and if it does query and update, if not add it as a new record. You may well have that all implemented already in your Engine project, in which case this should present no problem at all.

    3
    • "I would guess you have a reason for using WebForms" Firstly he didn't ask about MVC or Webforms, secondly neither MVC is a golden hammer not WebForms is an obsolete or discarded technology. I don't understand why MVC enthusiasts are so keen on deemphasizing WebForms!CommentedMar 9, 2015 at 20:34
    • @SimpleFellow quite right. People are still doing fine with VBA too. I hear a lot of banks are still running software in COBOL. There's certainly no requirement to change simply because a technology is easier to use, cleaner and better designed, but that doesn't mean there is no compelling reason.
      – glenatron
      CommentedMar 10, 2015 at 9:31
    • don;t want to turn it into a useless heated discussion. the only thing i would say is if Cobol is still in use there must be a reason. Besides often the introduction of some of the "better technologies" are often driven by Commercial reasons. For instance new version of windows and Visual Studio.CommentedMar 10, 2015 at 10:46

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.