How to Inject Controller for MVC4/VS2012/Web API

2019-05-18 08:24发布

问题:

I have read or tried to read far too many "how to"s on this and have gotten exactly nowhere. Unity? System.Web.Http.Dependencies? Ninject? StructureMap? Ugh. I just want something simple that works! I just can't figure out what the current state of this is. There are wildly different approaches and the examples appear to be incomplete. Heck the best lead had a sample project with it ... that I can't load in VS2010 or 2012. ARG! I waster 3/4 of the day on something that I feel should have been half an hour at most and move on! It's just plumbing!

I have a repository that's based on generics to process a number of data sets that all support the same operations.

IRepository

I want to control which repository each data set is bound to. This will allow me to bind everything to a test XML repository, transitioning them over to a SQL repository as the project advances.

I sure would appreciate some help getting this going! Thank you!

回答1:

Sounds like you are at the state I was a couple of years ago.

Note, if you need any further help I cn send you some code. It's just hard to put all the code in here.

Ill try to explain the current architecture in the project I am working on. This post is a bit long winded but I am trying to give you a big picture of how using IOC can help you in many ways.

So I use Ninject. After trying to use Castle Windsor for a while I found Ninject easy to get up and running. Ninject has a cool website that will get you started.

First of all my project structure is as follows: (top down and it's MVC)

View - razor ViewModel - I use 1 view model per view

ViewModelBuilder - Builds my view models for my views (used to abstract code away from my controller so my controller stays neat and tidy)

AutoMapper - to map domain entities to my view models

Controller - calls my service layer to get Domain Entities

Domain Entities - representations of my domain

ServiceLayer (business layer) - Calls my repository layer to get Domain entities or collections of these

AutoMapper again - to map custom types from my 3rd party vendors into my domain entities

RepositoryLayer - does CRUD operations to my data stores

This is a hierarchy but Domain entities kind of sit along side and are used in a few different layer.

Note: some extra tools mentioned in this post are:

AutoMapper - maps entities to other entities - eliminates the need to write loads of mapping code

Moq - Allows you to mock stuff for unit testing. This is mentioned later.

Now, regarding Ninject.

Each layer is marked with an interface. This must be done so Ninject can say to its self.

When I find IVehicleRepository inject it with a real VehicleRepository or even inject it with FakeVehicleRepository if I need a fake.

(this relates to your comment - "This will allow me to bind everything to a test XML repository")

Now every layer has a contstructor so that Ninject (or any other IOC container) can inject what it needs to:

public VehicleManager(IVehicleRepository vehicleRepository)
{
    this._vehicleRepository = vehicleRepository;
}

VehicleManager is in my serviceLayer (not to be confused with anything to do with web services). The service layer is really what we would call the business layer. It seems that a lot of people are using the word service. (even though I think it is annoying as it makes me think about web services or WCF instead of just a business layer.... anyway...)

now without getting into the nitty gritty of Ninject setup the following line of code in my NinjectWebCommon.cs tells ninject what to do:

kernel.Bind<IVehicleRepository>().To<VehicleRepository>().InRequestScope();

This says:

Hey Ninject, when I ask for IVehicleRepository give my a concrete implementation of VehicleRepository.

As mentioned before I could replace VehicleRepository with FakeVehicleRepository so that I didnt have to read from a real database.

So, as you can now imagine, every layer is only dependent on interfaces.

I dont know how much unit testing you have done but you can also imagine that if you wanted to unit test your service layer and it had concrete references to your repository layer then you would not be able to unit test as you would be ACTUALLY hitting your repository and hence reading from a real database.

Remember unit testing is called unit testing because it tests ONE thing only. Hence the word UNIT. So because everything only knows about interfaces, it means that you can test a method on your service layer and mock the repository.

So if your service layer has a method like this:

public bool ThisIsACar(int id)
{
   bool isCar = false;
   var vehicle = vehicleRepository.GetVehicleById(id);

   if(vehicle.Type == VehicleType.Car)
   {
       isCar = true;
   }
   else
   {
       isCar = false;
   }
}

You would not want the vehicleRepository to be call so you could Moq what the VehicleRepository gives you back. You can mostly only Mock stuff if it implements an interface.

So your unit test would look like this (some pseudo code here):
        [TestMethod]
        public void ServiceMethodThisIsACar_Returns_True_When_VehicleIsACar()
        {
            // Arrange
            _mockRepository.Setup(x => x.ThisIsACar(It.IsAny<int>)).returns(new Car with a type of VehicleType.Car)

            // Act
            var vehicleManager = new VehicleManager(_mockVehicleRepository.Object);
            var vehicle = vehicleManager.ThisIsACar(3);

            // Assert
            Assert.IsTrue(vehicle.VehicleType == VehicleType.Car)
        }

So as you can see, at this point and it is very simplified, you only want to test the IF statement in your service layer to make sure the outcome is correct.

You would test your repository in its own unit tests and possibly mock the entity frame work if you were using it.

So, overall, I would say, use what ever IOC container gets you up and running the fastest with the least amount of pain.

I would also say, try and unit test all that you can. This is great for a few different reasons. Obviously it tests the code you have written but it will also immediately show you if you have done some thing stupid like new-ing up a concrete repository. You will quickly see that you wont have an interface to mock in your unit tests and this will lead you to go back and refactor your code.

I found that with IOC, it takes a while to just get it. It confuses the crap out of you until one day it just clicks. After that it is so easy you wonder how you ever lived without it.

Here is a list of things I cant live without Automapper Moq Fluent Validation Resharper - some hate it, I love it, mostly for its unit testing UI.

Anyway, this is getting too long. Let me know what you think.

thanks RuSs