ASP.NET MVC:检索存储在属性中的信息

ASP.NET MVC: Retrieving Information Stored in Attributes

我的站点中有一个视图显示我的应用程序的所有控制器及其操作方法:
操作方法:

public ActionResult GetAllController()
{
    var controllers = typeof (MvcApplication).Assembly.GetTypes().Where(typeof (IController).IsAssignableFrom);
    return View(controllers.ToList());
}

查看:

<ul>
    @foreach (var item in Model)
    {
            <li>
                @item.Name
                <ul>
                    @foreach (var action in item.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(method => typeof(ActionResult).IsAssignableFrom(method.ReturnType)))
                    {
                        <li>action.Name</li>
                    }
                </ul>

            </li>
    }

</ul>

它就像一个魅力:

HomeController
     Index
     About
MainController
     Index
     Create
     Edit
     Delete
...

现在我想为控制器和操作方法显示另一个名称。为此,我创建了一个自定义属性:

public class DisplayNameAttribute : FilterAttribute
{
        public string Title { get; set; }

        public DisplayNameAttribute(string title)
        {
            this.Title = title;
        }

}

所以在这种情况下,我只是为每个控制器或操作方法设置该属性,如下所示:

[DisplayName("Latest News")]
public ActionResult News()
{
     return View();
}

在这种情况下,我创建了一个使用内部视图的扩展方法:

public static string DisplayAttribute<T>(this T obj, Expression<Func<T, string>> value)
        {
            var memberExpression = value.Body as MemberExpression;
            var attr = memberExpression.Member.GetCustomAttributes(typeof(DisplayNameAttribute), true);
            return ((DisplayNameAttribute)attr[0]).Title;
        }

所以在内部视图中,我使用这种方式来显示操作方法或控制器的标题:

@item.DisplayAttribute(p => p.Name)

但是当我 运行 申请时,我会得到这个错误:

{"Index was outside the bounds of the array."}

从这行代码抛出:

return ((DisplayNameAttribute)attr[0]).Title;

有什么想法吗?

仔细观察一下,我发现您的代码存在多个问题。不过,主要问题是您尝试读取框架的属性 属性,并期望它包含您自己定义的属性(据我所知,这甚至是不可能的)。

那么,让我们来分析一下这个调用@item.DisplayAttribute(p => p.Name)。我在这里假设,item 在您之前的剃刀代码中指的是相同的 item,这意味着它具有 MethodInfo 的类型(来自 System.Reflection)。然后您使用以下表达式调用您的方法 DisplayAttribute

DisplayAttribute<MethodInfo>(MethodInfo item, Expression<Func<MethodInfo, string>> mi => mi.Name);

请记住,这实际上不是 C# 中的有效调用,但我添加了类型信息以便更容易地显示正在发生的事情。

现在,您在 DisplayAttribute 方法中获得的 MemberExpression 根本不是控制器方法,而是 属性 string MethodInfo.Name {get;}。这是一个框架属性(据我所知是在mscorlib中定义的),显然没有你定义的属性.

您可能想做的是类似于此的事情:

public static string GetAttributedName(this ICustomAttributeProvider attributeProvider) {
    return attributeProvider.GetCustomAttributes(typeof(DisplayNameAttribute), true).Select(a => a.Title).FirstOrDefault() ?? "No title found";
}

最后的位"No title found"只是为了更容易找到问题所在(如果这不起作用)。如果你想让它在没有属性的情况下抛出异常,只需将FirstOrDefault替换为First,并删除方法调用后的部分

[编辑]

忘了说:用法是这样的:@item.GetAttributedName()

[编辑 2]

我做了一些改进,你可以在这里看到一个基本可用的示例:https://dotnetfiddle.net/7H5ITo

如果元素操作或控制器未使用 DisplayName 属性修饰,您将收到 IndexOutOfBounds 异常是很正常的。您可以在扩展方法中检查它:

public static string DisplayAttribute<T>(this T obj, Expression<Func<T, string>> value)
{
    var memberExpression = value.Body as MemberExpression;
    var attr = memberExpression.Member.GetCustomAttributes(typeof(DisplayNameAttribute), true);
    if (attr.Length > 0)
    {
        return ((DisplayNameAttribute)attr[0]).Title;
    }

    // Return some default value
    return memberExpression.Member.Name;
}

对于您的操作的另一个名称,请使用 Display(name="") 属性。 例如:

Display(name="View Action Name")
public ActionResult ActionName()
{
  return View();
}

获取动作视图名称,在视图中修改为

@foreach (var item in Model)
    {
        <li>
            @item.Name
            <ul>
                @foreach (var action in item.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Where(method => typeof(ActionResult).IsAssignableFrom(method.ReturnType)))
                {
                    try
                    {
                        <li>@action.CustomAttributes.SingleOrDefault(m => m.AttributeType.Name == "DisplayAttribute").NamedArguments.ElementAt(0).TypedValue</li>
                    }
                    catch(NullReferenceException){}



                }
            </ul>

        </li>
    }