Reputation: 7375
Please refer to this code
public class A : B
{
[Display(Name = "Initial Score Code", Order =3)]
public Code { get; set; }
[Display(Name = "Initial Score Code", Order =2)]
public Name{ get; set; }
............
}
I need to get all properties of class through order by orderAttribute of Display. I have tried with this code to do
var prop = typeof(A)
.GetProperties()
.OrderBy(p => ((DisplayAttribute)p.GetCustomAttributes(typeof(DisplayAttribute), true).FirstOrDefault).Order);
but it causes an error
object reference not to set an instance of object
I assumed this issue because of some property not having "Order" property in "DisplayAttribute" .
How to handle this kind of situation? I need to order all the properties even though some property not having the value of order property.
Upvotes: 4
Views: 4677
Reputation: 41
I like the approach with Comparer. However, when I tried it, my iterator went into a dead loop at first. Later, it started throwing exceptions. Also, I optimized it for the case when first property doesn't contain "Order" descriptor to avoid even checking for a second one. I also moved all comments into the class description:
/// <summary>
/// If the first property has no attribute, sort it first
/// If the second property has no attribute, sort it first
/// Compare the Order properties of both attributes
/// </summary>
public class PropertyInfoComparer : IComparer<PropertyInfo>
{
public int Compare(PropertyInfo x, PropertyInfo y)
{
if (x == y) return 0;
var attrX = (DisplayAttribute)x.GetCustomAttributes(typeof(DisplayAttribute)).FirstOrDefault();
int? orderX = attrX?.GetOrder();
if (orderX == null) return -1;
var attrY = (DisplayAttribute)y.GetCustomAttributes(typeof(DisplayAttribute)).FirstOrDefault();
int? orderY = attrY?.GetOrder();
if (orderY == null) return 1;
return ((int)orderX).CompareTo((int)orderY);
}
}
Unfortunately, classes that have properties without the "Order" descriptor lose their "natural" order. Therefore, I ended up checking for any properties that have the "Order" descriptor first. If at least one of them has that descriptor, then do the sorting.
Upvotes: 0
Reputation: 236228
You are missing brackets ()
on FirstOrDefault
operator. Also you should deal with case when default value is returned. I suggest to select Order
value before getting first or default value. That will return 0
for all properties which don't have DisplayAttribute
:
var prop = typeof(A)
.GetProperties()
.OrderBy(p => p.GetCustomAttributes(typeof(DisplayAttribute), true)
.Cast<DisplayAttribute>()
.Select(a => a.Order)
.FirstOrDefault());
If you want properties without DisplayAttribute to be last, you can provide Int32.MaxValue
as default value to be returned:
.Select(a => a.Order)
.DefaultIfEmpty(Int32.MaxValue)
.First()
Upvotes: 10
Reputation: 7961
Here is a much more complete answer that allows you to better control the ordering of PropertyInfo
instances without DisplayAttribute
attributes:
public class A
{
[Display(Name = "Initial Score Code", Order = 3)]
public int Code
{
get;
set;
}
[Display(Name = "Initial Score Code", Order = 2)]
public string Name
{
get;
set;
}
}
public class PropertyInfoComparer : IComparer<PropertyInfo>
{
public int Compare(PropertyInfo x, PropertyInfo y)
{
var attribute1 = (DisplayAttribute)x.GetCustomAttributes(typeof(DisplayAttribute)).FirstOrDefault();
var attribute2 = (DisplayAttribute)y.GetCustomAttributes(typeof(DisplayAttribute)).FirstOrDefault();
// If the first property has no attribute, sort it first
if (attribute1 == null)
{
return -1;
}
// If the second property has no attribute, sort it first
if (attribute2 == null)
{
return 1;
}
// Compare the Order properties of both attributes
return attribute1.Order.CompareTo(attribute2.Order);
}
}
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class DisplayAttribute : Attribute
{
public string Name
{
get;
set;
}
public int Order
{
get;
set;
}
}
Usage:
// Get all the properties of Foo and order them using PropertyInfoComparer
typeof(Foo).GetProperties().OrderBy(arg => arg, new PropertyInfoComparer());
Upvotes: 2
Reputation: 14846
Try this:
var props = from p in typeof(A).GetProperties()
let orderAttribute = (DisplayAttribute)(p.GetCustomAttributes(typeof(DisplayAttribute), true))
.FirstOrDefault()
where orderAttribute != null
orderby orderAttribute.Order
select p;
Or:
var props = from p in typeof(A).GetProperties()
let orderAttribute = (DisplayAttribute)(p.GetCustomAttributes(typeof(DisplayAttribute), true))
.FirstOrDefault()
orderby orderAttribute == null ? 0 : orderAttribute.Order
select p;
Upvotes: 3