【问题标题】:HtmlHelper on type parameter of generic view-model通用视图模型的类型参数上的 HtmlHelper
【发布时间】:2015-07-23 16:49:11
【问题描述】:

我正在尝试在强类型视图上创建 HtmlHelper 方法,其中视图模型是通用的。但是有一个并发症。我想简化我的语法,以便我可以在我的视图模型上指定属性的成员表达式。请考虑以下内容。

public interface ViewModel<T>
{
    T Model { get; set; }
}

public class DocViewModel : ViewModel<Document> 
{
    public IEnumerable<string> Options { get; set; }
    public Document Model { get; set; }
}

public class Document
{
    public string Name { get; set; }
    public string Value { get; set; }
}

现在,我可以轻松地创建一个引用视图模型本身的助手,并像这样使用它:

@model DocViewModel
<div>
    @Html.DoSomething(vm => vm.Options);
    @Html.DoSomethingElse(vm => vm.Model.Name);
    @Html.DoSomethingYetAgain(vm => vm.Model.Value);
</div>

但我希望能够在模型上简单地传递一个表达式,如下所示:

@model DocViewModel
<div>
    @Html.DoSomething(vm => vm.Options);
    @Html.DoSomethingElse(vm => vm.Name);
    @Html.DoSomethingYetAgain(vm => vm.Value);
</div>

现在,如果我在调用中显式引用类型参数,我可以编写帮助程序:

@model DocViewModel
<div>
     @Html.DoSomething<Document>(vm => vm.Name);
</div>

但这有点违背了我追求的目的。似乎我应该能够编写一个 HtmlHelper,它可以利用视图在 DocViewModel 上是强类型的这一事实,它本身就是一个 ViewModel,并且 @Html() 返回

HtmlHelper<DocViewModel> //HtmlHelper<ViewModel<Document>>

以便隐式知道 Document 表示的类型参数,以便我可以编写

public static string DoSomethingElse<TModel, TProperty>(this HtmlHelper<ViewModel<TModel>> helper, Expression<Func<TModel, TProperty>> expression) 
{
    return "Whatever";
}

但无法完成该转换。那么有什么办法可以做到这一点吗?还是我只能忍受丑陋的语法?

编辑:将对“KoViewModel”的错误引用更改为“ViewModel”

再次编辑:另外,使 ViewModel 通用的目的是我可以声明任何其他类型的 ViewModel:

public class ProgramViewModel : ViewModel<Program>
{
    //other stuff
    public Program Model { get; set; }
}
public class Program
{
    public string Something { get; set; }
    //other properties I want to reference
}

在另一个方面:

@model ProgramViewModel
<div>
    @Html.DoSomethingElse(vm => vm.Something)
</div>

再次,我希望能够取消引用视图的视图模型以及视图模型的 Model 属性的属性,而不必在我的方法调用中显式引用类型。

我上面定义的方法的问题是HtmlHelper和HtmlHelper之间显然不能进行隐式转换,以至于编译器无法解析视图中的@Html调用如上图(“不包含定义”)对于并且没有扩展方法等等等等”)。当然,到处都是显式强制转换可以使这项工作奏效,但这是我要避免的。

【问题讨论】:

  • 您必须在使用泛型时指定类型 &lt;T&gt;
  • 在什么上指定 ?该视图的类型为 WebViewPage。 DocViewModel 是 KoViewModel,所以我认为有一种方法可以将 T 隐式解析为 Document。
  • 我怀疑它无法识别TProperty 的类型。在这种情况下,通常第二级泛型会有所帮助,例如 Html.DoSomthingForModel().ForProperty(vm -&gt; vm.Name)... 确切的错误可以帮助诊断问题(不依赖自定义类的独立示例也很好)。

标签: c# asp.net-mvc-3 generics viewmodel html-helper


【解决方案1】:

如果您可以将 ViewModel 更改为抽象类,请执行以下操作:

public abstract class ViewModel<TModel>
{
    public class HtmlHelper : HtmlHelper<ViewModel<TModel>> {}
    public TModel Model;
}

然后编写如下静态扩展方法:

public static TProperty DoSomethingElse<TModel, TProperty>(this ViewModel<TModel>.HtmlHelper helper, Expression<Func<TModel, TProperty>> expression) 
{
    return default(TProperty);
}

这应该隐式解决。

【讨论】:

  • 抱歉,“KoViewModel”引用应为“ViewModel”。已编辑。
  • 啊,好的。这改变了一点。我会发布更新。
  • 我已经发布了更新。如果无法将 ViewModel 更改为抽象,则需要使用 VB.Net 或 IL 将 HtmlHelper 类嵌套在 ViewModel 接口中,因为 C# 不允许在接口中嵌套类型,但 CLR 和 VB.Net做。
  • 请注意,您需要使用新的嵌套 HtmlHelper 的实例而不是原来的实例才能使其工作。
  • 好的,当我在视图中手动实例化嵌套帮助器时隐式解决:@{ var h = new DocViewModel(Html..ViewContext, Html.ViewDataContainer); @h.DoSomethingElse(vm => vm.Name)。但是有没有一种方法可以在我的不同视图中解析所需类型的 HtmlHelper 而无需在每个视图中手动实例化它?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多