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?
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 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.
I'd like to reset the properties of a class back to their default values within a method of the class. My class is instantiated once (is actually a ViewModel in an MVVM framework) and I don't want to destroy and recreate the entire ViewModel, just clear many of the properties. The below code is what I have. The only thing I am missing is how to get the first parameter of the SetValue method - I know it is an instance of the property I am setting, but I cannot seem to figure out how to access that. I get error: "Object does not match target type".
public class myViewModel
{
...
...
public void ClearFields()
{
Type type = typeof(myViewModel);
PropertyInfo[] pi = type.GetProperties();
foreach (var pinfo in pi)
{
object[] attributes = pinfo.GetCustomAttributes(typeof(DefaultValueAttribute), false);
if (attributes.Length > 0)
{
DefaultValueAttribute def = attributes[0] as DefaultValueAttribute;
pinfo.SetValue(?, def.Value, null);
}
}
}
...
...
}
You should pass an instance of myViewModel, in your case use this to reference the current instance:
public class myViewModel
{
...
...
public void ClearFields()
{
Type type = typeof(myViewModel);
PropertyInfo[] pi = type.GetProperties();
foreach (var pinfo in pi)
{
object[] attributes = pinfo.GetCustomAttributes(typeof(DefaultValueAttribute), false);
if (attributes.Length > 0)
{
DefaultValueAttribute def = attributes[0] as DefaultValueAttribute;
pinfo.SetValue(this, def.Value, null);
}
}
}
...
...
}
You should put this as a first parameter. See MSDN for reference:
objType: System.Object
The object whose property value will be set.
I want to initialize all public properties of generic type.
I've written the following method :
public static void EmptyModel<T>(ref T model) where T : new()
{
foreach (PropertyInfo property in typeof(T).GetProperties())
{
Type myType = property.GetType().MakeGenericType();
property.SetValue(Activator.CreateInstance(myType));//Compile error
}
}
but it has a compile error
how can I do it?
There are three problems here:
PropertyInfo.SetValue takes two arguments, a reference to an object of to set the property on (or null for static properties)`, and the value to set it too.
property.GetType() will return PropertyInfo. To get the type of the property itself, you want to use property.PropertyType instead.
Your code doesn't handle cases when there is no parameterless constructor on the property type. You can't get too fancy here without radically changing the way you're doing things, so in my code, I'll initialize the property to null if no parameterless constructor is found.
I think what you're looking for is this:
public static T EmptyModel<T>(ref T model) where T : new()
{
foreach (PropertyInfo property in typeof(T).GetProperties())
{
Type myType = property.PropertyType;
var constructor = myType.GetConstructor(Type.EmptyTypes);
if (constructor != null)
{
// will initialize to a new copy of property type
property.SetValue(model, constructor.Invoke(null));
// or property.SetValue(model, Activator.CreateInstance(myType));
}
else
{
// will initialize to the default value of property type
property.SetValue(model, null);
}
}
}