call method with params in arguments through reflection c# - c#

Method return record from Database.
public T Find(params object[] primaryKeys)
{
var dbSet = _sessionContext.Set<T>() as DbSet<T>;
return dbSet != null ? dbSet.Find(primaryKeys) : null;
}
I'm trying to call in through reflection
var methodCreateReadRepositoryEntity =
typeof(IRepositoryFactory)
.GetMethod("CreateReadRepository")
.MakeGenericMethod(entityMetadata.GetEntityType());
var entityReadRepository =
methodCreateReadRepositoryEntity
.Invoke(_repositoryFactory, new object[] { _sessionMarketContext });
List<object> keys = new List<object>();
keys.Add(value);
var methodEntityGet =
entityReadRepository.GetType().GetMethod("Find", new Type[] { typeof(object[])});
var fromRepo =
methodEntityGet.Invoke(entityReadRepository, new object[]{new []{ keys.ToArray()[0]}});
value is Guid. And I have error
The type of one of the primary key values did not match the type defined in the entity.
Exception has been thrown by the target of an invocation.

Your last line should be as follows. You need to be explicit with the array type, and there is no need to create a List.
var fromRepo =
methodEntityGet.Invoke(entityReadRepository, new object[]{new object []{value}});

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.

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

Reflection.Emit and generic types

I am using Reflection.Emit and I want to create a type that would be the equivalent of the following type defined in C#:
class A
{
public Tuple<A, int> GetValue(int x)
{
return new Tuple<A, int>(this, x);
}
}
The trick is that I need to use a generic type from BCL that uses my custom type as a generic argument.
I'm messing with the following snippet:
var asmName = new AssemblyName("Test");
var access = AssemblyBuilderAccess.Run;
var asm = AppDomain.CurrentDomain.DefineDynamicAssembly(asmName, access);
var module = asm.DefineDynamicModule("Test");
var aType = module.DefineType("A");
var tupleType = typeof(Tuple<,>).MakeGenericType(aType, typeof(int));
var attrs = MethodAttributes.Public;
var method = aType.DefineMethod("GetValue", attrs, tupleType, new [] { typeof(int) });
var gen = method.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldarg_1);
// here is the fail:
var ctor = tupleType.GetConstructor(new [] { typeof(int), aType } );
gen.Emit(OpCodes.Newobj, ctor);
The call to GetConstructor fails with the following exception:
NotSupportedException: Specified method is not supported.
So, basically, it won't let me get the constructor of a type that merely references my custom type, and neither can I finalize the type before emitting the body of its method.
Can it really be impossible to get out of this vicious circle?
For some reason, you need to use the static overload of GetConstructor() to do this. In your case, the code could look like this:
var ctor = TypeBuilder.GetConstructor(
tupleType, typeof(Tuple<,>).GetConstructors().Single());

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

I need to get the value from Nested Property

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

Categories