C# Reflection and Attributes: Bug? I can't get around this - c#

This is in the Immediate console:
prop.GetCustomAttributes(typeof(RequiredParameterAttribute),true)
{BridgeStack.DataContracts.RequiredParameterAttribute[0]}
prop.GetCustomAttributes(typeof(RequiredParameterAttribute),true).Cast<RequiredParameterAttribute>()
{BridgeStack.DataContracts.RequiredParameterAttribute[0]}
[BridgeStack.DataContracts.RequiredParameterAttribute[]]: {BridgeStack.DataContracts.RequiredParameterAttribute[0]}
prop.GetCustomAttributes(typeof(RequiredParameterAttribute),true).Cast<RequiredParameterAttribute>().Any()
false
I get the same results in the application.
prop is Site in:
public class AnswerCollectionQuery : IPagedQuery, ISiteQuery, ISortableQuery, IOrderableQuery, IFilteredQuery
{
public int? Page { get; set; }
public int? PageSize { get; set; }
public string Site { get; set; }
[AllowedSortValues(QuerySortEnum.Activity, QuerySortEnum.Creation, QuerySortEnum.Votes)]
public QuerySortEnum? Sort { get; set; }
public object Min { get; set; }
public object Max { get; set; }
public DateTime? FromDate { get; set; }
public DateTime? ToDate { get; set; }
public QueryOrderEnum? Order { get; set; }
public string Filter { get; set; }
}
Site in turn comes from ISiteQuery
public interface ISiteQuery : IQuery
{
[RequiredParameter]
string Site { get; set; }
}
The awkward part is that the console shows the attribute, allows me to cast it, but I can't retrieve it at all, I get zero as the enumeration's length, which is why .Any() fails, too, .FirstOrDefault() returns null, .First() throws, etc.
Any explanation for this type of behavior?
PD: this works though if I decorate Site with [RequiredAttribute] in the concrete class. But I wanted to make this part of the interface.
Update for clarity:
prop comes exactly from here:
public static IEnumerable<PropertyInfo> GetAllProperiesOfObject(object o)
{
const BindingFlags flags = BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance;
PropertyInfo[] list = o.GetType().GetProperties(flags);
return list;
}
foreach (PropertyInfo prop in Utility.GetAllProperiesOfObject(entity))
This is the case for when prop becomes Site

The zero is because it is returning you a zero-length typed array, meaning: it doesn't have the attribute. You can also see this with Attribute.IsDefined (which will return false).
When using implicit interface implementation, the public property on the class does not automatically gain attributes from the interface that it satisfies. To see the attributes on the interface you would need to use
typeof(ITheInterface).GetProperties()
The Site property on the interface is unrelated to the Site property on the class. If the property on the class must have attributes: add the attributes explicitly.

Related

Setting nested properties via an Expression

I have the following object:
public class ContactImport {
public long Id {get;set;}
public Contact Contact {get;set;}
public Company Company {get;set;}
public Address Address {get;set;}
}
I want to be able to set the properties on the the nested objects dynamically based on an expression passed in (Expression<Func<T, dynamic>>).
To do this I have the following method, this works fine for setting properties on the top level object (such as Id) but fails when trying to set anything on the nested objects (such as Contact.FirstName)
public static void SetPropertyValue<T, TProp>(this T target, Expression<Func<T, TProp>> member, TProp value) {
var selectorExpr = (MemberExpression)(member.Body is UnaryExpression ? ((UnaryExpression)member.Body).Operand : member.Body);
if (selectorExpr != null) {
var property = selectorExpr.Member as PropertyInfo;
if (property != null) {
property.SetValue(target, value);
}
}
}
It looks like it's trying to set the property on the top level object but can't. I take it this is possible but I'm unsure how to achieve it with what I currently have.
The SetPropertyValue method is invoke like this:
public class ImportCheck<T> {
public int id { get; set; }
public string Name { get; set; }
public Type Type { get; set; }
public bool Required { get; set; }
public int? MinLength { get; set; }
public int? MaxLength { get; set; }
public string Value { get; set; }
public Expression<Func<T, dynamic>> AssociatedProperty { get; set; }
}
T pt = (T)Activator.CreateInstance(typeof(T));
foreach (var m in mapping) {
pt.SetPropertyValue(m.AssociatedProperty, Convert.ChangeType(m.Value, Nullable.GetUnderlyingType(m.Type) ?? m.Type));
}
In the above example T is ContactImport, m.AssociatedProperty is the expression and mapping is a List<ImportCheck<ContactImport>>.
Update
My issue seems to boil down to the fact that the expression being passed into SetPropertyValue has a return type of dynamic. If this is set to int and the property on the nested object is an int then it all works. The problem I have then is that I need to explicitly set the result of the expression to match the type of the target property. This however leaves me with another issue in that I need a list of ImportChecks with the possibility of each Expression<Func<T,dynamic>> having a different return type.

How to differentiate class properties using user-defined tags (custom-attributes)

Background:
I have a custom class, which represents a Data Base Table, each property corresponding to a table column. The properties can be classified in three ways.
Example: Take for example a Person object.
MetaProperties: (Columns that are needed by the program)
Person_ID: used in table for indexing etc...
UserDefinedType: (UDT), complex class handling write-permission on the table.
Timestamp: needed to handle the UDT in C# DataTables
RealProperties: (actual traits that describe the real Person)
FullName
DateOfBirth
PlaceOfBirth
EyeColor
etc... (many more)
RawDataProperties: (these columns hold raw data from external sources)
Phys_EyeColor: the eye-color, as directly imported from the physical traits database, may be in unknown format, may have conflicting value with entry from other db, or any other data quality issue...
HR_FullName: full name as given in HR file
Web_FullName: full name as taken from a web form
Web_EyeColor: eye color as taken from web form
etc...
public class Person
{
#region MetaProperties
public int Person_ID { get; set; }
public UserDefinedType UDT { get; set; }
public DateTime timestamp { get; set; }
#endregion
#region RealProperties
public string FullName { get; set; }
public DateTime DateOfBirth { get; set; }
public string PlaceOfBirth { get; set; }
public Color EyeColor { get; set; }
//...
#endregion
#region RawDataProperties
public string Phys_EyeColor { get; set; }
public string Phys_BodyHeight { get; set; }
public string Web_FullName { get; set; }
public string Web_EyeColor { get; set; }
public string HR_FullName { get; set; }
//...
#endregion
}
Question: How can I programmatically differentiate between these three types of properties in my Person class? The goal is to be able to iterate through properties of a certain type using System.Reflection or some other organisational construct. Pseudocode:
foreach(Property prop in Person.GetPropertiesOfType("RealProperty"){
... doSmth(prop);
}
I'm thinking about writing custom Attributes, and hanging them on to the properties, sort of like taggin.
But since I know nothing about custom Attributes, I would like to ask if I'm going down the proper path, or if there are any other better ways of doing this.
Note: the shown example may may not be the best in terms of program design, and I am well aware that inheritance or splitting up the class otherwise could solve this problem. But that is not my question - I want to know if properties in a class can be tagged or somehow differentiated between using custom categories.
You can do this with custom attributes.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class PropertyAttribute : System.Attribute
{
public PropertyType Type { get; private set; }
public PropertyAttribute (PropertyType type) { Type = type; }
}
public enum PropertyType
{
Meta,
Real,
Raw,
}
Then, you can do this with each property or field:
[PropertyType(PropertyType.Meta)]
public int Person_ID;
[PropertyType(PropertyType.Real)]
public string FullName;
[PropertyType(PropertyType.Raw)]
public string Phys_EyeColor;
Then you can access it with something like
foreach (PropertyAttribute attr in this.GetType().GetCustomAttributes(typeof(PropertyAttribute), false))
{
// Do something based on attr.Type
}

How can I use a custom Attribute within a DynamicProxy Class

I tried to assign a custom Attribute to class that comes from a dynamic proxy
System.Data.Entity.DynamicProxies.Login_A2947F53...
Example class Login
public partial class Login
{
[CustomAttribute]
public virtual int Id
{
get;
set;
}
}
Now I try to access the Attribute using Generics and Reflection
public static void Process(TSource source)
{
foreach (PropertyInfo p in target.GetType().GetProperties(flags))
{
object[] attr = p.GetCustomAttributes(true); // <- empty
}
}
But there is no Attribute. Is that due to the DynmaicProxy or what did I do wrong here?
When I use a concrete class without dynamic proxy like this one, then I get the attributes.
public class TestObject
{
[CustomAttribute]
public virtual string Name { get; set; }
[CustomAttribute]
public virtual string Street { get; set; }
public virtual int Age { get; set; }
public virtual string Something { get; set; }
}
OK, this one was obvious after a closer look;
System.Data.Entity.DynamicProxies.Login_A2947F53...
is a dynamicProxy type and know nothing about any Attributes. So I have to use the something like:
foreach (PropertyInfo p in typeof(Login).GetProperties(flags))
instead of the dynamicProxy instance to get the type from. And finaly there are my Attributes.
Use BaseType.
public static void Process(TSource source)
{
foreach (PropertyInfo p in target.GetType().BaseType.GetProperties(flags))
{
object[] attr = p.GetCustomAttributes(true);
}
}

Dynamic equality checking of multiple properties of a type's objects

I have a type like:
class Order
{
public List<IItem> AllItems { get; set; }
public string Name { get; set; }
public double TotalPurchases { get; set; }
public long Amount { get; set; }
public int Code { get; set; }
}
I've implemented the IEquatable<T> interface to check if two objects of this type are same or not. The current Equals method looks like:
public virtual bool Equals(Order other)
{
if ((object)other == null)
{
return false;
}
return (this.AllItems.Equals(other.AllItems)
&& this.Name.Equals(other.Name)
&& this.TotalPurchases.Equals(other.TotalPurchases)
&& this.Amount.Equals(other.Amount))
&& this.Code.Equals(other.Code));
}
But I wish to implement this method in such a way that it dynamically checks for equality of all the existing properties (or maybe certain properties of this type) without explicitly writing the code for comparison checks as above.
Hope I was able to express my question with clarity. :)
Thanks!
You could write a custom attribute that attaches to the properties on your type which you want to be included in the comparision. Then in the Equals method you could reflect over the type and extract all the properties which have the attribute, and run a comparison on them dynamically.
Psuedo code:
[AttributeUsage(AttributeTarget.Property)]
class IncludeInComparisonAttribute : Attribute { }
class Order
{
List<AllItem> Items { get; set; }
[IncludeInComparison]
string Name { get; set; }
long Amount { get; set; }
[IncludeInComparison]
int Code { get; set; }
override bool Equals(Order other)
{
Type orderType = typeof(Order);
foreach (PropertyInfo property in orderType.GetProperties()
{
if (property.CustomAttributes.Includes(typeof(IncludeInComparisonAttribute))
{
object value1 = property.GetValue(this);
object value2 = propetty.GetValue(other);
if (value1.Equals(value2) == false)
return false;
}
}
return true;
}
}
It'll certianly need to be a bit more elaborate than that, but that should hopefully set you on the right track :)
Two Orders are considered the same if all their properties are equal. It's OK for the 4 properties Name/TotalPurchases/Amount/Code, their default comparers are exactly what you want. But for the property AllItems (whose type is List<IItem>), you must tell how they consider to be equal. Currently you are using reference equals that is incorrect. this.AllItems.Equals(other.AllItems) should be something like:
this.AllItems.SequenceEqual(other.AllItems, new ItemComparer())
And the ItemComparer is a class implements IEqualityComparer<Item> to tell how to check if two Items are equal.

Why does GetFields() not return anything?

I am trying to retrieve the public properties of an object but it is returning nothing. Can you tell me what I'm doing wrong.
public class AdHocCallReportViewModel : ReportViewModel
{
public string OperatorForCustEquipID { get; set; }
public string OperatorForPriorityID { get; set; }
public string OperatorForCallTypeID { get; set; }
public string OperatorForStatusID { get; set; }
}
public UpdateReportParameters(AdHocCallReportViewModel rvm)
{
var type = rvm.GetType();
foreach (var f in type.GetFields().Where(f => f.IsPublic))
{
Console.WriteLine(f.Name);
Console.WriteLine(f.GetValue(rvm).ToString());
}
}
When stepping through the code, it skips over the foreach loop because GetFields returns zero items.
You haven't got public fields. They are properties. So try type.GetProperties() instead.
You are trying to get fields, you should try to call GetProperties()
Pass BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public to get all instance fields.
On second thought, I'm seeing that you are explicitly filtering for public fields. The class does not have any public fields. The fields that are automatically generated by the compiler as the backing store for the properties are private.

Categories

Resources