问题
Let's assume that I have three classes: Derived1ViewModel
, Derived2ViewModel
and BaseViewModel
which serves as a superclass for the Derived1ViewModel
and Derived2ViewModel
.
I have a DisplayTemplate which has as a model a List<BaseViewModel>
and I would like to call this template from views which are using as a model a List<Derived1ViewModel>
or a List< Derived2ViewModel>
.
If I try to do this, an exception is triggered saying that it cannot convert a List<Derived1ViewModel>
or List<Derived2VIewModel>
to a List<BaseViewModel>.
If I try to apply a cast such as @Html.DisplayFor(model=>model.Cast<BaseViewModel>.ToList(), "MyTemplate")
an InvalidOperationException is thrown.
Details: [InvalidOperationException: Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.]
Can this problem be solved or its necessary to take another approach?
回答1:
Note that in @Html.DisplayFor(model=>model, "MyTemplate")
the first parameter is not the Model. It is an Expression
that will be evaluated on the Model.
The Model is implicitly passed to the MVC Expression Compiler (using ViewData i think) and that Model's type will still be what you declare at the start of the Razor page. It has nothing to with the expression. The Expression just indicates what data is needed with the model as the starting parameter.
having said that, give this a shot:
@Html.DisplayFor(model=>model.Select(dm=>(BaseViewModel)dm).ToList(), "MyTemplate")
although i have a feeling that you might hit the MVC Expression Evaluation snag, where it can handle only a particular set of Expressions. (Constant Expression, Member access etc.)
we can come to that based on your results.
回答2:
If your model is subclass of IEnumerble<SomeModel>
MVC Will render it as SomeModel,
First, you cant cast List<A> to List<B> directly
So either convert to List<B> in view and use Display( instead of DisplayFor or change the model type of your display template from List<A> to IEnumerable<A>
Second,You have to set template name in UIHintAttribute or pass it as parameter to DisplayFor
回答3:
Solved the problem by using a template for BaseViewModel
instead of List<BaseViewModel>
. In this way, I can pass any derived type to the template (ex: Derived1ViewModel, Derived2ViewModel).
Example:
@*Derived1 View*@
@model List<Derived1ViewModel>
@for(var i = 0; i < Model.Count; i++)
{
int i1 = i;
@Html.DisplayFor(model=>model[i1], "MyTemplate")
}
@*Derived2 View*@
@model List<Derived2ViewModel>
@for(var i = 0; i < Model.Count; i++)
{
int i1 = i;
@Html.DisplayFor(model=>model[i1], "MyTemplate")
}
@*Template*@
@model BaseViewModel
<p>It works @Model.Name</p>
来源:https://stackoverflow.com/questions/23000679/mvc-html-displayfor-for-lists-of-derived-models