5

I'm currently working on an MVC 3 project using Ninject as my DI, the business objects are stored in a separate assembly. I'm running into an issue with the controller parameters, when posting back for CRUD operations I'm getting the error "Cannot create an instance of an interface". I am aware that you can't create an instance of an interface, but it seems like the only way I can get around this is to use a custom model binder and pass the FormCollection through. This seems really messy and I want to keep as much type specific code out of the project as I can - hence interfaces everywhere and Ninject to DI the concretes. Not only does custom model binding seem messy - won't I also lose my DataAnnotations?

Some code to describe what I have:

public ActionResult Create()
{
    // I'm thinking of using a factory pattern for this part
    var objectToCreate = new ConcereteType();
    return (objectToEdit);
}

[HttpPost]
public ActionResult Create(IRecord record)
{
    // check model and pass to repository
    if (ModelState.IsValue)
    {
        _repository.Create(record);
        return View();
    }

    return View(record);
}

Has anyone run into this before? How did you get over it?

Thanks!

Paul Aldred-Bann
  • 5,840
  • 4
  • 36
  • 55

3 Answers3

6

but it seems like the only way I can get around this is to use a custom model binder

A custom model binder is the correct way to go. And by the way you should use view models as action arguments, not domain models or interfaces.

Not only does custom model binding seem messy - won't I also lose my DataAnnotations?

I don't know why you think that a custom model binder would make things messy. For me it's a great way to separate mapping logic into a reusable class. And, no you will not lose DataAnnotations. They will work perfectly fine on the concrete instance that the custom model binder would return.

Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 1
    +1 Here's an elaboration of some of the points: http://stackoverflow.com/questions/2899680/how-to-use-ninject-or-other-di-ioc-container-with-the-model-binder-in-asp-ne/2902871#2902871 – Mark Seemann Nov 21 '11 at 18:44
  • Thanks for that, do you know of any practical examples that exist using this kind of solution? – Paul Aldred-Bann Nov 22 '11 at 08:48
3

Data passed to controllers action are simply holders for values. There shouldn't be any logic in them so there is nothing to decouple from. You can use concrete types (e.g Record) instead of interface (IRecord)

Novakov
  • 3,055
  • 1
  • 16
  • 32
  • 1
    Wouldn't I then be breaking the rule of loose coupling? What if I want / have to change the name of my concrete method for some reason i.e. Record becomes RecordDifferent. I can have RecordDifferent implementing IRecord and change my DI to now inject RecordDifferent in all cases of IRecord. – Paul Aldred-Bann Nov 21 '11 at 16:54
  • 1
    I prefere to use classes for model containers and inheritance rather than interfaces. By default DI is not used to create objects passed to actions. I use DI only for real logic not for data containers – Novakov Nov 21 '11 at 17:40
  • Didn't really understand what you meant initially, but having progressed a bit with this project I now realised I'm trying to "decouple" simple data containers as you said. There is no behaviour (yet) in any of the POCO objects that map a database table and so no reason to interface to these - nor use a factory to instantiate them. I guess what I had problem understanding is that decoupling should really be used for objects with behaviour rather than just data properties. – Paul Aldred-Bann Nov 23 '11 at 09:04
  • You have another strange thing in your controller. You should never pass input from action directly into repository. It is security hole. – Aleš Roubíček Nov 23 '11 at 14:04
  • Strictly it's not being directly passed to the repository, all entities are verified using data annotations and before the entity is passed to the repository the model state is being checked for correctness. Where is the security risk? I don't see it? – Paul Aldred-Bann Nov 23 '11 at 16:29
2

I made the same simple mistake. Ninject injects parameters into your constructor, but you added parameters to the Index Controller action.

It should look like this:

public class HomeController : Controller
{
    private IRecord _record;

    public HomeController(IRecord record)
    {
        _record = record;
    }

    public ActionResult Index()
    {
        ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application. " +
                          _record .HelloWorld();

        return View();
    }
}

Make sense?

Dan Csharpster
  • 2,662
  • 1
  • 26
  • 50