Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the property that has a DataMemberAttribute with a specified name?

How can I reflectively get the property that has the DataMember with a given name (let's assume every DataMember has a unique name)? For example, in the following code the property with the DataMember that has name "p1" is PropertyOne:

[DataContract(Name = "MyContract")]
public class MyContract
{
    [DataMember(Name = "p1")]
    public string PropertyOne { get; set; }

    [DataMember(Name = "p2")]
    public string PropertyTwo { get; set; }

    [DataMember(Name = "p3")]
    public string PropertyThree { get; set; }
}

Currently, I have:

string dataMemberName = ...;

var dataMemberProperties = typeof(T).GetProperties().Where(p => p.GetCustomAttributes(typeof(DataMemberAttribute), false).Any());

var propInfo = dataMemberProperties.Where(p => ((DataMemberAttribute)p.GetCustomAttributes(typeof(DataMemberAttribute), false).First()).Name == dataMemberName).FirstOrDefault();

This works, but it feels like it could be improved. I particularly don't like that GetCustomAttributes() is called twice.

How can it be re-written better? Ideally, it would be great if I could make it a simple one-liner.

like image 648
XåpplI'-I0llwlg'I - Avatar asked Feb 03 '13 10:02

XåpplI'-I0llwlg'I -


People also ask

What is DataMemberAttribute?

Data Member are the fields or properties of your Data Contract class. You must specify [DataMember] attribute on the property or the field of your Data Contract class to identify it as a Data Member. DataContractSerializer will serialize only those members, which are annotated by [DataMemeber] attribute.

What is Datacontract attribute?

[DataContract] attribute specifies the data, which is to serialize (in short conversion of structured data into some format like Binary, XML etc.) and deserialize(opposite of serialization) in order to exchange between the client and the Service.

What is Datacontract and DataMember in C#?

A datacontract is a formal agreement between a client and service that abstractly describes the data to be exchanged. In WCF, the most common way of serialization is to make the type with the datacontract attribute and each member as datamember.

Which attribute specifies that a public property of a class is capable of being serialized?

Applying the DataMemberAttribute to a field or property explicitly specifies that the member value will be serialized. In contrast, the BinaryFormatter serializes public and private fields of a type, and the XmlSerializer serializes only public fields and properties of a type.


4 Answers

// using System.Linq;
// using System.Reflection;
// using System.Runtime.Serialization;
obj.GetType()
   .GetProperties(…)
   .Where(p => Attribute.IsDefined(p, typeof(DataMemberAttribute)))
   .Single(p => ((DataMemberAttribute)Attribute.GetCustomAttribute(
                    p, typeof(DataMemberAttribute))).Name == "Foo");

Notes:

  • Attribute.IsDefined is used to check for the presence of a custom attribute without retrieving its data. Thus it is more efficient than Attribute.GetCustomAttribute and used to skip properties in a first step.

  • After the Where operator, we are left with properties that have exactly one DataMemberAttribute: Properties without this attribute have been filtered out, and it cannot be applied more than once. Therefore we can use Attribute.GetCustomAttribute instead of Attribute.GetCustomAttributes.

like image 123
stakx - no longer contributing Avatar answered Sep 29 '22 11:09

stakx - no longer contributing


You could use LINQ:

string dataMemberName = ...;
var propInfo =
    (from property in typeof(T).GetProperties()
    let attributes = property
        .GetCustomAttributes(typeof(DataMemberAttribute), false)
        .OfType<DataMemberAttribute>()
    where attributes.Any(a => a.Name == dataMemberName)
    select property).FirstOrDefault();

or if you prefer:

string dataMemberName = ...;
var propInfo = typeof(T)
    .GetProperties()
    .Where(p => p
        .GetCustomAttributes(typeof(DataMemberAttribute), false)
        .OfType<DataMemberAttribute>()
        .Any(x => x.Name == dataMemberName)
    )
    .FirstOrDefault();
like image 30
Darin Dimitrov Avatar answered Sep 29 '22 11:09

Darin Dimitrov


You could use Fasterflect to make your reflection code simpler and easier on the eyes:

var property = typeof(T).MembersAndAttributes( MemberTypes.Property, typeof(DataMemberAttribute) )
    .Where( ma => ma.Attributes.First().Name == dataMemberName )
    .Select( ma => ma.Member as PropertyInfo )
    .FirstOrDefault();

If you only need to check for the presence of the attribute, something like this could be used instead:

var property = typeof(T).PropertiesWith<DataMemberAttribute>( Flags.InstancePublic )
    .Where( p => p.Name == dataMemberName ).FirstOrDefault();

Fasterflect comes with a nice set of extension methods and includes some neat performance optimizations using IL generation if you also need speed.

like image 34
Morten Mertner Avatar answered Sep 29 '22 13:09

Morten Mertner


I needed to get the value of the property and not the property itself so used Darin Dimitrov's answer but added .GetValue(this) to the end to return the value instead.

Here is what my class ended up looking like:

[DataContract]
public class Item
{
    [DataMember(Name = "kpiId")]
    public string KPIId { get; set; }
    [DataMember(Name = "value")]
    public string Value { get; set; }
    [DataMember(Name = "unit")]
    public string Unit{ get; set; }
    [DataMember(Name = "status")]
    public string Status { get; set; }
    [DataMember(Name = "category")]
    public string Category { get; set; }
    [DataMember(Name = "description")]
    public string Description { get; set; }
    [DataMember(Name = "source")]
    public string Source { get; set; }
    [DataMember(Name = "messages")]
    public SysMessage[] Messages { get; set; }

    public object getDataMemberByName(string name)
    {
         return (typeof(Item).GetProperties().FirstOrDefault(p => p.GetCustomAttributes(typeof(DataMemberAttribute), false)
                              .OfType<DataMemberAttribute>()
                              .Any(x => x.Name == name))).GetValue(this);
    }
}
like image 34
RyanfaeScotland Avatar answered Sep 29 '22 12:09

RyanfaeScotland