Получить значение свойства модели из пользовательского атрибута - PullRequest
2 голосов
/ 06 мая 2011

У меня есть пользовательский атрибут с именем FileLink, который использует DisplayForModel() для генерации ActionLink для контроллера со значением ID.

Атрибут выглядит следующим образом:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
public sealed class FileLinkAttribute : Attribute
{
    public string ActionName { get; set; }
    public string ControllerName { get; set; }
    public string IdPropertyName { get; set; }

    public FileLinkAttribute(string actionName, string controllerName, string idPropertyName)
    {
        ActionName = actionName;
        ControllerName = controllerName;
        IdPropertyName = idName;
    }
}

Используется ли он в модели представления, например:

public class MyViewModel
{
    [HiddenInput(DisplayValue = false)]
    public int? Id { get; set; }

    [HiddenInput(DisplayValue = false)]
    public int? TargetId { get; set; }

    [FileLink("SomeAction", "SomeController", "TargetId")]
    public string Name { get; set; }
}

У меня есть пользовательский MetadataProvider, полученный из DataAnnotationsModelMetadataProvider, который помещает значения атрибута в метаданные AdditionalValues ​​следующим образом:

    // retrieve the values to generate a file link
    var fileLinkAttributes = attributes.OfType<FileLinkAttribute>();
    if (fileLinkAttributes.Any())
    {
        var fileLinkAttribute = fileLinkAttributes.First();
        metadata.AdditionalValues["FileLinkAttribute"] = fileLinkAttribute;
        metadata.TemplateHint = "FileLink";
    }

Цель состоит в том, чтобы DisplayForModel вызвал DisplayTemplate ниже, чтобы сгенерировать ссылку со значением в TargetId из MyViewModel.Поэтому, если TargetId равно 5, ссылка будет «SomeController / SomeAction / 5»

// This DisplayTemplate for use with the "FileLinkAttribute"
//
// Usage: [FileLinkAttribute("ActionName","ControllerName","IdPropertyName")]

var fileLinkAttribute = (FileLinkAttribute)ViewData.ModelMetadata.AdditionalValues["FileLinkAttribute"];

var targetId = *<GET VALUE OF CONTAINER MODEL IdPropertyName>*;

@Html.ActionLink((string)ViewData.Model, fileLinkAttribute.ActionName, fileLinkAttribute.ControllerName, new { Id = targetId}, null)

. После всего этого я не могу получить доступ к содержащему объекту, чтобы получить значение свойства из TargetId.Я попытался сделать это в Атрибуте, в Поставщике и в DisplayTemplate, но безуспешно.

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

Ответы [ 2 ]

1 голос
/ 06 мая 2011

После прочтения этого поста, описывающего назначение modelAccessor в пользовательском DataAnnotationsModelMetadataProvider:

Что такое параметр "Func modelAccessor" для MVC в DataAnnotationsModelMetadataProvider?

Мне удалось собрать воедино это (очень хакерское) решение, реализованное в пользовательском провайдере:

    // retrieve the values to generate a file link
    var fileLinkAttributes = attributes.OfType<FileLinkAttribute>();
    if (fileLinkAttributes.Any())
    {
        var fileLinkAttribute = fileLinkAttributes.First();

        // modelAccessor contains a pointer the container, but the type is not correct but contains the name of the correct type
        if (modelAccessor.Target.GetType().ToString().Contains("System.Web.Mvc.AssociatedMetadataProvider"))
        {
            // get the container model for this metadata
            FieldInfo containerInfo = modelAccessor.Target.GetType().GetField("container");
            var containerObject = containerInfo.GetValue(modelAccessor.Target);

            // get the value of the requested property
            PropertyInfo pi = containerObject.GetType().GetProperty(fileLinkAttribute.IdPropertyName);
            string idPropertyValue = pi.GetValue(containerObject, null).ToString();
            fileLinkAttribute.IdPropertyValue = idPropertyValue;
        }

        metadata.AdditionalValues["FileLinkAttribute"] = fileLinkAttribute;
        metadata.TemplateHint = "FileLink";
    }

Я не уверен, как разыменовать Func, чтобы получить его реальную цель, и имя типа приходитв этом примере яростно разлагаем все, например,

"System.Web.Mvc.AssociatedMetadataProvider + <> c__DisplayClassb"

Поэтому я преобразую в строку, чтобы проверить,содержит "System.Web.Mvc.AssociatedMetadataProvider".Наверное, не самая лучшая вещь, но это единственный способ заставить его работать.

О, я также добавил 4-е свойство в атрибут, чтобы оно содержало значение:

[AttributeUsage (AttributeTargets).Property, AllowMultiple = false, Inherited = true)] открытый закрытый класс FileLinkAttribute: Attribute {открытая строка ActionName {get;задавать;} публичная строка ControllerName {get;задавать;} публичная строка IdPropertyName {get;задавать;} публичная строка IdPropertyValue {get;задавать;}

public FileLinkAttribute(string actionName, string controllerName, string idPropertyName)
{
    ActionName = actionName;
    ControllerName = controllerName;
    IdPropertyName = idPropertyName;
}

}

и в DisplayTemplate я делаю:

var modelId = fileLinkAttribute.IdPropertyValue;
0 голосов
/ 06 мая 2011

Это должен быть шаблон редактора для strsing или объекта, так как вы применяете этот атрибут.Так внутри ~/Views/Shared/EditorTemplates/string.cshtml:

@model string

@{
    var fileLinkAttribute = (FileLinkAttribute)ViewData.ModelMetadata.AdditionalValues["FileLinkAttribute"];
    var targetId = fileLinkAttribute.IdPropertyName;
}

@Html.ActionLink(Model, fileLinkAttribute.ActionName, fileLinkAttribute.ControllerName, new { Id = targetId}, null)

и на вашем главном экране:

@Html.EditorFor(x => x.Name)
...