Search code examples
c#reflectiontypesdbset

X is a variable but used like a type when trying to cast


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.


Solution

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