简体   繁体   中英

How can a view use a model that uses generics?

In MVC 3, I've been refactoring my controllers to remove some duplicate code by using generics which I was successful in doing, but now I'm trying to refactor my views, but I can't seem to adjust the model that the view uses to use generics.

Here is the relevant portion of my refactored abstract base controller:

public abstract class CrudController<TEntity> : Controller where TEntity : Entity, INamedEntity, IOrderedEntity, IHasMembers, new()
{
  private readonly IUnitOfWork _unitOfWork;
  protected readonly IRepository<TEntity> Repository;

  protected CrudController(IUnitOfWork unitOfWork)
  {
    _unitOfWork = unitOfWork;
    Repository = _unitOfWork.RepositoryFor<TEntity>();
  }

  public ActionResult Edit(int id)
  {
    return PartialView(Repository.FindById(id));
  }

  // More actions here...
}

Here are my actual controllers (they are really basic since the refactoring):

public class StatusController : CrudController<MemberStatus>
{
  public StatusController(IUnitOfWork unitOfWork) : base(unitOfWork)
  {
  }
}

public class PositionController : CrudController<MemberPosition>
{
  public PositionController(IUnitOfWork unitOfWork) : base(unitOfWork)
  {
  }
}

Note that both MemberStatus and MemberPosition implement 'Entity, INamedEntity, IOrderedEntity, IHasMembers'

Now here are my Edit views for those two controllers which I want to refactor:

@model MyApp.Domain.Entities.MemberStatus
@using (Html.BeginForm("Save", @ViewContext.RouteData.Values["Controller"].ToString(), FormMethod.Post, new { id = "EntityForm" }))
{
  @Html.Hidden("Id")
  <label class="Name">
    <span>New Name:</span><br />
    @Html.EditorFor(x => x.Name)
  </label>
}

@model MyApp.Domain.Entities.MemberPositions
@using (Html.BeginForm("Save", @ViewContext.RouteData.Values["Controller"].ToString(), FormMethod.Post, new { id = "EntityForm" }))
{
  @Html.Hidden("Id")
  <label class="Name">
    <span>New Name:</span><br />
    @Html.EditorFor(x => x.Name)
  </label>
}

You can see that the views are almost identical, with the exception of the first line which declares the model. I want to find out how to pass in a generic parameter like I did with the controllers, which I thought (in my naivete) would look something like the following, but this obviously doesn't work.

@model <TEntity> where TEntity : Entity, INamedEntity, IOrderedEntity, IHasMembers, new()

How can I refactor these two views (by putting a generic view in the Shared folder) that would implement a non-concrete model in the view? Any suggestions are appreciated.

FYI - I think (though I haven't tried) that I could use a ViewModel, but I would prefer not to have to do that in this case (as I would have to implement the mapping from the entities to the ViewModel for each entity which somewhat defets the purpose of this refactoring).

If both of your models have common properties, create an interface with those properties in the interface, then make both models implement that interface. Now create a view that's strongly-typed to that interface. Another option is simply just a weakly-typed view that is dynamically evaluated at runtime.

Could you just have the model defined as whichever interface you're using rather than an object which implements many interfaces?:

@model INamedEntity
@using (Html.BeginForm("Save", @ViewContext.RouteData.Values["Controller"].ToString(), FormMethod.Post, new { id = "EntityForm" }))
{
 @Html.EditorForModel()
}

For this you can create an editor template for the INamedEntity interface.

@model Entity
@if (Model is INamedEntity)
{
  @{ Html.RenderPartial("NamedEntityFields", (INamedEntity)Model); }
}
@if (Model is IOrderedEntity)
{
  @{ Html.RenderPartial("OrderedEntityFields", (IOrderedEntity)Model); }
}

NamedEntityFields.cshtml:

@model INamedEntity
@* put name specific fields here *@

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM