How do I get DataContract attribute from PropertyInfo at the class level? - c#

I have an extension method that I use attempt to extract some metadata from an object, such as this:
public static string ToTypeName(this object obj)
{
string name;
var asType = obj.GetType();
if (Attribute.IsDefined(asType, typeof(DataMemberAttribute)) || Attribute.IsDefined(asType, typeof(DataContractAttribute)))
{
var attrName = obj.GetType().GetCustomAttributes(typeof(DataContractAttribute), true).SingleOrDefault() as DataContractAttribute;
name = string.IsNullOrEmpty(attrName.Name) ? asType.Name : attrName.Name;
}
else
{
name = asType.Name;
}
return name;
}
I'm currently using it to scrap metadata from objects that are decorated with various attributes. In this example, it is looking for the DataContractAttribute and then pulling the value from the Name property. This works fine.
However, there are times when that object is of the PropertyInfo type. What is happening is that the Attribute.IsDefined is testing true, thus entering the block to be scraped, however, it is failing to cast so attrName is coming out null.
I added this block before the previous block:
if (obj is PropertyInfo)
{
var asPropInfo = obj as PropertyInfo;
if (Attribute.IsDefined(asPropInfo, typeof(DataMemberAttribute)) || Attribute.IsDefined(asPropInfo, typeof(DataContractAttribute)))
{
var attrName = asPropInfo.GetType().GetCustomAttributes(typeof(Attribute), true).SingleOrDefault();
if (attrName is DataMemberAttribute)
{
var attr = attrName as DataMemberAttribute;
name = string.IsNullOrEmpty(attr.Name) ? asType.Name : attr.Name;
}
else if (attrName is DataContractAttribute)
{
var attr = attrName as DataContractAttribute;
name = string.IsNullOrEmpty(attr.Name) ? asType.Name : attr.Name;
}
}
}
the IsDefined check is still testing true, but still failing to cast. I looked at the CustomAttributes property of obj (as it enters the method) via watch variables and it shows 1 attribute of type DataContractAttribute, however, by the time i get to asPropInfo.GetType(), it has changed to Serializable.
It could be that i've been at this for too long and I'm not thinking clear, but does anyone have any input?
UPDATE:
I was able to remove the GetType() and just call GetCustomAttributes() directly, however, the result is still not what I need. Here's what appears to be happening:
Imagine a Person class contains a member of type Person like so:
[DataContract(Name = "Employee")]
public class PersonDto{
public string FirstName {get;set;}
public string LastName {get;set;}
[DataMember(Name = "Boss")]
public Person Supervisor {get;set;}
}
What is happening during the scrape is that Supervisor is getting passed in as PropertyInfo along with it's DataMember attribute, and my extension is reading it and returning "Boss," and this totally makes sense. However, what I actually need at this stage is the DataContractattribute and here's why:
When the Supervisor property gets serialized, it will serialize as "Boss" and I need to know that, so I save that to a DTO. However, I also need to know that "Boss" is of the serialized "type" which is "Employee". This might seem weird, but makes sense in the end lol. I'm using this to help generate help documentation for our API. Internally, the class type might be "PersonDto", but the type that is displayed to the client is "Employee". So in terms of help documentation, the developer knows that there's a "Boss" element, but they also need to know that it is simply an instance of "Employee" (as far as they're concerned), so that they can look up documentation for that object. Does that makes sense?

I believe you don't need to call GetType on asPropInfo. Try that:
var attrName = asPropInfo.GetCustomAttributes(typeof(Attribute), true).SingleOrDefault();

Related

Get property default value as expression using reflection

In order to build a custom transpiler, I'm trying to get the default value of all the properties inside a class as an expression, and not as a value itself.
Let me bring you some examples to clarify what I'm trying to do and what I've done/tried/investigated so far.
Source code could be the following one:
const string DATETIME_NOW = "____DATETIME_NOW____";
public class Person {
[DefaultValue("Foo")]
public string Name { get; set; } = "Foo";
[DefaultValue(DateTime.Now)] // This is not doable: "An attribute argument must be a constant expression"
public DateTime DateOfBirth { get; set; } = DateTime.Now;
[DefaultValue(DATETIME_NOW)]
public string DateOfBirthStringed { get; set; } = DATETIME_NOW; // which acts like DateTime.Now.ToString()
}
The ultimate goal of the transpiler, is to obtain a Javascript class that looks like this:
class Person {
name: string = "Foo";
dateOfBirth: Date = new Date(Date.now());
dateOfBirthStringed : Date = Date.now();
}
My current, and working, implementation is the use of DefaultValue attribute with some constants strings used when the default value is an expression (e.g. DateOfBirthStringed).
What I'm doing is using reflection on Person, getting all the PropertyInfo, looking for their DefaultValue attribute, and then checking if the given default value are some fixed constants like DATETIME_NOW.
This works, but I've a couple of problems:
The type in attribute DefaultValue could be different from the type of the property.. No type check :(
If I only have the DefaultValue, when I write new Person(), the default values are not actually set from the attribute.
Therefore, I need to write the default value after { get; set; }, but:
Or I wrote both attribute and default value, but then I should manually mantain synchronized them.
I write only the default value, but then I've no way to get it with reflection.
About point 3.2, why I can't get the default value via reflection?
Suppose the Person class defined above; if I use reflection, I need to instantiate it, but once instantiated, Person.DateOfBirth has an actual DateTime value, and I cannot know it was coming from DateTime.Now.
Also, if Person would be abstract.. Well, no way to instantiate it.
So, basically, the only way I could perfectly transpile the code is to read the code as a tree, something like parseTreeCode(typeof(Person)). At that point, I should navigate the tree and be able to do everything I want.
I did find Roslyn, which allows me to parse C# code, but.. It parses "stringed" code, and not code that belongs to the same project. I thought "well, get the file where Person is defined, and parse the whole file", but unfortunately, once the program is running, I cannot get the file where Person is defined.. I mean, I can do typeof(Person).Assembly, and getting the Assembly.. But it would be an assembly, so it would not be good.
At this point, I'm thinking that there is no real solution for this, but maybe I'm missing some C# packages/features that could help me

How to work around: AmbiguousMatchException in Expression.Property(myNewExpression, "nameOfproperty")

I have a class that looks something like this:
public class MyClass {
public string id { get; set; }
public string Id { get; set; }
public string SomethingMore {get; set;}
}
I don't control the class. So I have to live with the fact that both id (lowercase i) and Id (uppercase I) exist. Like it or not, I cannot change this, hence this answer answers my why, but it does not answer my how-to question.
In my Expression tree I have:
var newExpression = Expression.New(typeof(MyClass).GetConstructor(Type.EmptyTypes))
var propertyExpression = Expression.Property(newExpression, "Id");
The second line throws an Ambiguous Match Exception, because Expression.Property(..) is case insensitive - e.g. Expression.Property(newExpression, "SomethingMore") does not throw an exception.
What sort of work-around options do I have?
The next step in the code is:
Expression.Assign(propertyExpression, Expression.Constant("someNewValue", typeof(string));
I don't need to assign value to the id property and I know the meaning of id, which is special. I do however need to be able to assign a value to the Id property.
I guess I could create a derived version of my MyClass, that doesn't include the id property, but I need to do this at run-time. If this is the solution, how can it be done? Or, maybe there's a much better solution?
I don't control the MyClass. I only know that a developer would be inclined to define both idand Id in their class.
There are overloads for Expression.Property that allow a more explicit means of accessing the desired property.
Get the property info explicitly using reflection and use that.
var type = typeof(MyClass);
var newExpression = Expression.New(type.GetConstructor(Type.EmptyTypes));
PropertyInfo property = type.GetProperty("Id");
var propertyExpression = Expression.Property(newExpression, property);

How can I reach the "object" behind a property with reflection?

Is there a way to obtain the object behind a property by reflection?
I am trying to manage a dynamic setting of a property.
Example:
class Animal
{
public string Name {get;set;}
public string Family {get;set;}
}
class Zoo
{
public Animal Lion {get;set;}
public Animal Panda {get;set;}
}
class Test
{
public void SetLionNameWithReflection()
{
Zoo londonZoo = new Zoo();
Type zooType = typeof(Zoo);
PropertyInfo lionProperty = zooType.GetProperty("Lion");
// Now what to write here so that I can manage to set Lion's name to Kaspar?
// (to manage this by reflection Lion.Name = "Kaspar");
}
}
What lines should I add more at the commented part above?
Thanks!
I don't think you actually need to know the object behind a property. Use the SetValue method to set its value to "Kaspar":
EDIT - as per dlev's comment, this is how it should look like:
Lion kaspar = new Lion { Name="Kaspar" };
zooType.SetValue(londonZoo, kaspar, null);
A property doesn't necessarily have an object "behind" it.
It's defined by 2 functions, get and set, that can do whatever you want, and not necessarily return an object's value at all.
What you used is just a syntactic sugar to make it easier to make a property to wrap a member.
No you can't use reflection to definitively get the object behind an arbitrary property. Largely because it's not guaranteed that every property is bound to an object. It could just as easily be a calculated value.
public class Student {
public string m_firstName;
public string m_lastName;
public string FullName {
get { return String.Format("{0} {1}", m_firstName, m_lastName); }
}
}
In this case the property FullName produces a calculated value and has no single backing object.
The case you're listing though is for auto-properties. There is likely a way to dig through the fields and use a form of name matching to get the one backing a given auto-property. However such a solution would be fragile to versioning and certainly not recomended.
Can you add an overloaded constructor to your animal object, which will allow you to pass in the animal name, like this?:
londonZoo.GetProperty("Lion").SetValue(londonZoo, new Lion("Kaspar"), null);
First you need to create the lion:
var lion = Activator.CreateInstance(lionProperty.PropertyType);
Then you need to set the Lion property of the zoo:
lionProperty.SetValue(londonZoo, lion, null);
Then you can get the Name property of the lion:
PropertyInfo property = lion.GetType().GetProperty("Name",
BindingFlags.Public | BindingFlags.Instance);
Then you can set its name:
if (property != null && property.CanWrite)
{
property.SetValue(lion, "Kaspar", null);
}

Help me understand NewExpression.Members

Does NewExpression.Members inform the LINQ runtime how to map a type's constructor parameters to its properties? And if so, is there an attribute to set the mapping? I'm imagining something like this:
public class Customer
{
public Customer(int id, string name)
{
Id = id;
Name = name;
}
[CtorParam("id")]
public int Id { get; set; }
[CtorParam("name")]
public string Name { get; set; }
}
But none of the MSDN docs really inform you how exactly Members is initialized.
My limited understanding is that you don't usually need to pass the member information; the arguments are taken (by position) from the arguments parameter. The member info is (I suspect) intended to help some internal APIs when dealing with things like anonymous-types, which look (in C#) like they are initialized by member (like an object-initializer), but which are actually initialized by constructor. This means things like LINQ-to-SQL will see a constcutor use, and then (in the next part of the query) access to obj.Name - it needs a way to understand that this means "the 3rd parameter to the constructor (which never actually gets called). In particular for things like groupings.
So this is fine:
var param = Expression.Parameter(typeof(string), "name");
var body = Expression.New(typeof(Customer).GetConstructor(new[] {typeof(int), typeof(string)}),
Expression.Constant(1), param);
var func = Expression.Lambda<Func<string, Customer>>(body, param).Compile();
var cust = func("abc");
If you do need them, I would expect them to be positional relative to the "arguments" expressions - so you would pass in (in an array) the member for id and name. Note that there is also a separate expression for intialzer-style binding.

Consuming Custom Attributes

Taking a look at the following question, Real world use of custom .NET attributes how would you implement the solution proposed by #Esteban?
I'm a little confused as to when and where the code would get executed I think. Could you please supply a good sample of code.
I've asked this question before but didn't properly phrase it I think so...
With respect to the question/answer that you reference, I assume that there would be some code that runs either in the data layer or in the class itself that does validation. That code would use Reflection on the object being validated to find the properties with different attributes and run the specific validation logic associated with that attribute on that property.
It might look something like the following:
public void Validate( object obj )
{
foreach (var property in obj.GetType().GetProperties())
{
var attribute = property.GetCustomAttributes(typeof(ValidationAttribute), false);
var validator = ValidationFactory.GetValidator( attribute );
validator.Validate( property.GetValue( obj, null ) );
}
}
On submit(save) of the html form(win form) you get back changed Customer class. For each property you check if it has custom attribute(inherited from ValidationAttribute or implementing IValiador interface or something like this) associated with it. For each such property you call the validate method of attribute(create appropriate validation class and call validate method) on the property value.
You would use reflection:
public class MyClass
{
[Description("I'm an attribute!")]
public int MyField;
public Attribute GetAttribute(string fieldName)
{
FieldInfo field = typeof(MyClass).GetField("MyField");
Attribute[] attributes = (Attribute[])field.GetCustomAttributes(typeof(Attribute), false);
DescriptionAttribute desc = (DescriptionAttribute)attributes[0];
return desc;
}
}
If the attributed member is a field, you would use FieldInfo, as used in the example. If it's a property, you would use PropertyInfo, the members of FieldInfo and PropertyInfo are pretty much the same.

Categories