从自定义AdditionalMetadataAttribute(asp.net mvc 5)访问模型类实例

本文关键字:访问 模型 实例 mvc AdditionalMetadataAttribute 自定义 asp net | 更新日期: 2023-09-27 18:27:13

我有以下情况-我需要编写一个自定义的附加元数据属性,该属性基于另一个属性值(来自同一模型),向AdditionalValues字典添加一个值。现在,我的问题是无法访问属性类中的模型实例。

[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中,我需要访问Model类实例,但是,尽管ContainerType是正确的,Container属性是NULL。

关于这个问题,你能给我一个提示吗?

提前感谢!

Evdin

稍后编辑

考虑到我没有给出太多解释,我还将在这里粘贴一个关于如何在模型类上使用此属性的示例:

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

稍后&后期编辑

尽管@stephen-muecke给出的响应在当前情况下更加简单和可接受,但出于编程挑战的考虑,我已经寻找了其他选项,并发现了以下可能性:实现自定义DataAnnotationsModelMetadataProvider类。简单地说,它是有效的,我能够获得模型类实例,但只有当模型类是一个简单的类时,否则就会有很多缺点。例如,如果你有一个model类,并且你在视图中使用它,那么它是可以的,但如果你在另一个类中有一个类(视图模型中的模型),这种方法就不再可用了。

再次感谢你@stephen muecke!

从自定义AdditionalMetadataAttribute(asp.net mvc 5)访问模型类实例

由于您似乎需要访问模型的多个属性,因此该属性应该以classAttributeTargets.Class)为目标并应用于模型,而不是属性。这可能意味着您需要添加另一个属性,该属性是您尝试将其应用到的属性的名称。注意,metadata.ContainerType只提供type,而不是此实例,因此您只能获得其属性的默认值。

编辑

如果需要将属性应用于模型中的多个属性,则无法访问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);
    }
  }
}

并在您看来用作

@Html.ReadOnlyTextBoxIf(m => m.SomeTextProperty, Model.SomeBooleanValue)

创建"只读"复选框有点困难,因为readonly属性对checkbox没有影响。为了防止用户交互,你需要禁用它,但这意味着该值不会返回

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)