Updated Answer:
Because attributes are "static metadata", they cannot be persistently changed after compilation. (Can attributes be added dynamically in C#?)
So, one way to accomplish OP's request to make persistent changes to Attribute values is to compile the code at runtime. 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#?). 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):
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
Original Answer:
Technically, it demonstrates that you can, indeed, change the attribute's value, but as OP pointed out - this doesn't persist.
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);
}
}
}