3

I am passing a string of the name of the entity type I want to query and getting the type based on the string. I want to get the DbSet back and return an IQueryable. The problem is where I am doing (DbSet<tableEntity>) and getting the following error:

tableEntity is a variable but used like a type

when trying to cast. Is there a way to resolve this?

public object GetList(string tableEntity)
{
    Type tableEntity = Type.GetType("TestProject." + typeName + ", TestProject");

    var dbObject = (DbSet<tableEntity>)typeof(DbContext).GetMethod("Set", Type.EmptyTypes)
                        .MakeGenericMethod(tableEntity)
                        .Invoke(databaseContext, null);

    return dbObject.AsQueryable();            
}

EDIT

Just to add I don't have access to the type that's we I am passing the name through a string.

Kogroth
  • 87
  • 1
  • 2
  • 8
  • The difference between compile-time and runtime is very important! Unless you get it down pat, you'll be constantly tripping up on nasty gotchas like this. [Read an intro on compilers.](https://meta.stackexchange.com/questions/25840/can-we-stop-recommending-the-dragon-book-please) It's the tool you're working with professionally, you should know how it works in principle if not in detail. – Anton Tykhyy May 26 '17 at 20:37
  • You may find the following useful if the DB is MS Sql Server or has the same data type definitions: https://stackoverflow.com/a/28561947/4228193. System.Web of all places. I use it as follows `TypeCode systc = Parameter.ConvertDbTypeToTypeCode([SqlMetaData_instance].DbType); try { return Convert.ChangeType(val, systc); }` – mpag Dec 12 '17 at 17:47

2 Answers2

5

So it turns out that the entity type is literally not known, or knowable, at compile time. It has to be a string.

The only place you're using the type at compile time is in the cast to (DbSet<tableEntity>). Well, you may not need that. All you need from that type is to call AsQueryable(), and AsQueryable() is an extension method for IEnumerable, with generic and non-generic versions. IF we call it through non-generic IEnumerable, that's non-generic AsQueryable(), returning non-generic IQueryable. But we're returning object anyway, so hey. For the result of this thing to be useful, something somewhere must be doing a fair amount of reflection on it anyway, so the declared type is likely to be of little consequence.

See if this works:

public object GetList(string typeName)
{
    Type tableEntity = Type.GetType("TestProject." + typeName + ", TestProject");

    var dbObject = (System.Collections.IEnumerable)
                        typeof(DbContext).GetMethod("Set", Type.EmptyTypes)
                        .MakeGenericMethod(tableEntity)
                        .Invoke(databaseContext, null);

    return dbObject.AsQueryable();            
}

If it turns out you need generic IQueryable<TEntityType>, we'll have to use reflection to get MethodInfo for AsQueryable<TEntityType> for the unknown (at compile time) entity type, and call MakeGenericMethod(tableEntity) on that.


First try:

In the language, type parameters to generics must be actual types, not instances of the Type class. That's because they're resolved at compile time.

But that's no problem; to pass a type parameter to a generic method, simply write a generic method with a type parameter.

You can't do this:

var stuff = GetList("MyTableEntityClass");

But this is just as good:

var stuff = GetList<MyTableEntityClass>();

...

public object GetList<TTableEntity>()
{
    var dbObject = (DbSet<TTableEntity>)typeof(DbContext)
                        .GetMethod("Set", Type.EmptyTypes)
                        .MakeGenericMethod(typeof(TTableEntity))
                        .Invoke(databaseContext, null);

    return dbObject.AsQueryable();            
}

Reflection is different; that's why we pass typeof(TTableEntity) to MakeGenericMethod().

And once we're using an actual type that the compiler can check, we can do better with our return type, too:

public IQueryable<TTableEntity> GetList<TTableEntity>()
{
    var dbObject = (DbSet<TTableEntity>)typeof(DbContext)
                        .GetMethod("Set", Type.EmptyTypes)
                        .MakeGenericMethod(typeof(TTableEntity))
                        .Invoke(databaseContext, null);

    return dbObject.AsQueryable();            
}
Karthikeyan VK
  • 5,310
  • 3
  • 37
  • 50
  • The problem with this solution is I don't have access to the Type or I would of done it this way. The situation I am in now is the BAL has to go through the DAL to get to Entity Framework objects. Sorry I think should of added that to my question. – Kogroth May 26 '17 at 19:18
  • @Kogroth Oh my golly, this may be incredibly simple. – 15ee8f99-57ff-4f92-890c-b56153 May 26 '17 at 19:18
1

Since, as Ed mentioned, you don't use the table entity type at compile time, why not just use the non-generic databaseContext.Set (tableEntity).AsQueryable ()? But if you've set your heart on Set<>, try this:

public object GetList(string tableEntity)
{
    Type tableEntity = Type.GetType("TestProject." + typeName + ", TestProject");

    return GetType ()
        .GetMethod ("GetListHelper")
        .MakeGenericMethod (tableEntity)
        .Invoke    (this) ;
}

public object GetListHelper<T> () where T : class
{
    var dbObject = databaseContext.Set<T> (null) ;
    return dbObject.AsQueryable();            
}
Anton Tykhyy
  • 19,370
  • 5
  • 54
  • 56