C# 从自定义AdditionalMetadataAttribute(asp.net mvc 5)访问模型类实例
我有以下情况-我需要编写一个自定义的附加元数据属性,该属性基于另一个属性值(来自同一个模型),向AdditionalValues字典添加一个值。现在,我的问题是无法访问属性类中的模型实例C# 从自定义AdditionalMetadataAttribute(asp.net mvc 5)访问模型类实例,c#,asp.net,asp.net-mvc,asp.net-mvc-4,asp.net-mvc-5,C#,Asp.net,Asp.net Mvc,Asp.net Mvc 4,Asp.net Mvc 5,我有以下情况-我需要编写一个自定义的附加元数据属性,该属性基于另一个属性值(来自同一个模型),向AdditionalValues字典添加一个值。现在,我的问题是无法访问属性类中的模型实例 [AttributeUsage(AttributeTargets.Property)] public class ExtendedAdditionalMetadataAttribute : Attribute, IMetadataAware { #region Private properties
[AttributeUsage(AttributeTargets.Property)]
public class ExtendedAdditionalMetadataAttribute : Attribute, IMetadataAware
{
#region Private properties
private string extraFieldToCheck { get; set; }
private string extraFieldValueToCheck { get; set; }
private string fieldToBeAdded { get; set; }
private string fieldValueToBeAdded { get; set; }
#endregion
#region Constructor
public ExtendedAdditionalMetadataAttribute(string extraFieldToCheck, string extraFieldValueToCheck,
string fieldToBeAdded, string fieldValueToBeAdded)
{
this.extraFieldToCheck = extraFieldToCheck;
this.extraFieldValueToCheck = extraFieldValueToCheck;
this.fieldToBeAdded = fieldToBeAdded;
this.fieldValueToBeAdded = fieldValueToBeAdded;
}
#endregion
public void OnMetadataCreated(ModelMetadata metadata)
{
// HOW TO GET THE MODEL CLASS INSTANCE???
// metadata.ContainerType is correct by metadata.Container is null.
}
}
正如您从代码注释中看到的,在OnMetadataCreated内部,我需要访问模型类实例,但是,尽管ContainerType是正确的,但Container属性为NULL
你能帮我就这个问题给我一个提示吗
提前谢谢你
埃夫丁
后期编辑
考虑到我没有给出太多解释,我还将在这里粘贴一个示例,说明如何在模型类上使用此属性:
/// <summary>
/// Gets or sets the IsAccountCreated
/// </summary>
/// <value>The IsAccountCreated.</value>
[UIHint("FormFieldStringTemplate")]
[ExtendedAdditionalMetadata("IsExternalAccount", "true", "ReadOnly", "true")]
public override Boolean IsAccountCreated { get; set; }
/// <summary>
/// Gets or sets the IsAccountEnabled
/// </summary>
/// <value>The IsAccountEnabled.</value>
[Display(Name = "Este cont activ?")]
[UIHint("FormFieldStringTemplate")]
[ExtendedAdditionalMetadata("IsExternalAccount", "true", "ReadOnly", "true")]
public override Boolean IsAccountEnabled { get; set; }
/// <summary>
/// Gets or sets the IsExternalAccount
/// </summary>
/// <value>The IsExternalAccount.</value>
[Display(Name = "Este cont extern?")]
[UIHint("FormFieldStringTemplate")]
[AdditionalMetadata("ReadOnly", "true")]
public override Boolean IsExternalAccount { get; set; }
//
///获取或设置创建的ISAccount
///
///ISAccount已创建。
[UIHint(“FormFieldStringTemplate”)]
[ExtendedAdditionalMetadata(“IsExternalAccount”、“true”、“ReadOnly”、“true”)]
公共重写布尔值IsAccountCreated{get;set;}
///
///获取或设置ISAccountable
///
///这件事是可以解释的。
[显示(Name=“Este cont activ?”)]
[UIHint(“FormFieldStringTemplate”)]
[ExtendedAdditionalMetadata(“IsExternalAccount”、“true”、“ReadOnly”、“true”)]
公共重写布尔值ISAccountable{get;set;}
///
///获取或设置IsExternalAccount
///
///IsExternalAccount。
[显示(Name=“Este cont extern?”)]
[UIHint(“FormFieldStringTemplate”)]
[附加元数据(“只读”、“真”)]
公共重写布尔IsExternalAccount{get;set;}
以后编辑和以后编辑
尽管@stephen muecke给出的响应在当前情况下更加简单和可接受,但为了编程挑战,我已经寻找了其他选项,并发现了以下可能性:实现自定义DataAnnotationsModelMetadataProvider
类。简单地说,它是有效的,我能够获得模型类实例,但只有当模型类是一个简单类时,否则会有很多缺点-例如,如果您有一个模型类,并且您在视图中使用它,那么这是可以的,但是如果您在另一个类中有一个类(viewmodel中的模型)这种方法已经不可用了
再次感谢您@stephen muecke 由于您似乎需要访问模型的多个属性,因此该属性应以
类
(AttributeTargets.class
)为目标,并应用于模型,而不是属性。这可能意味着您需要添加另一个属性,该属性是您试图将此应用于的属性的名称。注意元数据。ContainerType
仅提供类型,而不是此实例,因此只能获取其属性的默认值
编辑
如果需要将属性应用于模型中的多个属性,则无法访问OnMetadataCreated
中的容器,因为元数据是从最里面的属性创建的,因此尚未创建模型的元数据
根据OP的评论,更好的解决方案是创建一个定制的html助手。例如,根据另一个属性的值生成只读的文本框
namespace MyHelpers.Html
{
public static class ReadOnlyHelpers
{
public static MvcHtmlString ReadOnlyTextBoxIf<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, bool isReadOnly)
{
object attributes = isReadOnly ? new { @readonly = "readonly" } : null;
return InputExtensions.TextBoxFor(helper, expression, attributes);
}
}
}
创建“只读”复选框有点困难,因为Readonly
属性对复选框没有影响。为了防止用户交互,您需要禁用它,但这意味着该值不会发回
public static MvcHtmlString ReadOnlyCheckBoxIf<TModel>(this HtmlHelper<TModel> helper, Expression<Func<TModel, bool>> expression, bool isReadOnly)
{
if (isReadOnly)
{
// If you want to 'visually' render a checkbox (otherwise just render a div with "YES" or "NO")
ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
StringBuilder html = new StringBuilder();
// Add a hidden input for postback
html.Append(InputExtensions.HiddenFor(helper, expression).ToString());
// Add a visual checkbox without name so it does not post back
TagBuilder checkbox = new TagBuilder("input");
checkbox.MergeAttribute("type", "checkbox");
checkbox.MergeAttribute("disabled", "disabled");
if ((bool)metaData.Model)
{
checkbox.MergeAttribute("checked", "checked");
}
html.Append(checkbox.ToString());
return MvcHtmlString.Create(html.ToString());
}
else
{
// return normal checkbox
return InputExtensions.CheckBoxFor(helper, expression);
}
}
尝试metadata.Model
它没有帮助,因为metadata.Model
为我提供了应用属性的属性的值,我不需要这个值-如果从extraFieldToCheck
获取的属性值等于属性的值extraFieldValueToCheck
。到目前为止,我已经尝试了以下方法-通过这样的反射访问“other”属性:PropertyInfo modelProperty=metadata.ContainerType.GetProperty(this.extraFieldToCheck,bindingsflags.Public | bindingsflags.NonPublic | bindingsflags.Static)
但是modelProperty
总是空的。首先,这(理论上)只能解决获取类实例的问题,但另一方面,它会使代码更难看(请原谅,但这是我个人的观点),因为我将有一个有25个属性的模型,我需要把这个属性放在其中的10个上,它会变得非常丑陋。。。其次,我尝试了您的方法,虽然我已经声明了属性作用域为class,并且用这个属性装饰了模型类,但是没有调用OnMetadataCreated
方法。。。不知道为什么…:(我同意,如果它适用于多个属性,那么这不是正确的解决方案。至于不起作用,不确定为什么-它对我来说确实有效。只是为了给出一些额外的解释,元数据是从内部到外部创建的,因此模型(类)的元数据是最后创建的。您尚未解释此操作的上下文,但我假设您有一个自定义html帮助程序。如果是这种情况,请将值添加到字典中(metadata.AdditionalValues[“SomeKey”]=extraFieldToCheck
),在帮助程序中,您可以访问完整的metatdata,然后可以通过元数据访问这些键。属性是的-我认为您是对的-我没有对我的目标给出太多解释…简单地说,这是一个真实的示例:我有一个表,其中包含一个字段,例如-IsExternalAccount
;取决于此字段lue,我需要为其他字段向AdditionalValues字典添加一个名为ReadOnly
的值;例如:[ExtendedAdditionalMetadata(“IsExternalAccount”、“true”、“ReadOnly”、“true”)]public overri
public static MvcHtmlString ReadOnlyCheckBoxIf<TModel>(this HtmlHelper<TModel> helper, Expression<Func<TModel, bool>> expression, bool isReadOnly)
{
if (isReadOnly)
{
// If you want to 'visually' render a checkbox (otherwise just render a div with "YES" or "NO")
ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
StringBuilder html = new StringBuilder();
// Add a hidden input for postback
html.Append(InputExtensions.HiddenFor(helper, expression).ToString());
// Add a visual checkbox without name so it does not post back
TagBuilder checkbox = new TagBuilder("input");
checkbox.MergeAttribute("type", "checkbox");
checkbox.MergeAttribute("disabled", "disabled");
if ((bool)metaData.Model)
{
checkbox.MergeAttribute("checked", "checked");
}
html.Append(checkbox.ToString());
return MvcHtmlString.Create(html.ToString());
}
else
{
// return normal checkbox
return InputExtensions.CheckBoxFor(helper, expression);
}
}
@Html.ReadOnlyCheckBoxIf(m => m.IsAccountCreated, Model.IsExternalAccount)