Reflection for composite property names - c#

I have a Dictionary<string, object> which holds a property name as string and it's value as object. I also have a Bind method extension which, through reflection, sets that propery name with its corresponding value:
public static T Bind<T>(this T #this,
Dictionary<string, object> newValues,
params string[] exceptions) where T : class
{
var sourceType = #this.GetType();
foreach (var pair in newValues.Where(v => !exceptions.Contains(v.Key)))
{
var property = sourceType.GetProperty(pair.Key,
BindingFlags.SetProperty |
BindingFlags.Public |
BindingFlags.Instance);
var propType = Nullable.GetUnderlyingType(property.PropertyType) ??
property.PropertyType;
property.SetValue(#this, (pair.Value == null) ? null :
Convert.ChangeType(pair.Value, propType), null);
}
return #this;
}
For instance, consider a class like this:
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
Everything runs fine, except when I got a class with a property name of another object, like this:
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string PostalCode { get; set; }
}
So, if I try to send a Name property name, ok, but I got problems with composite property names, like Address.PostalCode.
Can you advise a way to handle that situation?
EDIT #1:
To summarize the problem: calling sourceType.GetProperty("Name", ...) in the context of a User class instance correctly allows to set its value, but it doesn't work using a sourceType.GetProperty("Address.PostalCode", ...) in same instance.
EDIT #2:
A more complete example should be:
var user = new User{ Address = new Address() };
var values = new Dictionary<string, object>
{
{ "Name" , "Sample" },
{ "Date" , DateTime.Today },
{ "Address.PostalCode", "12345" } // Here lies the problem
}
user.Bind(values);

My guess is that Convert.ChangeType only works for objects implementing IConvertible. Thus, I'd just add a check, and only use Convert.ChangeType if pair.Value has a type that implements IConvertible. Furthermore, afaik Convert does not use overloaded conversion operators, so you can save this check whenever pair.Value is not a struct, i.e.
object value;
if (pair.Value == null) {
value = null;
} else {
value = pair.Value.GetType().IsStruct ? Convert.ChangeType(pair.Value, propType) : pair.Value;
}
...

There are many binding engines out there, WPF, ASP.NET MVC, winforms in the core .NET and who knows how many others, you can check out all their source codes and documentation about their syntax.
Let's see the most simple case. Let's say that the variable X holds an object and you have the binding expression "A.B.C". Let's split up the binding path, the first part is "A". So you use reflection to get the property named "A" in X, and you put that other object into X. Now comes the second part, "B", so let's find a property named "B" in (the new) X. You find that, and put that into X. Now you get to the final part, "C", and now you can either read or write that property in X. The point is that you don't need recursion or anything, it's just a simple loop, you iterate over the parts of the binding expression, evaluate them, and you keep the current object in the same variable.
But the fact is that it can get much more complex than that. You could ask for array indexing, like "A.B[2].C". Or what if you have a path "A.B", and X.A is null, what do you do? Instantiate X.A, but what if it lacks a public parameterless constructor?
I want you to see that it can be a very complex problem. You have to specify a syntax and rules, and then implement that. You didn't specify in your question the exact syntax and rules you want to use. And if it happens to be more than the simple case I mentioned above, then the solution could be too lengthy.

I was able to solve it identifying if the property name have a period and recurring it:
public static T Bind<T>(this T #this,
Dictionary<string, object> newValues,
params string[] exceptions) where T : class
{
var sourceType = #this.GetType();
var binding = BindingFlags.Public | BindingFlags.Instance;
foreach (var pair in newValues.Where(v => !exceptions.Contains(v.Key)))
{
if(pair.Key.Contains("."))
{
var property = sourceType.GetProperty(
pair.Key.Split('.').First(),
binding | BindingFlags.GetProperty);
var value = property.GetValue(#this, null);
value.Bind(new Dictionary<string, object>
{
{
String.Join(".", pair.Key.Split('.').Skip(1).ToArray()),
pair.Value
}
});
}
else
{
var property = sourceType.GetProperty(pair.Key,
binding | BindingFlags.SetProperty);
var propType = Nullable.GetUnderlyingType(property.PropertyType) ??
property.PropertyType;
property.SetValue(#this, (pair.Value == null) ? null :
Convert.ChangeType(pair.Value, propType), null);
}
}
return #this;
}
Usage:
var user = new User {Address = new Address{ User = new User() }};
var values = new Dictionary<string, object>()
{
{"Name", "Sample"},
{"Date", DateTime.Today},
{"Address.PostalCode", "12345"},
{"Address.User.Name", "Sub Sample"}
};
user.Bind(values);
public class User
{
public string Name { get; set; }
public DateTime Date { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string PostalCode { get; set; }
public User User { get; set; }
}

Related

C# - Generic custom List.Add method to add checks before adding?

I am wondering if something like this is possible. I am looking to create a method that can be called instead of List.Add. The method would check any/all string properties and make sure they don't exceed their specific given max lengths, and if so truncate to proper size. I would ideally like it to be generic so that it will not only work for ObjectA, but also ObjectB, ObjectC, etc.
I am open to any and all suggestions. I know it seems like a weird thing to do, but I have a lot of different objects I am working with and potentially millions of those object instances in totality across all my lists. I mainly just need a way to ensure that any objects with properties exceeding their max string limit are truncated and logged via the Worker class in a timely way. Thanks!
public class ObjectA {
public Guid aID {get; set;}
[MaxLength(128)]
public string aName {get; set;}
[MaxLegnth(30)]
public string aType {get; set;}
}
--
public class Worker {
private void Work() {
List<ObjectA> listOfA = new List<ObjectA>();
listOfA.CustomAddMethod(new ObjectA(new Guid, "Something", "Unknown"));
}
// ??????
private CustomAddMethod(T object) {
foreach property {
if (isStringProperty && isGreaterThanMaxLength) {
// truncate to proper size
// log for truncation message
}
// Then add to list
}
}
}
You can create an extension method.
Here is a code snip. You can improve the performance by implementing a cache, for example, using a dictionary to store the properties and the MaxLengthAttribute based on object's type.
public static class ListExtensions
{
public static void CustomAdd<T>(this List<T> list, T item, Action<string> logger = null)
{
var propertyInfos = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(propertyInfo => propertyInfo.PropertyType == typeof(string))
.ToList();
foreach (var propInfo in propertyInfos)
{
var maxLengthAttr = propInfo
.GetCustomAttributes(typeof(MaxLengthAttribute))
.Cast<MaxLengthAttribute>()
.FirstOrDefault();
if (maxLengthAttr is null)
continue;
var currentString = (string)propInfo.GetValue(item);
if (!maxLengthAttr.IsValid(currentString))
{
var newValue = currentString.Substring(0, maxLengthAttr.Length);
logger?.Invoke(
$"Resolving error: {maxLengthAttr.FormatErrorMessage(propInfo.Name)}\n" +
$"Old Value: {currentString}\n" +
$"New Value: {newValue}"
);
propInfo.SetValue(item, newValue);
}
}
list.Add(item);
}
}
Example of usage (code removed for brevity):
public class Person
{
[MaxLength(4)]
public string Name { get; set; }
}
...
var personList = new List<Person>();
personList.CustomAdd(
new Person {Name = "John Doe"},
message => Debug.WriteLine(message)
);
...
As result the Jhon Doe string will be trimmed to Jhon

Reflection - SetValue from deep context

I am facing an issue, surely due to my lack of knowledge in the reflection process, while trying to set a "complex" class hierarchy based on Json files.
Here are my main model :
public class Names
{
public Weapons Weapons { get; set; }
public Armors Armors { get; set; }
public Utilities Utilities { get; set; }
public Names()
{
Weapons = new Weapons();
Armors = new Armors();
Utilities = new Utilities();
}
}
Each of them having a list of sub-model like this:
public class Weapons
{
public BattleAxe BattleAxe { get; set; } = new BattleAxe();
public Bomb_Missile Bomb_Missile { get; set; } = new Bomb_Missile();
// etc... Around 20 to 25
}
And finally the ended model which is the exact equivalent of each json files but may have very different properties :
public class BattleAxe
{
public string[] Normal { get; set; } = new string[0];
public string[] DescriptiveAdjective { get; set; } = new string[0];
public string[] Material { get; set; } = new string[0];
public string[] Type { get; set; } = new string[0];
public string[] Title { get; set; } = new string[0];
public string[] Of { get; set; } = new string[0];
public string[] NormalForTitle { get; set; } = new string[0];
}
Since the MS Json deserializer does not support the conversion to a $type as Newtonsoft before, I tried to populate the values using reflection too like this (I've removed all the null-check for code readability) :
public static void Load()
{
Names = new Names();
foreach (var category in Names.GetType().GetProperties())
{
if (category is not null && !(category.GetGetMethod()?.IsStatic ?? false))
{
var categoryType = category.PropertyType;
foreach (var item in category.PropertyType.GetProperties())
{
var itemType = item.PropertyType;
var subTypeData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(File.ReadAllText($"./Assets/Names/{categoryType.Name}/{itemType.Name}.json"));
var concreteObj = Activator.CreateInstance(itemType);
foreach (var key in subTypeData.Keys)
{
if (itemType.GetProperty(key) is not null && concreteObj is not null)
{
var prop = concreteObj.GetType().GetProperty(key);
var convertedValue = ConvertJsonType(subTypeData[key], subTypeData[key].ValueKind, out var isReferenceType);
// It fails here
prop.SetValue(
isReferenceType ? convertedValue : null,
!isReferenceType ? convertedValue : null
);
}
}
item.SetValue(concreteObj, null);
}
}
}
}
So it fails at the prop.SetValue(...) of the deepest object in the hierarchy with a different error depending on the type of value to set.
If it is a reference, it throws a System.Reflection.TargetException : 'Object does not match target type' Exception
And if it is value, it throw a System.Reflection.TargetException : 'Non-static method requires a target.'
Knowing that I do not have problems around the deserialization as shown here, only the fact that I use a dynamic type (and my instinct tells me it is actually the problem...)
I do not add the ConvertJsonType(...) body as it is functional and really simple
I am more interested in the 'why' than the 'how' so if you can explain me the 'theory' behind the problem, that would help quite a lot :)
Thank you!
PS: I know I can simplify the things in a more readable/performant way but I must achieve it with reflection for personal learning :)
Same for the System.Text.Json namespace, I do not intend to switch back to Newtonsoft for that
When calling SetValue(instance, value) you should pass the object which property should be set.
It's a wild guess, but you could try this:
prop.SetValue(concreteObj,
!isReferenceType ? convertedValue : null);
Because you want to fill the properties of concreteObj, not the value it self.
If you look at the object prop it was a return value of concreteObj.GetType().GetProperty(key);. If you look at it close, The GetProperty is a method from Type which isn't bound to any instance. So that's why you need to pass the instance of the object as the first parameter.
I mean this in a positive way: The itemType.GetProperty(key) is called every iteration, it will be the same value each iteration, you could bring it before the loop.
As docs state TargetException is thrown when:
The type of obj does not match the target type, or a property is an instance property but obj is null.
Passing null for obj in SetValue is valid when you are trying to set value for static property, not an instance one. Property type being a reference one has nothing to do with property being instance or static one so your call should look something like:
prop.SetValue(concreteObj, convertedValue);
Also your item.SetValue(concreteObj, null); does not look right cause concreteObj should be second argument in this call. Something like this:
item.SetValue(Names, concreteObj);
Also if you want only instance properties you can provide BindingFlags to get only instance properties:
foreach (var category in Names.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
Also I would say that category is not null check is redundant so in pair with providing BindingFlags you should remove the if completely.

Custom attribute to change property value

I have a class called say
Class1
public string store { get; set; }
What I want is to decorate it with something like this;
Class1
[GetStoreNumberFromName]
[IsNumeric]
public string store {get; set; }
So the value might be 1234, or it might be 1234 - Store name
What I need to do is check to see if the value passed has only numbers in it. If it doesn't then I need, in the second example, to grab the first 4 chrs and change the value of the property to that.
So if the passed in value was 1234 - Store Name then at the end of [GetStoreNumberFromName] the value of store should be 1234 so that [IsNumeric] will pass as valid.
Okay.. hopefully I've understood your requirement:
class GetStoreNumberFromNameAttribute : Attribute {
}
class Class1 {
[GetStoreNumberFromName]
public string store { get; set; }
}
class Validator<T>
{
public bool IsValid(T obj)
{
var propertiesWithAttribute = typeof(T)
.GetProperties()
.Where(x => Attribute.IsDefined(x, typeof(GetStoreNumberFromNameAttribute)));
foreach (var property in propertiesWithAttribute)
{
if (!Regex.Match(property.GetValue(obj).ToString(), #"^\d+$").Success)
{
property.SetValue(obj, Regex.Match(property.GetValue(obj).ToString(), #"\d+").Groups[0].Value);
}
}
return true;
}
}
..usage:
var obj = new Class1() { store = "1234 - Test" };
Validator<Class1> validator = new Validator<Class1>();
validator.IsValid(obj);
Console.WriteLine(obj.store); // prints "1234"
..obviously needs some changes on your end.. but it should give you an idea (I'm aware that the method naming probably isn't the best.. :/)
If I've missed the point entirely let me know and I'll delete.

Pass Property to the Method in C#

I need to pass selection of properties of some types(one type each time), assume this is my type:
public class Product {
[PrimaryKey]
public long Id { get; set; }
[DisplayName("Name")]
public string Title { get; set; }
[Foreignkey(Schema = "Products", Table = "MajorCategory", Column = "Id")]
[DisplayName("MCat")]
public string MajorCategory { get; set; }
[Foreignkey(Schema = "Products", Table = "Category", Column = "Id")]
[DisplayName("Cat")]
public string Category { get; set; }
public long CategoryId { get; set; }
[BoolAsRadio()]
public bool IsScanAllowed { get; set; }
}
So I need a way to pass the list of properties of this type to other Type(Target Type), and use property name, and attributes, and I don't need values, something like the following Pseudo-code:
List<Property> propertyList = new List<Property>();
propertyList.Add(Product.Id);
PropertyList.Add(Product.Title);
TargetType target = new TargetType();
target.Properties = propertyList;
public class TargetType {
public List<Property> Properties { get; set;}
GetAttributes() {
foreach(Property item in Properties){
Console.WriteLine(item.Name)
//Get Attributes
}
}
}
Is there any way to pass just like Product.Id and use name and attributes of that? I don't sure but maybe PropertyInfo can help, I think just can pass List of Object but in that case I can't use attributes and names, what is your suggestion to handle this? or something like this? if I am wrong at all so how can I implement it?
Funny, I was just answering a similar question, or at least I think it is.
It looks like you're trying to concatenate the properties of two types into one? You need an ExpandoObject:
http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject%28v=vs.100%29.aspx
For an implementation of a nested merge, see this:
C# deep/nested/recursive merge of dynamic/expando objects
Basically, you want a keyed list of properties, to start from. The following code will do that for any .NET object:
var props = object.GetType().GetProperties().ToDictionary<PropertyInfo, string>(prop => prop.Name);
And after that it depends on what precisely it is you want to achieve - a true copy of the object, merge with another, or just maintaining the list.
You can make use of reflection in .NET here:
List<PropertyInfo> propertyList = new List<PropertyInfo>();
Type productType = typeof (Product);
propertyList.Add(productType.GetProperty("Id"));
propertyList.Add(productType.GetProperty("Title"));
TargetType target = new TargetType();
target.Properties = propertyList;
public class TargetType {
public List<PropertyInfo> Properties { get; set;}
List<object> GetAttributes()
{
List<object> attributes = new List<object>();
foreach(PropertyInfo item in Properties)
{
Console.WriteLine(item.Name);
attributes.AddRange(item.GetCustomAttributes(true));
}
return attributes;
}
}
You can use a list of PropertyInfo, List<PropertyInfo> as the type of your TargetType .Properties. To get the properties you can try it like this using Reflection.
targetType.Properties = product.GetType().GetProperties().ToList();
You can build list of properties using expression trees, e.g. you can make something like this:
var propertiesListBuilder = new PropertiesListBuilder<Product>();
propertiesListBuilder
.AddProperty(_ => _.Id)
.AddProperty(_ => _.Title);
var target = new TargetType();
target.Properties = propertiesListBuilder.Properties;
The only concern here is performance, i.e. it might be not good idea to recreate such property lists over and over again, most probably they should be cached. At the same time you'll get intellisense, compiler checks and refactoring support for your property lists.
Below is a sample implementation of this stuff.
static class PropertyInfoProvider<T>
{
public static PropertyInfo GetPropertyInfo<TProperty>(Expression<Func<T, TProperty>> expression)
{
var memberExpression = (MemberExpression)expression.Body;
return (PropertyInfo)memberExpression.Member;
}
}
class PropertiesListBuilder<T>
{
public IEnumerable<PropertyInfo> Properties
{
get
{
return this.properties;
}
}
public PropertiesListBuilder<T> AddProperty<TProperty>(
Expression<Func<T, TProperty>> expression)
{
var info = PropertyInfoProvider<T>.GetPropertyInfo(expression);
this.properties.Add(info);
return this;
}
private List<PropertyInfo> properties = new List<PropertyInfo>();
}
typeof(Product).GetProperties() would give you all (public) properties as PropertyInfo[].
See also MSDN.

Accessing C# property name or attributes

I would like to automatically generate SQL statements from a class instance. The method should look like Update(object[] Properties, object PrimaryKeyProperty). The method is part of an instance (class, base method - generic for any child). Array of properties is an array of class properties, that will be used in update statement. Property names are equal to table field names.
The problem is that I can't get property names.
Is there any option to get a property name inside class instance?
sample:
public class MyClass {
public int iMyProperty { get; set; }
public string cMyProperty2 { get; set; }
{
main() {
MyClass _main = new MyClass();
_main.iMyProperty.*PropertyName* // should return string "iMyProperty"
{
I am aware of PropertyInfo, but I don't know hot to get the ID of a property from GetProperties() array.
Any suggestion?
Just wrote an implementation of this for a presentation on lambdas for our usergroup last Tuesday.
You can do
MembersOf<Animal>.GetName(x => x.Status)
Or
var a = new Animal()
a.MemberName(x => x.Status)
the code:
public static class MembersOf<T> {
public static string GetName<R>(Expression<Func<T,R>> expr) {
var node = expr.Body as MemberExpression;
if (object.ReferenceEquals(null, node))
throw new InvalidOperationException("Expression must be of member access");
return node.Member.Name;
}
}
Link to the presentation and code samples.
Also in SVN (more likely to be updated): http://gim-projects.googlecode.com/svn/presentations/CantDanceTheLambda
I found a perfect solution in This Post
public static string GetPropertyName<T>(Expression<Func<T>> propertyExpression)
{
return (propertyExpression.Body as MemberExpression).Member.Name;
}
And then for the usage :
var propertyName = GetPropertyName(
() => myObject.AProperty); // returns "AProperty"
Works like a charm
You can do something like this:
Type t = someInstance.getType();
foreach (MemberInfo mi in t.GetMembers())
{
if (mi.MemberType == MemberTypes.Property)
{
Console.WriteLine(mi.Name);
}
}
to get all the property names for instance's type.
You can get the name (I assume that's what you meant by ID) of a property using PropertyInfo.Name. Just loop through the PropertyInfo[] returned from typeof(className).GetProperties()
foreach (PropertyInfo info in typeof(MyClass).GetProperties())
{
string name = info.Name;
// use name here
}
Since you already have an explicit handle to the specific property you want, you know the name - can you just type it?
Not 100% sure if this will get you what you're looking for, this will fetch all properties with [Column] attribute inside your class:
In the datacontext I have:
public ReadOnlyCollection<MetaDataMember> ColumnNames<TEntity>( )
{
return this.Mapping.MappingSource.GetModel(typeof(DataContext)).GetMetaType(typeof(TEntity)).DataMembers;
}
Fetching the table column-names that are properties inside the class:
MyDataContext db = GetDataContext();
var allColumnPropertyNames = db.ColumnNames<Animal>().Where(n => n.Member.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).FirstOrDefault() != null).Select(n => n.Name);
Let's say (from the first sample, method update of a class MyClass):
public class MyClass {
public int iMyStatusProperty { get; set; }
public int iMyKey { get; set; }
public int UpdateStatusProperty(int iValue){
this.iMyStatusProperty = iValue;
return _Update( new[iMyStatusProperty ], iMyKey); // this should generate SQL: "UPDATE MyClass set iMyStatusProperty = {iMyStatusProperty} where iMyKey = {iMyKey}"
}
{iMyStatusProperty} and {iMyKey} are property values of a class instance.
So, the problem is how to get property name (reflection) from a property without using names of properties as strings (to avoid field name typos).

Categories