views:

246

answers:

2

Ok, so I have an abstract class called Product. I have 3 tables called Items, Kits, and Packages that implement Product. Product has a public property that exposes the object's primary key.

That said I have a form where I pass a product. I would would like to pull that product out of a fresh datacontext without having to write a big switch reflecting it's type to get its proper table.

I wanted to do something like this but the cast bit won't accept foo.

public BuilderInclusionsForm(Product p) : this()
        {            
            Type foo = p.GetType();
            product = db2.GetTable(p.GetType()).Cast<foo>().SingleOrDefault(a => 
                a.ProductID == p.ProductID);

or this:

public BuilderInclusionsForm(Product p) : this()
        {            
            Type foo = p.GetType();
            product = db2.GetTable(p.GetType()).OfType<foo>().SingleOrDefault(a => 
                a.ProductID == p.ProductID);
+4  A: 

No, because the type argument has to be known at compile-time to appear in source code.

You could either make BuilderInclusionsForm generic in the type of product, or write a generic method like this:

private static T FindProduct<T>(T product) where T : Product
{
    return db2.GetTable(typeof(T))
                     .OfType<T>()
                     .SingleOrDefault(a => a.ProductID == p.ProductID);
}

and then invoke it with reflection:

public BuilderInclusionsForm(Product p) : this()
{            
    MethodInfo method = typeof(BuilderInclusionsForm).GetMethod("FindProduct",
         BindingFlags.Static | BindingFlags.NonPublic);
    MethodInfo concrete = method.MakeGenericMethod(new Type[] { p.GetType() });
    product = (Product) concrete.Invoke(null, new object[] { p });
}

(Obviously you could cache the open form of the method.)

Not nice, but it should work. I suspect it would be nicer to just make BuilderInclusionsForm generic though - you could always have a helper class:

public static class BuilderInclusionsForm
{
    public static BuilderInclusionsForm<T> Create<T>(T product) where T : Product
    {
        return new BuilderInclusionsForm<T>(product);
    }
}

which would allow you to use type inference.

Jon Skeet
At the risk of sounding foolish. The form is not static, this seems to complicate things. public partial class BuilderInclusionsForm : Office2007FormAdditionally it seems to want a return type for FindProduct.
Echostorm
Whoops - fixed FindProduct. If you can't make BuilderInclusionsForm a generic type, the generic method call may be the best way to go.
Jon Skeet
Thanks. I'm still having some trouble as FindProduct can't be static as it needs to have the datacontext and the original productID, I think that is causing a nullref on the MakeGenericMethod bit.
Echostorm
+2  A: 

With thanks to Mr. Skeet a bright member of my team pointed out the following solution.

public BuilderInclusionsForm(Product p) : this()
{
    IEnumerable<Product> ps = db2.GetTable(p.GetType()).Cast<Product>();
    product = ps.SingleOrDefault(a => a.ProductID == p.ProductID);
}

Sorry to waste your time. Please don't garbage collect my sorry ass John. =oD

Echostorm