I am using ASP.NET MVC 4 with SimpleInjector and came across an issue when the MVC framework is trying to resolve the concrete type it should create when passing the object back to the controller action from the view. This is the only place it does not work, I get an error [MissingMethodException: Cannot create an instance of an interface.]

I know there have been similar questions asked, but none that really deal with this exactly. Is the best practice to write a custom ModelBinder? or am I missing something with SimpleInjector? It seems curious to me that this does not work when everything else (like constructor injection) works pretty well.

View:

@model IEntityType
... Rest of View

Controller:

public ActionResult Update(IEntityType entityType)
    {
        ... controller code

        return RedirectToAction("Index");
    }

Global.asax

Container container = new SimpleInjector.Container();
container.Register<IEntityType, EntityTypeModel>();

IEntityType Definition:

public interface IEntityType
{
    Int32 EntityTypeId { get; set; }
    Guid EntityTypeGUID { get; set; }
    String EntityTypeName { get; set; }
    String EntityTypeDescription { get; set; }
}

EntityTypeModel Definition:

public class EntityTypeModel : IEntityType
{
    public Int32 EntityTypeId { get; set; }
    public Guid EntityTypeGUID { get; set; }
    public String EntityTypeName { get; set; }
    public String EntityTypeDescription { get; set; }
}
有帮助吗?

解决方案

By default, MVC can't model bind interfaces for you and you should typically not try to model bind interfaces. The idea is that you model bind simple data containers or DTOs, such as View Models. So your problem is not specific to Simple Injector; none of the common DI frameworks for .NET allow integration into the MVC model binding process by default, since this is not an advised approach.

So typically, you should send view model objects between the view and the controller, or in very simple scenarios you could even just send entities, but this only works when your entities are simple data containers as well. In case your entities are simple data containers, defining an interface for each entity is useless, since interfaces are meant to abstract behavior, not data. It would be rather useless to mock or fake a class that only contains data.

In case you're practicing Domain Driven Design, your entities will have behavior, but even in that case it's very unusual to define interfaces for your entities, since the entities are the core of your domain, they don't need mocking, faking, intercepting or decorating. Instead you pass in abstractions into the entity's methods. And when practicing DDD, it will be very unlikely to use the entities directly as view model in MVC, since in DDD you tend to hide the entity's invariants, while a view model is just a simple data container with get/set properties (or public fields).

Also note that entities are short lived objects, while the services you register in your DI container tend to be long lived. In other words, entities are newables, while services are injectables. You should not register newables in your container. In the case of entities, its usually your repository layer or ORM that creates entities; not the container.

So instead of trying to add custom model binders that hook into the container framework, my advice is to do the following:

  1. Ditch the IEntityType abstraction; its a useless abstraction.
  2. Remove the Register<IEntityType, EntityTypeModel>() registration, since it registers a newable, which is not the container's job.
  3. Model bind your EntityTypeModel directly by chaning your action method to accept an EntityTypeModel instead of accepting an interface, or wrap it in a custom view model that contains a public EntityTypeModel Model { get; set; } property.

Another -more sophisticated- approach is to define message objects in the core layer of your application that each describe a single Use Case. You can use these DTOs to map directly on the MVC view. This approach gets really useful when your your application is doing more than simply CRUD operations. Take a look here.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top