Writing web applications has gotten vastly easier using ASP.NET MVC4 and Entity Framework, especially if you’re counting lines of code in your application layer as an indicator. I’m a huge fan of EntityFramework 5 CodeFirst as a mechanism for creating and interacting with your database for line of business apps. With that said, you should STILL design and normalize your database using good practices. EF code first simply allows you to specify that database design using C# code in terms of the domain entities instead of SQL scripts.
If you follow the Microsoft script for ASP.NET MVC4 using Entity Framework for your models, you’ll end up with your database interaction logic crowding your Controllers and Views.
The trick to keeping your application lightweight and easy to change is in abstracting the database away into Repositories. This lets you have your Controllers and Views talking in terms of simpler Domain objects while using an entirely abstract mechanism for handling the CRUD (Create-Retrieve-Update-Delete) operations. Even Domain logic should be factored out whenever possible to keep the Controllers and views as void of decision logic as possible.
I’m still on the fence about the value of having a separating Unit-Of-Work and Repository when dealing with such short lived contexts as we have in MVC based Web applications. I use both extensively in client side desktop applications where the repositories may live quite a while and have multiple operations performed against them before they go away.
Implementation of this one can range from simple to complex depending on how much you want or need to abstract.
Essentially A repository enables the following kind of conversation in your controller
- Get me an instance of a Repository class suitable for this controller
- Ask that Repository to get you a collection of simple domain objects(s) that your app can work with. ie: Repository.GetAllCustomers().
- Repository says “Here they are” as a collection of friendly POCOs, complete with navigation props etc.
- Your controller sends this off to the View.
- The View does stuff to these and says “Here, I’m done.”
- The Controller says “Repository, put these back where you got them from.” Or – “I created some new ones myself, Add these.”
The closer you can get to this simple level of conversation between your controllers, views and repositories, the easier it is to change your user interface as needs and requirements change.
In my ASP.NET MVC4 apps, I actually prefer NOT to even have ‘models’ in my web app. Various repository classes should be serving customized mini-models that represent only the specific business areas of interest. You want to manage the Cars owned by various Customers? Then even though you have lots of other things in your database, create a repository that properly models only what’s needed for the UI to handle this task.
Ultimately that’s it. The main goal is to have your Repository implement an interface that is simple enough to be easy to maintain yet rich enough to solve the functional needs of the controller and its Views. It should abstract away details of what sort of procedure is necessary to get/save db items and where they’re being saved. It is perfectly ok to have your repository implement things that you might think of as complex queries. It’s really “data or business layer in a box”.
Generally speaking here are the steps I take:
- Define an IEFClientsRepository interface with the methods you want. Ie: Add(), Update(), Find(), FindMany(), ComputeXYZ(), GetCustomersWithBalancesDue(), PhoneHome(), Save(), Discard(), whatever.
- Implement concrete MyEFClientsRepository : IEFClientsRepository and write all your methods to solve these problems. MyEFClientsRepository should mostly hold the kind of code you’d previously been putting in your Controller. For Entity Framework related repositories, this repository will hold an instance of your custom DbContext.
- Then, defer everything dbcontext related to custom methods that you add inside your custom DbContext class ie: ClientsDbContext. Don’t put those directly inside your Repository.
- So, for your ClientsDbContext that derives from DbContext, embellish that with all extra public methods you need for interacting specifically with the DbContext and managing the actual entity state/validation, etc., generating complex queries etc… ie: Actually does the “GetCustomersWithBalancesDue()” and returns that Linq IQueryable or the actual data collection.
- Again, your MyEFClientsRepository will hold an instance of your ClientsDbContext which can get created at the time your Repository is constructed and goes away with the Repository.
- The UnitOfWork pattern is essentially VERY SIMPLE to add. It is simply the container that holds/manages your DbContext and has Save/Discard methods. It’s not really necessary I think for ASP.Net MVC4 work because the contexts live for such a short time. UnitOfWork is very useful for Client side Desktop apps where you might create a repository that sticks around as long as some UI module lives. In this case, a New Unit of work is created each time you want to simulate a “transactional” step that might be discarded prior to saving. So, your window / control /whatever has a Repository instance. Any time you run some new command, you get a new Unit Of Work, do your job, then toss it.
A Repository is a storage/retrieval class implemented for some specific back end data store like EF, or XmlDb or whatever. So if your app can have optional ways of storing data, you’d implement a Repository concrete class for each storage type, but they’d all share the common IRepository interface you defined.
Notes: The most obvious reasons for creating custom IRepository interfaces are:
- They allow you to transparently implement more than one type of backend storage system,
- They allow testing your front end / middle layers without needing any backend storage system.
- They ensure that your front end is only loosely coupled with your database.
I still think you can Skip the UnitOfWork and just have the repository manage an instance and lifetime of your ClientsDbContext for MVC web apps because the Repository gets recreated every time the controller is instantiated.
Now, if you introduce MEF into this picture, you can have MEF actually manage the lifetime for you and have it shared and live beyond a single controller instance.
Final NOTE: I suggest creating an INTERFACE for your ClientsDbContext, ie: IClientsDbContext and having your Repository work against this. That allows you to test your app down to the Repository layer without needing an Actual DbContext. You can implement a cheap fake DbContext that has static data.
That’s all for now, but I have alot more I’ll post on this topic as time permits…