5

Ok, so i have asked a few questions on this before, but I really am just having a hrad time understanding this.

I am using the Service/Repository/EF 4 w/Pocos approach, and I have Ninject setup and injecting the controllers with the services but I am trying to figure out where to inject the context?

I want to be able to use multiple services on the controllers which in turn might access multiple repositories using the same context so all the changes would be persisted at once.

I studied the UnitOfWork pattern, but I don't understand how the MVC (controllers) would implement this as they only know of the service layer and the domain entities.

Edit

As Mohamed suggested below, inject the context into the repositories and then use a per request instance of it. How do you configure the binding in the MVC app? I would assume something like this:

Bind(Of IContext).To(MyDataContext)

Problem is, the MVC app knows nothing of the context, right?

Edit 2

Public Class ProductController
    Private _Service As IProductService

    Public Sub New(Service As IProductService)
        _Service = Service
    End Sub

End Class

Public Class NinjectWebModule

    Public Sub New()
        Bind(Of IProductService).To(ProductService)
    End Sub

End Class

Public Interface IProductService

End Interface

Public Class ProductService
    Implements IProductService

    Private _Repository As IRepository(Of Product)

    Public Sub New(Repository As IRepository(Of Product))
        _Repository = Repository
    End Sub

End Class

Public Class NinjectServiceModule

    Public Sub New()
        Bind(Of IRepository(Of Product)).To(EFRepository(Of Product))
    End Sub

End Class

Public Interface IRepository(Of T As Class)

End Interface

Public Class EFRepository(Of T As Class)
    Implements IRepository(Of T)

    Private _UnitOfWork As MyUnitOfWork

    Public Sub New (UnitOfWork As IUnitOfWork)
        _UnitOfWork = UnitOfWork
    End Sub

End Class

Public Class NinjectRepositoryModule

    Public Sub New()
        Bind(Of IUnitOfWork).To(EFUnitOfWork).InRequestScope()
    End Sub

End Class

Public Interface IUnitOfWork
    Sub Commit()
End Interface

Public Class EFUnitOfWork()
    Implements IUnitOfWork

    Public Property Context As MyContextType

    Public Sub New()
        _Context = New MyContextType
    End Sub

End Class

I would then register all three modules from the MVC app?

Sam
  • 15,336
  • 25
  • 85
  • 148

2 Answers2

7

Components you need:

  1. Repository(s) (generic or specific)
  2. Unit Of Work
  3. Services
  4. Controllers

What each is:

  1. Repository: executes queries against supplied context
  2. Unit of Work: wraps Entity Framework ObjectContext
  3. Services: calls methods on Repository
  4. Controllers: calls methods on Services, and calls "Commit" on Unit of Work.

With that in line, you Controller's ctor should look like this:

public ProductController(IUnitOfWork unitOfWork, IProductService productService)

You need the UoW because you mentioned you want to make changes across multiple repositories (a fairly common scenario). So by passing the UoW (which is the ObjectContext under the hood) to the Repositories, you can enable that.

Use a DI container to setup the Unit of Work as Http Context scoped.

RPM1984
  • 72,246
  • 58
  • 225
  • 350
  • @RPM1984 - Ok, so I will create an Ninject module in the repositories project that resolves the context, one in the services layer that resolves the repositories and one in the mvc app that resolves the services and then register all of them from the mvc app? So in repositories project I would have a EFUnitOfWork that implements IUnitOfWork, and that would have a Context property of the type MyProjectCotext, and then the Ninject module would Bind(Of IUnitOfWork).To(Of EFUnitOfWork) and be scoped per request? – Sam Mar 07 '11 at 05:54
  • @Sam Striano - spot on! Nicely summarized. :) Best thing to do is get started, see how you go - come back and let us know what problems you've face/how you've gotten on and we'll go from there. Not sure what you mean by "module" though. You should only have one registry - in the Web tier. All components (repository/service/controllers) should work off interfaces and thus "dont care" about the actual implementation. – RPM1984 Mar 07 '11 at 05:56
  • @RPM - The Ninject binding has to know of the concrete implementation right? So how would you tell it about the EFUnitOfWork and keep the SoC? See my edit above if you would please. – Sam Mar 07 '11 at 06:07
  • @Sam Striano - i think your getting confused with **SoC** and **adding an assembly reference**. Of course your web tier will need an assembly reference to all of the "other layers", so there is a dependancy, but it's a dependency on the "existence" of the implementation, not the actual implementation itself. SoC is about abstraction, interfaced-based programming, loose coupling - all of which are still achieved. Your web project still knows nothing about how a Unit of Work or Repository is implemented, just about the interface. – RPM1984 Mar 08 '11 at 00:35
  • @RPM - Thank you... So I have it down to where only my Repository Layer and My Data Layer (contains EF Model and Context) are the only two dll's that reference System.Data.Entity. I really don't see any way around the reference in the Repository Layer, as the concrete implementation of the EFRepository is dependent on EF. They way I see it, you really don't test your repositories, rather supply them when testing your service layer and controllers? – Sam Mar 08 '11 at 04:41
  • @Sam Striano - exactly. You can certainly have an "integration test", which tests all the way through from a controller to the service, through to the repository and back again. But to "unit test" a repository, you should supply the repository with a mock unit of work. Don't know why you have a Repository AND Data Layer though - isn't it the same thing? What's the difference? Sounds like you should just have one. – RPM1984 Mar 08 '11 at 08:26
  • @RPM1984 - My Project.Data.dll contains the EF model and context. That is it. So really my Repository is my data layer. I put the generated POCO classes into a different dll, so the domain layer know nothing about EF. All in all, I think I am starting to get the hang of this. Thanks again for all of your help. – Sam Mar 08 '11 at 23:09
2

How about injecting ObjectContext in repository classes? and use single instance per request?

Update

See this example ASP.NET MVC 3 Application using Ninject, Entity Framework 4 Code-First CTP 5, Patterns

Update 2

This example is for NHibernate session, but it is exactly the same idea with EF ObjectContext or LinqToSQL DataConext or similar stuff. Really good example. Check it and tell me what you think.

http://www.itslet.nl/?p=904

Update 3

The place where you define your DI bindings HAS to know about all the concrete implementations of course. If you really want your MVC code to not know about the context, you either

  • put that code in new project that does know about it,
  • or use XML for doing the bindings (there is documentation in Ninject site itself on how to do that, but it's not the default intended use of this particular DI container),
  • or use Ninject Modeules (basically this means splitting your bindings, so, you can have one module that does the context bidning in the same project as the context, and the MVC just uses all discoverable Ninject Modules for binding).

This is one example of using Ninject Modules: What is the intention of Ninject modules?

Community
  • 1
  • 1
Meligy
  • 35,654
  • 11
  • 85
  • 109
  • @Mohamed - That sounds awesome, but how do I do it? Do you have an example or a link to one? Thanks!! – Sam Mar 07 '11 at 05:09
  • See this example http://stackoverflow.com/questions/4782364/asp-net-mvc-3-application-using-ninject-entity-framework-4-code-first-ctp-5-pat – Meligy Mar 07 '11 at 05:15
  • Check **Update 2** in my answer as well. – Meligy Mar 07 '11 at 05:21
  • @Mohamed - That looks good, but wouldn't the MVC app then have to know about the EF context, and it would be tightly coupled by that? Correct me if I am wrong. Thanks!! – Sam Mar 07 '11 at 05:26
  • @Mohamed - OK, so I would have my module in the mvc app that resolves the services, and then in my service layer (separate dll) i would have my module to resolve the context for the repositories and then register that and the mvc module in the global.asax? - Thanks for your help, a lot!! – Sam Mar 07 '11 at 05:37
  • Ahh.. That's too much "modules" for me :). Look, you'll have Ninject Module that injects the context into the repositories. and have some class somewhere in ASP.MVC app that registers all Ninject Modules in one method. This method you call from Application_Start in global.asax. You'll not need to create any ASP.NET MVC modules, but Ninject itself may require you to add its module to web.config for instance-per-web-request to work. – Meligy Mar 07 '11 at 05:40
  • Sorry for submitting previous comment before it's complete. – Meligy Mar 07 '11 at 05:42