views:

824

answers:

3

I am early in development on a new ASP.Net MVC project and I using this project to get into DI. I'm pretty sure that I am going to go with Structure Map, but that isn't what I am asking about. What I am trying to figure out is how best to organize my solution. Do both the unit test project and the model get a configuration file to map their dependencies or is there one class to rule them all?

Also, are there any newbie traps to avoid before I get too far into this?

Many Thanks, All.....

Update I should add that when I say "organize the solution", I'm not referring to the number of files/folders, etc., but rather how to structure the classes that are involved with DI. In particular, how to manage the bootstrapper. I can see where poor phrasing on my part could cause confusion.

+7  A: 

If you're the only one working on the project I'd do what makes sense to you first. Nothing is worse than having a directory or project structure imposed on you that you find unintuitive. Is the BaseController class in the \Core\ folder or the \Controller\ folder? Personally I'd look in the Controller but some people swear it should be in \Core\ or \Bases.

The first newbie trap is thinking that you can organize your code in the wrong way and somehow this reflects on the success of the project. I've seen projects where 30 files were in one folder and other projects where there were 20 folders for 30 files.

The second newbie trap is forgetting that compared to other languages you have the benefit of awesome intellisense, code navigation tools, and refactoring support from Visual Studio. You also have a compiler which makes misplacing a file far less painful. If you put something in the "wrong" spot, its ok, you can always find it and drag it around to where it needs to be.

I'll be honest I'm working on a project right now and I'm not even sure where certain classes reside in my file structure. Go To Definition/Declaration are keyboard shortcuts I use a lot. Because its only me working with the code this is fine. If I had to add another developer onto the project I'd probably clean things up.

Personally I tend to put Interfaces with their implementing types inside the same folder. IPaymentGateway is in the same folder as AuthorizeNetGateway and PaypalGateway. If I can't view all the files in that folder at once in my solution explorer sidebar then I move all the Gateway files into a \Gateway\ folder.

With Dependency Injection added to the mix I'd advise you to only be concerned with namespace explosions. The worst thing that you can do is clutter up your bootstrappers and files with long using declarations and aliases.

ForRequestedType<Customer>

is cleaner than

using KevDog.Models
using Customer=KevDog.Models.Customer

or

ForRequestedType<KevDog.Models.Customer>

Another way to avoid this problem is to be explicit when your naming things: Customer, CustomerViewModel, CustomerController, CustomerDataRow, CustomerView

For TDD you almost have to have two bootstrappers to manage your concrete types. You really don't want your unit tests to use AuthorizeNetGateway : IPaymentGateway, rather StubGateway : IPaymentGateway.

Now I'm also new at DI so I tend to make things very simple and mirror the 101 level tutorials and documentation. Getting into dynamic injection based on a build configuration should only be used when a specific situation requires it and you know exactly why your doing.

I usually keep the default structure for MVC apps as well. Its just easier to have your code in the same structure as 99% of all tutorials and videos.

Hope this helps.

jfar
Thanks jfar! The comment on TDD is really the kind of stuff I meant to be asking about. I think the idea of a bootstrapper for each project is the way to go. One in the unit test project and one in the MVC project
KevDog
+2  A: 

To encourage better TDD. Have two testing projects and or namespeaces X.Unit.Tests & X.Integrations.Tests.

I have my DI code in my main project in a "namespace directory" (/Config) but in my integration code tests, I might just call those registries or override if I required in my base fixtures or setups.

E.g.

/Config/ServiceRegistry.cs /Config/RepositoryRegistry.cs /Config/Bootstrapper.cs

In global.asax I call Bootstrapper.Init() and this will call x.AddRegistry(new ServiceRegistry()) and so on.

In my unit tests you dont need to be using DI only in your integration tests. In my IntegrationTests e.g. if I'm testing NHibernate through to the database I might initialise SM with RepositoryRegistry in TestSetUp with a helper method just wrappering GetInstance().

I don't split out to projects .Bootstraper and .Domain until I have absolutely have to... Three projects, X, X.UnitTests, X.Integration if you required more move later. I came from a background/company enforce of having dozens of projects it felt dirty a first reducing but not now, I go hit the grow running quickly and reorganize solutions structure later if required.

mattcodes
A: 

Here is my first attempt at solving the same problem for myself, but since it is my first attempt I would hope that people might comment on or critique it as much as I would hope that it might serve as one possible solution for you:

public VatManager()
 : this(new VatManagerRegistry()) { }

public VatManager(Registry registry)
 : this(new Action<IInitializationExpression>(x => { x.AddRegistry(registry); }))
  {
  }

public VatManager(Action<IInitializationExpression> action)
  {
   ObjectFactory.Initialize(action);
   data = ObjectFactory.GetInstance<IVatManagerData>();
  }

I have three constructor overloads - the parameterless default constructor has knowledge of the concrete StructureMap Registry that needs to be created for use in a production context. The other two allow other code that instantiates this manager class to provide their own StructureMap Registry(s) or Actions so so that they can control the dependency injections themselves, such as in the case of an automated test providing mocks instead of concrete instances of those dependencies. I should add that this solution is not particular to an ASP.NET MVC context and does not pull any configuration information from *.config files.

Dave Falkner