We need that if the member doesn't exist on source but it exists in the destination, to have the destination member initialized. Currently we get nulls unless we manually initialize every single of the 15 members with:
List<string> Property = new List<string>();
This is our service
public MappingService()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Car, Bike>();
});
_mapper = config.CreateMapper();
}
public Bike MapToBike(Car car)
{
return _mapper.Map<Car, Bike >(car);
}
You probably should not rely on AutoMapper initializing your collections. Having null as property value is a valid case after all.
That said, although there is no standard way to do that, you can devise something that might work. For example, as mentioned, you can use .AfterMap() to populate all null collections with empty ones. Here is an extension method to do so:
/// <summary>
/// Populates all properties of standard collection types with empty collections
/// </summary>
public static IMappingExpression<TSrc, TDest> InitializeNullCollectionProps<TSrc, TDest>(this IMappingExpression<TSrc, TDest> map)
{
return map.AfterMap((src, dest) =>
{
var destType = typeof(TDest);
// find all applicable properties
var collectionProps = destType.GetProperties(BindingFlags.Public)
.Where(propInfo =>
{
if (!propInfo.CanRead)
{
return false;
}
// check if there is public setter
if (propInfo.GetSetMethod() == null)
{
return false;
}
var propertyType = propInfo.PropertyType;
// it can be Array, List, HashSet, ObservableCollection, etc
var isCollection = !propertyType.IsAssignableFrom(typeof(IList));
if (!isCollection)
{
return false;
}
var haveParameterlessCtor =
propertyType.GetConstructors().Any(ctr => !ctr.GetParameters().Any());
if (!haveParameterlessCtor)
{
return false;
}
return true;
})
.ToList();
// assign empty collections to applicable properties
foreach (var propInfo in collectionProps)
{
var value = propInfo.GetValue(dest);
if (value == null)
{
propInfo.SetValue(dest, Activator.CreateInstance(propInfo.GetType()));
}
}
});
}
It's usage will simply be:
Mapper.CreateMap<Src,Dest>()
// some mappings
.InitializeNullCollectionProps()
// some mappings
But I don't think there is a way to accurately determine if the member was already mapped to null from source, so it will have empty collection value anyway.
I'm an amateur and not English native speaker. I'm trying to do mapper for use it with Dapper's dynamic results, I need it for a personal project and I couldn't adapt self Dapper mapper either Automapper to my needs (probably due to my lack of knowledge), so I decided to make mine.
It works flawlessly when I add the mapper code with "Add -> Existing Item", but if I try to use it as a DLL it throws the "not accessible due to access level" exception, pointing this part of the code:
public T Map(IEnumerable<dynamic> dapperResult, bool cleanResult = false)
{
var parser = new PrePostFixesParser(this);
T mapped = this.NewObject(dapperResult.First()); <------//EXCEPTION HERE
if (_OnlyConstructor.Contains(this.TType)) return mapped;
//... Other things
The method NewObject:
private T NewObject(dynamic dyn)
{
Type t = typeof(T);
bool IsInterfaceNotIEnumerable = t.IsInterface
&& !typeof(IDictionary).IsAssignableFrom(t)
&& !(typeof(IEnumerable).IsAssignableFrom(t) && !typeof(string).IsAssignableFrom(t));
if (IsInterfaceNotIEnumerable)
throw new CustomException_DapperMapper(
#"DapperMapper.NewObject: Exception trying to instantiate an interface that isn't an IEnumerable. This is a BUG.");
T newObj;
//if there are a constructor configurated
if (_Constructors.ContainsKey(this.TType))
{
//object[] constructorParams = GetConstructorParams();
try
{
Func<dynamic, T> constDelegate = (Func<dynamic, T>)_Constructors[this.TType];
newObj = constDelegate(dyn);
}
catch (Exception err)
{
throw new CustomException_DapperMapper(
$#"DapperMapper.NewObject: Exception using constructor to create object of type {TType.Name} with delegate {_Constructors[TType]}.", err);
}
}
//if there are no constructor configurated, use parameterless constructor
else newObj = Activator.CreateInstance<T>();
return newObj;
}
Both methods are part of the same class called DapperMapper. I've read the page always want all the possible code in the same question, so I'm posting the entire class here, but it's a large one, so I'm posting too the link to Github where I have all the solution... just in case:
public class DapperMapper<T> : DMStatic_Mapper, iDapperMapper
{
public DapperMapper(MapperStore store)
{
this.TType = typeof(T);
this.MappersStore = store;
this.MappersStore.StoreMapper(this.TType, this);
}
#region properties
public MapperStore MappersStore { get; private set; }
public Dictionary<MemberInfo, MemberTypeInfo> mtInfos { get { return _mtInfos[this.TType]; } }
public Type TType { get; private set; }
public IEnumerable<string> NamesList { get { return _NamesList[this.TType]; } }
public Tuple<string[], bool> Prefixes { get { return _Prefixes.ContainsKey(this.TType) ? _Prefixes[this.TType] : null; } }
public Tuple<string[], bool> Postfixes { get { return _Postfixes.ContainsKey(this.TType) ? _Postfixes[this.TType] : null; } }
#endregion
#region helpers
private T NewObject(dynamic dyn)
{
Type t = typeof(T);
bool IsInterfaceNotIEnumerable = t.IsInterface
&& !typeof(IDictionary).IsAssignableFrom(t)
&& !(typeof(IEnumerable).IsAssignableFrom(t) && !typeof(string).IsAssignableFrom(t));
if (IsInterfaceNotIEnumerable)
throw new CustomException_DapperMapper(
#"DapperMapper.NewObject: Exception trying to instantiate an interface that isn't an IEnumerable. This is a BUG.");
T newObj;
//if there are a constructor configurated
if (_Constructors.ContainsKey(this.TType))
{
//object[] constructorParams = GetConstructorParams();
try
{
Func<dynamic, T> constDelegate = (Func<dynamic, T>)_Constructors[this.TType];
newObj = constDelegate(dyn);
}
catch (Exception err)
{
throw new CustomException_DapperMapper(
$#"DapperMapper.NewObject: Exception using constructor to create object of type {TType.Name}
with delegate {_Constructors[TType]}.", err);
}
}
//if there are no constructor configurated, use parameterless constructor
else newObj = Activator.CreateInstance<T>();
return newObj;
}
#endregion
#region public methods
/// <summary>
/// Remove duplicated results due to JOINs.
/// </summary>
/// <param name="origDapperResult"></param>
/// <param name="cleanResult"></param>
/// <returns></returns>
public IEnumerable<dynamic> GetDistinctDapperResult(IEnumerable<dynamic> origDapperResult, bool cleanResult)
{
PrePostFixesParser parser = new PrePostFixesParser(this);
IEnumerable<string> names = this.NamesList;
List<dynamic> result = new List<dynamic>();
foreach(dynamic dyn in origDapperResult)
{
IDictionary<string, object> dict =
(!cleanResult ? parser.GetTypeMembersWithoutPrePostFixes(dyn, names) : dyn)
as IDictionary<string, object>;
bool distinct = true;
foreach(dynamic resultDyn in result)
{
IDictionary<string, object> resDict = resultDyn as IDictionary<string, object>;
if(dict.Keys.SequenceEqual(resDict.Keys) && dict.Values.SequenceEqual(resDict.Values))
{
distinct = false;
break;
}
}
if (distinct) result.Add(dyn);
}
return result;
}
/// <summary>
/// Check if the dynamic object have all the members needed to map a new T object, except those setted as IEnumerable,
/// which should be provided in others dynamic.
/// </summary>
/// <param name="dyn"></param>
/// <returns></returns>
public bool CheckIfDynamicHasAllTypeMembersByName(dynamic dyn)
{
IDictionary<string, object> membersDict = dyn as IDictionary<string, object>;
IEnumerable<string> dynList = membersDict.Select(kvp => kvp.Key);
PrePostFixesParser parser = new PrePostFixesParser(this);
IEnumerable<string> list = parser.GetCleanNamesList(this.NamesList);
return !dynList.Except(list).Any() && !list.Except(dynList).Any();
}
/// <summary>
/// Check if the dynamic object have all the members needed to map a new T object, except those setted as IEnumerable,
/// which should be provided in others dynamic.
/// </summary>
/// <param name="membersDict"></param>
/// <returns></returns>
public bool CheckIfDynamicHasAllTypeMembersByName(IDictionary<string, object> membersDict)
{
IEnumerable<string> dynList = membersDict.Select(kvp => kvp.Key);
PrePostFixesParser parser = new PrePostFixesParser(this);
return dynList.SequenceEqual(parser.GetCleanNamesList(this.NamesList));
}
/// <summary>
/// Generic Map.
/// </summary>
/// <param name="dapperResult"></param>
/// <returns></returns>
public T Map(IEnumerable<dynamic> dapperResult, bool cleanResult = false)
{
var parser = new PrePostFixesParser(this);
T mapped = this.NewObject(dapperResult.First());
if (_OnlyConstructor.Contains(this.TType)) return mapped;
//TODO: divide el siguiente foreach en dos con dos nuevos diccionarios estáticos, uno para pInfos y otro para fInfos,
//aunque se repita código: hacer métodos para cada parte del código del tipo:
//private T PreMapCreator(KeyValuePair<PropertyInfo, MemberTypeInfo> kvp, IEnumerable<dynamic> dapperResult, bool cleanResult = false)
//private T PreMapIEnumerable(KeyValuePair<PropertyInfo, MemberTypeInfo> kvp, IEnumerable<dynamic> dapperResult, bool cleanResult = false)
//...
//Loop through all members
foreach (KeyValuePair<MemberInfo, MemberTypeInfo> kvp in mtInfos)
{
if (kvp.Value == MemberTypeInfo.Ignore)
continue;
//Member have a creator
else if ((kvp.Value & MemberTypeInfo.Creator) == MemberTypeInfo.Creator)
{
//MemberDelegate mDel = (MemberDelegate)_MembersCreators[this.TType][kvp.Key.Name];
Func<dynamic, object> mDel = (Func<dynamic, object>)_MembersCreators[this.TType][kvp.Key.Name];
if (kvp.Key.MemberType == MemberTypes.Property) ((PropertyInfo)kvp.Key).SetValue(mapped, mDel(dapperResult));
else ((FieldInfo)kvp.Key).SetValue(mapped, mDel(dapperResult));
}
//Member is IDictionary or IEnumerable
else if ((kvp.Value & MemberTypeInfo.IEnumerable) == MemberTypeInfo.IEnumerable)
{
Type t = GetMemberType(kvp.Key);
//if ((kvp.Value & MemberTypeInfo.Interface) == MemberTypeInfo.Interface) t = ResolveInterface(kvp.Key, dapperResult);
//else t = GetMemberType(kvp.Key);
/*
{
//Type of property or field
if (kvp.Key.MemberType == MemberTypes.Property) t = ((PropertyInfo)kvp.Key).PropertyType;
else t = ((FieldInfo)kvp.Key).FieldType;
}*/
bool isAnInterface = (kvp.Value & MemberTypeInfo.Interface) == MemberTypeInfo.Interface;
bool isNested = (kvp.Value & MemberTypeInfo.Nested) == MemberTypeInfo.Nested;
//If member is a dictionary
if (typeof(IDictionary).IsAssignableFrom(t))
{
//Create a dummy dictionary with the dapper's dynamic result which should be equal to the final one
DictionaryMapper dictMapper = new DictionaryMapper(dapperResult, kvp.Key.Name, isNested, isAnInterface, cleanResult, t, this);
try
{
if (kvp.Key.MemberType == MemberTypes.Property) ((PropertyInfo)kvp.Key).SetValue(mapped, dictMapper.DummyDictionary);
else ((FieldInfo)kvp.Key).SetValue(mapped, dictMapper.DummyDictionary);
}
catch (Exception err)
{
throw new CustomException_DapperMapper(
$#"DapperMapper.Map: Couldn't map IDictionary member {kvp.Key.Name} with value contained by dynamic object.
Incorrect type of value?: {kvp.Value.ToString()}",
err);
}
}
//Rest of enumerables
else
{
IEnumerable<dynamic> iEnumDapperResult;
//Select current member's values from dynamic
if (isNested && !cleanResult)
{
//Type mType = t; // GetMemberType(kvp.Key);//IEnumerable<T>
Type genericType = t.GenericTypeArguments[0];//mType.GenericTypeArguments[0];//T
if ((kvp.Value & MemberTypeInfo.Interface) == MemberTypeInfo.Interface)
{
bool genericIsInterfaceNotIEnumerable =
genericType.IsInterface &&
!typeof(IDictionary).IsAssignableFrom(genericType) &&
!(typeof(IEnumerable).IsAssignableFrom(genericType) && !typeof(string).IsAssignableFrom(genericType));
if (genericIsInterfaceNotIEnumerable) genericType = ResolveInterface(genericType, dapperResult);
}
iDapperMapper nestedMapper = MappersStore.GetMapper(genericType);
var nestedParser = new PrePostFixesParser(nestedMapper);
iEnumDapperResult = dapperResult
.Select(dyn => nestedParser.GetTypeMembersWithoutPrePostFixes(dyn, nestedMapper.NamesList));
}
else if (!cleanResult) iEnumDapperResult = dapperResult.Select(dyn => parser.RemovePrePostFixesFromDictionary(dyn));
else iEnumDapperResult = dapperResult;
//Create dummy IEnumerable
EnumerableMapper enumMapper = new EnumerableMapper(iEnumDapperResult, kvp.Key.Name, isNested, t, this.TType); ;
var dummy = Activator.CreateInstance(t, enumMapper.DummyEnumerable);
try
{
if (kvp.Key.MemberType == MemberTypes.Property) ((PropertyInfo)kvp.Key).SetValue(mapped, dummy);
else ((FieldInfo)kvp.Key).SetValue(mapped, dummy);
}
catch (Exception err)
{
throw new CustomException_DapperMapper(
$#"DapperMapper.Map: Couldn't map IEnumerable member {kvp.Key.Name} with value contained by dynamic object.
Incorrect type of value?: {kvp.Value.ToString()}",
err);
}
}
}//End IDictionary/IEnumerable
//If Built-in
else if ((kvp.Value & MemberTypeInfo.BuiltIn) == MemberTypeInfo.BuiltIn)
{
string name = parser.RemoveFieldsUnderscore(kvp.Key.Name);
IDictionary<string, object> dapperDict;
if (!cleanResult)
dapperDict = parser.GetTypeMembersWithoutPrePostFixes(dapperResult.First(), NamesList) as IDictionary<string, object>;
else
dapperDict = dapperResult.First() as IDictionary<string, object>;
if (!dapperDict.ContainsKey(name))
throw new CustomException_DapperMapper(
$#"DapperMapper.Map: There's no member in dynamic dapper result with name {kvp.Key.Name}. Cannot Map object.");
try
{
if (kvp.Key.MemberType == MemberTypes.Property) ((PropertyInfo)kvp.Key).SetValue(mapped, dapperDict[name]);
else ((FieldInfo)kvp.Key).SetValue(mapped, dapperDict[name]);
}
catch (Exception err)
{
throw new CustomException_DapperMapper(
$#"DapperMapper.Map: Couldn't map BuiltIn-type member {kvp.Key.Name} with value contained by dynamic object.
Incorrect type of value?: {kvp.Value.ToString()}",
err);
}
}
//if nested
else if ((kvp.Value & MemberTypeInfo.Nested) == MemberTypeInfo.Nested)
{
Type mType = GetMemberType(kvp.Key);
if ((kvp.Value & MemberTypeInfo.Interface) == MemberTypeInfo.Interface)
mType = ResolveInterface(mType, dapperResult);
//access generic Map method through nongeneric interface method
iDapperMapper nestedMapper = MappersStore.GetMapper(mType);
if (nestedMapper == null)
throw new CustomException_DapperMapper(
$#"DapperMapper.Map: No Mapper found at store for property {kvp.Key.Name} of type {mType.ToString()}.
If you want to map a nested property you have to create a mapper for that property type.");
if (kvp.Key.MemberType == MemberTypes.Property)
((PropertyInfo)kvp.Key).SetValue(mapped, nestedMapper.NoGenericMap(dapperResult, cleanResult));
else ((FieldInfo)kvp.Key).SetValue(mapped, nestedMapper.NoGenericMap(dapperResult, cleanResult));
}
}
return mapped;
}
/// <summary>
/// Generic map to IEnumerables. Result HAVE to be ordered by the splitOn column.
/// </summary>
/// <typeparam name="R"></typeparam>
/// <param name="dapperResult"></param>
/// <param name="splitOn"></param>
/// <param name="cleanResult"></param>
/// <returns></returns>
public R Map<R>(IEnumerable<dynamic> dapperResult, string splitOn = "Id", bool cleanResult = false)
where R : IEnumerable<T>
{
R result;
Type r = typeof(R);
List<dynamic> singleObjectDynamic = new List<dynamic>();
object splitObject = (dapperResult.First() as IDictionary<string, object>)[splitOn];
if (typeof(IList).IsAssignableFrom(r))
{
result = (R)Activator.CreateInstance(typeof(List<>).MakeGenericType(this.TType));
foreach (dynamic dyn in dapperResult)
{
IDictionary<string, object> dict = dyn as IDictionary<string, object>;
if (!dict.ContainsKey(splitOn))
throw new CustomException_DapperMapper(
$#"DapperMapper.Map(IEnumerable): Dapper result doesn't have a member with name equals to splitOn parameter.
SplitOn = {splitOn}");
if (!object.Equals(splitObject, dict[splitOn]) || dapperResult.Last() == dyn)
{
((IList)result).Add(Map(singleObjectDynamic));
singleObjectDynamic.Clear();
splitObject = dict[splitOn];
}
else
singleObjectDynamic.Add(dyn);
}
}
else
{
//http://stackoverflow.com/questions/18251587/assign-any-ienumerable-to-object-property
var addMethod = r.GetMethod("Add", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);
// Property doesn't support Adding
if (addMethod == null)
throw new CustomException_DapperMapper(
$#"DapperMapper.Map(IEnumerable): Method Add doesn't exist in enumerable type to map.
enumType: {r.Name}.");
if (r.IsGenericTypeDefinition) result = (R)Activator.CreateInstance(r.MakeGenericType(this.TType));
else result = (R)Activator.CreateInstance(r);
foreach (dynamic dyn in dapperResult)
{
IDictionary<string, object> dict = dyn as IDictionary<string, object>;
if (!dict.ContainsKey(splitOn))
throw new CustomException_DapperMapper(
$#"DapperMapper.Map(IEnumerable): Dapper result doesn't have a member with name equals to splitOn parameter.
SplitOn = {splitOn}");
if (!object.Equals(splitObject, dict[splitOn]) || dapperResult.Last() == dyn)
{
addMethod.Invoke(result, new object[] { NoGenericMap(dyn) });
singleObjectDynamic.Clear();
splitObject = dict[splitOn];
}
else
singleObjectDynamic.Add(dyn);
}
}
return result;
}
/// <summary>
/// Non-generic Map.
/// </summary>
/// <param name="dapperResult"></param>
/// <returns></returns>
public object NoGenericMap(dynamic dapperResult, bool cleanResult = false)
{
IEnumerable<dynamic> ienum = new List<dynamic>() { dapperResult } as IEnumerable<dynamic>;
return this.Map(ienum, cleanResult);
}
/// <summary>
/// Non-generic Map.
/// </summary>
/// <param name="dapperResult"></param>
/// <param name="cleanResult"></param>
/// <returns></returns>
public object NoGenericMap(IEnumerable<dynamic> dapperResult, bool cleanResult = false)
{
return this.Map(dapperResult, cleanResult);
}
#endregion
}
QUESTION
So, what's happening?
I'm astonished by this one. Am I terribly wrong or class's methods should be able to see same class private methods?
For the DLL I just began it with a new Class Library project in visual studio 2015 community, and when done build it with the typical release configuration, then I went to a console project I'm using to testing it (I just made a simple database there, to have direct Dapper's dynamic results objects to test it) and added reference to the DLL. Could it be that I build the DLL wrong? Do I have to do something more in the testing project apart from referencing the DLL and add the corresponding using namespace?
I've tried searching for private methods, DLLs, private methods in DLLs, private methods in the public class in DLLs, but it seems I'm not able to find anything that solves it. Apart from checking the class and method have the proper access keywords (c# default access is private if no other is specified, right?), and clean, build, rebuild again both solutions, I don't see anymore.
I'm afraid I'm just forgetting something stupid, but it works if I copy-paste the code (or add it with "Add->Existing Item" as I said before") instead of using the DLL, therefore the code is correct, right?
EDIT:
Due to comment of GSerg I'm adding the way I call DapperMapper in the testing project after adding the reference to the DLL:
MapperStore store = new MapperStore();
DapperMapper<Persons> mapper = (DapperMapper<Persons>)store.GetMapper(typeof(Persons));
Persons p = mapper.Map(result);
EDIT2:
As can be seen in the comments, as suggested by GSerg I created a new console project for testing, add the reference to the DLL, copy-paste ONLY the test (mock classes, database), hit F5 and got the same result. I double checked that I builded the DLL and the test project at release mode, and use windows explorer to confirm that there were no DLL's .cs files at the testing project.
I have a huge user-defined class with lots of properties and some of them has to be set with a certain value.
To be more specific, all the public properties with the type of string in this user-defined class have to be "emptied" at the end of execution.
So i reached all the public properties one by one (like 300 properties) and assigned them with my 300 lines of code. What i did to solve this problem, did what i needed, but didn't satisfy me of course.
So i decided to write a Helper Method (as Extension Method) in C# that iterates through the properties of an object instance and access / manipulate them dynamically. I've seen some similar questions about iterating through properties in here, but didn't see anything about changing property values so far. Here is what i tried:
public static void SetDefaultStringValues(this Object myObject)
{
PropertyInfo[] aryProperties = entityObject.GetType().GetProperties();
foreach (object property in aryProperties)
{
if (property is String)
{
//property = String.Empty;
}
}
}
The commented part had to be the line that i set the variables but that wasn't really a success story :) I'll appreciate any help.
Use PropertyInfo.SetValue method, more on this in here
You can use PropertyDescriptor of System.ComponentModel for lazy coding. Assuming you want to iterate over public instance methods (not statics) you can try the following sample:
Test class:
public class TestClass
{
private int mIntMemeber = 0; // # to test int type
private string mStringMember = "abc"; // # to test string type (initialized)
private string mNullStringMember = null; // # to test string type (null)
private static string mStaticNullStringMember; // # to test string type (static)
// # Defining properties for each member
public int IntMember
{
get { return mIntMemeber; }
set { mIntMemeber = value; }
}
public string StringMember
{
get { return mStringMember; }
set { mStringMember = value; }
}
public string NullStringMember
{
get { return mNullStringMember; }
set { mNullStringMember = value; }
}
public static string StaticNullStringMember
{
get { return mStaticNullStringMember; }
set { mStaticNullStringMember = value; }
}
}
SetDefaultStringValues() Extension Method:
public static string SetDefaultStringValues(this TestClass testClass)
{
StringBuilder returnLogBuilder = new StringBuilder();
// # Get all properties of testClass instance
PropertyDescriptorCollection propDescCollection = TypeDescriptor.GetProperties(testClass);
// # Iterate over the property collection
foreach (PropertyDescriptor property in propDescCollection)
{
string name = property.Name;
Type t = property.PropertyType; // # Get property type
object value = property.GetValue(testClass); // # Get value of the property (value that member variable holds)
if (t == typeof(string)) // # If type of propery is string and value is null
{
property.SetValue(testClass, String.Empty); // # Set value of the property (set member variable)
value = String.Empty; // # <-- To prevent NullReferenceException when printing out
}
returnLogBuilder.AppendLine("*****\nName:\t{0}\nType:\t{1}\nValue:\t{2}", name, t.ToString(), value.ToString());
}
returnLogBuilder.AppendLine("*****");
return returnLogBuilder.toString();
}
You could also check if value is null within the loop. SetDefaultStringValues method parameter could be any object instance. You can change it to SetDefaultStringValues(this object o) and use it as extension method for your defined class instance.
You need a different syntax in order to check the property type; furthermore, you shold check that the property has a public setter.
if (property.PropertyType == typeof(string) && property.GetSetMethod() != null)
property.SetValue(entityObject, "");
foreach (PropertyInfo property in aryProperties)
{
if (property.PropertyType == typeof(string) && property.CanWrite)
{
property.SetValue(myObject, "", null);
}
}
I am trying to get the values from objects inside a list which is part of a main object.
I have the main object which contains various properties which can be collections.
Right now I am trying to figure out how to access a generic list which is contained in the object.
///<summary>
///Code for the inner class
///</summary>
public class TheClass
{
public TheClass();
string TheValue { get; set; }
} //Note this class is used for serialization so it won't compile as-is
///<summary>
///Code for the main class
///</summary>
public class MainClass
{
public MainClass();
public List<TheClass> TheList { get; set; }
public string SomeOtherProperty { get; set; }
public Class SomeOtherClass { get; set }
}
public List<MainClass> CompareTheValue(List<object> MyObjects, string ValueToCompare)
{
//I have the object deserialised as a list
var ObjectsToReturn = new List<MainClass>();
foreach(var mObject in MyObjects)
{
//Gets the properties
PropertyInfo piTheList = mObject.GetType().GetProperty("TheList");
object oTheList = piTheList.GetValue(MyObject, null);
//Now that I have the list object I extract the inner class
//and get the value of the property I want
PropertyInfo piTheValue = oTheList.PropertyType
.GetGenericArguments()[0]
.GetProperty("TheValue");
//get the TheValue out of the TheList and compare it for equality with
//ValueToCompare
//if it matches then add to a list to be returned
//Eventually I will write a Linq query to go through the list to do the comparison.
ObjectsToReturn.Add(objectsToReturn);
}
return ObjectsToReturn;
}
I've tried to use a SetValue() with MyObject on this, but it errors out with (paraphrased):
object is not of type
private bool isCollection(PropertyInfo p)
{
try
{
var t = p.PropertyType.GetGenericTypeDefinition();
return typeof(Collection<>).IsAssignableFrom(t) ||
typeof(Collection).IsAssignableFrom(t);
}
catch
{
return false;
}
}
}
To Get/Set using reflection you need an instance. To loop through the items in the list try this:
PropertyInfo piTheList = MyObject.GetType().GetProperty("TheList"); //Gets the properties
IList oTheList = piTheList.GetValue(MyObject, null) as IList;
//Now that I have the list object I extract the inner class and get the value of the property I want
PropertyInfo piTheValue = piTheList.PropertyType.GetGenericArguments()[0].GetProperty("TheValue");
foreach (var listItem in oTheList)
{
object theValue = piTheValue.GetValue(listItem, null);
piTheValue.SetValue(listItem,"new",null); // <-- set to an appropriate value
}
See if something like this helps you in the right direction: I got the same error a while back and this code snipped solved my issue.
PropertyInfo[] properties = MyClass.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.Name == "MyProperty")
{
object value = results.GetType().GetProperty(property.Name).GetValue(MyClass, null);
if (value != null)
{
//assign the value
}
}
}
This is my method:
/// <summary>
/// Uses Dictionary(Key,Value) where key is the property and value is the field name.
/// Matches the dictionary of mandatory fields with object properties
/// and checks whether the current object has values in it or
/// not.
/// </summary>
/// <param name="mandatoryFields">List of string - properties</param>
/// <param name="o">object of the current class</
/// <param name="message">holds the message for end user to display</param>
/// <returns>The name of the property</returns>
public static bool CheckMandatoryFields(Dictionary<string,string > mandatoryFields, object o,out StringBuilder message)
{
message = new StringBuilder();
if(mandatoryFields !=null && mandatoryFields.Count>0)
{
var sourceType = o.GetType();
var properties = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Static);
for (var i = 0; i < properties.Length; i++)
{
if (mandatoryFields.Keys.Contains(properties[i].Name))
{
if (string.IsNullOrEmpty( properties[i].GetValue(o, null).ToString()))
{
message.AppendLine(string.Format("{0} name is blank.", mandatoryFields.Values));
}
}
}
if(message.ToString().Trim().Length>0)
{
return false;
}
}
return true;
}
In this I have params Dictionary which will hold the property name of the class and its corresponding fieldname from the UI(manually fed by developer in the business layer or UI).
So what I want is that when the property is on the way to validate, if the property is found null or blank, then its corresponding fieldname, which is actually the value of the dictionary will get added to the stringbuilder message in the method above.
I hope i am clear.
Do the loop the other way:
public static bool CheckMandatoryFields(Dictionary<string,string > mandatoryFields, object o,out StringBuilder message)
{
message = new StringBuilder();
if(mandatoryFields == null || mandatoryFields.Count == 0)
{
return true;
}
var sourceType = o.GetType();
foreach (var mandatoryField in mandatoryFields) {
var property = sourceType.GetProperty(mandatoryField.Key, BindingFlags.Public | BindingFlags.Static);
if (property == null) {
continue;
}
if (string.IsNullOrEmpty(property.GetValue(o, null).ToString()))
{
message.AppendLine(string.Format("{0} name is blank.", mandatoryField.Value));
}
}
return message.ToString().Trim().Length == 0;
}
This way you iterate over the properties you want to check, so you always have a handle on the "current" property and know the corresponding key and value from the dictionary.
The snippet
if (property == null) {
continue;
}
causes the function to treat properties that exist as names in the dictionary but not as actual properties on the type to be treated as valid, to mirror what your original code does.