In short I would like to be able to pass a generic ViewModel into my views
Here is some simplified code of the gist of what I am trying to achieve
public interface IPerson
{
string FirstName {get;}
string LastName {get;}
}
public class FakePerson : IPerson
{
public FakePerson()
{
FirstName = "Foo";
LastName = "Bar";
}
public string FirstName {get; private set;}
public string LastName {get; private set;}
}
public class HomeViewModel<T>
where T : IPerson, new()
{
public string SomeOtherProperty{ get; set;}
public T Person { get; private set; }
public HomeViewModel()
{
Person = new T();
}
}
public class HomeController : Controller {
public ViewResult Index() {
return View(new HomeViewModel<FakePerson>());
}
}
If I create my view as follows all works as expected
<%@ Page Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<HomeViewModel<FakePerson>>" %>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<%: Html.DisplayFor(m=>m.Person.FirstName) %>
<%: Html.DisplayFor(m=>m.Person.LastName) %>
</asp:Content>
However I do not want to depend directly on FakePerson in the view in the event that I want to pass some other IPerson implementation, so I tried to change the page directive to
<%@ Page Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<HomeViewModel<IPerson>>" %>
But of course that does not work, so, after a whole day of mucking about, I have more grey hair and no clue what to do next.
Can anybody help please.
[UPDATE]
Some advise has sugested that I should use a covariant interface; define a non-generic interface and use it in the View. Unfortuanetly I have tried this but there is one aditional implication. I would like the HtmlHelper function to be able to access any data annotation attributes that may be defined in the IPerson derived class
public class FakePerson : IPerson
{
public FakePerson()
{
FirstName = "Foo";
LastName = "Bar";
}
[DisplayName("First Name")]
public string FirstName {get; private set;}
[DisplayName("Last Name")]
public string LastName {get; private set;}
}
So while using a covariant interface this way does work, partly, to access the derived type through the ViewModel; since the view is typed to the interface, it appears the attributes are not accessible.
Is there perhaps a way, in the view, to get access to these attributes, maybe with reflection. Or could there be anotherway to type the View to the generic.