MVC2 - Как получить родительскую модель (контейнер) внутри шаблона - PullRequest
6 голосов
/ 24 октября 2010

Я пишу приложение MVC2 с использованием DataAnnotations.У меня есть следующая модель:

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.ContainerType равно FooModel (как и ожидалось)
  2. this.ViewData.TemplateInfo имеет закрытое свойство VisitedObjects (типа System.Collections.Generic.HashSet<object>), которое содержит два элемента: FooModel и DateTime?.

Как я могу получить доступ к моей FooModel?Я не хочу взламывать свой путь, используя Reflection.

Обновление:

Я принял ответ mootinator, поскольку он выглядит для меня как лучшее решение, которое позволяеттип-безопасности.Я также проголосовал за ответ Tx3, так как ответ mootinator основан на нем.Тем не менее, я думаю, что в таких сценариях должна быть лучшая форма поддержки MVC, которая, как мне кажется, довольно распространена в реальном мире, но отсутствует в примерах приложений.

Ответы [ 5 ]

3 голосов
/ 15 февраля 2011

Возможно, вы могли бы создать новый класс, скажем, UserDateTime, и он будет содержать обнуляемый DateTime и остальную информацию, которая вам нужна. Тогда вы будете использовать пользовательский шаблон отображения для UserDateTime и получите доступ к необходимой информации.

Я понимаю, что вы можете искать другое решение.

2 голосов
/ 18 февраля 2011

Я думаю, что вам лучше извлечь эту функцию из вызова HtmlHelper из родительского представления.

Возможно, что-то вроде RenderSpecialDateTime<TModel>(this HtmlHelper html, Expression<Func<TModel,DateTime?>> getPropertyExpression) сработает.

В противном случае вам придется сделать что-то вроде того, что предложил Tx3.Я проголосовал за его ответ, но разместил его как альтернативу.

1 голос
/ 21 января 2015

Может показаться, что где-то между 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 исправляет это превышение, поэтому, возможно, в будущем можно будет заменить код отражения.

1 голос
/ 16 февраля 2011

Не могли бы вы использовать объект словаря ViewData в контроллере, а затем захватить его в ViewUserControl? Он не будет строго напечатан, но ... вы можете написать помощника, который ничего не делает, если он пуст, и ссылку, чтобы указать пример страницы истории входа, если она имеет значение.

0 голосов
/ 21 февраля 2011

Извините, если это предложение кажется ненормальным, я не пробовал его, но вы не могли бы сделать то, что предложил Tx3, не создавая кучу новых классов, определив универсальный класс для ссылки на любой тип родителя, который вы хотите?

    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>>, таким образом, вам не нужно было бы подробно называть какой шаблон использовать.Это больше похоже на то, как все должно работать.

...