从自定义属性中获取模型的属性值

时间:2011-05-06 18:40:07

标签: asp.net-mvc-3 attributes data-annotations

我有一个名为FileLink的自定义属性,它使用DisplayForModel()为具有ID值的控制器生成ActionLink

该属性如下所示:

[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;
    }
}

是否在viewmodel中使用,如下所示:

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中提取属性值。我试图在Attribute中,Provider内和DisplayTemplate中执行此操作,但没有运气。

我是否有办法或地方可以访问此值以实现从包含该属性的viewmodel对象中读取属性值的意图?

2 个答案:

答案 0 :(得分:1)

阅读此帖后,在自定义DataAnnotationsModelMetadataProvider中描述modelAccessor的用途:

What is the "Func<object> modelAccessor" parameter for in MVC's DataAnnotationsModelMetadataProvider?

我能够将自定义提供程序中实现的这个(非常hackish)解决方案拼凑起来:

    // 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 + LT;&GT; c__DisplayClassb”

所以我转换为一个字符串来检查它是否包含“System.Web.Mvc.AssociatedMetadataProvider”。可能不是最好的东西,但这是我能让它发挥作用的唯一方法。

哦,我还在属性中添加了第4个属性以包含值:

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

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

}

并在DisplayTemplate中执行:

var modelId = fileLinkAttribute.IdPropertyValue;

答案 1 :(得分:0)

这应该是strsing或object的编辑器模板,就像你应用属性一样。所以在~/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)