How to set nested property value using FastMember - c#

I get an exception when I try to set a nested member Property using FastMember. For example when having these classes
public class A
{
public B First { get; set; }
}
public class B
{
public string Second { get; set; }
}
and I want to set First.Second of an instance to "hello".
var b = new B{ Second = "some value here" };
var a = new A{ First = b };
var accessor = ObjectAccessor.Create(a);
accessor["First.Second"] = value; // this does not work and gives ArgumentOutOfRangeException
I can't split it up into ["First"]["Second"] because I don't know the depth at this point. Is there a magical access for nested properties or do I have to split the hierarchy myself?

I solved the problem recursively using an Extension Method this way:
public static class FastMemberExtensions
{
public static void AssignValueToProperty(this ObjectAccessor accessor, string propertyName, object value)
{
var index = propertyName.IndexOf('.');
if (index == -1)
{
accessor[propertyName] = value;
}
else
{
accessor = ObjectAccessor.Create(accessor[propertyName.Substring(0, index)]);
AssignValueToProperty(accessor, propertyName.Substring(index + 1), value);
}
}
}
... and this is started as follows:
ObjectAccessor.Create(a).AssignValueToProperty("First.Second", "hello")

You need to traverse the object graph using multiple ObjectAccessor instances.
public static void UseFastMember()
{
var b = new B { Second = "some value here" };
var a = new A { First = b };
var value = "hello";
var a_accessor = ObjectAccessor.Create(a);
var first = a_accessor["First"];
var b_accessor = ObjectAccessor.Create(first);
b_accessor["Second"] = value;
}

Hats off to #Beachwalker for the inspiration. But should you be using TypeAccessor as opposed to ObjectAccessor this is an extension method I've had much success with:
public static class TypeAccessorExtensions
{
public static void AssignValue<T>(this TypeAccessor accessor, T t, MemberSet members, string fieldName, object fieldValue)
{
var index = fieldName.IndexOf('.');
if (index == -1)
{
if (members.Any(m => string.Equals(m.Name, fieldName, StringComparison.OrdinalIgnoreCase)))
accessor[t, fieldName] = fieldValue;
}
else
{
string fieldNameNested = fieldName.Substring(0, index);
var member = members.FirstOrDefault(m => string.Equals(m.Name, fieldNameNested, StringComparison.OrdinalIgnoreCase));
if (member != null)
{
var nestedAccesor = TypeAccessor.Create(member.Type);
var tNested = accessor[t, fieldNameNested];
if (tNested == null)
{
tNested = Activator.CreateInstance(member.Type);
accessor[t, fieldNameNested] = tNested;
}
nestedAccesor.AssignValue(tNested, nestedAccesor.GetMembers(), fieldName.Substring(index + 1), fieldValue);
}
}
}
}

Related

Modify multiple fields of an object in an Enumerable at the same time in C#

If I have an Enumerable of objects and want to modify multiple fields of a single one that I already know the index, I currently do:
var myObject = myEnumerable[index];
myObject.one = 1;
myObject.two = 2;
Is there a way to compact that? To make it simpler?
As an example, in VB you can do:
With myEnumerable[index]
.one = 1
.two = 2
End With
PS: using doesn't work here as the object would need to implement IDisposable, we don't' always control the object. I'm looking for a generic way to do this.
Handmade way of obtaining what you wanted. You can use it on every object.
class Program
{
static void Main(string[] args)
{
var item = new Item
{
One = 0,
Two = 0
};
item.SetProperties(new string[] { "One", "Two" }, new object[] { 1, 2 });
}
}
public class Item
{
public int One { get; set; }
public int Two { get; set; }
}
public static class Extensions
{
public static void SetProperties<T>(this T obj, IEnumerable<string> propertiesNames, IEnumerable<object> propertiesValues)
{
if (propertiesNames.Count() != propertiesValues.Count())
{
throw new ArgumentNullException();
}
var properties = obj.GetType().GetProperties();
for (int i = 0; i < propertiesNames.Count(); i++)
{
var property = properties.FirstOrDefault(x => x.Name == propertiesNames.ElementAt(i));
if (property is null)
{
throw new ArgumentException();
}
property.SetValue(obj, propertiesValues.ElementAt(i));
}
}
public static void SetFields<T>(this T obj, IEnumerable<string> fieldsNames, IEnumerable<object> fieldsValues)
{
if (fieldsNames.Count() != fieldsValues.Count())
{
throw new ArgumentNullException();
}
var fields = obj.GetType().GetFields();
for (int i = 0; i < fieldsNames.Count(); i++)
{
var field = fields.FirstOrDefault(x => x.Name == fieldsNames.ElementAt(i));
if (field is null)
{
throw new ArgumentException();
}
field.SetValue(obj, fieldsValues.ElementAt(i));
}
}
}

invoke method with list of parameter c#

I have a class:
namespace TestLib1
public class TestLib1
{
public IEnumerable<TestedParameter> Measure1(IEnumerable<TestedParameter> _inParameters)
{ // this is just to do something
foreach (TestedParameter item in _inParameters)
{
item.Param_Description = "TestParam";
}
return _inParameters;
}
}
that contains class from
public class Acceptance_Criteria
{
private double _lowest;
private double _highest;
public bool Compare(double _verifiedValue)
{
if ((_verifiedValue >= _lowest) && (_verifiedValue <= _highest))
{
return true;
}
else
{
return false;
}
}
public double Acceptance_Lowest { get =>_lowest;set =>_lowest = value; }
public double Acceptance_Highest {get=>_highest;set =>_highest = value; }
}
public class TestedParameter : Acceptance_Criteria
{
private string _param_Description;
private double _value;
private string _units;
private bool _value_matches_acceptance_critera;
private bool _value_checked_against_criteria;
private string pass_fail_reason;
public string Param_Description { get => _param_Description; set => _param_Description = value; }
public double Value { get => _value; set => _value = value; }
public string Units { get => _units; set => _units = value; }
public bool Value_matches_acceptance_critera { get => _value_matches_acceptance_critera; set => _value_matches_acceptance_critera = value; }
public bool Value_checked_against_criteria { get => _value_checked_against_criteria; set => _value_checked_against_criteria = value; }
public string Pass_fail_reason { get => pass_fail_reason; set => pass_fail_reason = value; }
}
and I placed TestLib in DLL and call it from other file :
Assembly SampleAssembly = Assembly.LoadFrom("TestLib1.dll");
Type[] types2 = SampleAssembly.GetTypes();//e
Type SearchedType = null;
foreach (Type type in types2)
{
if (type.FullName.Contains("TestLib1.TestLib1")) { SearchedType = SampleAssembly.GetType(type.FullName); }
}
if (SearchedType != null)
{
MethodInfo Method = SearchedType.GetMethod("Measure1");
if (Method != null)
{
object result = null;
ParameterInfo[] parameters = Method.GetParameters();
object classInstance = Activator.CreateInstance(SearchedType, null);
if (parameters.Length == 0)
{
//This works fine
result = Method.Invoke(classInstance, null);
}
else
{
List<TestedParameter> parametersIn = new List<TestedParameter>();
TestedParameter param1 = new TestedParameter();
param1.Param_Description = "testparam1"; // GET NAME FROM XML
parametersIn.Add(param1);
IEnumerable<TestedParameter> enumarableList = parametersIn.AsEnumerable();
object[] parametersArrayx = new object[] { enumarableList };
object resx = Method.Invoke(classInstance, parametersArrayx.ToArray());
List<TestedParameter> res = (List<TestedParameter>)resx;
}
}
}
When I execute Method.Invoke
I get thie exception:
System.ArgumentException:
'Object of type 'System.Collections.Generic.List`1[WindowsFormsApp1.TestedParameter]'
cannot be converted to type 'System.Collections.Generic.List`1[WindowsFormsApp1.TestedParameter]'.'
I also tried to push parametersArrayx without ToArray() but it also didnt work
Can anyone look a the code a let me know what do I do wrong ?
I want to execute code from DLL lib with list of parameters pushed (later I want to import those parameters from XML file.)
Thank You :)
SearchedType.GetMethod("Measure1");
convert to
SearchedType.GetMethod("Measure1", new Type[] { typeof(List<Filter>) });

Get all properties and subproperties from a class

I am using reflection to get a class name, and need to get all sub properties of the class, and all the sub properties' properties.
I am running into a recursion issue where the items get added to the incorrect list.
My code is as follows:
private List<Member> GetMembers(object instance)
{
var memberList = new List<Member>();
var childMembers = new List<Member>();
foreach (var propertyInfo in instance.GetType().GetProperties())
{
var member = new Member
{
Name = propertyInfo.PropertyType.IsList() ? propertyInfo.Name + "[]" : propertyInfo.Name,
Type = SetPropertyType(propertyInfo.PropertyType),
};
if (propertyInfo.PropertyType.IsEnum)
{
member.Members = GetEnumValues(propertyInfo).ToArray();
}
if (propertyInfo.PropertyType.BaseType == typeof(ModelBase))
{
var childInstance = propertyInfo.GetValue(instance) ?? Activator.CreateInstance(propertyInfo.PropertyType);
childMembers.AddRange(GetMembers(childInstance));
member.Members = childMembers.ToArray();
}
if (propertyInfo.PropertyType.IsGenericType && (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List<>) ||
propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
{
var itemType = propertyInfo.PropertyType.GetGenericArguments()[0];
var childInstance = Activator.CreateInstance(itemType);
childMembers.AddRange(GetMembers(childInstance));
member.Members = childMembers.Distinct().ToArray();
}
memberList.Add(member);
}
return memberList;
}
I can't know for certain since I don't have the knowledge of your code to debug and test it; however, I believe your problem may be stemming from the fact that you're re-using the childMembers list. Let me know if this is not the case.
private List<Member> GetMembers(object instance)
{
var memberList = new List<Member>();
foreach (var propertyInfo in instance.GetType().GetProperties())
{
var childMembers = new List<Member>(); // Moved to here, so it's not shared among all propertyInfo iterations.
var member = new Member
{
Name = propertyInfo.PropertyType.IsList() ? propertyInfo.Name + "[]" : propertyInfo.Name,
Type = SetPropertyType(propertyInfo.PropertyType),
};
if (propertyInfo.PropertyType.IsEnum)
{
member.Members = GetEnumValues(propertyInfo).ToArray();
}
if (propertyInfo.PropertyType.BaseType == typeof(ModelBase))
{
var childInstance = propertyInfo.GetValue(instance) ?? Activator.CreateInstance(propertyInfo.PropertyType);
childMembers.AddRange(GetMembers(childInstance));
member.Members = childMembers.ToArray();
}
if (propertyInfo.PropertyType.IsGenericType && (propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(List<>) ||
propertyInfo.PropertyType.GetGenericTypeDefinition() == typeof(IList<>)))
{
var itemType = propertyInfo.PropertyType.GetGenericArguments()[0];
var childInstance = Activator.CreateInstance(itemType);
childMembers.AddRange(GetMembers(childInstance));
member.Members = childMembers.Distinct().ToArray();
}
memberList.Add(member);
}
return memberList;
}
Wouldn't the following do?
public static IEnumerable<PropertyInfo> GetProperties(this Type type, int depth = 1)
{
IEnumerable<PropertyInfo> getProperties(Type currentType, int currentDepth)
{
if (currentDepth >= depth)
yield break;
foreach (var property in currentType.GetProperties())
{
yield return property;
foreach (var subProperty in getProperties(property.PropertyType,
currentDepth + 1))
{
yield return subProperty;
}
}
}
if (depth < 1)
throw new ArgumentOutOfRangeException(nameof(depth));
return getProperties(type, 0);
}
Given the following type:
class Foo
{
public string S { get; }
public int I { get; }
}
The output of
Console.WriteLine(string.Join(Environment.NewLine,
typeof(Foo).GetProperties(2)
.Select(p => $"{p.DeclaringType.Name}: {p.Name}")));
would be:
Foo: S
String: Chars
String: Length
Foo: I

C# - Class with List<> of other Classes

I have a class, that has several elements of normal types, like int, String, etc.
It also has several elements that are various lists of other classes, that could be empty or have 1 to many items.
I have a function that I call with a generic type of the parent class, and I want to analyze data that could be in the sub elements, without knowing the types.
I am getting the parent members with the following code:
var getProperty = System.Runtime.CompilerServices.
CallSite<Func<System.Runtime.CompilerServices.CallSite,
object, object>>
.Create(Microsoft.CSharp.RuntimeBinder.
Binder.GetMember(0, property.Name, thisObject.GetType(), new[]
{
Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null)
}));
var thisValue = getProperty.Target(getProperty, thisObject);
I get the value into the var thisValue. At this point if I determine the underlying type of thisValue is a type of list, how can I grab the type of the list contents?
Here is the actual function....I can't seem to get it formatted nicely.
public static bool ObjectIsLike<T>(this T thisObject, T compareObject, params object[] argumentsToExclude)
{
for (int counter = 0; counter < argumentsToExclude.Length - 1; counter++)
{
argumentsToExclude[counter] = argumentsToExclude[counter].ToString().ToUpper();
}
bool objectIsLike = true;
foreach (var property in thisObject.GetType().GetProperties())
{
string fieldName = property.Name;
if (!argumentsToExclude.Contains(fieldName.ToUpper()))
{
try
{
var getProperty = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, property.Name, thisObject.GetType(), new[] { Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null) }));
var thisValue = getProperty.Target(getProperty, thisObject);
getProperty = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, property.Name, compareObject.GetType(), new[] { Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0, null) }));
var compareValue = getProperty.Target(getProperty, compareObject);
if (!(compareValue == null && thisValue == null))
{
if (compareValue == null || thisValue == null)
objectIsLike = false;
else
if (compareValue.GetType().FullName.Contains("List"))
{
//Ignore Lists
}
else
if (!compareValue.Equals(thisValue))
{
objectIsLike = false;
}
}
}
catch
{
objectIsLike = false;
}
}
}
return objectIsLike;
}
would GetType() work for you?
class Program
{
static void Main(string[] args)
{
MyClass1 c1 = new MyClass1();
foreach (var s in c1.pp)
{
Console.WriteLine(s.GetType());
}
Console.Read();
}
}
public class MyClass1
{
public MyClass2 p;
public List<object> pp;
public MyClass1()
{
p = new MyClass2();
pp = new List<object>();
pp.Add(new MyClass2());
pp.Add(new MyClass3());
pp.Add(new MyClass4());
}
}
public class MyClass2
{
public List<object> ppp;
public MyClass2()
{
ppp = new List<object>();
ppp.Add(new MyClass3());
ppp.Add(new MyClass4());
}
}
public class MyClass3
{
public int v;
}
public class MyClass4
{
public int v;
}

Get Attribute info with generics

actually, I can make a relation between a table field and a variable by doing this inside my OE:
public class MyOE
{
[Column("AGE_FIELD")]
public int ageField { get; set; }
}
My OE class just need to use this other class:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = true)]
public class ColumnAtt : Attribute
{
private string name;
public string Name
{
get { return name; }
}
public ColumnAtt (string name)
{
this.name = name;
}
}
Well, using the code above, Im doing a generic method that I will need to get the "Column" value. How I could do that?
Here is my method:
public void CompareTwoObjectsAndSaveChanges<TObjectType>(TObjectType objectA, TObjectType objectB )
{
if(objectA.GetType() == objectB.GetType())
{
foreach (var prop in objectA.GetType().GetProperties())
{
if(prop.GetValue(objectA, null) != prop.GetValue(objectB, null))
{
string colvalue = "";//Here I need to get the Column value of the attribute.
string nameOfPropertyThatChanges = prop.Name;
string objectAValue = prop.GetValue(objectA, null).ToString();
string objectBValue = prop.GetValue(objectB, null).ToString();
}
}
}
}
Use reflection:
private static void Main(string[] args)
{
MyOE zz = new MyOE { ageField = 45 };
foreach (PropertyInfo property in zz.GetType().GetProperties())
{
// Gets the first attribute of type ColumnAttribute for the property
// As you defined AllowMultiple as true, you should loop through all attributes instead.
var attribute = property.GetCustomAttributes(false).OfType<ColumnAttribute>().FirstOrDefault();
if (attribute != null)
{
Console.WriteLine(attribute.Name); // Prints AGE_FIELD
}
}
Console.ReadKey();
}
You need to use reflection to get the attributes applied to your object. If you know the attribute is always going to be ColumnAtt, then you can do something like this to get the value:
public void CompareTwoObjectsAndSaveChanges<TObjectType>(TObjectType objectA, TObjectType objectB )
{
if(objectA.GetType() == objectB.GetType())
{
foreach (var prop in objectA.GetType().GetProperties())
{
if(prop.GetValue(objectA, null) != prop.GetValue(objectB, null))
{
// Get the column attribute
ColumnAttr attr = (ColumnAttr)objectA.GetType().GetCustomAttributes(typeof(ColumnAttr), false).First();
string colValue = attr.Name;
string nameOfPropertyThatChanges = prop.Name;
string objectAValue = prop.GetValue(objectA, null).ToString();
string objectBValue = prop.GetValue(objectB, null).ToString();
}
}
}
}
This makes use of the GetCustomAttributes(...) method.
Try this:
var columnAtt = prop.GetCustomAttributes(typeof(CustomAtt),true).Cast<ColumnAtt>().FirstOrDefault();
(fixed)

Categories

Resources