簡體   English   中英

如何以編程方式更新 DisplayAttribute 上的 Order 屬性?

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

鑒於以下代碼:

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();
        }
    }
}

如何以編程方式更新 Order 屬性?

我想在將其設置為新值后對其進行更新。 (用例不必指定 Order 而是自動為 Order 分配一個值以匹配 MyObject 上的屬性從上到下出現的順序。)

正如 Skeet 先生在回答中所寫的那樣,據我所知,Thumper 指出這無法完成。 您可以更新 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
            }
        }
    }
}

而不是你所擁有的做這個

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

更新答案:因為屬性是“靜態元數據”,編譯后不能持久更改。 可以在 C# 中動態添加屬性嗎?

因此,完成 OP 對屬性值進行持久更改的請求的一種方法是在運行時編譯代碼 如果這是需要的,那么認真考慮采用不同的方法來解決您要解決的問題可能是值得的,因為這對您來說意味着更多的工作(但這對我來說非常酷)。

還有可能使用稱為TypeBuilder 的東西( 如何在 C# 中在運行時向類添加屬性? )。 我對此並不熟悉,但看起來很有希望。 它看起來也像使用運行時編譯。

運行時編譯:

這是可以通過在運行時編譯代碼來實現的一種方法。 (使用 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());

            }
        }
}

在 C# 中運行時編譯的一個很好的入門指南(這是我的一點熱情): http : //www.codeproject.com/Tips/715891/Compiling-Csharp-Code-at-Runtime

原答案:

從技術上講,它表明您確實可以更改屬性的值,但正如 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM