How select derived props in Aggregate function with 2 class - c#

Plz help with System.Linq.Aggregate
I have the following class
public class MainClass
{
public ClassA someProp { get; set; }
public ClassA someProp2 { get; set; }
}
public class ClassA
{
public virtual Type Types => Type.None;
}
public class ClassB:ClassA
{
public override Type Types => Type.Default;
public string FieldName { get; set; }
}
public class ClassC:ClassA
{
public override Type Types => Type.Value;
public int FieldValue { get; set; }
}
And i want to get FieldName if it is filled in ClassB or the value from ClassC if it is also filled for someProp2 and someProp
I understand how to get 1 value
//for get name
IEnumerable<string> values = entities
.Where(x=>x.someProp!=null || x.someProp2!=null)
.SelectMany(mainClass => new[] {mainClass.someProp,mainClass.someProp2 })
.OfType<ClassB>()
.Select(classB => classB.FieldName)
//for get value
IEnumerable<int> values = entities
.Where(x=>x.someProp!=null || x.someProp2!=null)
.SelectMany(mainClass => new[] {mainClass.someProp,mainClass.someProp2 })
.OfType<ClassC>()
.Select(classC => classC.FieldValue)
but I don’t understand how to get 2 values in 1 requests, because there will be type 2 classes ClassB and ClassC
Probably the best way would be through the Aggregate method!
Tell me how to make the conditions for the selection and the Aggregate method itself

If you want to use .Aggregate() to achieve your goal, I would suggest that you use an overload that lets you define a seed in addition to the accumulator function after flattening the property pairs.
It could be implemented as follows:
IEnumerable<string> values = entities
.SelectMany(ent => new[] { ent.someProp, ent.someProp2 })
.Aggregate(new List<string>(),
( fields, property ) =>
{
if (property is ClassB)
{
fields.Add(((ClassB)property).FieldName);
}
else if (property is ClassC)
{
fields.Add(((ClassC)property).FieldValue.ToString());
}
return fields;
});
Here, our seed is
new List<string>()
and our accumulator function is
( fields, property ) =>
{
// accumulation logic
return fields;
}
, containing the accumulator value parameter fields and element parameter property.
Our seed is the initial value of our accumulator value. For the first property in the flattened property collection (provided by the .SelectMany()), our accumulator value (fields) is therefore an empty list of strings.
For each element (property), field values are extraced based on the property's class type:
if property is a ClassB, FieldName is extracted and added to our accumulator value.
if property is a ClassC, FieldValue is extracted, and its string value is added to our accumulator value.
(if property is neither of those classes, nothing is added to fields).
After conditionally adding a field value to fields, fields is returned from the accumulator function to be used as the accumulator value in the next iteration of the accumulator function (i.e. for the next property in the flattened collection).
For entities given as follows:
List<MainClass> entities = new()
{
new()
{
someProp = new ClassC() { FieldValue = 4 },
someProp2 = new ClassB() { FieldName = "Flower" }
},
new()
{
someProp = new ClassA() { },
someProp2 = new ClassC() { FieldValue = 7 }
}
};
, the resulting values will contain:
4
Flower
7
Example fiddle here.

Related

A Generic method to remove items from a collection when the type is sure to have a particular property

Consider this code.
distinctBrandIds is a collection of type long
List<long> distinctBrandIds = new List<long>{ 1, 5, 4};
BrandYesNo is a Dictionary with BrandId as key
Dictionary<long, bool> BrandYesNo = new Dictionary<long, bool>();
BrandYesNo.Add(1, false);
BrandYesNo.Add(2, false);
BrandYesNo.Add(3, false);
SomeClass has BrandId property
public class SomeClass{
public int ItemId {get; set;}
public long BrandId {get; set;}
// other properties
}
Items from CollectionOfSomeClass are removed when BrandId equals BrandId key in BrandYesNo dictionary with the value of false
distinctBrandIds.ForEach((v) =>
{
if (!(BrandYesNo[v]))
{
CollectionOfSomeClass.RemoveAll(sc => sc.BrandId == v);
}
});
In other places, the same code repeats with other collections of types in place of CollectionOfSomeClass.
The common thing is that the type of which the other collection is made of also has BrandId. So the check is always on BrandId property.
To create a generic method there are suggestions to use reflection and on those lines I have this:
public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass, List<long> distinctBrandIds, object propertyToCheck) where T : class
{
distinctBrandIds.ForEach((v) =>
{
if (!(BrandYesNo[v]))
{
CollectionOfSomeClass.RemoveAll((rd) => {
PropertyInfo pi = typeof(T).GetProperty("BrandId");
pi.GetValue(rd) == v;
});
}
});
}
The predicate is not correct.
How do I proceed with this?
Thanks in advance.
Accepted Answer
Initially that was not the case but I convinced the powers that be for CodeCaster's solution.
No need for reflection here, if you know and control the types that are to be processed by this method. Given you say
The common thing is that the type of which the other collection is made of also has BrandId. So the check is always on BrandId property.
Create an interface:
public interface IBrand
{
long BrandId { get; }
}
Apply that to the appropriate classes:
public class SomeClass : IBrand
{ ... }
And modify your constraint:
public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass, List<long> distinctBrandIds)
where T : IBrand
{
distinctBrandIds.ForEach((v) =>
{
if (!(BrandYesNo[v]))
{
CollectionOfSomeClass.RemoveAll(rd => rd.BrandId == v);
}
}
}
To illustrate my comment, the following code should work but will be slow:
public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass,
List<long> distinctBrandIds)
where T : class
{
PropertyInfo pi = typeof(T).GetProperty("BrandId");
distinctBrandIds.ForEach(v =>
{
if (!(BrandYesNo[v]))
{
CollectionOfSomeClass.RemoveAll(rd => ((long)pi.GetValue(rd) == v));
}
});
}
Or use Equals.
Note that this code should only be used if you don't have access to the classes code or cannot modify it. If not the case, #CodeMaster's solution is much safer + faster.
Similarly to #vc 74s answer, you could use reflection but cache it using a dictionary:
// Stores functions that take objects with "BrandId" properties
// and returns the value of the "BrandId" property casted to long
private static readonly Dictionary<Type, Func<object, long>> _getBrandId = new();
public void RemoveItemsFromList<T>(List<T> CollectionOfSomeClass,
List<long> distinctBrandIds)
where T : class
{
// Check if the function is already cached
if (!_getBrandId.TryGetValue(typeof(T), out var getBrandId))
{
// Create the lambda using an expression tree
var lambda = Expression.Lambda<Func<object, long>>(
Expression.Convert(
Expression.Property(
Expression.Parameter(typeof(object)),
"BrandId"),
typeof(long)),
target);
// Store it to the cache
getBrandId = _getBrandId[typeof(T)] = lambda.Compile();
}
distinctBrandIds.ForEach((v) =>
{
if (BrandYesNo[v]) continue;
CollectionOfSomeClass.RemoveAll(rd => getBrandId(rd) == v);
});
}

How do I identify List fields using reflection and add new objects to that list?

I have two objects that have mostly properties with the same type and name, and I have a method that assigns values from object A to the matching properties in object B. I'm writing a unit test which should call that method, and then assert that for the each property in object A that has a matching property in object B, the value was copied.
My idea to accomplish this is to use reflection to enumerate all the properties in object A and then assign a random value to each property. Then I call my "copy values" method to copy the values to object B, and then use reflection to enumerate the fields on each object and make sure that fields with matching names have the same value.
Here is my code to assign random values to object A
var objectA = new ObjectA();
var random = new Random();
var fields = typeof(ObjectA).GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var field in fields)
{
if (field.CanWrite)
{
if (field.PropertyType == typeof(bool) || field.PropertyType == typeof(bool?))
{
field.SetValue(objectA, Convert.ToBoolean(random.Next(2)));
}
if (field.PropertyType == typeof(decimal) || field.PropertyType == typeof(decimal?))
{
field.SetValue(objectA, Convert.ToDecimal(random.Next()));
}
if (field.PropertyType == typeof(string))
{
field.SetValue(objectA, random.Next().ToString());
}
// similar code for other data types
}
}
Then I call my method to copy the values:
ObjectB objectB = ObjectB.FromObjectA(objectA);
Then I call this method to compare the values of the two objects:
public static void AssertMatchingFieldAreEqual<T1, T2>(T1 a, T2 b)
{
foreach (var fieldA in typeof(T1).GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
var fieldInfoB = typeof(T2).GetProperty(fieldA.Name);
if (fieldInfoB != null)
{
var propertyA = typeof(T1).GetProperty(fieldA.Name);
var propertyB = typeof(T2).GetProperty(fieldA.Name);
// Adding field names to make error messages for failed Assert calls list the field name
var valueA = $"{propertyA.Name}: {propertyA.GetValue(a)}";
var valueB = $"{propertyB.Name}: {propertyB.GetValue(b)}";
Assert.AreEqual(valueA, valueB);
}
}
}
This works for basic data types. My problem is that I have some fields that are Lists, and I'd like to populate them with a random number of objects of their type, and then assert that when the fields are copied, each list has the same number of objects.
My two questions:
When I'm assigning values, how do I check if a property is a List without knowing the type of item in the list? I've tried if (field.PropertyType == typeof(List<object>), but that doesn't work.
How do I create a new object of type T add and it to my list when my property type is a list?
Or alternatively if there's a better way to check that my "copy values" method does copy all identically named fields, what's the better way?
The direct answer to your question is that you'd need to incorporate generic testing into your reflection.
if(field.PropertyType.GetGenericTypeDefinition() == typeof(List<>)){
var typeOfThingInside = field.PropertyType.GetGenericArguments()[0];
// ...
}
The better way is to use AutoFixture to create your objects, and use Fluent Assertions to check that everything got copied.
void Main()
{
// Arrange
var fixture = new Fixture();
var a = fixture.Create<ObjectA>();
// Act
var b = ObjectB.FromObjectA(a);
// Assert
b.Should().BeEquivalentTo(a, options => options.ExcludingMissingMembers());
}
public class ObjectA {
public int A {get;set;}
public string B {get;set;}
public List<string> C {get;set;}
public decimal Z {get;set;}
}
public class ObjectB
{
public int A { get; set; }
public string B { get; set; }
public List<string> C { get; set; }
public decimal Y { get; set; }
public static ObjectB FromObjectA(ObjectA a) => JsonConvert.DeserializeObject<ObjectB>(JsonConvert.SerializeObject(a));
}

Changing object property while storing it in list

lets say i have class with a lot of redundant properties and i want to store them in list, dictionary or whatever
public class Foo
{
public Bar Bar1 {get;set;}
public Bar Bar2 {get;set;}
public Bar Bar3 {get;set;}
public Buzz Buzz1 {get;set;}
public Buzz Buzz2 {get;set;}
public Buzz Buzz3 {get;set;}
public void UpdateObject(Buzz newValue)
{
var dict = new List<KeyValuePair<Bar, Func<Buzz >>>()
{
new KeyValuePair<Bar, Func<Buzz>>(this.Bar1 ,()=>this.Buzz1),
new KeyValuePair<Bar, Func<Buzz>>(this.Bar2 ,() => this.Buzz2 ),
new KeyValuePair<Bar, Func<Buzz>>(this.Bar3 ,() => this.Buzz3 )
};
foreach (var item in dict)
{
if (true)
{
var value = item.Value.Invoke();
value = newValue;
}
}
}
}
of course value is changed but Foo's Buzz1/2/3 property is not. How can i store some kind of reference to object's property in list, get this item and change object's value?
Instead of the key value pairs with a key and a setter, store a key, a getter, and a setter:
List<Tuple<Bar, Func<Buzz>, Action<Buzz>>
Action<Buzz> is a lambda that takes a new value for that Buzz as a parameter.
var dict = new List<Tuple<Bar, Func<Buzz>, Action<Buzz>>
{
new Tuple<Bar, Func<Buzz>, Action<Buzz>(this.Bar1 ,()=>this.Buzz1, x => this.Buzz1 = x),
// ...etc...
};
Not sure why you're doing this, but that'll work.
If it were me, instead of a Tuple or KeyValuePair, I'd write a ThingReference<T> class that takes the two lambdas, and store those in a Dictionary<Bar, ThingReference<Buzz>>.

Reflection for composite property names

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; }
}

Using LINQ to loop through inner class properties in outer class collection

Leveraging off the Q&As dealing with looping through an object's properties (https://stackoverflow.com/questions/15586123/loop-through-object-and-get-properties, Loop Through An Objects Properties In C#), where you have:
a collection of Class1 objects (i.e. listObj1)
each Class1 contains a collection of Class2 objects (i.e. dictObj2)
How would you:
efficiently determine the properties of the inner class (Class2)
loop through the the properties of the inner class (Class2)
loop through the collection of Class1 objects (listObj1) selecting all instances of the the Class2 property
output the collection of Class2 property (e.g. the first iteration would return a collection of MeasurementA, one from each Class1 object).
and group the collection by Class1.PropertyA and Class1.PropertyB
Please find below a rough map of the classes involved.
I have been trying to use a LINQ query without success. Any ideas or guidance would be greatly appreciated.
class MainClass {
List<Class1> listObj1
}
class Class1 {
// a number of fields including...
int PropertyA { get; set; }
int PropertyB { get; set; }
Dictionary<int, Class2> dictObj2 { get; set; }
}
class Class2 {
// a number of fields all of type double...
double MeasurementA { get; set; }
double MeasurementB { get; set; }
double MeasurementC { get; set; }
}
Given data:
MainClass mainClass = new MainClass();
mainClass.listObj1 = new List<Class1>()
{
new Class1() {
dictObj2 = new Dictionary<int,Class2>() {
{ 1, new Class2() { MeasurementA = 2.0, MeasurementB = 3.0, MeasurementC = 4.0 }},
{ 2, new Class2() { MeasurementA = 5.0, MeasurementB = 6.0, MeasurementC = 7.0 }}
}
}
};
you can write with LINQ:
var fields = typeof(Class2)
// Get Properties of the ClassB
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
// Map each PropertyInfo into collection of its values from c1.dictObj2
.SelectMany(pi => mainClass.listObj1
.SelectMany(c1 => c1.dictObj2)
.Select(p => new
{
Property = pi.Name,
Value = pi.GetValue(p.Value)
}))
// Group data with respect to the PropertyName
.GroupBy(x => x.Property, x => x.Value)
// And create proper dictionary
.ToDictionary(x => x.Key, x => x.ToList());
and now you have a Dictionary with keys of ClassB property names and values as List of those properties values.
Efficiently determine the properties of the inner class (Class 2)
Regardless of efficiency, there really is only one way you can do it (assuming you mean at runtime) and that's using Reflection.
Loop through the properties of the inner class (Class 2)
foreach (var prop in instance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
Console.WriteLine(prop.Name);
}
loop through the collection of Class1 objects (listObj1) selecting all instances of the the Class2 property
foreach (var obj in mainClass.listObj1)
{
var innerClasses = obj.dictObj2.Values;
// do something with inner classes
}
output the collection of Class2 property (e.g. the first iteration would return a collection of MeasurementA, one from each Class1 object).
foreach (var prop in typeof(Class2).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
foreach (var obj in mainClass.listObj1)
{
foreach (var innerClass in obj.dictObj2.Values)
{
Console.WriteLine(prop.GetValue(innerClass, null));
}
}
}
Not sure why you are so keen on using LINQ here, I think a couple of simple for loops are all you need here.

Categories