Commonly identifying the patterns in C# - c#

I have two classes
public class ClassA
{
public int ID { get; set; }
public string Countries {get;set;}
public string City { get;set; }
}
and
public class ClassB
{
public int ID { get; set; }
public string Countries {get;set;}
public string Village{ get;set; }
}
These two classes are in another class
public class ComponentClass
{
public List<ClassA> classAObj { get; set; }
public List<ClassB> classBObj { get; set; }
}
The data for the ComponentClass is coming from third party where the data for ClassA and ClassB is similar in structure. "City" in ClassA will have data in comma separated values "Manchester,Sydney" etc similarly with Village as well with comma separated values.
Now I am building a customized object at business layer where I am iterating through each property of ComponentClass and extracting information.
Main()
{
ComponentClass[] c = //Data from 3rd party;
foreach(var data in c)
{
Parent p = new Parent();
if(data.classAObj.count > 0)
{
Star s = new Star();
s.Area = "infinite";
s.Color = "red";
List<string> sep = data.City.Split(',').Select(string.Parse).ToList();
foreach(var b in sep)
{
TinyStar t = new TinyStar();
t.smallD = b;
s.Values.Add(t);
}
p.Curves.Add(s);
}
if(data.classBObj.count > 0)
{
Star s2 = new Star();
s2.Area = "infinite";
s2.Color = "red";
List<string> sep = data.Village.Split(',').Select(string.Parse).ToList();
foreach(var b in sep)
{
TinyStar t = new TinyStar();
t.smallD = b;
s2.Values.Add(t);
}
p.Curves.Add(s);
}
}
}
In the code above , the two if statements are doing exactly the same operation apart from property names "City" and "Village". I want to simplify this by making use of any design pattern possibly strategy pattern which I just know theoretically or any other patterns.
Here is what I have tried :
public abstract class Base
{
public int ID { get; set; }
public string Countries {get;set;}
}
public class ClassA : Base
{
public string City { get;set; }
}
public class ClassB : Base
{
public string Village{ get;set; }
}
I want to make it as a common factory method which will do the looping and build the object for me for avoiding duplication of the code
public void CommonMethod(Base)
{
// How do I differentiate the properties for looping
}

If the goal is to reduce the code repetition, the two statements could be refactored into a single action as follows.
foreach(var data in c)
{
Parent p = new Parent();
Action<string> iAction = iString =>
{
Star s = new Star();
s.Area = "infinite";
s.Color = "red";
List<string> sep = iString.Split(',').Select(string.Parse).ToList();
foreach(var b in sep)
{
TinyStar t = new TinyStar();
t.smallD = b;
s.Values.Add(t);
}
p.Curves.Add(s);
}
if(data.classAObj.count > 0)
{
iAction(data.City);
}
if(data.classBObj.count > 0)
{
iAction(data.Village);
}
}

You have a same datatype properties for the both of the types in the json than you can create a single class to map it,
public class ClassA
{
public int ID { get; set; }
public string Countries {get;set;}
public string Areas{ get;set; }
}
public class ComponentClass
{
public List<ClassA> classAObj { get; set; }
public List<ClassA> classBObj { get; set; }
}
Main()
{
ComponentClass[] c = //Data from 3rd party;
foreach(var data in c)
{
Parent p = new Parent();
GetParent (p ,data.classAObj )
GetParent (p ,data.classBObj )
}
}
void GetParent (Parent p, ClassA classObj){
if(data.classAObj.count > 0)
{
Star s = new Star();
s.Area = "infinite";
s.Color = "red";
List<string> sep = data.Areas.Split(',').Select(string.Parse).ToList();
foreach(var b in sep)
{
TinyStar t = new TinyStar();
t.smallD = b;
s.Values.Add(t);
}
p.Curves.Add(s);
}
return p ;
}

I would suggest inheriting the ClassA and ClassB from a common base as you did and then casting them, after typechecking. If your only goal is minimizing code-repition this will get your there:
class Program
{
static void Main(string[] args)
{
ComponentClass[] c = new List<ComponentClass>().ToArray();//Data from 3rd party;
foreach (var data in c)
{
Parent p = new Parent();
if (data.classObjs.Count > 0)
{
Star s = new Star
{
Area = "infinite",
Color = "red"
};
foreach (var b in data.classObjs)
{
string bStr = b.GetType() == typeof(ClassA) ? ((ClassA)b).City : ((ClassB)b).Village;
bStr = bStr.Split(',').Select(string.Parse).ToList();
TinyStar t = new TinyStar
{
smallD = bStr
};
s.Values.Add(t);
}
p.Curves.Add(s);
}
}
}
public class ComponentClass
{
public List<ClassObj> classObjs { get; set; }
}
public class ClassObj
{
public int ID { get; set; }
public string Countries { get; set; }
}
public class ClassA : ClassObj
{
public string City { get; set; }
}
public class ClassB : ClassObj
{
public string Village { get; set; }
}
}
It might be necessary to refactor the tenary to if...else or switch depending on if you add more classes of type ClassObj.
Be warned though that GetType actually queries the assembly (at runtime) and should be used with caution from a performance point of view as it can slow down applications a lot when executed often.

I have used reflection, check if it works for you
public static void CommonMethod(dynamic collection)
{
Parent p = new Parent();
Star s = new Star();
s.Area = "infinite";
s.Color = "red";
foreach (var data in collection)
{
var properties = data.GetType().GetProperties();
foreach (var p in properties)
{
string propertytName = p.Name;
var propertyValue = p.GetValue(data, null);
if (propertytName == "City" || propertytName == "Village")
{
List<string> sep = propertyValue.Split(',').ToList();
foreach (var b in sep)
{
TinyStar t = new TinyStar();
t.smallD = b;
s.Values.Add(t);
}
p.Curves.Add(s);
}
}
}
}
static void Main(string[] args)
{
ComponentClass[] c Data from 3rd party;
foreach (var data in c)
{
CommonMethod(data.classAObj);
CommonMethod(data.classBObj);
}
}

Related

How to use reflection on nested classes in order to get values

I have to an upper class with nested classes
public class Preferences
{
public FunctionClass function { get; set; } = new FunctionClass();
public class FunctionClass
{
public string programfolder { get; set; } = "";
...
}
public LoggerClass logger { get; set; } = new LoggerClass();
public class LoggerClass
{
public string logFolder { get; set; } = "Log";
...
}
public OptionClass options { get; set; } = new OptionClass();
public class OptionClass
{
public bool showGraphics { get; set; } = true;
...
}
public MqttSpSetupClass MqttSpSetup { get; set; } = new MqttSpSetupClass();
public class MqttSpSetupClass
{
public string strAddress { get; set; } = "localhost";
...
}
}
so I want reflection to cycle on all member of each inner class
PropertyInfo[] props_Outer = typeof(IoAppPreferences).GetProperties();
int counter = 0;
foreach (PropertyInfo prop_Upper in props_Outer)
{
var sName_Outer = prop_Upper.Name;
var val_Outer = props_Outer.GetValue(counter ++);
PropertyInfo[] properties_Inner;
switch (sName_Outer.ToUpper())
{
case "DIMS": properties_Inner = typeof(IoAppPreferences.DimsClass).GetProperties(); break;
...
}
foreach (PropertyInfo prop_Inner in properties_Inner)
{
var sName = prop_Inner.Name;
//prefs.function
var sVal = prop_Inner.GetValue(val_Outer);<------ERROR
switch (prop_Inner.Name.ToUpper())
{
...
}
}
I get an error where I put the arrow. And the reason is that val_Outer is FunctionClass function while if I hardcode prefs.function it is ok. Of course, I can put a switch per each one, but my question is: is there a better way to solve it?
I have seen this solution but can't fit to my needs
You got error because val_Outer is wrong instance. You are trying to get value out of counter integer props_Outer.GetValue(counter ++)
If your goal is to get property values from nested classes you must have instance of Preferences object:
var appPreferences = new Preferences();
var propsOuter = appPreferences.GetType().GetProperties();
foreach (var po in propsOuter)
{
var valueOuter = po.GetValue(appPreferences);
Console.WriteLine($"{po.Name}");
if (valueOuter == null) continue;
var propsInner = valueOuter.GetType().GetProperties();
foreach (var pi in propsInner)
{
var valueInner = pi.GetValue(valueOuter);
Console.WriteLine($"{pi.Name}: {valueInner}");
}
}
But getting values through reflection is pretty much useless if you already have object instance.

How to find difference in two models which has property of own list?

I have three models as describe below:
public class ComponentData //Contains list of Component Model
{
public int id { get; set; }
public List<Component> Components { get; set; }
}
public class Component //Contains list of ComponentValue Model
{
public Int32 SiteId { get; set; }
public IList<ComponentValue> ComponentValues { get; set; }
}
public class ComponentValue //Contains list of it self i.e. ComponentValue Model
{
public String Id { get; set; }
public String Name { get; set; }
public String DisplayName { get; set; }
public IList<ComponentValue> ChildComponents { get; set; }
}
Now I have two objects old ComponentData and new ComponentData, I want to compare these two object and want to find whether new list has any new child added or any changes in existing child. How to do that?
Note : public IList<ComponentValue> ChildComponents { get; set; } can have multiple children in it recursively.
I tried:
using (var e1 = cdOld.Components.GetEnumerator())
using (var e2 = cdNew.Components.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext())
{
var item1 = e1.Current.ComponentValues;
var item2 = e2.Current.ComponentValues;
using (var i1 = item1.GetEnumerator())
using (var i2 = item2.GetEnumerator())
{
while (i1.MoveNext() && i2.MoveNext())
{
//Here not sure how many children both list has and how to make recursive call
var item11 = i1.Current;
var item12 = i2.Current;
if (item11.Id != item12.Id || item11.Name != item12.Name)
{
cvlistold.Add(item11);
cvlistnew.Add(item12);
}
}
}
//var firstNotSecond = item1.Except(item2).ToList();
//var secondNotFirst = item2.Except(item1).ToList();
//var v = item1.Select(a => a.Name).Intersect(item2.Select(b => b.Name)).ToList();
//bool c = !(!firstNotSecond.Any() && !secondNotFirst.Any());
}
}
Screenshot:
Use IEquatable interface (or override Equals and GetHashCode):
public class ComponentValue : IEquatable<ComponentValue>
{
public String Id { get; set; }
public String Name { get; set; }
public String DisplayName { get; set; }
public IList<ComponentValue> ChildComponents { get; set; }
public bool Equals(ComponentValue other)
{
return Id == other.Id && Name == other.Name;
}
}
Then, check the differences:
bool equals = e1.SequenceEqual(e2);
I had similar structure in one of my projects with the difference that they could've contain more element types then one so I've made some reflection code to look up and compare these :
public static bool Compare ( this T obj, T comparer )
{
bool isOkay = true;
foreach(var field in typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
{
if (!isOkay) break;
object value = field.GetValue(obj);
object comparerValue = field.GetValue(comparer);
Type type = field.FieldType;
if(Type.GetTypeCode(type) != TypeCode.Object)
{
if(type == typeof(IList))
{
for(int i = 0; i < ((IList)value).Count; i++)
{
isOkay = isOkay &&(bool)_GenericCompare.MakeGenericMethod(((IList)value)[i].GetType()).Invoke(((IList)value)[i], ((IList)comparerValue)[i]);
}
}
}
else
{
isOkay = isOkay && value.Equals(comparerValue);
}
}
return isOkay;
}
// _GenericVompare is :
typeof(MeTypeThatHasCompareMethod).GetMethod("Compare", BindingFlags.Static | BindingFlags.Public);
This is just a shortened method which I've done so you should modify this for your needs but basically it iterates through all items of IList typed field and Invokes the same method on that items.
FYI. This method can be slow as hell on huge lists so you should implement some kind of "cache" mechanism to store FieldInfo[] for some types and _GenericCompare MethodInfo to speed up the process.

Get Property Value of Nested Classes is always null

I have following two classes
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
I have an instance of Employee class as follows.
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
I have a method which gets the property value based on the property name as follows:
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
In the above method,when I try to get property value of nested class like
GetPropertyValue(employee, "employee.Child.ChildName")
or
GetPropertyValue(GetPropertyValue(employee, "Family"), "ChildName"
doesn't return any value because type.GetRuntimeProperty(part) is always null.
Is there any way to fix this problem?
You problem lies in this line:
foreach (String part in nameParts)
Because you are iterating over each part of nameParts, you are also iterating over "employee", which of course is not a valid property.
Try either this:
foreach (String part in nameParts.Skip(1))
Or calling the method like this:
GetPropertyValue(employee, "Child.ChildName")
(Notice no "employee.", because you already pass in an employee)
The problem in this case is that when you split the string employee.Child.ChildName, the "employee" is the first part. However, employee is not a property of the source i.e. Employee Class.
Try this:
public class Program
{
public static void Main()
{
Employee employee = new Employee();
employee.Name = "Ram";
employee.Id = 77;
employee.Child = new Family() { ChildName = "Lava" };
GetPropertyValue(employee, "employee.Child.ChildName");
}
public class Family
{
public string ChildName { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
}
public static object GetPropertyValue(object src, string propName)
{
string[] nameParts = propName.Split('.');
if (nameParts.Length == 1)
{
return src.GetType().GetRuntimeProperty(propName).GetValue(src, null);
}
nameParts = nameParts.Skip(1).ToArray();
foreach (String part in nameParts)
{
if (src == null) { return null; }
Type type = src.GetType();
PropertyInfo info = type.GetRuntimeProperty(part);
if (info == null)
{ return null; }
src = info.GetValue(src, null);
}
return src;
}
Here, i have skipped the first part of the string i.e. "employee". However, you can solve the problem by passing Child.ChildName
This question is around 2 years old, but I found a another working solution for you question, which is easy to understand. If you initialize the object in calling calss constructor you can use dot(.) notation to assign or read property. Example -
public class Family{
public string ChildName { get; set; }
}
public class Employee{
public int Id { get; set; }
public string Name { get; set; }
public Family Child { get; set; }
public Employee(){
Child = new Family();
}
}
Employee emp = new Employee();
emp.Family.ChildName = "Nested calss attribute value";

complex way for update generic data model with reflection and collections

I am standing on a complex issue for me. I need update some models, but I like to work with a generic class not to rewrite some code individually.
I need to update data that have lists on their properties, with possible exclusion or inclusion of items on these lists, but these lists can be of any other class / type. My questions are commented on the code.
These models are unrealistic and a bit absurds but have similarities with my real models, note that the logic is reversed on these relationships during updates.
Thanks for all.
public class RedNotebook
{
[Key]
public int Id { get; set; }
public string PageTitle { get; set; }
public virtual ICollection<Signature> Signatures { get; set; }
}
public class BlueNotebook
{
[Key]
public int Id { get; set; }
public DateTime Entrance { get; set; }
public DateTime Leave { get; set; }
public virtual ICollection<Guest> GuestList { get; set; }
}
public class Signature
{
[key]
public int Id { get; set; }
public string PeopleSignature { get; set; }
public int IdRedNotebook { get; set; }
public int IdBlueNotebook { get; set; }
[ForeignKey("IdRedNotebook")]
public virtual RedNotebook { get; set; }
[ForeignKey("IdBlueNotebook")]
public virtual BlueNotebook { get; set; }
}
public class Guest
{
[key]
public int Id { get; set; }
public string Name { get; set; }
public int SeatNumber { get; set; }
public int IdBlueNotebook { get; set; }
[ForeignKey("IdBlueNotebook")]
public virtual BlueNotebook { get; set; }
}
/**********************/
public void UpdateData(T newData, out string msg)
{
try
{
var propId = newData.GetType().GetProperty("Id");
if (propId == null)
{
msg = "Unable to identify the identity of the reported data.";
return;
}
int id = Convert.ToInt32(propId.GetValue(newData));
if (id <= 0)
{
msg = "Unable to identify the identity of the reported data.";
return;
}
//instance a determined DbContext and Model<T>
var contexto = new CtxCliente(DAO.Classes.Util.InstanciarConexao(strCripto, (DAO.Conectores) Conector));
var model = contexto.Set<T>();
var targetData = model.Find(id);
if (targetData == null)
{
model.Add(newData);
contexto.Entry(model).State = EntityState.Added;
msg = "An addition was made because there was no previous reference.";
}
if (Convert.ToInt32(targetData.GetType().GetProperty("Id").GetValue(targetData)) > 0)
{
contexto.Entry(targetData).CurrentValues.SetValues(newData);
contexto.Entry(targetData).State = EntityState.Modified;
msg = string.Empty;
}
//TODO - 1) GET THE VIRTUAL PROPERTIES OF WHICH TYPE targetData ICollection
//TODO - 2) COMPARE THE CONTENT OF VIRTUAL PROPERTIES OF targetData WITH THE CONTENTS OF VIRTUAL PROPERTIES UPDATE, BOTH ICollection
//TODO - 3) REMOVE EXCESS OF targetData AND / OR ADD THAT AS THE CASE MAY BE MISSING (A - CLEAR DIFFERENCE, B - ADD DIFFERENCE)
//through the properties to identify those that are of the collection type
foreach (var propertytargetData in targetData.GetType().GetProperties())
{
if (!propertytargetData.PropertyType.IsGenericType)
continue;
var propsNewData = newData.GetType().GetProperty(propertytargetData.Name);
#region
//if all list items were removed on update
if (propsNewData == null && propertytargetData != null)
{
// NOT TESTED, MAYBE NOT WORK CORRECTLY
propertytargetData.SetValue(targetData,null);
}
//If an item was included or removed
else if (propsNewData != null)
{
var valTargetData = propertytargetData.GetValue(targetData);
var valNewData = propsNewData.GetValue(newData);
var listItemsTargetData = (IEnumerable) valTargetData;
var listItemsNewData = (IEnumerable) valNewData;
int countItemsTargetData = listItemsTargetData.Cast<object>().Count();
int countItemsNewData = listItemsNewData.Cast<object>().Count();
if (countItemsTargetData > countItemsNewData) //remove discarded
{
foreach (var itemtargetData in listItemsTargetData)
{
var idItemtargetData = itemtargetData.GetType().GetProperty("Id").GetValue(itemtargetData);
var existing = (from object itemListNewData in listItemsNewData
select itemListNewData.GetType().GetProperty("Id").GetValue(itemListNewData))
.Any(iditemListNewData => (int) idItemtargetData == (int) iditemListNewData);
if (!existing) //remove
{
//how to remove from the list?????? (targetData)
}
else //update
{
foreach (var itemListNewData in listItemsNewData)
{
var props = itemListNewData.GetType().GetProperties();
foreach (var propertyInfo in props)
{
foreach (var item in listItemsTargetData)
{
var p = item.GetType().GetProperty(propertyInfo.Name);
if (p != null && !p.PropertyType.IsGenericType)
{
p.SetValue(item, propertyInfo.GetValue(itemListNewData));
}
}
}
}
}
}
}
else if (countItemsTargetData < countItemsNewData) //Items need to be included
{
foreach (var newItem in listItemsNewData)
{
var idnewItem = newItem.GetType().GetProperty("Id").GetValue(newItem);
if ((int) idnewItem == 0)
{
//how to insert in list???????? (targetData)
}
else // remove and/or update some before (reduntant!?)
{
foreach (var itemtargetData in listItemsTargetData)
{
var idItemtargetData = itemtargetData.GetType().GetProperty("Id").GetValue(itemtargetData);
var existing = (from object itemListNewData in listItemsNewData
select itemListNewData.GetType().GetProperty("Id").GetValue(itemListNewData))
.Any(iditemListNewData => (int)idItemtargetData == (int)iditemListNewData);
if (!existing) //remove
{
//how to remove from the list?????? (targetData)
}
else //update
{
foreach (var itemListNewData in listItemsNewData)
{
var props = itemListNewData.GetType().GetProperties();
foreach (var propertyInfo in props)
{
foreach (var item in listItemsTargetData)
{
var p = item.GetType().GetProperty(propertyInfo.Name);
if (p != null && !p.PropertyType.IsGenericType)
{
p.SetValue(item, propertyInfo.GetValue(itemListNewData));
}
}
}
}
}
}
}
}
}
}
}
contexto.SaveChanges(); //save data on model
}
catch(...){}
}
Haven't tested it . But it should work if both source and dest implement the same ICollection interface and T has an Id property of type System.Int32. It uses the new dynamic keyword that enables you to do duck typing ;
private class IdComparer : IEqualityComparer<object>
{
public bool Equals(object x, object y)
{
//return ((dynamic) x).Id = ((dynamic) y).Id; //previous with convertion error
return ((dynamic) x).Id == ((dynamic) y).Id;
}
public int GetHashCode(object obj)
{
return ((dynamic) obj).Id;
}
}
private static void Copy(IEnumerable source, IEnumerable dest)
{
var cmp = new IdComparer();
var toRemove = dest.Cast<object>().Except(source.Cast<object>(),cmp).ToList();
var toAdd= source.Cast<object>().Except(dest.Cast<object>(),cmp).ToList();
foreach(var item in toAdd)
{
// dynamic runtime tries to find method that matches signiture void Add(T value so we add dummy variable so that it knows to search for bool Add(T value)
var dummy= ((dynamic) dest).Add(item);
}
foreach (var item in toRemove)
{
var dummy= ((dynamic)dest).Remove(item);
}
}

Setting properties from one class to another via GetProperties

here is a simple example to clear my intentions.
class A {
public int Id
public string Name
public string Hash
public C c
}
class B {
public int id
public string name
public C c
}
class C {
public string name
}
var a = new A() { Id = 123, Name = "something", Hash = "somehash" };
var b = new B();
I want to set b's properties from a. I have tried something but no luck.
public void GenericClassMatcher(object firstModel, object secondModel)
{
if (firstModel != null || secondModel != null)
{
var firstModelType = firstModel.GetType();
var secondModelType = secondModel.GetType();
// to view model
foreach (PropertyInfo prop in firstModelType.GetProperties())
{
var firstModelPropName = prop.Name.ElementAt(0).ToString().ToLower(System.Globalization.CultureInfo.InvariantCulture) + prop.Name.Substring(1); // lowercase first letter
if (prop.PropertyType.FullName.EndsWith("Model"))
{
GenericClassMatcher(prop, secondModelType.GetProperty(firstModelPropName));
}
else
{
var firstModelPropValue = prop.GetValue(firstModel, null);
var secondModelProp = secondModelType.GetProperty(firstModelPropName);
if (prop.PropertyType.Name == "Guid")
{
firstModelPropValue = firstModelPropValue.ToString();
}
secondModelProp.SetValue(secondModel, firstModelPropValue, null);
}
}
}
}
What shall I do?
It sounds like you are trying to map one class to another. AutoMapper is the best tool I've come across to do this.
public class A
{
public int Id;
public string Name;
public string Hash;
public C c;
}
public class B
{
public int id;
public string name;
public C c;
}
public class C
{
public string name;
}
class Program
{
static void Main(string[] args)
{
var a = new A() { Id = 123, Name = "something", Hash = "somehash" };
var b = new B();
AutoMapper.Mapper.CreateMap<A, B>();
b = AutoMapper.Mapper.Map<A, B>(a);
Console.WriteLine(b.id);
Console.WriteLine(b.name);
Console.ReadLine();
}
}

Categories

Resources