Reputation: 3373
Let's say we have the following code (highly simplified modification of my own code):
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace ReflectionTest
{
public interface IDataContext
{ }
sealed class DataContext : IDataContext
{ }
public interface ITable<T> : IEnumerable<T>
where T : class
{
List<T> source { get; }
}
sealed class Table<T> : ITable<T>
where T : class, new()
{
public Table()
{
source = new List<T>() { new T() };
}
public List<T> source { get; set; }
public IEnumerator<T> GetEnumerator() => source.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => source.GetEnumerator();
}
public static class DataExtensions
{
public static ITable<T> GetTable<T>(this IDataContext dataContext)
where T : class, new()
{
return new Table<T>();
}
}
public class TestData
{
public string Name = "Test";
}
internal class Program
{
static void Main(string[] args)
{
var dataContext = new DataContext();
//var ret = dataContext.GetTable<TestData>().ToList(); but via reflection:
var getTableMethod = typeof(DataExtensions).GetMethod("GetTable", new[] { typeof(IDataContext) });
var getTableGeneric = getTableMethod.MakeGenericMethod(typeof(TestData));
var testDataITable = getTableGeneric.Invoke(null, new object[] { dataContext });
//var toListMethod = typeof(Enumerable).GetMethod("ToList", new[] { typeof(IEnumerable) }); this doesn't find IEnumerable<T>.ToList() method!
var toListMethod = typeof(Enumerable).GetMethod("ToList");
var toListGeneriс = toListMethod.MakeGenericMethod(testDataITable.GetType());
//System.ArgumentException: 'Unable to cast object of type "ReflectionTest.Table`1[ReflectionTest.TestData]"
//to type "System.Collections.Generic.IEnumerable`1[ReflectionTest.Table`1[ReflectionTest.TestData]]".'
var ret = toListGeneriс.Invoke(null, new object[] { testDataITable });
Console.ReadKey();
}
}
}
I'm trying to call var ret = dataContext.GetTable<TestData>().ToList();
but via reflection. And I have two problems here:
typeof(Enumerable).GetMethod("ToList", new[] { typeof(IEnumerable) });
doesn't find IEnumerable.ToList() method. I know that there is only one version of this method and I can simply use typeof(Enumerable).GetMethod("ToList");
- but still, how to find it by explicitly specifying type of parameters?ITable<TestData>
parameter type? With my code I get an exception:System.ArgumentException: 'Unable to cast object of type "ReflectionTest.Table`1[ReflectionTest.TestData]" to type System.Collections.Generic.IEnumerable`1[ReflectionTest.Table`1[ReflectionTest.TestData]]
Upvotes: -1
Views: 102
Reputation: 27282
For linq2db
there are helper methods which helps to wotk with reflections. But if your goal is to copy database to another database, there is simplier option.
public static class BulkCopyExtensions
{
public static void CopyData<T>(this IQueryable<T> sourceQuery, IDataContext destination) where T : class
{
destination.GetTable<T>().BulkCopy(sourceQuery.AsEnumerable());
}
public static void CopyData<T>(this IDataContext source, IDataContext destination) where T : class
{
CopyData(source.GetTable<T>(), destination);
}
// MemberHelper.MethodOfGeneric is linq2db method
private static readonly MethodInfo _copyDataConnections = MemberHelper.MethodOfGeneric(() => ((DataConnection)null!).CopyData<object>(null!));
public static void CopyAllData(this IDataContext source, IDataContext destination)
{
var typesForCopy = source.GetType().GetProperties()
.Where(p => typeof(ITable<>).IsSameOrParentOf(p.PropertyType))
.Select(p => p.PropertyType.GetGenericArguments()[0])
.ToList();
foreach (var type in typesForCopy)
{
_copyDataConnections.MakeGenericMethod(type).Invoke(null, [source, destination]);
}
}
}
So, in your case, copying databse is just call CopyAllData
extension method.
dataContextSqlCe.CopyAllData(dataContextSqLite);
Note that this method do not care about order of inserts an you many have FK errors.
Upvotes: 1
Reputation: 271410
For the first problem, you can use Type.MakeGenericMethodParameter
to create a "type parameter".
Returns a signature type object that can be passed into the
Type[]
array parameter of aGetMethod
method to represent a generic parameter reference.
Then use that to create a Type
representing IEnumerable<TSource>
. Passing that to GetMethod
will gets you the desired method.
var typeVariable = Type.MakeGenericMethodParameter(0);
var parameterType = typeof(IEnumerable<>).MakeGenericType(typeVariable)
var toListMethod = typeof(Enumerable).GetMethod("ToList", [parameterType]);
For the second problem, it is because you parameterised ToList
incorrectly. Recall the signature of ToList
:
public static List<TSource> ToList<TSource> (
this IEnumerable<TSource> source
);
TSource
should be the element type of the collection - TestData
, not the type of the collection itself.
var toListGeneriс = toListMethod.MakeGenericMethod(typeof(TestData));
Upvotes: 0