【问题标题】:ASP.NET MVC: Retrieving Information Stored in AttributesASP.NET MVC:检索存储在属性中的信息
【发布时间】:2015-03-08 12:50:15
【问题描述】:

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

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;

有什么想法吗?

【问题讨论】:

  • 渲染出来的html是什么样子的?这可能是可用的前端吗?
  • 很简单。您正在尝试引用不存在的数组中的索引,即 attr.Length 将为 0,但您要求获取 attr 数组中的第一项 - 没有第一项。始终在访问对象/数组之前对其进行验证。
  • 您在操作中使用ControllerName 而不是DisplayName 是不是错字?如果没有,那可能是你的问题。
  • 另外,你可能应该从Attribute而不是FilterAttribute派生你的属性,这样MVC就不会将它添加到过滤器管道中。
  • @jbutler483 正如我提到的结果是这个错误:Index was outside the bounds of the array.

标签: c# asp.net-mvc custom-attributes


【解决方案1】:

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

那么,让我们分析一下调用@item.DisplayAttribute(p =&gt; 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

【讨论】:

  • 太好了 :) 非常感谢
【解决方案2】:

如果元素操作或控制器没有使用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;
}

【讨论】:

    【解决方案3】:

    对于您的操作的另一个名称,请使用 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>
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-14
      • 1970-01-01
      • 2014-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-12
      相关资源
      最近更新 更多