【问题标题】:ModelMetaData, Custom Class Attributes and an indescribable questionModelMetaData、自定义类属性和一个难以形容的问题
【发布时间】:2011-10-13 16:05:59
【问题描述】:

我想做的事情似乎很简单。

在我的 index.cshtml 中,我想显示 WizardStepAttribute

因此,用户将在每个页面的顶部看到Step 1: Enter User Information


我有一个名为 WizardViewModel 的 ViewModel。这个 ViewModel 有一个属性是 IList<IStepViewModel> Steps

每个“步骤”都实现了接口IStepViewModel,它是一个空接口。

我有一个名为 Index.cshtml 的视图。此视图显示EditorFor() 当前步骤。

我有一个自定义 ModelBinder,它将 View 绑定到基于 WizardViewModel.CurrentStepIndex 属性实现 IStepViewModel 的具体类的新实例

我创建了一个自定义属性WizardStepAttribute

我的每个 Steps 类都是这样定义的。

[WizardStepAttribute(Name="Enter User Information")] 
[Serializable]
public class Step1 : IStepViewModel
....

不过我有几个问题。

我的视图被强输入到WizardViewModel,而不是每一步。我不想为IStepViewModel的每个具体实现创建一个视图

我以为我可以向接口添加一个属性,但是我必须在每个类中显式地实现它。 (所以这也好不到哪里去)

我想我可以在接口中使用反射来实现它,但是你不能在接口的方法中引用实例。

【问题讨论】:

    标签: c# asp.net-mvc-3 model-binding modelmetadata


    【解决方案1】:

    可以做到,但既不简单也不漂亮。

    首先,我建议将第二个字符串属性添加到您的 WizardStepAttribute 类 StepNumber,以便您的 WizardStepAttribute 类如下所示:

    [AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
    public class WizardStepAttribute : Attribute
    {
        public string StepNumber { get; set; }
        public string Name { get; set; }
    }
    

    然后,每个类都必须被装饰:

    [WizardAttribute(Name = "Enter User Information", StepNumber = "1")]
    public class Step1 : IStepViewModel
    {
        ...
    }
    

    接下来,您需要创建一个自定义 DataAnnotationsModelMetadataProvider,以获取自定义属性的值并将它们插入到 Step1 模型的元数据中:

    public class MyModelMetadataProvider : DataAnnotationsModelMetadataProvider 
    {
        protected override ModelMetadata CreateMetadata(
            IEnumerable<Attribute> attributes,
            Type containerType,
            Func<object> modelAccessor,
            Type modelType,
            string propertyName)
        {
            var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
            var additionalValues = attributes.OfType<WizardStepAttribute>().FirstOrDefault();
    
            if (additionalValues != null)
            {
                modelMetadata.AdditionalValues.Add("Name", additionalValues.Name);
                modelMetadata.AdditionalValues.Add("StepNumber", additionalValues.StepNumber);
            }
            return modelMetadata;
        }
    }
    

    然后,为了呈现您的自定义元数据,我建议创建一个自定义 HtmlHelper 来为每个视图创建您的标签:

        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcHtmlString WizardStepLabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression)
        {
            return WizardStepLabelFor(htmlHelper, expression, null /* htmlAttributes */);
        }
    
        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcHtmlString WizardStepLabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
        {
            return WizardStepLabelFor(htmlHelper, expression, new RouteValueDictionary(htmlAttributes));
        }
    
        [SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "Users cannot use anonymous methods with the LambdaExpression type")]
        [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
        public static MvcHtmlString WizardStepLabelFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("expression");
            }
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            var values = metadata.AdditionalValues;
    
            // build wizard step label
            StringBuilder labelSb = new StringBuilder();
            TagBuilder label = new TagBuilder("h3");
            label.MergeAttributes(htmlAttributes);
            label.InnerHtml = "Step " + values["StepNumber"] + ": " + values["Name"]; 
            labelSb.Append(label.ToString(TagRenderMode.Normal));
    
            return new MvcHtmlString(labelSb.ToString() + "\r");
        }
    

    如您所见,自定义助手使用您的自定义元数据创建一个 h3 标签。

    那么,最后,在你看来,输入以下内容:

    @Html.WizardStepLabelFor(model => model)
    

    两个注意事项:首先,在您的 Global.asax.cs 文件中,将以下内容添加到 Application_Start():

            ModelMetadataProviders.Current = new MyModelMetadataProvider();
    

    其次,在 Views 文件夹的 web.config 中,确保为您的自定义 HtmlHelper 类添加命名空间:

    <system.web.webPages.razor>
      <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <pages pageBaseType="System.Web.Mvc.WebViewPage">
        <namespaces>
          <add namespace="System.Web.Mvc" />
          <add namespace="System.Web.Mvc.Ajax" />
          <add namespace="System.Web.Mvc.Html" />
          <add namespace="System.Web.Routing" />
          <add namespace="YOUR NAMESPACE HERE"/>
        </namespaces>
      </pages>
    </system.web.webPages.razor>
    

    瞧。

    辅导员

    【讨论】:

    • 这看起来很棒。你又在我身下点燃了一把火......我已经放弃了,只是在我的界面上打了一个属性,我所有的具体类都实现了String WizardStepName..
    • 这引入了我之前遇到的同样问题....@Html.WizardStepLabelFor(model =&gt; model) 传入的模型始终是 IStepViewModel 类型。因此,没有添加任何属性,因为注释位于接口的具体类实现上
    • Doug,在我的测试视图中,我使用了@model MyApp.Models.IStepViewModel,在我的控制器中,我将一个新的 Step1 模型传递给了视图。 Step1 模型的注释通过了,我在生成的 HTML 中得到了所需的标签。也许我没有正确理解您的要求。尽管我对您为什么不将具体模型传递给视图感到困惑,因为您似乎拥有传递具体模型所需的一切。
    • 好吧,我认为问题是由于我的知识有限。现在。
    【解决方案2】:

    在我们的例子中,我们只需要一个实现IMetadataAware 接口的属性:

    https://msdn.microsoft.com/en-us/library/system.web.mvc.imetadataaware(v=vs.118).aspx

    在你的情况下,这可能是:

    public class WizardStepAttribute : Attribute, IMetadataAware
    {
        public string Name;
    
        public void OnMetadataCreated(ModelMetadata metadata)
        {
            if (!metadata.AdditionalValues.ContainsKey("WizardStep"))
            {
                metadata.AdditionalValues.Add("WizardStep", Name);
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-19
      • 1970-01-01
      • 2015-06-05
      相关资源
      最近更新 更多