简体   繁体   English

如何以编程方式更新 DisplayAttribute 上的 Order 属性?

[英]How do I update the Order property on a DisplayAttribute programmatically?

Given the following code:鉴于以下代码:

using System.ComponentModel.DataAnnotations;
using System.Linq;
using Xunit;

namespace Company.Tests
{
    public class MyObject
    {
        [Display(Order = 1000)]
        public virtual string StringPropertyB { get; set; }

        [Display(Order = 2000)]
        public virtual string StringPropertyA { get; set; }
    }

    public class MyObjectTest
    {
        [Fact]
        public void X()
        {
            var properties = typeof(MyObject).GetProperties();
            var stringPropertyBPropertyInfo = properties[0];
            var stringPropertyAPropertyInfo = properties[1];

            // Debugger Display = "{[System.ComponentModel.DataAnnotations.DisplayAttribute(Order = 1000)]}"
            var bDisplayAttribute = stringPropertyBPropertyInfo.GetCustomAttributesData().FirstOrDefault();

            // Debugger Display = "{[System.ComponentModel.DataAnnotations.DisplayAttribute(Order = 2000)]}"
            var aDisplayAttribute = stringPropertyAPropertyInfo.GetCustomAttributesData().FirstOrDefault();
        }
    }
}

How do I update the Order property programmatically?如何以编程方式更新 Order 属性?

I want to update it after it's been set to a new value.我想在将其设置为新值后对其进行更新。 (Use case being not having to specify the Order but instead assign the Order a value automatically to match the order that the properties on MyObject appear from top to bottom.) (用例不必指定 Order 而是自动为 Order 分配一个值以匹配 MyObject 上的属性从上到下出现的顺序。)

Just as Mr Skeet wrote in the answer that Thumper pointed to this can't be done as far as I can tell.正如 Skeet 先生在回答中所写的那样,据我所知,Thumper 指出这无法完成。 You can update the Order value but it won't persist:您可以更新 Order 值,但它不会持续存在:

using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using Shouldly;
using Xunit;

namespace Tests
{
    public class ObjectWithDisplayOrder
    {
        [Display(Order = 0)]
        public virtual string StringPropertyB { get; set; }

        [Display(Order = 0)]
        public virtual string StringPropertyA { get; set; }
    }

    public class DisplayOrderTests
    {
        [Fact]
        public void ShouldUpdateDisplayOrderProperty()
        {
            const int updatedOrderValue = 1000;

            var properties = typeof(ObjectWithDisplayOrder).GetProperties();
            foreach (var property in properties)
            {
                var displayAttribute = (DisplayAttribute) property.GetCustomAttributes().First(a => a is DisplayAttribute);
                var props = displayAttribute.GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).ToList();
                props.Single(p => p.Name == "Order").SetValue(displayAttribute, updatedOrderValue);
                // displayAttribute Order is 1000 here, but it's not persisted...
            }

            foreach (var property in properties)
            {
                var displayAttribute = (DisplayAttribute) property.GetCustomAttributes().First(a => a is DisplayAttribute);
                displayAttribute.GetOrder().ShouldBe(updatedOrderValue); // Fails - Order is still 0
            }
        }
    }
}

Instead of what you have do this而不是你所拥有的做这个

public class MyObject
{
    [DefaultValue(1000)]
    public virtual int StringPropertyBOrder { get; set; }
    public virtual string StringPropertyB { get; set; }

    [DefaultValue(2000)]
    public virtual int StringPropertyAOrder { get; set; }
    public virtual string StringPropertyA { get; set; }
}

Updated Answer: Because attributes are "static metadata", they cannot be persistently changed after compilation.更新答案:因为属性是“静态元数据”,编译后不能持久更改。 ( Can attributes be added dynamically in C#? ) 可以在 C# 中动态添加属性吗?

So, one way to accomplish OP's request to make persistent changes to Attribute values is to compile the code at runtime .因此,完成 OP 对属性值进行持久更改的请求的一种方法是在运行时编译代码 If this is what's needed, it may be worth seriously considering a different approach to the problem you are trying to solve, since this means a lot more work for you (but it's super cool to me).如果这是需要的,那么认真考虑采用不同的方法来解决您要解决的问题可能是值得的,因为这对您来说意味着更多的工作(但这对我来说非常酷)。

There is also the possiblity of using something called TypeBuilder ( How Can I add properties to a class on runtime in C#? ).还有可能使用称为TypeBuilder 的东西( 如何在 C# 中在运行时向类添加属性? )。 I am not familiar with this, but it looks promising.我对此并不熟悉,但看起来很有希望。 It does also look like it uses runtime compilation.它看起来也像使用运行时编译。

Runtime Compiling:运行时编译:

Here is one way this can be accomplished, by compiling code at runtime.这是可以通过在运行时编译代码来实现的一种方法。 (Using NUnit, not XUnit, as OP has done): (使用 NUnit,而不是 XUnit,就像 OP 所做的那样):

namespace OpUnitTest {

    [TestClass]
    public class OpTest{

        //Use some web templating model so we can easily change it later (#=variable#)
        string myClassToCompile = @"

using System.ComponentModel.DataAnnotations;

namespace Test {
    public class ObjectWithDisplayOrder {
        [Display(Order = #=0#)]
        public virtual string StringPropertyB { get; set; }

        [Display(Order = #=1#)]
        public virtual string StringPropertyA { get; set; }
    }
}
";
        [TestMethod]
        public void AssignAtributeValuesDynamically() {

            const int order = 1000;     

            //Escape curly braces.
            myClassToCompile = myClassToCompile.Replace("{", "{{").Replace("}", "}}");

            //We could use Regex, or even a for loop, to make this more-elegant and scalable, but this is a Proof of Concept.
            myClassToCompile = myClassToCompile.Replace("#=0#", "{0}").Replace("#=1#", "{1}");

            myClassToCompile = string.Format(myClassToCompile, order, order);

            CSharpCodeProvider provider = new CSharpCodeProvider();
            CompilerParameters parameters = new CompilerParameters();
            parameters.ReferencedAssemblies.Add("System.ComponentModel.DataAnnotations.dll");
            parameters.GenerateInMemory = true;
            CompilerResults results = provider.CompileAssemblyFromSource(parameters, myClassToCompile);

            //You would normally check for compilation errors, here.

            Assembly assembly = results.CompiledAssembly;
            Type myCompiledObject = assembly.GetType("Test.ObjectWithDisplayOrder");
            PropertyInfo[] properties = myCompiledObject.GetProperties();

            foreach (var property in properties) {
                var displayAttribute = (DisplayAttribute)property.GetCustomAttributes().First(a => a is DisplayAttribute);
                Assert.AreEqual(order, displayAttribute.GetOrder());

            }
        }
}

A good ramp-up guide on runtime compiling in C# (something that is a bit of a passion of mine): http://www.codeproject.com/Tips/715891/Compiling-Csharp-Code-at-Runtime在 C# 中运行时编译的一个很好的入门指南(这是我的一点热情): http : //www.codeproject.com/Tips/715891/Compiling-Csharp-Code-at-Runtime

Original Answer:原答案:

Technically, it demonstrates that you can, indeed, change the attribute's value, but as OP pointed out - this doesn't persist.从技术上讲,它表明您确实可以更改属性的值,但正如 OP 所指出的那样 - 这不会持续存在。

using System.ComponentModel.DataAnnotations;
using System.Linq;
using Xunit;

namespace Company.Tests
{
    public class MyObject
    {
        [Display(Order = 1000)]
        public virtual string StringPropertyB { get; set; }

        [Display(Order = 2000)]
        public virtual string StringPropertyA { get; set; }
    }

    public class MyObjectTest
    {
        [Fact]
        public void X()
        {
            var properties = typeof(MyObject).GetProperties();
            var stringPropertyBPropertyInfo = properties[0];
            var bDisplayAttribute = (DisplayAttribute)stringPropertyBPropertyInfo.GetCustomAttributes().First();
            var props = bDisplayAttribute.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public).ToList();
            props.Single(p => p.Name == "Order").SetValue(bDisplayAttribute, 5);

        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何通过Reflection获取属性的DisplayAttribute? - How to get DisplayAttribute of a property by Reflection? 如何显示 DisplayAttribute.Description 属性值? - How do I display the DisplayAttribute.Description attribute value? 如何在 WPF 中以编程方式添加或删除 DisplayAttribute - How to add or remove DisplayAttribute programmatically in WPF 使用ASP.NET MVC 5,如何在View中获取Enum值的本地化DisplayAttribute字符串? - Using ASP.NET MVC 5, how do I get the localized DisplayAttribute string for an Enum value in a View? 如何以编程方式读取附加依赖属性的值? - How do I programmatically read the value of an attached dependency property? 如何以编程方式获取 NotifyIcon.Icon 属性的值? - How do I get the value of NotifyIcon.Icon property programmatically? 如何以编程方式在aspx文件中设置控件的属性? - How do I programmatically set property of control in aspx file? DisplayAttribute名称属性在Silverlight中不起作用 - DisplayAttribute name property not working in Silverlight 如何从接口的DisplayAttribute检索数据? - How can I retrieve data from DisplayAttribute of interface? 以编程方式将工作表添加到Excel时,如何设置工作表的顺序? - How do I set the order of Worksheets as I add them to Excel Programmatically?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM