I need to get the value from Nested Property - c#

I need to get the value from nested property.
In MainClass i have a Students Property which is Type of Student Class.
MainClass obj = new MainClass ();
obj.Name = "XII";
obj.Students = new List<Students>()
{
new Students() { ID = "a0", Name = "A" },
new Bridge() { ID = "a1", Name = "B" }
};
Type t = Type.GetType(obj.ToString());
PropertyInfo p = t.GetProperty("Students");
object gg = p.GetValue(obj, null);
var hh = gg.GetType();
string propertyType = string.Empty;
if (hh.IsGenericType)
{
string[] propertyTypes = hh.ToString().Split('[');
Type gggg = Type.GetType(propertyTypes[1].Remove(propertyTypes[1].Length - 1));
foreach (PropertyInfo pro in gggg.GetProperties())
{
if (pro.Name.Equals("Name"))
{
// Get the value here
}
}
}

First of all,
Type t = Type.GetType(obj.ToString());
is wrong. This only works if a class has not overloaded the ToString() method, and will fall at runtime if it has. Fortunately, every class has a GetType() metod (it's defined on object, so Type t = obj.GetType() is the correct code.
Second, the
string[] propertyTypes = hh.ToString().Split('[');
Type gggg = Type.GetType(propertyTypes[1].Remove(propertyTypes[1].Length - 1)
is an awful way to get the type the specified the generic type, as there is a method GetGenericArguments that does that for you, so this code could be changed with
Type[] genericArguments = hh.GetGenericArguments();
Type gggg = genericArguments[0];
And now to the real problem, accessing an list item. The best way to do that is to use the indexer ([]) of the List<> class. In c# when you define an indexer, it's automatically transfered to a property called Item, and we can use that property to extract our values (the Item name can be inferred from the type definition, but for the most part, that can be hardcoded)
PropertyInfo indexer = hh.GetProperty("Item"); // gets the indexer
//note the second parameter, that's the index
object student1 = indexer.GetValue(gg, new object[]{0});
object student2 = indexer.GetValue(gg, new object[]{1});
PropertyInfo name = gggg.GetProperty("Name");
object studentsName1 = name.GetValue(student1, null); // returns "A"
object studentsName2 = name.GetValue(student2, null); // returns "B"

Override Equals method for both Students and Bridge class then use Find or use LINQ

Related

How to create specific list type whose parameter is known at runtime and then loop through this list?

I know I can create an object whose type is known only at run time like this:
Type t = record.GetType();
var src = Activator.CreateInstance(t.BaseType);
How can I do something like List<Record>=new List<Record>() at run time?
Suppose I am getting Child Record list using Reflection like this
var ChildRecorList=src.GetType().GetProperty(propName).GetValue(src, null);
and how can then I loop through this using foreach or for loop because foreach only works for known type list. It does now work with var types. Is there way to cast Reflection value to cast at specific type whose value is known at runtime(mentioned in point 1)
You can try this to create generic type in runtime:
Type genericListType = typeof (List<>);
// if you have more than one generic argumens
// you can add your types here like typeof(MyClass),typeof(MyClass2)
Type[] genericArguments = { typeof (Record) };
// create your generic type with generic arguments
Type myGenericType = genericListType.MakeGenericType(genericArguments);
// and then you can create your instance
var recordList = Activator.CreateInstance(myGenericType);
// get your property value
recordList = src.GetType().GetProperty(propName).GetValue(src, null);
And I guess you sure your type is a List then when you creating your instance you can make a cast like this:
var recordList = (IList)Activator.CreateInstance(myGenericType);
Then you can loop through your list
foreach (var item in recordList)
{
...
}
I'm not sure If you're looking for something like this, But you can use List Inside another List or Dictionary, Also, You can store the base value with any type and get the type whenever needed, I recommend using Dictionary so that way you can name your lists:
Dictionary<String, List<Object>> Data = new Dictionary<String, List<Object>>();
Data["MyUser"] = new List<Object>();
Data["MyUser"].Add(MyDBObject);
var obj = Data["MyUser"].Find(x => x.MyKey == "MyKeyObject");
var t = obj.GetType();
var dta = (MyDBObject)obj;
foreach (var db in Data)
{
if (db.Key == "MyUser")
db.Value.Find(x => x.Name == "MyName");
}
Or If you're looking for Creating lists on runtime:
List<List<Object>> Data = new List<List<Object>>();
var mydb = Data.Count;
Data.Add(new List<Object>());
Data[mydb].Add(MyOBJ);
foreach (var db in Data)
{
if (db.Contains(MyOBJ)
return db;
}
Why don't you just use generics? You could do something like this:
public void DoSomething<T>(T type)
{
var list = new List<T>();
}
This will allow you to create a list from whichever type is passed in at runtime.

How to iterate properties of two objects dynamically

I have two object of same class, I want to update the p2 with fields which are are in Dirty list. So far I managed to write the following code but struggling to get the value of p1 properties. What object should I pass here as parameter to GetValue method.
Person p1 = new Person();
p1.FirstName = "Test";
Person p2 = new Person();
var allDirtyFields = p1.GetAllDirtyFields();
foreach (var dirtyField in allDirtyFields)
{
p2.GetType()
.GetProperty(dirtyField)
.SetValue(p1.GetType().GetProperty(dirtyField).GetValue());
}
_context.UpdateObject(p2);
_context.SaveChanges();
Thanks in advance.
You should try that:
foreach (var dirtyField in allDirtyFields)
{
var prop = p2.GetType().GetProperty(dirtyField);
prop.SetValue(p2, prop.GetValue(p1));
}
It is a better to store PropertyInfo instance in a variable, then trying to resolve it twice.
Did you know that you don't need to retrieve the property for each object?
Type metadata is common to any object of the whole type.
For example:
// Firstly, get dirty property informations!
IEnumerable<PropertyInfo> dirtyProperties = p2.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where
(
property => allDirtyFields.Any
(
field => property.Name == field
)
);
// Then, just iterate the whole property informations, but give the
// "obj" GetValue/SetValue first argument the references "p2" or "p1" as follows:
foreach(PropertyInfo dirtyProperty in dirtyProperties)
{
dirtyProperty.SetValue(p2, dirtyProperty.GetValue(p1));
}
Check that the first parameter of PropertyInfo.GetValue(...) and PropertyInfo.SetValue(...) is the object for which you want to get or set the value of the whole property.
In each iteration, you have to get a reference to the PropertyInfo. When you call it's SetValue method, you should pass in 2 parameters, the object for which you will set the property and the actual value you are setting. For the latter one, you should invoke the GetValue method on the same property, passing in the p1 object as parameter, i.e. the source for the value.
Try this:
foreach (var dirtyField in allDirtyFields)
{
var p = p2.GetType().GetProperty(dirtyField);
p.SetValue(p2, p.GetValue(p1));
}
I would recommend you to keep the dirtyField variables in a dictionary and retrieve the associated PropertyInfo object from this dictionary. It should be much faster.
Firstly, declare some static variable in your class:
static Dictionary<string, PropertyInfo>
personProps = new Dictionary<string, PropertyInfo>();
Then you may change your method to:
foreach (var dirtyField in allDirtyFields)
{
PropertyInfo p = null;
if (!personProps.ContainsKey(dirtyField))
{
p = p2.GetType().GetProperty(dirtyField);
personProps.Add(dirtyField, p);
}
else
{
p = personProps[dirtyField];
}
p.SetValue(p2, p.GetValue(p1));
}
You need to pass the instance from which you want to get the property value, like so:
p1.GetType().GetProperty(dirtyField).GetValue(p1, null)
The second parameter, can be used to retrieve a value at a certain index if the property type is indexed.
IIrc you send p1 being the instance that holds the value and null to indicate you're not searching for a specific index value.

Reflection to call generic method with lambda expression parameter

I'm looking for a way to call a generic method with a lambda expression that calls Contains in an array of items.
In this case I'm using Entity Framework Where method, but the scenario could be applied in other IEnumerables.
I need to call the last line of the above code through Reflection, so I can use any type and any property to pass to the Contains method.
var context = new TestEntities();
var items = new[] {100, 200, 400, 777}; //IN list (will be tested through Contains)
var type = typeof(MyType);
context.Set(type).Where(e => items.Contains(e.Id)); //**What is equivalent to this line using Reflection?**
In research, I've noticed that I should use GetMethod, MakeGenericType and Expression to achieve that, but I couldn't figure out how to do it. It would be very helpful to have this sample so I can understand how Reflection works with Lambda and Generic concepts.
Basically the objective is to write a correct version of a function like this:
//Return all items from a IEnumerable(target) that has at least one matching Property(propertyName)
//with its value contained in a IEnumerable(possibleValues)
static IEnumerable GetFilteredList(IEnumerable target, string propertyName, IEnumerable searchValues)
{
return target.Where(t => searchValues.Contains(t.propertyName));
//Known the following:
//1) This function intentionally can't be compiled
//2) Where function can't be called directly from an untyped IEnumerable
//3) t is not actually recognized as a Type, so I can't access its property
//4) The property "propertyName" in t should be accessed via Linq.Expressions or Reflection
//5) Contains function can't be called directly from an untyped IEnumerable
}
//Testing environment
static void Main()
{
var listOfPerson = new List<Person> { new Person {Id = 3}, new Person {Id = 1}, new Person {Id = 5} };
var searchIds = new int[] { 1, 2, 3, 4 };
//Requirement: The function must not be generic like GetFilteredList<Person> or have the target parameter IEnumerable<Person>
//because the I need to pass different IEnumerable types, not known in compile-time
var searchResult = GetFilteredList(listOfPerson, "Id", searchIds);
foreach (var person in searchResult)
Console.Write(" Found {0}", ((Person) person).Id);
//Should output Found 3 Found 1
}
I'm not sure if the other questions address this scenario, because I don't think I could clearly understand how Expressions work.
Update:
I can't use Generics because I only have the type and the property to be tested (in Contains) at run-time. In the first code sample, suppose "MyType" is not known at compile time. In the second code sample, the type could be passed as a parameter to the GetFilteredList function or could be get via Reflection (GetGenericArguments).
Thanks,
After a wide research and a lot of study of Expressions I could write a solution myself. It certainly can be improved, but exactly fits my requirements. Hopefully it can help someone else.
//Return all items from a IEnumerable(target) that has at least one matching Property(propertyName)
//with its value contained in a IEnumerable(possibleValues)
static IEnumerable GetFilteredList(IEnumerable target, string propertyName, IEnumerable searchValues)
{
//Get target's T
var targetType = target.GetType().GetGenericArguments().FirstOrDefault();
if (targetType == null)
throw new ArgumentException("Should be IEnumerable<T>", "target");
//Get searchValues's T
var searchValuesType = searchValues.GetType().GetGenericArguments().FirstOrDefault();
if (searchValuesType == null)
throw new ArgumentException("Should be IEnumerable<T>", "searchValues");
//Create a p parameter with the type T of the items in the -> target IEnumerable<T>
var containsLambdaParameter = Expression.Parameter(targetType, "p");
//Create a property accessor using the property name -> p.#propertyName#
var property = Expression.Property(containsLambdaParameter, targetType, propertyName);
//Create a constant with the -> IEnumerable<T> searchValues
var searchValuesAsConstant = Expression.Constant(searchValues, searchValues.GetType());
//Create a method call -> searchValues.Contains(p.Id)
var containsBody = Expression.Call(typeof(Enumerable), "Contains", new[] { searchValuesType }, searchValuesAsConstant, property);
//Create a lambda expression with the parameter p -> p => searchValues.Contains(p.Id)
var containsLambda = Expression.Lambda(containsBody, containsLambdaParameter);
//Create a constant with the -> IEnumerable<T> target
var targetAsConstant = Expression.Constant(target, target.GetType());
//Where(p => searchValues.Contains(p.Id))
var whereBody = Expression.Call(typeof(Enumerable), "Where", new[] { targetType }, targetAsConstant, containsLambda);
//target.Where(p => searchValues.Contains(p.Id))
var whereLambda = Expression.Lambda<Func<IEnumerable>>(whereBody).Compile();
return whereLambda.Invoke();
}
In order to avoid using generics (since the types are not known at design time) you could use some reflection and build the expression "by hand"
You would need to do this by defining a "Contains" expression inside one Where clause:
public IQueryable GetItemsFromContainsClause(Type type, IEnumerable<string> items)
{
IUnitOfWork session = new SandstoneDbContext();
var method = this.GetType().GetMethod("ContainsExpression");
method = method.MakeGenericMethod(new[] { type });
var lambda = method.Invoke(null, new object[] { "Codigo", items });
var dbset = (session as DbContext).Set(type);
var originalExpression = dbset.AsQueryable().Expression;
var parameter = Expression.Parameter(type, "");
var callWhere = Expression.Call(typeof(Queryable), "Where", new[] { type }, originalExpression, (Expression)lambda);
return dbset.AsQueryable().Provider.CreateQuery(callWhere);
}
public static Expression<Func<T, bool>> ContainsExpression<T>(string propertyName, IEnumerable<string> values)
{
var parameterExp = Expression.Parameter(typeof(T), "");
var propertyExp = Expression.Property(parameterExp, propertyName);
var someValue = Expression.Constant(values, typeof(IEnumerable<string>));
var containsMethodExp = Expression.Call(typeof(Enumerable), "Contains", new[] { typeof(string) }, someValue, propertyExp);
return Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
}
In this case "Codigo" is hard-coded, but it could be a parameter to get any property of the type you define.
You could test it by using:
public void LambdaConversionBasicWithEmissor()
{
var cust= new Customer();
var items = new List<string>() { "PETR", "VALE" };
var type = cust.GetType();
// Here you have your results from the database
var result = GetItemsFromContainsClause(type, items);
}
You can solve your problem by using the following set of classes.
First, we need to create a Contains class which will decide which items will be chosen from the source array.
class Contains
{
public bool Value { get; set; }
public Contains(object[] items, object item)
{
Value = (bool)(typeof(Enumerable).GetMethods()
.Where(x => x.Name.Contains("Contains"))
.First()
.MakeGenericMethod(typeof(object))
.Invoke(items, new object[] { items, item }));
}
}
Then we need to create a Where class which will be used to form a predicate based on which items will be selected. It should be clear that in our case, we are going to use the Contains class for our predicate method.
class Where
{
public object Value { get; set; }
public Where(object[] items, object[] items2)
{
Value = typeof(Enumerable).GetMethods()
.Where(x => x.Name.Contains("Where"))
.First()
.MakeGenericMethod(typeof(object))
.Invoke(items2, new object[] { items2, new Func<object, bool>(i => new Contains(items, i).Value) });
}
}
The last step is simply to invoke the result we got from the Where class, which is actually of type Enumerable.WhereArrayIterator and not of type List, since the result of the Where Extension method is a product of deferred execution.
Thus we need to create a non deferred object, by calling its ToList Extension Method, and get our result.
class ToList
{
public List<object> Value { get; set; }
public ToList(object[] items, object[] items2)
{
var where = new Where(items, items2).Value;
Value = (typeof(Enumerable).GetMethods()
.Where(x => x.Name.Contains("ToList"))
.First()
.MakeGenericMethod(typeof(object))
.Invoke(where, new object[] { where })) as List<object>;
}
}
In the end, you can simply test the whole process out by using the following class.
class Program
{
static void Main()
{
var items = new object[] { 1, 2, 3, 4 };
var items2 = new object[] { 2, 3, 4, 5 };
new ToList(items, items2).Value.ForEach(x => Console.WriteLine(x));
Console.Read();
}
}

Recursive call to generic function with DataRow parameter - MakeGenericMethod Fails

the question is : if i have MyConvertDataRowToEntity(DataRow row )
and I call in with T object from type Parent and inside I call the same function with desendant type Child how should I pass the DataRow parameter ?
The problem is created when Invoke of MakeGenericMethod called.
Did change the type to DataSet , string and String types .
No luck.
(I recognize the children object bu prefix in column names - PrefixDataColumn )
public static T MyConvertDataRowToEntity<T>(DataRow row ) where T : class, new()
{
Type objType = typeof(T);
Type parentObjType = typeof(T);
T obj = Activator.CreateInstance<T>(); //hence the new() contsraint
PropertyInfo propertyGenericType = null;
object childInstance = null;
PropertyInfo property;
string childColumnName = string.Empty ;
foreach (DataColumn column in row.Table.Columns)
{
column.ColumnName = column.ColumnName.Replace("_", "");
string PrefixDataColumn;
if (column.ColumnName.IndexOf(".") > (-1))
{
///gets the prefix that is the same as child entity name
PrefixDataColumn = column.ColumnName.Substring(0, column.ColumnName.IndexOf("."));
///the column name in the child
int length = column.ColumnName.Length - 1;
int start = column.ColumnName.IndexOf(".") + 1;
childColumnName = column.ColumnName.Substring(column.ColumnName.IndexOf(".") + 1);
propertyGenericType = objType.GetProperty(PrefixDataColumn,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy);
parentObjType = objType;
if (propertyGenericType != null)
{
Type childType = propertyGenericType.PropertyType;
objType = childType;
childInstance = Activator.CreateInstance(propertyGenericType.PropertyType);
// get the get method for the property
MethodInfo method = propertyGenericType.GetGetMethod(true);
// get the generic get-method generator
MethodInfo genericHelper = typeof(DataUtil).GetMethod("MyConvertDataRowToEntity", BindingFlags.Public | BindingFlags.Static);
List<Type> signature = new List<Type>();
// first type parameter is type of target object
signature.Add(childType);
//next parameters are real types of method arguments
foreach (ParameterInfo pi in genericHelper.GetParameters())
{
signature.Add(pi.ParameterType);
}
// last parameters are known types of method arguments
signature.AddRange(typeof(T).GetGenericArguments());
// reflection call to the generic get-method generator to generate the type arguments
//MethodInfo constructedHelper = genericHelper.MakeGenericMethod(signature.ToArray());
// reflection call to the generic get-method generator to generate the type arguments
MethodInfo constructedHelper = genericHelper.MakeGenericMethod(childType );
// now call it. The null argument is because it's a static method.
object ret = constructedHelper.Invoke(null, new object[] { method });
// object myObj = method.Invoke(null, row);
//// property.SetValue(obj, MyConvertDataRowToEntity<childInstance>(DataRow row),null);
// childInstance = DataUtil.GetMethod("MyConvertDataRowToEntity").MakeGenericMethod(childType); //MyConvertDataRowToEntity<object>(row);
//childType initializedChild = ;
//property.SetValue(obj, value, null);
//objType = parentObjType;
}
else
{
continue;
}
}
}
return obj;
}
Getting this error :
Object of type 'System.Reflection.RuntimeMethodInfo' cannot be converted to type 'System.Data.DataRow'.
Is there any solution for this ?
p.s.
Narrowed down the code as much as i could.
How can I invoke the Method recursivly with desedant types and pass datarow ?
The reason for the error is because you are calling MyConvertDataRowToEntity<ChildType> but then passing in the getaccessor methodinfo for the property as the parameter instead of a data row containing only those fields.
If you want to continue with the code processing logic you are currently using you would need to construct a new datarow containing the fields you wanted (with the prefix and ".") removed from the start of the column names.
Alternatively you could create a helper method the accepted a column name, the source object and it simply updated the value.
static void UpdateItemProperty<T>(T item, string columnName, object rowValue) {
var prefixColumn=columnName.IndexOf(".")==-1 ? columnName : columnName.Split(".")[0];
var pi = typeof(T).GetProperty(prefixColumn // Add your binding flags);
// if pi==null then there is an error...
if (column.ColumnName.IndexOf(".") == (-1)) { // No Nesting
pi.SetValue(item,rowValue);
return;
}
// Nesting
var child=pi.GetValue(item);
if (child==null) {
// Logic here to get childs type and create an instance then call pi.SetValue with child
}
var remainder=string.Join(',',columnName.Split(".").Skip(1).ToArray());
// make your generic method info for UpdateItemProperty with pi.PropertyType into mi
mi.Invoke(null,new object[] { child,remainder,value };
}

How do I create and access a new instance of an Anonymous Class passed as a parameter in C#?

I have created a function that takes a SQL command and produces output that can then be used to fill a List of class instances. The code works great. I've included a slightly simplified version without exception handling here just for reference - skip this code if you want to jump right the problem. If you have suggestions here, though, I'm all ears.
public List<T> ReturnList<T>() where T : new()
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
Type objectType = typeof (T);
FieldInfo[] typeFields = objectType.GetFields();
while (nwReader.Read())
{
T obj = new T();
foreach (FieldInfo info in typeFields)
{
for (int i = 0; i < nwReader.FieldCount; i++)
{
if (info.Name == nwReader.GetName(i))
{
info.SetValue(obj, nwReader[i]);
break;
}
}
}
fdList.Add(obj);
}
nwReader.Close();
return fdList;
}
As I say, this works just fine. However, I'd like to be able to call a similar function with an anonymous class for obvious reasons.
Question #1: it appears that I must construct an anonymous class instance in my call to my anonymous version of this function - is this right? An example call is:
.ReturnList(new { ClientID = 1, FirstName = "", LastName = "", Birthdate = DateTime.Today });
Question #2: the anonymous version of my ReturnList function is below. Can anyone tell me why the call to info.SetValue simply does nothing? It doesn't return an error or anything but neither does it change the value of the target field.
public List<T> ReturnList<T>(T sample)
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
// Cannot use FieldInfo[] on the type - it finds no fields.
var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read())
{
// No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (PropertyDescriptor info in properties)
{
for (int i = 0; i < nwReader.FieldCount; i++)
{
if (info.Name == nwReader.GetName(i))
{
// This loop runs fine but there is no change to obj!!
info.SetValue(obj, nwReader[i]);
break;
}
}
}
fdList.Add(obj);
}
nwReader.Close();
return fdList;
}
Any ideas?
Note: when I tried to use the FieldInfo array as I did in the function above, the typeFields array had zero elements (even though the objectType shows the field names - strange). Thus, I use TypeDescriptor.GetProperties instead.
Any other tips and guidance on the use of reflection or anonymous classes are appropriate here - I'm relatively new to this specific nook of the C# language.
UPDATE: I have to thank Jason for the key to solving this. Below is the revised code that will create a list of anonymous class instances, filling the fields of each instance from a query.
public List<T> ReturnList<T>(T sample)
{
List<T> fdList = new List<T>();
myCommand.CommandText = QueryString;
SqlDataReader nwReader = myCommand.ExecuteReader();
var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read())
{
int objIdx = 0;
object[] objArray = new object[properties.Count];
foreach (PropertyDescriptor info in properties)
objArray[objIdx++] = nwReader[info.Name];
fdList.Add((T)Activator.CreateInstance(sample.GetType(), objArray));
}
nwReader.Close();
return fdList;
}
Note that the query has been constructed and the parameters initialized in previous calls to this object's methods. The original code had an inner/outer loop combination so that the user could have fields in their anonymous class that didn't match a field. However, in order to simplify the design, I've decided not to permit this and have instead adopted the db field access recommended by Jason. Also, thanks to Dave Markle as well for helping me understand more about the tradeoffs in using Activator.CreateObject() versus GenUninitializedObject.
Anonymous types encapsulate a set of read-only properties. This explains
Why Type.GetFields returns an empty array when called on your anonymous type: anonymous types do not have public fields.
The public properties on an anonymous type are read-only and can not have their value set by a call to PropertyInfo.SetValue. If you call PropertyInfo.GetSetMethod on a property in an anonymous type, you will receive back null.
In fact, if you change
var properties = TypeDescriptor.GetProperties(sample);
while (nwReader.Read()) {
// No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (PropertyDescriptor info in properties) {
for (int i = 0; i < nwReader.FieldCount; i++) {
if (info.Name == nwReader.GetName(i)) {
// This loop runs fine but there is no change to obj!!
info.SetValue(obj, nwReader[i]);
break;
}
}
}
fdList.Add(obj);
}
to
PropertyInfo[] properties = sample.GetType().GetProperties();
while (nwReader.Read()) {
// No way to create a constructor so this call creates the object without calling a ctor. Could this be a source of the problem?
T obj = (T)FormatterServices.GetUninitializedObject(typeof(T));
foreach (PropertyInfo info in properties) {
for (int i = 0; i < nwReader.FieldCount; i++) {
if (info.Name == nwReader.GetName(i)) {
// This loop will throw an exception as PropertyInfo.GetSetMethod fails
info.SetValue(obj, nwReader[i], null);
break;
}
}
}
fdList.Add(obj);
}
you will receive an exception informing you that the property set method can not be found.
Now, to solve your problem, what you can do is use Activator.CreateInstance. I'm sorry that I'm too lazy to type out the code for you, but the following will demonstrate how to use it.
var car = new { Make = "Honda", Model = "Civic", Year = 2008 };
var anothercar = Activator.CreateInstance(car.GetType(), new object[] { "Ford", "Focus", 2005 });
So just run through a loop, as you've done, to fill up the object array that you need to pass to Activator.CreateInstance and then call Activator.CreateInstance when the loop is done. Property order is important here as two anonymous types are the same if and only if they have the same number of properties with the same type and same name in the same order.
For more, see the MSDN page on anonymous types.
Lastly, and this is really an aside and not germane to your question, but the following code
foreach (PropertyDescriptor info in properties) {
for (int i = 0; i < nwReader.FieldCount; i++) {
if (info.Name == nwReader.GetName(i)) {
// This loop runs fine but there is no change to obj!!
info.SetValue(obj, nwReader[i]);
break;
}
}
}
could be simplified by
foreach (PropertyDescriptor info in properties) {
info.SetValue(obj, nwReader[info.Name]);
}
I had the same problem, I resolved it by creating a new Linq.Expression that's going to do the real job and compiling it into a lambda: here's my code for example:
I want to transform that call:
var customers = query.ToList(r => new
{
Id = r.Get<int>("Id"),
Name = r.Get<string>("Name"),
Age = r.Get<int>("Age"),
BirthDate = r.Get<DateTime?>("BirthDate"),
Bio = r.Get<string>("Bio"),
AccountBalance = r.Get<decimal?>("AccountBalance"),
});
to that call:
var customers = query.ToList(() => new
{
Id = default(int),
Name = default(string),
Age = default(int),
BirthDate = default(DateTime?),
Bio = default(string),
AccountBalance = default(decimal?)
});
and do the DataReader.Get things from the new method, the first method is:
public List<T> ToList<T>(FluentSelectQuery query, Func<IDataReader, T> mapper)
{
return ToList<T>(mapper, query.ToString(), query.Parameters);
}
I had to build an expression in the new method:
public List<T> ToList<T>(Expression<Func<T>> type, string sql, params object[] parameters)
{
var expression = (NewExpression)type.Body;
var constructor = expression.Constructor;
var members = expression.Members.ToList();
var dataReaderParam = Expression.Parameter(typeof(IDataReader));
var arguments = members.Select(member =>
{
var memberName = Expression.Constant(member.Name);
return Expression.Call(typeof(Utilities),
"Get",
new Type[] { ((PropertyInfo)member).PropertyType },
dataReaderParam, memberName);
}
).ToArray();
var body = Expression.New(constructor, arguments);
var mapper = Expression.Lambda<Func<IDataReader, T>>(body, dataReaderParam);
return ToList<T>(mapper.Compile(), sql, parameters);
}
Doing this that way, i can completely avoid the Activator.CreateInstance or the FormatterServices.GetUninitializedObject stuff, I bet it's a lot faster ;)
Question #2:
I don't really know, but I would tend to use Activator.CreateObject() instead of FormatterServices.GetUninitializedObject(), because your object might not be created properly. GetUninitializedObject() won't run a default constructor like CreateObject() will, and you don't necessarily know what's in the black box of T...
This method stores one line of a sql query in a variable of anonymous type. You have to pass a prototype to the method. If any property of the anonymous type can not be found within the sql query, it is filled with the prototype-value. C# creates constructors for its anonymous classes, the parameters have the same names as the (read-only) properties.
public static T GetValuesAs<T>(this SqlDataReader Reader, T prototype)
{
System.Reflection.ConstructorInfo constructor = prototype.GetType().GetConstructors()[0];
object[] paramValues = constructor.GetParameters().Select(
p => { try { return Reader[p.Name]; }
catch (Exception) { return prototype.GetType().GetProperty(p.Name).GetValue(prototype); } }
).ToArray();
return (T)prototype.GetType().GetConstructors()[0].Invoke(paramValues);
}

Categories