【问题标题】:Access model class instance from a custom AdditionalMetadataAttribute (asp.net mvc 5)从自定义 AdditionalMetadataAttribute (asp.net mvc 5) 访问模型类实例
【发布时间】:2014-10-21 08:37:12
【问题描述】:

我有以下情况 - 我需要编写一个自定义附加元数据属性,该属性基于另一个属性值(来自同一模型),将一个值添加到 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.
    }
}

正如您从代码 cmets 中看到的,在 OnMetadataCreated 内部我需要访问 Model 类实例,但是,尽管 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; } 

稍后编辑

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

再次感谢@stephen-muecke!

【问题讨论】:

  • 试试metadata.Model
  • 它没有帮助,因为metadata.Model 给了我应用属性的属性的值,我不需要这个值 - 我需要做的是添加一个值如果取自extraFieldToCheck 的属性值等于属性extraFieldValueToCheck 的值,则添加到AdditionalValues 字典。
  • 到目前为止,我已经尝试了以下方法 - 通过反射访问“其他”属性,如下所示:PropertyInfo modelProperty = metadata.ContainerType.GetProperty(this.extraFieldToCheck, BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static);modelProperty 始终为空。

标签: c# asp.net asp.net-mvc asp.net-mvc-4 asp.net-mvc-5


【解决方案1】:

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

编辑

如果需要将属性应用于模型中的多个属性,那么您将无法访问OnMetadataCreated 中的容器,因为元数据是从最里面的属性创建的,因此尚未创建模型的元数据。

基于 OP 的 cmets,更好的解决方案是创建自定义 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)

【讨论】:

  • 首先 - 这将(理论上)解决(理论上)获取类实例的问题,但另一方面它会使代码有点难看(请原谅,但这是我个人的意见)因为,如果(再次,理论上)我将有一个具有 25 个属性的模型,并且我需要将这个属性放在其中的 10 个上,它会变得非常非常难看......其次,我已经尝试过你的方法,虽然我已经将属性范围声明为 class 并且我用这个属性装饰了模型类,但 OnMetadataCreated 方法没有被调用......不知道为什么...... :(
  • 我同意,如果它适用于多个属性,这不是正确的解决方案。至于不起作用,不知道为什么 - 它确实对我有用。
  • 只是为了提供一些额外的解释,元数据是从内到外创建的,因此模型(类)的元数据是最后创建的。您还没有解释上下文,但我假设您有一个自定义的 html 帮助程序。如果是这种情况,那么您将值添加到字典 (metadata.AdditionalValues["SomeKey"] = extraFieldToCheck),然后在可以访问完整元数据的帮助程序中,您可以通过 metadata.Properties 访问这些键
  • 是的 - 我认为你是对的 - 我没有对我的目标做出太多解释......简而言之,一个真实的例子:我有一个包含例如字段的表格 - IsExternalAccount;根据这个字段值,我需要在 AdditionalValues 字典中添加一个值,比如说 ReadOnly 用于其他字段;例如:[ExtendedAdditionalMetadata("IsExternalAccount", "true", "ReadOnly", "true")] public override Boolean IsAccountCreated { get; set; }。我想要实现的是向字典添加(基于条件)一个值。就是这样……
  • 我认为您以错误的方式解决此问题并使问题过于复杂。如果IsExternalAccount 为真(如果为假,则可编辑),自定义帮助器,例如@Html.ReadOnlyIfFor(m =&gt; m.IsAccountCreated, Model.IsExternalAccount),它为IsAccountCreated 呈现只读控件(如果为假,则可编辑)会更简单且更可重用。已经晚了,但如果您需要进一步的帮助,我会在早上看看。
猜你喜欢
  • 2013-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-27
  • 2016-03-08
  • 1970-01-01
  • 2016-03-15
  • 1970-01-01
相关资源
最近更新 更多