本文介绍了从自定义 AdditionalMetadataAttribute 访问模型类实例(asp.net mvc 5)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have the following situation - I need to write a custom additional metadata attribute, that based on another property value (from the same model), adds a value to the AdditionalValues dictionary. Right now, my issue is that I'm not able to access the model instance inside my attribute class.

[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.

As you see from the code comments, inside OnMetadataCreated I need to access the Model class instance but, though ContainerType is correct, the Container property is NULL.

你能帮我就这个问题给我一个提示吗?

Can you please help me by giving me a hint regarding this issue?

提前致谢!

埃文丁

后期编辑

考虑到我没有给出太多解释,我还将在此处粘贴一个示例,说明我希望如何在模型类上使用此属性:

Considering that I haven't gave to much explanations, I will also paste here an example on how I would like to use this attribute on a model class:

/// <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 类并且在您的视图中使用它那么没关系,但是如果您在另一个类中有一个类(视图模型中有一个模型),则这种方法不再可用.

Though the response given by @stephen-muecke is more then simple and acceptable in current situation, for the sake of programming challenge I've looked for other options and I found the following possibility: implementing a custom DataAnnotationsModelMetadataProvider class. In few simple words - it works and I'm able to obtain the model class instance BUT only if the model class is a simple class, otherwise there are many drawbacks - for example if you have a Model class and you use it in your view then it's ok but if you have a class inside another class (a model inside a viewmodel) that this approach is not usable anymore.

再次感谢@stephen-muecke!

Thank you again @stephen-muecke!

推荐答案

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

Since you seem to need access to multiple properties of the model, the attribute should target class (AttributeTargets.Class) and be applied to the model, not a property. This might mean you need to add another property that is the name of the property you were trying to apply this to. Note metadata.ContainerType only gives you the type, not this instance so you can only get the default value of its properties.

编辑

如果属性需要应用到模型中的多个属性,那么你不能在OnMetadataCreated中访问容器,因为元数据是从最里面的属性创建的,所以模型的元数据还没有被创建.

If the attributes need to be applied to multiple properties in the model, then you cannot access the container in OnMetadataCreated because metadata is created from the innermost properties out so the model's metadata has not yet been created.

根据 OP 的评论,更好的解决方案是创建自定义 html 帮助程序.例如根据另一个属性的值生成一个只读的文本框

Based on OP's comments, a better solution would be to create a custom html helper. For example to generate a textbox that is readonly based on the value of another property

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 没有影响.为了防止用户交互,您需要禁用它,但这意味着该值不会回传

Creating a 'Readonly' checkbox is a little more difficult because the readonly attribute has no affect with a checkbox. In order to prevent user interaction you need to disable it but that means the value wont post back

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)

这篇关于从自定义 AdditionalMetadataAttribute 访问模型类实例(asp.net mvc 5)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-05 12:01