Access Property Value using Reflection - c#

I have an object
public class School
{
public Employee Emp{get;set;}
public string City{get;set;}
}
public class Employee
{
public string Name{get;set;}
}
Using reflection I need to fetch this Employee Name from the school object
I Tried
School schl=New School();schl.Employee=new Employee{Name="Shalem"};
var empName= schl.GetType().GetProperty("Emp.Name").GetValue(schl, null)
Also tried
PropertyDescriptorCollection allProp= TypeDescriptor.GetProperties(schl);
var empName=allProp["Emp.Name"].GetValue(schl);
I always get null. How can i get?
Note: The thing is i dont know what object it will contain. But all i know is i will get the exact name with inner object like "Emp.Name" etc. Need a generic solution

School schl=New School();
schl.Employee=new Employee{Name="Shalem"};
var emp = schl.GetType().GetProperty("Emp").GetValue(schl, null)
var empName = emp.GetType().GetProperty("Name").GetValue(emp, null);
Split it up, get the Employee object and then get the Name of the Employee.
Although I don't see the benefit for this in this instance when you can do string empName = schl.Employee.Name - unless the scope of the code is different?

Related

When would you create an Object from an inherited class?

I'm currently looking into inheritance and polymorphism and I'm a bit confused about where you'd want to create a Person object of type Student?
assuming the following code:
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
}
class Student : Person
{
public int YearOfStudy { get; set; }
public string Course { get; set; }
public string PredictedGrade { get; set; }
}
Now looking online, there are a few options here in terms of creating an object:
Person p = new Person();
Student s = new Student();
Person ps = new Student();
The first objects allows me to set name, age and gender, while the second allows me to set those 3, as well as yearsOfStudy, course and predictedGrade. But I'm unsure of what the third object allows me to do? I can still set all 6 parameters, however I can only use the attributes set in the Person class? Any explanation on the correct usage of the third object would be appreciated.
Thanks
Don't think of this as Person ps = new Student() yet.
The real benefit is being able to abstract common code for all types of Person. So your methods may take in a Person because that's all it needs and will work with any person type you create such as Janitor, Teacher, etc.
var myStudent = new Student()
VerifyAge(myStudent);
VerifyYearOfStudy(myStudent);
public bool VerifyAge(Person person)
{
return person.Age < 200;
}
public bool VerifyYearOfStudy(Student student)
{
return student.YearOfStudy <= DateTime.Now.Year;
}
To clear up some confusion the only time you ever really declare the base in a method is when you want to actually denote that this variable is only meant to be used as that specific type. Think of it as if you had declared your variable using an interface instead. Sure I am working with a Student instance, but I am only working with it as a Person instance or as IPerson.
Normally as a variable in a method you wouldn't do that because pretty much the defacto standard is to just use var for everything nowadays. Where you do make the choice to define Person is normally on properties, method return values, and method parameters. Local variable is not really important.
Because Student class is derived from Person class, any Student object is also a Person object. Thus a notation Person ps = new Student(); means we're declaring variable ps to be of type Person and instantiate it as Student. It could be used if you have a method that takes Person object as parameter, e.g.
public void Foo(Person p) { if(p.Age > 21) Console.WriteLine("OK to drink!"); }
However, if you have a method that operates on properties of derived class you must declare and instantiate the instance of it. So for
public void Foo(Student s) {if(s.YearOfStudy == 1) Console.WriteLine("Freshman"); }
you must use Student s = new Student();.

Passing generic class name in dbcontext

My entities context file code:
public partial class MyDbContext : DbContext
{
//dbset 1
public DbSet<Customer> Customers { get; set; }
//dbset 2
public DbSet<Order> Orders { get; set; }
}
Class Shipments has a method GetCustomerName
public class Shipments
{
public string GetName(object caller, System.Data.Entity.DbSet objectContext)
{
//This one passes.
IQueryable<Customer> myCustomer = from p in objectContext
select p where p.Id=1; //get customer name
//This one fails
IQueryable<caller.GetType()> myCustomer = from p in objectContext
select p where p.Id=1; //get customer name
}
}
Question: I would like to remove the hard coding of class Customer there, instead call this method by passing the class name as parameter?
How can I implement this? In this case caller.GetType().Name didn't work for me.
GetName(Order, mydbContext);
GetName(Customer, mydbContext);
Both should work with the same code (trying it to make generic, I am not sure how to cast it generic). Any help will be great. Thanks.
Like so many people, including myself early on, you are not seeing the difference between a data type and an instance of the Type class. A data type is something that is known at compile time. When you call GetType, what you get back is an object whose data type is Type. It contains information about a data type but it is not a data type itself.
What you need to do is make your GetName method generic as well:
public string GetName<T>(T caller, System.Data.Entity.DbSet objectContext)
{
IQueryable<T> myEntity = from p in objectContext
select p where p.Id=1; //get customer name
}
To be able to do that though, the compiler must know that type T actually has an Id property. That means that T has to be constrained to be a specific base class or interface that declares an Id property. In the case of auto-generated EF classes, an interface is your only option.
There are still other issues with your code, but that covers what you are actually asking about.
I don't see the point of passing an object argument just for the sake of getting its type. You can use generics for that:
public string GetName<T>(DbSet objectContext) where T : BaseEntity {
IQueryable<T> myCustomer = from p in objectContext
select p where p.Id = 1;
//Get the name
//Return the name
}
Please note that I added a constraint for T to extend from a BaseEntity.
All your entities should then extend from this abstract class (which should contain the property Id and Name for getting the name).
You would then be able to retrieve the name of, not only Customer entities, but even Order entities:
string customerName = GetName<Customer>(context);
string orderName = GetName<Order>(context);
If only your Customer entity has the property Name, then don't use generics at all, you would be better defining the type explicitly:
public string GetName(context) {
IQueryable<Customer> customer = from p in context
select p where p.Id = 1;
//Get the name
//Return the name
}

How to extract an object name

i've got a class filled with lists of subclasses:
public class ClassOfKb
{
public List<Data> KbDatas {get;set;}
public List<Product> KbProducts {get;set}
}
public class Data
{
public Guid ID {get;set;}
public byte[] data {get;set;}
public string Name {get;set;}
}
public class Product
{
public Guid ID {get;set;}
public string Name {get;set;}
public byte[] Image {get;set;}
}
i create an object:
ClassOfKb kb = new ClassOfKb
now i'd like to extract the string "Datas" from the sub-object kb.KbDatas, I tried:
string name = kb.KbDatas.GetType().BaseType.Name.Substring(2);
aswell as:
string name = kb.KbDatas.GetType().Name.Substring(2);
but nothing gave me what I need, is there any way to do this?
EDIT: to specify my question, the string I need is the name of the list, except the first two letters! KbDatas => Datas
EDIT2: i did a mistake, the list-names and class-names are different and i need the list-name
You can use Type.GetGenericArguments to solve this
ClassOfKb kb=new ClassOfKb();
kb.KbData = new List<Data>();
string nameOfData = Type.GetType(kb.KbData.ToString()).GetGenericArguments().Single().Name;
OUTPUT : nameOfData = Data
kb.KbProduct = new List<Product>();
string nameOfProduct = Type.GetType(kb.KbProduct.ToString()).GetGenericArguments().Single().Name;
OUTPUT : nameOfProduct = Product
Since that's a collection it is likely that there are multiple Data objects in it, each with a name. You can use String.Join to concat them with a separator:
string names = string.Join(",", kb.KbData.Select(d => d.Name));
If there's just one object you don't get a comma at the end. If there's no object you get an empty string.
erm, since you have a List of Data there will be a sequence of Names.
IEnumerable<string> names = kb.KbData.Select(d => d.Name);
maybe you want just the first one?
string firstName = kb.KbData.First(d => d.Name);
Try this one
string name = kb.KbData[0].Name.Substring(2);
From the sounds of what you've written, you're looking to get the name of the type in the List instance KbData?
If so, I think this may be what you're looking for: https://stackoverflow.com/a/1043778/775479
If you are trying to get the name of the property. There are several methods for doing so.
Get the name of the generic argument from the property itself - If you know the name of the property.
ClassOfKb kb = new ClassOfKb()
{ KbData = new List<Data>(), KbProduct = new List<Product>() };
Console.WriteLine(kb.KbData.GetType().GetGenericArguments()[0].Name);
Get the name of the property from reflection, if you know the data type of the property.
System.Reflection.PropertyInfo pi = kb.GetType()
.GetProperties()
.FirstOrDefault(p=>p.PropertyType == typeof(List<Data>));
Console.WriteLine(pi.Name.Substring(2)); // ignoring the kb prefix
You can achieve this with reflection. This is example without any checks - just show the mechanism:
PropertyInfo propertyInfo = typeof(ClassOfKb).GetProperty("KbData");
Type propertyType = propertyInfo.PropertyType;
Type genericArgument = propertyType.GenericTypeArguments[0];
string name = genericArgument.Name;
Because property KbData is generic List<Data> you need ask for generic arguments of property type: propertyType.GenericTypeArguments[0] and you should test if the type is really generic by genericArgument.IsGenericType and check generic arguments count
If you need the property name than you can use Expression.
The code below define function for extract name prom a property:
public string GetPropertyName<T>(Expression<Func<T>> property)
{
return ((MemberExpression)property.Body).Member.Name;
}
This converts property to property name string:
GetPropertyName(()=>k.KbDatas).Substring(2)

How can I load a property on a new object?

Basically put, I have something along the lines of:
public class Product
{
public int ID {Get;Set;}
public string foo {Get;Set;}
public string bar {Get;Set;}
public virtual SubProduct subproduct{get;set;}
public int SubProductID{get;set;}
}
public class SubProduct
{
public int ID {Get;Set;}
public string foo {Get;Set;}
}
I have a method that takes a string and does a search through foo -
var correctproduct = (db.product.SingleOrDefault(x => x.foo == my_string);
and, if it can find it, I use correctproduct, but, if it doesn't exist (checked via if correctproduct==null, I create a new object and perform .SaveChanges();.
However, I go straight on to the next part where I interact with subproduct. The part where I create the new object assigned a numerical value to subproduct, but, trying to expand, I just get Object reference not set to an instance of an object. The next time it is run, it works perfectly.
I have an idea where I can do a search for subproduct for id and assign the object to the subproduct property direct, but, I don't feel this is the best way. I also feel my use of SingleOrDefault is just a hack and was wondering if I can have some feedback on the best way of doing this?
You can use following code,
objectProduct.subProduct = new SubProduct{ ID = id,foo = value };

LINQ - Help me grasp it with an example I think LINQ should be able to solve!

I am trying to get into LINQ to objects as I can see the power of it. Lucky enough I have a question that I think LINQ should be able to solve.
Here is the question (the details are an example);
public class SchoolClass
{
public int ID;
public string Name;
public string Teacher;
public string RoomName;
public string Student_Name;
public int Student_Age;
}
As you can see by the example, there is a one to many relationship between the ClassName, Teacher and Room and the Students, i.e. there are potentially many students in the one class.
If we have a List is it possible using LINQ to create a List but have only one instance ID, Name, Teacher, RoomName and an ArrayList of Student_Name and Age?
Producing this:
public class Students
{
public string Student_Name;
public int Student_Age;
}
public class SchoolClass
{
public int ID;
public string Name;
public string Teacher;
public string RoomName;
public ArrayList Students;
}
Essentially, using LINQ to clean the List to a more logical structure?
To give some background to this example. The second structure is used by a DataGrid to produce a Master-Child relationship. We store SchoolClass and StudentInformation in classes as shown above. It would be good use of LINQ to be able to convert our initial List into a structure which can be used by the DataGrid.
I changed the ArrayList to List<Students>, and:
List<SourceData> source = new List<SourceData>();
//...your data here ;-p
var classes = (from row in source
group row by new {
row.ID, row.Name,
row.Teacher, row.RoomName }
into grp
select new SchoolClass
{
ID = grp.Key.ID,
Name = grp.Key.Name,
Teacher = grp.Key.Teacher,
RoomName = grp.Key.RoomName,
Students = new List<Students>(
from row in grp
select new Students
{
Student_Age = row.Student_Age,
Student_Name = row.Student_Name
})
}).ToList();
If I'm understanding this correctly, I would've thought the best way to implement the SchoolClass class would be to create a Student class (probably a LINQ-to-SQL entity, if you're using it) and to have a generic list of type student, something similar to this:
public class SchoolClass
{
public int ID;
public string Name;
public string Teacher;
public string RoomName;
public List<Student> Students;
}
The list of students could then be populated using a linq query, although I'm not sure exactly how without more information.
Hope this is some help.

Categories