C# type parameter only reads as base class

110 Views Asked by At

I have a base class I'll call TypeBase and several classes derived from it, for grins lets call them TypeImage and TypeAsset. Here's what happens in the code:

...
TypeBase b = null;
MethodDoingStuff(passedID, ref b); 
RepositoryCall(b, otherArgs);

So in MethodDoingStuff we have something like this:

public bool MethodDoingStuff (long passedID, ref TypeBase b)
{
     object concrete = DbCallThatGetsSubclass(passedID);//returns a subclass 
                              of TypeBase(in reality more than 2  possibilities)
     TypeBase tb = (TypeBase)concrete;
     b=tb;
     return true;
}

So the method sig for the Repository call looks like this:

  public virtual T FindByID<T>( T typeInstance, long id) where T : TypeBase
    {
        T item = (T)Activator.CreateInstance(typeof(T));

        using (IDbConnection cn = Connection)
        {
            item = cn.Get<T>(id);
        }

        return item;
    }

The problem arises in that cn.Get<T>(id) is a call to a Dapper Extension, the Table it looks for is based on that type parameter, which of course it sees as TypeBase. The relevant table data is of course in the tables TypeImage or TypeAsset, or whatever. My freedom mostly lies in the Repository: I can change how the method works or introduce overloads. I can also change what arguments are passed into MethodDoingStuff() but for a variety of reasons can't really change MethodDoingStuff itself.

2

There are 2 best solutions below

0
On

You are casting the type to the base class. So when you use in with Dapper it sees TypeBase. This is a problem with inheritance. Using a interface might help you solve the problem.

0
On

Here's what ended up working: I made an overload for my FindByID<T> method where I pass b.GetType.ToString() and this gives the correct subclass. In the overload I instantiate one of these and use reflection to pass it as a type parameter to the Dapper extension method.

 public virtual T FindByID<T>(T typeInstance, long id, string typeName) where T : TypeBase
    {
        Assembly asm = Assembly.GetCallingAssembly();
        var item = asm.CreateInstance(typeName);

        using (IDbConnection cn = Connection)
        {
            MethodInfo getInfo = typeof(SqlMapperExtensions).GetMethod("Get");
            MethodInfo getGeneric = getInfo.MakeGenericMethod(item.GetType());
            item = getGeneric.Invoke(cn, new object[] { cn,id,null,null });
        }

        return (T)item;
    }

This works though I would have like to avoid reflection if I could.