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>
}
我的站点中有一个视图显示我的应用程序的所有控制器及其操作方法:
操作方法:
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>
}