I am working on a logic where I have populate a composite class instance from a dictionary Key/Value pairs. The composite classes will be marked with attributes which will be mapped against the keys from the dictionary.
One specific requirement is that if class C1 is having a property of type class C2, but there are no pairs in the dictionary for the properties of class C2 to be mapped against then C2 should be set to null. Otherwise if there is at least a single property of C2 which can be mapped, then C2 property of C1 must be instantiated.
I have written a recursive function to implement this logic. The requirement is not working as intended. I am using a flag isInstanceValuePresent which check if at least one of the property of C2 can be mapped. Else, it's false value should tell me that I have to assign null to the C2 property of class C1. If anyone can help me understand why the logic fails and what could be the correct solution, I would very well appreciate that. Following is the static recursive method:
/// <summary>
/// Populates the given instance object with the supplied source dictionary values
/// </summary>
/// <param name="modelInstance">The object whose properties are to be initialized with the data</param>
/// <param name="source">The source dictionary containing Schema(Keys) and corresponding Values</param>
private static void PopulateModelInstance(object modelInstance, IDictionary<string, string> source)
{
bool isInstanceValuePresent = false;
foreach (PropertyInfo propInfo in modelInstance.GetType().GetProperties())
{
//Identify Custom attribute
DataMappingKeyAttribute attribute = (DataMappingKeyAttribute)Attribute.GetCustomAttribute(propInfo, typeof(DataMappingKeyAttribute));
if (attribute != null && !string.IsNullOrEmpty(attribute.MappingKey))
{
if (propInfo.PropertyType.IsPrimitive || propInfo.PropertyType.Equals(typeof(string)))
{
string sourceKey = attribute.MappingKey;
if (source.ContainsKey(sourceKey))
{
isInstanceValuePresent = true;
// Get propInfo attribute value from Dictionary
//var propertySourceValue = source[(propInfo.PropertyType.GetCustomAttribute(typeof(DataMappingKeyAttribute)) as DataMappingKeyAttribute).MappingKey];
string sourceValue = source[attribute.MappingKey];
// Set propInfo value on the model instance
if (CanChangeType(sourceValue, propInfo.PropertyType) && propInfo.CanWrite && (!propInfo.PropertyType.IsClass || propInfo.PropertyType.Equals(typeof(string))))
propInfo.SetValue(modelInstance, Convert.ChangeType(sourceValue, propInfo.PropertyType), null);
}
}
}
if (propInfo.PropertyType.IsClass && !propInfo.PropertyType.Equals(typeof(string)) && propInfo.CanWrite)
{
isInstanceValuePresent = false;
object referenceTypeInstance = Activator.CreateInstance(propInfo.PropertyType);
PopulateModelInstance(referenceTypeInstance, source);
if (isInstanceValuePresent == false)
{
propInfo.SetValue(modelInstance, null, null);
referenceTypeInstance = null;
}
else
{
propInfo.SetValue(modelInstance, referenceTypeInstance, null);
}
}
}
}
One major problem with your code is the use of the variable isInstanceValuePresent. Before recursively calling PopulateModelInstance you set the variable to false and then test the value once the method returns. Unfortunately this variable is a local variable, resides on the stack and as such is local to each call. It won't reflect the value set in your recursive call.
I have a suggestion how you could change your method. Instead of passing in an object to be populated, you could pass in the type of the object to be populated. Using the same logic you have already implemented, you would only instantiate an object of this type if you find a property value that can be set. The method then passes back the instance. If no property has been found to set then the method passes back null.
private static Object CreateAndPopulateModelInstance(Type modelInstanceType, IDictionary<string, string> source)
{
// this variable will hold the reference to the instance that is to be
// populated. It will only hold a value, if a property is found that
// can be populated.
Object modelInstance = null;
foreach (PropertyInfo propInfo in modelInstanceType.GetProperties())
{
//Identify Custom attribute
DataMappingKeyAttribute attribute = DataMappingKeyAttribute)Attribute.GetCustomAttribute(propInfo, typeof(DataMappingKeyAttribute));
if (attribute != null && !string.IsNullOrEmpty(attribute.MappingKey))
{
if (propInfo.PropertyType.IsPrimitive || propInfo.PropertyType.Equals(typeof(string)))
{
string sourceKey = attribute.MappingKey;
if (source.ContainsKey(sourceKey))
{
// Get propInfo attribute value from Dictionary
//var propertySourceValue = source[(propInfo.PropertyType.GetCustomAttribute(typeof(DataMappingKeyAttribute)) as DataMappingKeyAttribute).MappingKey];
string sourceValue = source[attribute.MappingKey];
// Set propInfo value on the model instance
if (CanChangeType(sourceValue, propInfo.PropertyType) && propInfo.CanWrite && (!propInfo.PropertyType.IsClass || propInfo.PropertyType.Equals(typeof(string))))
{
// create instance if necessary
if (modelInstance == null)
modelInstance = Activator.CreateInstance(modelInstanceType);
propInfo.SetValue(modelInstance, Convert.ChangeType(sourceValue, propInfo.PropertyType), null);
}
}
}
}
else if (propInfo.PropertyType.IsClass && !propInfo.PropertyType.Equals(typeof(string)) && propInfo.CanWrite)
{
Object propertyValue = CreateAndPopulateModelInstance(propInfo.PropertyType, source);
if (propertyValue != null)
{
// create instance if necessary
if (modelInstance == null)
modelInstance = Activator.CreateInstance(modelInstanceType);
// set property value
propInfo.SetValue(modelInstance, propertyValue, null);
}
}
}
return modelInstance;
}
I renamed the method to reflect the fact that the object is created (if necessary).
Before a property is set the method checks to see if the modelInstance has already been instantiated, if not an object of the type passed in is instantiated.
If the method passes back null you know that your property value couldn't be instantiated because it didn't have any properties that could be initialized. This should work regardless of how deep the recursion goes.
I haven't tested this code (or even compiled it), so there maybe syntax errors in there. The logic ought to be ok.
You'll have to change the initial call to this method, or maybe add a third parameter that initializes the modelInstance variable like this:
private static Object CreateAndPopulateModelInstance(Object instance, Type modelInstanceType, IDictionary<string, string> source)
{
// this variable will hold the reference to the instance that is to be
// populated. It will only hold a value, if a property is found that
// can be populated.
Object modelInstance = instance;
The recursive call would then look like this:
Object propertyValue = CreateAndPopulateModelInstance(null, propInfo.PropertyType, source);
if (propertyValue != null)
{
Hope this helps.
Related
So, I am interested in getting all properties of a class via reflection ... and all properties of class-type properties (recursively).
I managed to get the first level of properties via reflections with a code like this:
foreach (var property in Target.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
var foobar = Factory.GetDealer(Target, property.Name);
}
and in the factory:
public static BaseDealer GetDealer(Object obj, string property)
{
var prop = obj.GetType().GetProperty(property);
if (prop == null)
{
return null;
}
if (prop.PropertyType.IsPrimitive)
{
return GetPrimitivDealer(obj, property, Type.GetTypeCode(prop.PropertyType));
}
if (prop.PropertyType.IsClass)
{
return new CompositeDealer(obj, prop);
}
return null;
}
Obviously (in my mind that is), I'd then have something like this in the CompositeDealer:
public CompositeDealer(Object obj, PropertyInfo propInfo)
{
ComponentDealers = new List<BaseDealer>();
Object compProp = propInfo.GetValue(obj); // The new "origin" object for reflection, this does not work
foreach (var property in compProp.GetType().GetProperties())
{
var dealer = Factory.GetDealer(compProp, property.Name);
if (dealer != null)
{
ComponentDealer.Add(dealer);
}
}
}
As noted in the comment, getting the new "base object" for reflection, that is to start the "second level" of reflection does not work, it returns null.
I really struggle at this stage although I came so far and am pretty close (I think).
Help me StackOverflow,
you're my only hope.
Edit:
So, answering the question what the data would look like:
class A {
int primitiveProp {get;}
}
class B {
A compositeProp {get;}
}
If I were to call the Factory on an instance of A, I'd make a PrimitiveDealer.
If we call it on an instance of B, I'd get a CompositeDealer, which in turn has a primitiveDealer.
Also note that I need the actual property object of the passed instance (since I need to manipulate it) rather than a new object.
Instead of calling different functions to get the properties of your object, dependent on the property type, the function should then call itself with an instance of the object we want to get the properties of.
object o = Activator.CreateInstance(property.PropertyType)
Recursively call the function which gets the properties of this object.
Edit: If you instead need the actual object, ensure to cast it to the correct type.
Type compProp = (Type)propInfo.GetValue(obj);
So, as the heading says, I have an object which is propertyInfo. What I want to get is that property, but I can't seem to find a way to do it.
Firstly I had this method:
public object GetPropertyInfo(object parent, String propertyName)
{
object propInf = null;
PropertyInfo[] propList = parent.GetType().GetProperties();
foreach (PropertyInfo pInf in propList)
{
if (propertyName == pInf.Name)
{
propInf = pInf;
}
}
return propInf;
}
And it works rather well, assuming the supplied 'parent' object is a regular class and not a reflected type.
But some of the properties returned themselves contain properties that I want to access. In these instances, I need to feed the PropertyInfo back into this method and get another PropertyInfo for the property. But if I put a PropertyInfo object into this method, it just returns me a property list of PropertyInfo (as you might imagine).
I have read up on it and it seems that what I may want is the 'GetValue' method of the PropertyInfo class. I'm a little unsure of it though since I can't seem to parse what it is that the method requires.
Even so, I wrote it as such:
public object GetPropertyInfo(object parent, String propertyName)
{
object propInf = null;
object o = null;
if (parent is PropertyInfo)
{
PropertyInfo p = (parent as PropertyInfo);
o = p.GetValue(p, null);
}
else
o = parent;
PropertyInfo[] propList = o.GetType().GetProperties();
foreach (PropertyInfo pInf in propList)
{
if (propertyName == pInf.Name)
{
propInf = pInf;
}
}
return propInf;
}
Obviously I hoped the second one would work. It passes through the 'if' statement fine, acknowledging that it is a PropertyInfo type, but then the next part provides an exception which is the following:
TargetException: Object does not match target type.
Maybe I made a mistake with the 'GetValue' since I'm not entirely familiar with it, but if I could do it without specifying a type, that would be great.
Assuming I understand what you are trying to do:
PropertyInfo represents a property of a class without being aware of the instance of the class whose property is being inspected.
GetValue method, however, can provide you the value of the property for a given instance.
object value = somePropertyInfo.GetValue(someInstance);
// where someInstance is of the type which has someProperty's represented property.
If you want the properties of the Type of the property you are currently inspecting you can use PropertyInfo.PropertyType.GetProperties(); but this will only get you the properties of the Type of the property and not the concrete (maybe derived) Type that it contains.
I've writing a small method whose sole purpose is to check if a property is null for a given class. If the property is null, then create a new instance of it. I'm getting stuck on part where I'm actually setting a value:
public static void CheckIfPropertyIsNull<TEntity>(SomeBusinessEntity someBusinessEntity) where TEntity : new()
{
var properties = typeof(SomeBusinessEntity).GetProperties();
foreach (PropertyInfo propertyInfo in properties)
{
Type currentType = propertyInfo.PropertyType;
if (currentType == typeof(TEntity))
{
var propertyData = propertyInfo.GetValue(someBusinessEntity, null);
if (propertyData == null)
{
object instance = Activator.CreateInstance(typeof(TEntity));
// And then?
//propertyInfo.SetValue(null, instance);
return;
}
}
}
}
I try to use the SetValue() method but with no luck.
In your SetValue you still have to give the instance of the owner of the property: someBusinessEntity.
object instance = new TEntity();
// And then
propertyInfo.SetValue(someBusinessEntity, instance);
Note that your logic seems odd to me. You are using a generic type to set all properties. Why not use the type of the property?
I'm creating a small validation framework, I've a custom Validation Attribute which is assignable to methods and a IsValid property in ValidationCore class. When IsValid called inside the method my ValidationCore finds a caller method and get its attribute assigned to method. My custom Validation attribute has a property name named TypeToValidate. So when I find validation attribute I look for any types in class scope of that type. I don't have any problem till now, but the problem is when I want to get the value of property which I've to validate I don't have any instance of that class to get that property value. I don't know how can I handle this situation please help me.
This is my sample :
public class TestClass
{
public static TestModel Model { get; set; }
public static ModelValidator ModelState
{
get { return new ModelValidator(); }
}
[Validate(typeof(TestModel))]
public static void DoSomething()
{
if (ModelState.IsValid)
{
// Do something else....
}
}
}
Edit : This is my IsValid property
public virtual Boolean IsValid
{
get
{
// Get IsValid caller method
var method = GetCallerMethod();
// Get method attribute
var Attrib = GetMethodAttribute(typeof(ValidateAttribute), method);
// Get model to validate inside class scope
var modelProperty = GetModelToValidateInClassScope(Attrib, method);
if (modelProperty != null)
{
ValidateModel(modelProperty);
}
....
}
}
and here is ValidateModel method :
protected virtual void ValidateModel(PropertyInfo modelProperty)
{
// Here I've model property
// But I can't get its value
var model = modelProperty.GetValue(null, null);
var properties = model.GetType().GetProperties(
BindingFlags.FlattenHierarchy |
BindingFlags.Public |
BindingFlags.Instance |
BindingFlags.DeclaredOnly);
foreach (var propertyInfo in properties)
{
// Add error to error list
GetPropertyErrors(model, propertyInfo);
}
}
Thanks in advance.
You need an instance for which to get the property value of. It looks like you need to modify the method GetModelToValidateInClassScope so that it returns the instance of the model itself, along with the PropertyInfo.
If this method is changed so it looks something like this (note the new out parameter):
PropertyInfo GetModelToValidateInClassScope(Type attributeType, MethodInfo methodInfo, out object instance)
{
// same implementation as before ...
// assign the model to use,
// which is likely accessible from somewhere in the is method
instance = theModelInstanceFromThisMethod;
// .. or if the property is static, then uncomment the next line:
// instance = null;
// same return value as before ...
}
I'm just guessing here because you didn't provide the implementation of this method. If GetModelToValidateInClassScope doesn't have access to the model instance, then you'll have to get it from somewhere else.
Once you get the model instance, it could be used like the following. Note that ValidateModel has been modified so that it accepts the model instance as the first parameter.
...
// Get model to validate inside class scope
object instance; // <--- will hold the model instance
var modelProperty = GetModelToValidateInClassScope(Attrib, method, out instance);
if (modelProperty != null)
{
ValidateModel(instance, modelProperty); // <--- make sure to pass the instance along!
}
...
protected virtual void ValidateModel(object instance, PropertyInfo modelProperty)
{
// get value of instance property
var model = modelProperty.GetValue(instance, null);
...
}
Answer to the question actually asked
I don't have any problem till now, but the problem is when I want to get the value of property which I've to validate I don't have any instance of that class to get that property value.
If it's a static property, that's fine - just use null as the first argument:
// First argument is the instance: null as it's a static property
// Second argument is indexer arguments: null as we don't have any
var value = property.GetValue(null, null);
From the documentation:
Because static properties belong to the type, not individual objects, get static properties by passing null as the object argument.
Alternative approach
If you're just trying to get the TypeToValidate property from the Validate attribute, then you should have an instance of the attribute, and you can just cast to ValidateAttribute and retrieve the property directly.
Basically, it's not clear where properties really come into what you're trying to do. Your attribute is on a method rather than a property, and your attribute doesn't say which properties to validate...
I'm starting to work with dynamic objects in .Net and I can't figure out how to do something.
I have a class that inherits from DynamicObject, and I override the TryInvokeMember method.
e.g.
class MyCustomDynamicClass : DynamicObject
{
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
// I want to know here the type of the generic argument
}
}
And inside that method I want to know the type (if any) of the generic arguments in the invocation.
e.g.
If I invoke the following code, I want to get the value of System.Boolean and System.Int32 inside the overrided method of my dynamic object
dynamic myObject = new MyCustomDynamicClass();
myObject.SomeMethod<bool>("arg");
myObject.SomeOtherMethod<int>("arg");
Currently if I place a breakpoint inside the overrided method I can get the name of the method being invoked ("SomeMethod" and "SomeOtherMethod", and also the values of the arguments, but not the generic types).
How can I get these values?
Thanks!
Actually I looked through the hierarchy of the binder and found a property with the needed values in the internal fields of the object.
The problem is that the property isn't exposed because it uses C#-specific code/classes, therefore the properties must be accessed using Reflection.
I found the code in this japanese blog: http://neue.cc/category/programming (I don't read any japanese, therefore I'm not sure if the author actually describes this same issue
Here's the snippet:
var csharpBinder = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
var typeArgs = (csharpBinder.GetProperty("TypeArguments").GetValue(binder, null) as IList<Type>);
typeArgs is a list containing the types of the generic arguments used when invoking the method.
Hope this helps someone else.
A bit of googling and I have quite generic solution for .NET and Mono:
/// <summary>Framework detection and specific implementations.</summary>
public static class FrameworkTools
{
private static bool _isMono = Type.GetType("Mono.Runtime") != null;
private static Func<InvokeMemberBinder, IList<Type>> _frameworkTypeArgumentsGetter = null;
/// <summary>Gets a value indicating whether application is running under mono runtime.</summary>
public static bool IsMono { get { return _isMono; } }
static FrameworkTools()
{
_frameworkTypeArgumentsGetter = CreateTypeArgumentsGetter();
}
private static Func<InvokeMemberBinder, IList<Type>> CreateTypeArgumentsGetter()
{
if (IsMono)
{
var binderType = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder");
if (binderType != null)
{
ParameterExpression param = Expression.Parameter(typeof(InvokeMemberBinder), "o");
return Expression.Lambda<Func<InvokeMemberBinder, IList<Type>>>(
Expression.TypeAs(
Expression.Field(
Expression.TypeAs(param, binderType), "typeArguments"),
typeof(IList<Type>)), param).Compile();
}
}
else
{
var inter = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
if (inter != null)
{
var prop = inter.GetProperty("TypeArguments");
if (!prop.CanRead)
return null;
var objParm = Expression.Parameter(typeof(InvokeMemberBinder), "o");
return Expression.Lambda<Func<InvokeMemberBinder, IList<Type>>>(
Expression.TypeAs(
Expression.Property(
Expression.TypeAs(objParm, inter),
prop.Name),
typeof(IList<Type>)), objParm).Compile();
}
}
return null;
}
/// <summary>Extension method allowing to easyly extract generic type arguments from <see cref="InvokeMemberBinder"/>.</summary>
/// <param name="binder">Binder from which get type arguments.</param>
/// <returns>List of types passed as generic parameters.</returns>
public static IList<Type> GetGenericTypeArguments(this InvokeMemberBinder binder)
{
// First try to use delegate if exist
if (_frameworkTypeArgumentsGetter != null)
return _frameworkTypeArgumentsGetter(binder);
if (_isMono)
{
// In mono this is trivial.
// First we get field info.
var field = binder.GetType().GetField("typeArguments", BindingFlags.Instance |
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
// If this was a success get and return it's value
if (field != null)
return field.GetValue(binder) as IList<Type>;
}
else
{
// In this case, we need more aerobic :D
// First, get the interface
var inter = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
if (inter != null)
{
// Now get property.
var prop = inter.GetProperty("TypeArguments");
// If we have a property, return it's value
if (prop != null)
return prop.GetValue(binder, null) as IList<Type>;
}
}
// Sadly return null if failed.
return null;
}
}
Have fun. By the way Impromptu is cool, but I can't use it.
The open source framework Dynamitey can call properties that internal/protected/private using the DLR and thus works with Silverlight. But it get's a little tricky with interface explicit members as you have to use the actual full name of the member on the the type, rather than the interface member name. So you can do:
var typeArgs = Dynamic.InvokeGet(binder, "Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder.TypeArguments")
as IList<Type>;