MVC2 - 如何在模板中获取父模型(容器)

时间:2010-10-24 16:28:00

标签: asp.net-mvc-3 templates asp.net-mvc-2 model modelmetadata

我正在使用DataAnnotations编写MVC2应用程序。我有以下模型:

public class FooModel 
{
    [ScaffoldColumn("false")]
    public long FooId { get; set; }

    [UIHint("BarTemplate")]
    public DateTime? Bar { get; set;}
}

我想为Bar创建自定义显示模板。我创建了以下模板:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<DateTime?>" %>

<div class="display-label">
    <span><%: Html.LabelForModel() %></span>
</div>
<div class="display-field">
    <span><%: Html.DisplayForModel()%></span>
    <%: Html.ActionLink("Some link", "Action", new { id = ??FooId?? }) %>
</div>

现在,我的问题是内部模板,我希望从我的模型中访问另一个属性。我不想为FooModel创建单独的模板,因为我将不得不对所有其他FooModel属性进行硬编码。

在使用调试器进行简短调查后,我可以看到:

  1. this.ViewData.ModelMetadata.ContainerTypeFooModel(正如预期的那样)
  2. this.ViewData.TemplateInfo有一个 非公有财产VisitedObjects (类型 System.Collections.Generic.HashSet<object>) 其中包含两个元素: FooModelDateTime?
  3. 如何访问我的FooModel?我不想破解使用Reflection的方法。

    更新

    我接受了mootinator的答案,因为它认为我是允许类型安全的最佳解决方案。我也赞成了Tx3的答案,因为mootinator的答案建立在它之上。不过,我认为在这种场景中应该有更好的MVC支持,我认为这在现实世界中非常普遍,但样本应用程序却缺少。

5 个答案:

答案 0 :(得分:3)

也许你可以创建新类,比方说UserDateTime,它将包含可空的DateTime和你需要的其他信息。然后,您将使用UserDateTime的自定义显示模板,并访问您需要的信息。

我意识到您可能正在寻找其他类型的解决方案。

答案 1 :(得分:2)

我认为您最好从父视图中将此功能提取到HtmlHelper调用。

RenderSpecialDateTime<TModel>(this HtmlHelper html, Expression<Func<TModel,DateTime?>> getPropertyExpression)之类的东西可能会起作用。

否则,你将不得不做类似Tx3建议的事情。我赞同他的答案,但将其作为替代方案发布。

答案 2 :(得分:1)

你不能在控制器中使用ViewData字典对象,然后在ViewUserControl中获取它吗?它不是强类型的但是......如果它是空的,你可以写一个帮助器什么都不做,如果它有一个值,则链接到示例登录历史页面。

答案 3 :(得分:1)

似乎介于MVC 5.0和5.2.2之间的某个“容器”属性被添加到ModelMetadata类。

但是,因为负责元数据创建的提供程序中的所有方法(GetMetadataForProperty,Create等)在其签名中都没有容器,所以只在某些情况下分配Container属性(GetMetadataForProperties和GetMetadataFromProvider根据反映的代码)和在我的情况下通常是空的。

所以我最终做的是覆盖新元数据提供程序中的GetMetadataForProperty并将其设置在那里:

public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
{
  var propMetaData = base.GetMetadataForProperty(modelAccessor, containerType, propertyName);
  Object container = modelAccessor.Target.GetType().GetField("container").GetValue(modelAccessor.Target);
  propMetaData.Container = container;
  return propMetaData;
}

我知道这是反思,但它相当简洁。似乎MS正在纠正这种情况,因此将来可能会替换反射代码。

答案 4 :(得分:0)

很抱歉,如果这个建议看起来很糟糕,我还没有尝试过,但你不能做Tx3所建议的,而不必通过定义一个通用类来引用你想要的任何类型的父类来创建一堆新类吗? / p>

    public class FooModel 
    {
        [ScaffoldColumn("false")]
        public long FooId { get; set; }

        [UIHint("BarTemplate")]
        public ParentedDateTime<FooModel> Bar { get; set;}

        public FooModel()
        {
            Bar = new ParentedDateTime<FooModel>(this);
        }
    }


    public class ParentedDateTime<T>
    {
        public T Parent {get; set;}
        public DateTime? Babar {get; set; }

        public ParentedDateTime(T parent)
        {
            Parent = parent;
        }

}

你可以扩展它以封装任何带有<Parent, Child>类型泛型的旧类型,甚至。

这也可以为您提供强类型模板的好处

Inherits="System.Web.Mvc.ViewUserControl<ParentedDateTime<FooType>>因此您不必明确指出在任何地方使用哪个模板。这更符合事物的工作方式。