How loop sub classes properties from the main class?
public class GroupA
{
public string FullName = "", BirthDay = "";
}
public class GroupB
{
public string Email = "";
}
public class GroupC
{
public string Phone;
}
public class MainGroup
{
public GroupA GroupA;
public GroupB GroupB;
public GroupC GroupC;
}
protected void Page_Load(object sender, EventArgs e)
{
GroupA NewGroupA = new GroupA();
NewGroupA.FullName="TEST MASTER";
NewGroupA.BirthDay="02/20/1984";
GroupB NewGroupB = new GroupB();
NewGroupB.Email="noreply#test.com";
GroupC NewGroupC=new GroupC();
NewGroupC.Phone="555 123 4567";
//Assign new class instances to the main class
MainGroup NewMainGroup= new MainGroup();
NewMainGroup.GroupA=NewGroupA;
NewMainGroup.GroupB=NewGroupB;
NewMainGroup.GroupC=NewGroupC;
//Loop through the main class
foreach (var Group in typeof(MainGroup).GetFields())
{
Response.Write("<BR>MainGroupName= " + Group.Name + " Value= " + Group.GetValue(NewMainGroup).ToString());
//PROBLEM IS HERE. Can't loop through the subclass We need to display the GroupA, GroupB, GroupC below.
foreach (var SubGroup in Group)
{
Response.Write("<BR>");
}
}
}
If I understand your question right, I think this code is your answer:
foreach (var group in NewMainGroup.GetType().GetFields())
{
var groupInstance = group.GetValue(NewMainGroup);
foreach (var subGroup in groupInstance.GetType().GetFields())
{
Response.Write("<br />" + subGroup.Name + " = " + subGroup.GetValue(groupInstance));
}
}
What you should to is store a reference to the group-variable (not just the type). So, if you take your code and do something like this:
foreach (var GroupType in typeof(MainGroup).GetFields())
{
object Group = GroupType.GetValue(NewMainGroup);
Response.Write("<BR>MainGroupName= " + GroupType.Name + " Value= " + Group.ToString());
foreach(var SubGroupType in Group.GetType().GetFields()) {
object SubGroup = SubGroupType.GetValue(Group);
Response.Write("<BR>SubGroupName= " + SubGroupType.Name + " Value= " + SubGroup.ToString());
}
}
Haven't tested it, but I think that should about work. At least get you started.
Oh, and by the way, I think I have a better method for you, try this one:
public Dictionary<string, object> GetObjectFields(object obj) {
var dict = new Dictionary<string, object>();
var t = obj.GetType();
foreach(var f in t.GetFields()) {
dict[f.Name] = f.GetValue(obj);
}
return dict;
}
Then you can simply do this:
var groupVars = GetObjectFields(NewMainGroup);
foreach(var gv in groupVars) {
Response.Write(<BR>MainGroupName= " + gv.Key + " Value= " + gv.Value.ToString());
var subGroupVars = GetObjectFields(gv.Value);
foreach(var sgv in subGroupVars) {
Response.Write(<BR>SubGroupName= " + sgv.Key + " Value= " + sgv.Value.ToString());
}
}
First thing first GroupA, GroupB and GroupC are not subclasses of MainGroup.
You are using composition and have members which are of GroupA, GroupB and GroupC types.
Not sure why you want to relflect the types when you already know the type you have.
You could have used MainGroup.GroupA.Name and so on. May be the code you posted is just an example?
The below method should reflect the basic structure and suit your purpose.
static String Reflect(Object data)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (PropertyInfo propertyInfo in data.GetType().GetProperties())
{
if (propertyInfo.PropertyType.IsClass && propertyInfo.PropertyType != typeof(String))
{
stringBuilder.AppendFormat("{0} : {1} {2}", propertyInfo.Name, Environment.NewLine
, Reflect(propertyInfo.GetValue(data, null)));
}
else
{
stringBuilder.AppendFormat("{0} = {1}, {2}", propertyInfo.Name, propertyInfo.GetValue(data, null), Environment.NewLine);
}
}
return stringBuilder.ToString();
}
usage:
MainGroup main = new MainGroup
{
A = new GroupA { Name = "GroupA", ID = 1 },
B = new GroupB { Date = DateTime.UtcNow },
C = new GroupC { HasData = false }
};
Reflect(main);
Related
Below I have the constructors of two classes. Student and Course
I am trying to compare the value of _completedCourses value from the Student class against the coursecode of the Course class.
_completedCourses is a dictionary of Courses and their grades. I am trying to find which course is not completed and print that list.
Am I going about this correct.
public Course(string coursName, string courseCode, char passingGrade, double noOfCredits, Semester semesterOfferd, int major)
{
this.CourseName = coursName;
this.CourseCode = courseCode;
this.PassingGrade = passingGrade;
this.NoOfCredits = noOfCredits;
this.SemesterOfferd = semesterOfferd;
this.prerequisiteCourses = new List<Course>();
this._enrolledStudents = new List<Student>();
this._major = major;
}
public Student(int studentID, string studentName, Status studentStatus, StudentMajor studentMajor)
{
this._studentID = studentID;
this._studentName = studentName;
this._studentStatus = studentStatus;
this._studentMajor = studentMajor;
this._course = new Course();
this._completedCourses = new Dictionary<string, char>();
countStudent++;
}
public void RemainingCourses()
{
var cKey = _completedCourses.ContainsKey(_course.CourseCode);
Console.WriteLine("Remaining Courses");
if (cKey.Equals(_course.CourseCode))
{
foreach (KeyValuePair<string, char> count in _completedCourses)
{
{
{
Console.WriteLine(count.Key + " " + count.Value);
// Console.WriteLine("Course " + count.Value);
count.ToString();
}
}
}
}
}
UPDATE!!!
The following line of code in my driver class achieves what I want
Console.WriteLine("Enter Student ID ");
input = Convert.ToInt32(Console.ReadLine());
Console.Clear();
if (input.Equals(id.StudentID)) {
id.DisplayCompletedCourse();
foreach (var sub in isdCourses) {
var cKey = id.CompletedCourses.ContainsKey(sub.CourseCode);
if (!cKey)
{
Console.WriteLine(sub.CourseCode);
}
}
}
The ContainsKey method returns a boolean value (true/false), so in your if condition:
if (cKey.Equals(_course.CourseCode))
cKey is a boolean but _course.CourseCode is a string, so this will never be true, thus this will never go within the if block.
Try to rewrite it like this:
public void RemainingCourses()
{
var cKey = _completedCourses.ContainsKey(_course.CourseCode);
Console.WriteLine("Remaining Courses");
if (cKey) // if cKey is "true", then the dictionary contains the CourseCode
{
foreach (KeyValuePair<string, char> count in _completedCourses)
{
Console.WriteLine(count.Key + " " + count.Value);
}
}
}
As a side note, please beware of the formatting, you are adding unnecessary curly braces. Also, this could be simplified with linq, but I kept it as close as possible to the original code.
Im trying to change data type of objects dynamically ,
here is my scenario
I have data in a data table , and i need to map it to objects in c#
Here are steps i have followed
Loop through data table and fetch each data row
get columns inside that loop
Pass column name , new object to assign values to properties , data table cell value to new method.
here is sample code
m1()
{
foreach (DataRow row in inputTable.Rows)
{
foreach (DataColumn col in inputTable.Columns)
{
m2(col.caption,row[col.caption].toString(),new product())
}
}
}
m1(string columnName,string cellValue,product mappingEntity){
foreach (var prop in entityToMap.GetType().GetProperties())
{
if (prop.Name.ToLower() == column.ToLower())
{
prop.SetValue(entityToMap, GetConverter(t)(cellValue));
break;
}
else if (prop.Name == "Site")
{
entityToMap.Site = MapSite(column, cellValue, new Domain.Entities.Site());
break;
}
else if (prop.Name == "Product_categories")
{
entityToMap.Product_categories.Add(MapProductToCategory(column, cellValue, new Domain.Entities.ProductToCategory()));
}
else if (prop.Name == "Category_searchgroups")
{
entityToMap.Category_searchgroups.Add(MapCategoryToSearchGroup(column, cellValue, new Domain.Entities.CategoryToSearchGroup()));
}
}
Now i need to dynamically change data types of assigning values.
if (prop.Name.ToLower() == column.ToLower())
{
Type t = prop.PropertyType;
prop.SetValue(entityToMap, GetConverter(t)(cellValue));
break;
}
so i have found type inference question here
Change data type dynamically in c#
static Func<string, T> GetConverter<T>(T value)
{
return (x) => Convert<T>(x);
}
static T Convert<T>(string val)
{
Type destiny =typeof(T);
// See if we can cast
try
{
return (T)(object)val;
}
catch { }
// See if we can parse
try
{
return (T)destiny.InvokeMember("Parse", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Public, null, null, new object[] { val });
}
catch { }
// See if we can convert
try
{
Type convertType = typeof(Convert);
return (T)convertType.InvokeMember("To" + destiny.Name, System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Public, null, null, new object[] { val });
}
catch { }
// Give up
return default(T);
}
the issue is i have reflection object , i cant pass reflection object because it's not valid at that context ,
Can anyone help me to resolve this ??
thanks
I'm not sure if this is what you need, but it sounds like.
public static T RowToObjectClass<T>(DataRow r) where T : new()
{
T obj = new T();
foreach (PropertyInfo pi in typeof(T).GetProperties().Where(p => p.CanWrite))
{
pi.SetValue(obj, Convert.ChangeType(r[pi.Name], pi.PropertyType));
}
return obj;
}
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("id", typeof(string)));
dt.Columns.Add(new DataColumn("date", typeof(string)));
dt.Rows.Add(new object[] { 1.ToString(), new DateTime(2018, 1, 1).ToString() });
dt.Rows.Add(new object[] { 2.ToString(), new DateTime(2018, 2, 2).ToString() });
foreach (DataRow r in dt.Rows)
{
Console.WriteLine("Row: " + r[0].ToString() + " (" + r[0].GetType() + ")" + ", " + r[1].ToString() + " (" + r[1].GetType() + ")");
MyClass c = RowToObjectClass<MyClass>(r);
Console.WriteLine("Class: " + c.Id.ToString() + " (" + c.Id.GetType() + ")" + ", " + c.Date.ToString() + " (" + c.Date.GetType() + ")");
}
}
public class MyClass
{
public int Id { get; set; }
public DateTime Date { get; set; }
}
I'm using Entity Framework with an MVC5 Application and currently I am trying to save some form data that touches multiple tables in my database. When I am adding data to the tables it seems to be working fine but once I hit the bridge tables I am getting a null ref exception that, to me, doesn't make sense.
I am new to programming so any help would be greatly appreciated.
public void RegisterNewUser(IDCRegisterViewModel model)
{
//
string fullAddress = model.AddressLine1 + "\n" + model.AddressLine2 + (string.IsNullOrEmpty(model.AddressLine2) ? "" : "\n" ) + model.City + ", " + model.State + " " + model.Zip + "\n" + model.Country;
using (var AuthContext = new InfoKeeperEntities1())
{
AuthContext.Locations.Add(new Location {
Line1 = model.AddressLine1,
Line2 = model.AddressLine2,
City = model.City,
State = model.State,
Zip = model.Zip,
Country = model.Country,
UserID = model.UserID,
FullAddr = fullAddress
});
AuthContext.ProfileDatas.Add(new ProfileData
{
UserID = model.UserID,
UACC = model.UACC,
isRecCenter = model.IsRecCenter,
isCustAdmin = model.IsCustAdmin
});
//Add to bridge tables for user/dept and group/dept
List<Department> deptList = new List<Department>();
foreach (var ID in model.GroupIDs)
{
deptList.Add(AuthContext.Departments.FirstOrDefault(x => x.ID == ID));
}
foreach (var department in deptList)
{
//NULL REF EXCEPTION HERE
AuthContext.AspNetUsers.FirstOrDefault(x => x.Id == model.UserID).Departments.Add(department);
foreach (var groupID in model.GroupIDs)
{
AuthContext.Groups.FirstOrDefault(x => x.ID == groupID).Departments.Add(department);
}
}
}
}
If you turn the LazyLoadingEnabled and ProxyCreationEnabled off you always face with an error because of using Department after FirstorDefault Query and EntityFramework doesn't include it for AppUsers, You have the same problem with adding the department to the Group. So you must include the Department first for both of them.
put using System.Data.Entity; in the very first of the codes.
change the code statement to this:
public void RegisterNewUser(IDCRegisterViewModel model)
{
string fullAddress = model.AddressLine1 + "\n" + model.AddressLine2 + (string.IsNullOrEmpty(model.AddressLine2) ? "" : "\n" ) + model.City + ", " + model.State + " " + model.Zip + "\n" + model.Country;
using (var AuthContext = new InfoKeeperEntities1())
{
AuthContext.Locations.Add(new Location {
Line1 = model.AddressLine1,
Line2 = model.AddressLine2,
City = model.City,
State = model.State,
Zip = model.Zip,
Country = model.Country,
UserID = model.UserID,
FullAddr = fullAddress
});
AuthContext.ProfileDatas.Add(new ProfileData
{
UserID = model.UserID,
UACC = model.UACC,
isRecCenter = model.IsRecCenter,
isCustAdmin = model.IsCustAdmin
});
//Add to bridge tables for user/dept and group/dept
List<Department> deptList = new List<Department>();
foreach (var ID in model.GroupIDs)
{
deptList.Add(AuthContext.Departments.FirstOrDefault(x => x.ID == ID));
}
var user = AuthContext.AspNetUsers.Include("Departments").FirstOrDefault(x => x.Id == model.UserID);
foreach (var department in deptList)
{
user.Departments.Add(department);
foreach (var groupID in model.GroupIDs)
{
var group = AuthContext.Groups.Include("Departments").FirstOrDefault(x => x.ID == groupID);
group.Departments.Add(department);
}
}
}
}
Tip: Don't forget to make a new instance of List<Depatment> in the constructor of AspNetUsers and Groups:
public class ApplicationUser
{
Public ApplicationUser()
{
this.Departments = new List<Department>();
}
}
public class Group
{
Public Group()
{
this.Departments = new List<Department>();
}
}
In the code below, I would like the return from GetChainDetails to go where i have "I WANT MY LIST HERE" in GetChains method. Not sure how to accomplish or what other way to do this.
public static IEnumerable GetChains(int actGroupid, int dispid)
{
EEDBEntities db = new EEDBEntities();
var query = from c in db.Chains
where c.Activity_Basis.activity_group_id == actGroupid && c.Activity_Basis.discipline_id == dispid
select new
{
ChainID = c.ChainID,
ChainDesc = #"<span data-toggle=""tooltip"" title =""" + I WANT MY LIST HERE + #""">" + c.ChainID + "</span>"
};
return query.ToList();
}
public string GetChainDetails(string chainID)
{
string sStep = null;
var chainDetailList = from c in db.Chains_Detail
where c.chainID == chainID
orderby c.Order
select new
{
Order = c.Order,
Step = c.Step
};
foreach (var oItem in chainDetailList.ToList())
{
sStep = sStep + "\n" + oItem.Order + ": " + oItem.Step;
}
return sStep;
}
Your method public string GetChainDetails(string chainID) is not static. Perhaps this is the reason you are getting error. Make it static and try and run the code.
public static string GetChainDetails(string chainID)
Also you can follow this approach :
class X
{
public int Property1;
public int Property2;
public string ChainID;
public string MyListToolTipText;
}
class Y
{
public string ChainID;
public string ChainDesc;
}
And your main code
class Program
{
static void Main()
{
var myResult = GetChains(1, 1);
foreach (var result in myResult)
{
result.ChainDesc = GetChainDetails(result.ChainID);
}
//you can use either foreach or linq
//var m = myResult.Select(result => result = new Y { ChainID = result.ChainID, ChainDesc = GetChainDetails(result.ChainDesc) });
}
public static IEnumerable<Y> GetChains(int actGroupid, int dispid)
{
var Chains = new List<X>();
var query = from c in Chains
where c.Property1 == actGroupid && c.Property2 == dispid
select new Y
{
ChainID = c.ChainID,
ChainDesc = #"<span data-toggle=""tooltip"" title =""" + c.MyListToolTipText + #""">" + c.ChainID + "</span>"
};
return query.ToList<Y>();
}
public static string GetChainDetails(string chainID)
{
string sStep = null;
var chainDetailList = from c in db.Chains_Detail
where c.chainID == chainID
orderby c.Order
select new
{
Order = c.Order,
Step = c.Step
};
foreach (var oItem in chainDetailList.ToList())
{
sStep = sStep + "\n" + oItem.Order + ": " + oItem.Step;
}
return sStep;
}
}
Hence after calling GetChains, I am modifying each member property.
I have fallen into a doubt and I don't know how to solve it, the case is:
I have created an "arrayed string" list like this:
List<string[]> definitions;
I have added to it values like this:
definitions.Add(new string[2] { "A", "Def.1" });
definitions.Add(new string[2] { "B", "Def.2" });
In order to show the values I do it like this:
foreach (string[] theDefinition in definitions)
{
Console.WriteLine(theDefinition[0] + "\tdef: " + theDefinition[1]);
}
So far this works fine, but how can I show the values without the foreach I mean something like this:
Console.WriteLine(definitions[0] ...)
What should I write in the 3 dots to show either the "A" or the "Def.1" from the list in index 0.
I guess overcoming this is by doing something like:
string[] temp = definitions[0]
Console.WriteLine(temp[0] + ", " + temp[1]);
How to achieve it just using the Console.WriteLine without using extra variables, is this possible? and how? Thank you in advance.
Console.WriteLine(definitions[0][0] + "\tdef: " + definitions[0][1]);
The other answers are correct of course but why not just use a Dictionary instead of List of a 2 dimensional string array.
var definitions = new Dictionary<string, string>
{
{ "A", "Def.1" },
{ "B", "Def.2" }
};
foreach (var keypair in definitions)
{
Console.WriteLine("{0} \tdef: {1} ", keypair.Key, keypair.Value);
}
A better way would be to declare a definition type
public class Definition
{
public string Name { get; set; }
public string Value { get; set; }
public override string ToString()
{
return Name + "\tdef: " + Value;
}
}
Now you can simplify your code like this
List<Definition> definitions = new List<Definition> {
new Definition { Name = "A", Value = "Def.1" },
new Definition { Name = "B", Value = "Def.2" },
};
foreach (Definition theDefinition in definitions)
{
Console.WriteLine(theDefinition);
}
Of cause you can use a fluent version of it as proposed by Nikhil Agrawal, which is now even simpler.
definitions.ForEach(def => Console.WriteLine(def));
prints
A def: Def.1
B def: Def.2
And accessing the fields is more descriptive than using array indexes
Definition def = definitions[0];
Console.WriteLine(def.Name + ", " + def.Value);
// compared to
// Console.WriteLine(temp[0] + ", " + temp[1]);
You can access it like this: definitions[definition_index][string_index].
Try:
Console.WriteLine(definitions[0][0] + "\tdef: " + definitions[0][1]);
for (var i = 0; i < definitions.Length; i++)
{
Console.WriteLine(definitions[i][0] + "\tdef: " + definitions[i][1]);
}
One Line Answer instead of 3 Lines. No use of For or foreach Loop or Extra Variable when LINQ is here
definitions.ForEach(x => Console.WriteLine(x[0] + "\tdef: " + x[1]));