1. Install IoC Container - The 'IoC container' is a term for your Dependency Injection program of choice.
2. Create an interface (or abstract class, thats up to you, I generally use an interface) to represent the repository methods
3. Implement the repository in a concrete class, which is an actual implementation of the interface in a class. Your repository needs to implement IDisposable so your context can be disposed of after EVERY REQUEST.
Do not keep the repository around because we do not want to keep the Entity Framework context around after a request. We'll end up injecting a context into our repository so it's very important it's disposed at the end of the request.
4. Create a constructor in your controller with your dependencies to inject as an interface. Ex:
public class CustomerController { private ICustomerRepository _customerRepository; public CustomerController(ICustomerRepository customerRepository) { _customerRepository = customerRepository; } }
5. Tell your IoC container what to actually instantiate when asking for an ICustomerRepository in addition tell your container to Dispose() of your object when done. Unity for instance won't do this by default.
I recommend the Unity.MVC3 Nuget package to do this. It will install unity and will contain a HierarchicalLifetimeManager you can use to tell Unity to dispose of your object. This takes many more characters to describe rather than just doing it so don't worry : )
6. Define an IContext interface which contains IDbSet
public CustomerRepository(IContext context) { }
The flow that your application follows is:
Application starts upon request in IIS
Starting with global.asax
You register your container with MVC:
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
You then register your types to resolve (interfaces to concrete types) via calls like: container.RegisterType
This is optional though as your DI container may do this by convention and look for a classname without the "I" and not require explicit mapping.
Register your controllers with MVC. In the Unity.MVC package this is done via a custom class included in the nuget package: container.RegisterControllers();
This is all setup for you automatically when you install the nuget package.
Your route is processed, MVC determines you need controller CustomerController.
MVC sees you have a dependency resolver setup, so it allows Unity to create the instance of the controller. Unity sees the constructor arguments and also since you've defined a mapping from interface to concrete type, it knows to create an instance of CustomerRepository when it sees ICustomerRepository.
Your controller is disposed of and the dependencies are disposed of if they are configured to do so via the lifetime manager. You can have an injected dependency stay around for the lifetime of your app domain however we DO NOT want to keep the context around. One per request and that's it. This is a very important rule to remember. If your context is reused across requests you are going to see some weird behavior so please make sure you set breakpoints on your repository Dispose() method to ensure it is being called after every request.
MVC determines your controller needs to be called.
Note you can't inject anywhere. I can't just create a random class with a contructor and expect the dependency to be injected in it, the system you are working with (MVC in this case) has to support it and MVC has various points in a request lifecycle that you can request dependencies be injected.
Take a controller in one case. When a controller is created, MVC will check if you have registered a dependency resolver (ie your Dependency Injection framework of choice
Be very cautious where you are going to sprinkle DI code in your application so as not to get caught up in the service locator anti-pattern. Some containers allow you to define [Dependency] properties to do the injection. In general, steer clear. We want to create your dependency graph at the composition root which is essentially as early as possible. Think of how difficult it would be if someone deep down in a library called:
var someConcreteImplementaiton = container.Resolve();
It would be an nightmare trying to figure out what was injected and where. Some containers use convention over configuration meaning if you have a constructor defined as:
public class CustomerController { private ICustomerRepository _customerRepository; public CustomerController(ICustomerRepository customerRepository) { _customerRepository = customerRepository; } }
By naming convention the DI container will look for something named without the "I" - this it will look for a CustomerRepository
As defined by best DI practices stick to constructor injection when necessary. Mark Seeman has an excellent book: Dependency Injection in .Net where DI practices are covered.
Now notice as of this point I haven't made ANY mention of the Entity Framework. I'm not injecting a context object into the constructor. You could - but I feel like that is a brittle implementation to do something like:
public class CustomerController { private YourEntityContext _context; public CustomerController(YourEntityContext context) { _context = context; } }
You could do this and it would work fine. Your context would be disposed. However what does this give you?
How can you test your controller in a unit test? You would have to create a new context class and pass it in.
A context class has a specific implementation. Its not an abstract class.
Do I know exactly what it will do when I instantiate it in a test to pass it into a controller? Nope. If DbContext was an interface instead like IDbContext this still wouldn't work as this interface wouldn't know about our Entities.
So if I create a fake class
public class FakeContext : DbContext { }
This has the problem that what happens when DbContext initializes? What happens behind the scenes? Since it's not our code, we don't know exactly.
When you call Context.Customers.Where(o=>o.id==100) your context is initialized, it looks for a database connection, etc. So we don't want to inject a context class, plan and simple.
I would have to mock/fake several aspects of it's behavior and hope for the best so it's better to not inject this.
To really pound the point home, if I look at the metadata for DbContext I seeit clearly isn't just an abstract class. It has specific functionality that we aren't 100% sure what it does internally.
////// A DbContext instance represents a combination of the Unit Of Work and Repository patterns such that /// it can be used to query from a database and group together changes that will then be written /// back to the store as a unit. /// DbContext is conceptually similar to ObjectContext. /// /// /// ////// DbContext is usually used with a derived type that contains properties for /// the root entities of the model. These sets are automatically initialized when the /// instance of the derived class is created. This behavior can be modified by applying the ///attribute to either the entire derived context /// class, or to individual properties on the class. /// /// The Entity Data Model backing the context can be specified in several ways. When using the Code First /// approach, theproperties on the derived context are used to build a model /// by convention. The protected OnModelCreating method can be overridden to tweak this model. More /// control over the model used for the Model First approach can be obtained by creating a/// explicitly from a and passing this model to one of the DbContext constructors. /// /// When using the Database First or Model First approach the Entity Data Model can be created using the /// Entity Designer (or manually through creation of an EDMX file) and then this model can be specified using /// entity connection string or anobject. /// /// The connection to the database (including the name of the database) can be specified in several ways. /// If the parameterless DbContext constructor is called from a derived context, then the name of the derived context /// is used to find a connection string in the app.config or web.config file. If no connection string is found, then /// the name is passed to the DefaultConnectionFactory registered on theclass. The connection /// factory then uses the context name as the database name in a default connection string. (This default connection /// string points to .\SQLEXPRESS on the local machine unless a different DefaultConnectionFactory is registered.) /// /// Instead of using the derived context name, the connection/database name can also be specified explicitly by /// passing the name to one of the DbContext constructors that takes a string. The name can also be passed in /// the form "name=myname", in which case the name must be found in the config file or an exception will be thrown. /// /// Note that the connection found in the app.config or web.config file can be a normal database connection /// string (not a special Entity Framework connection string) in which case the DbContext will use Code First. /// However, if the connection found in the config file is a special Entity Framework connection string, then the /// DbContext will use Database/Model First and the model specified in the connection string will be used. /// /// An existing or explicitly created DbConnection can also be used instead of the database/connection name. /// /// Acan be applied to a class derived from DbContext to set the /// version of conventions used by the context when it creates a model. If no attribute is applied then the /// latest version of conventions will be used. /// /// [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", Justification = "Casing is intentional")] public class DbContext : IDisposable, IObjectContextAdapter { ////// Constructs a new context instance using conventions to create the name of the database to /// which a connection will be made. The by-convention name is the full name (namespace + class name) /// of the derived context class. /// See the class remarks for how this is used to create a connection. /// /// protected DbContext();
Now comes the slightly trickier part, although still fairly easy. We could inject a context into our repository, but that leaves us in the same boat I was describing above we don't want to be in.
So our
This comment has been removed by a blog administrator.
ReplyDeletehmm...i think the blog post got cut off at the end. I am eager to get the rest of this post. Also, is there anyway you can format your code snippets (comment as well) so that they render in your blog template a little better? It's all double-spaced right now.
ReplyDeleteI attended your session at TechEd, and asked you a question about patterns and architecture at the end, before you sat down for Russinovich's talk.
thanks!
--Thiago
Heya Thiago, I remember! : ) I'll be putting together the sample project, the post is in draft but I couldn't save draft changes without publishing since it's already a published link. I'll be working to finish this off shortly with code. Email me too @gmail (first dt last) I'll send you code when it's done.
Deletehi Adam Tuliper, this post helped me alot, did you get chance to finish the post....regards
DeleteI follow the thinking. It is helpful.
ReplyDeleteI want to see your sample code. I am hving trouble seeing the implementation in my mind.
Do I need to create my own interface that wraps the DbContext class?
Then implement it and inject my BusinessContext?
The problem I see with DI here is that my controller is either going to inject BusinessContext OR DbContext. When neither one has all the methods I need. I need the DbSet properties and I need the SaveChanges method. How do it get both with DI?
Any more news on this post???
ReplyDeletecheers