Question

I have an abstract class called Fruit. I then have a derived class called Apple.

I have these two extension methods:

public static IQueryable<TFruit> WithEagerLoading<TFruit>(this IQueryable<TFruit> query) where TFruit : Fruit
{
   return query.EagerLoad(x => x.Distributors); // Fruit.Distributors
}

public static IQueryable<Apple> WithEagerLoading(this IQueryable<Apple> query)
{
   query = query.EagerLoad(x => x.AppleBrands); // Apple.AppleBrands

   // now bubble up to base extension method
   return query.WithEagerLoading<Apple>();
}

Now, here is the generic method i have in a Repository:

public TFruit FindById<TFruit>(int fruitId) where TFruit : Fruit
{
   var query = _ctx.Fruits // IQueryable<Fruit>
                   .OfType<TFruit>(); // IQueryable<TFruit>

   query = query.WithEagerLoading();

   return query.SingleOrDefault(x => x.FruitId == fruitId);
}

The problem i have, is when i do this:

var apple = repository.FindById<Apple>(1);

It goes into the IQueryable<Fruit> extension method.

I want it to go into IQueryable<Apple> extension method. For other types of Fruit, it should go into the IQueryable<TFruit> extension method.

I thought the compiler would pick the most specific extension method.

Any ideas?

EDIT

Thanks for the comments/answer. I see now why this doesn't work.

So what are options to resolve this? If i create a method:

public static IQueryable<Apple> WithAppleEagerLoading(this IQueryable<Apple> query)

How would i call it from my generic repository? I would have to inspect the type of TFruit:

public TFruit FindById<TFruit>(int fruitId) where TFruit : Fruit
{
   var query = _ctx.Fruits // IQueryable<Fruit>
                   .OfType<TFruit>(); // IQueryable<TFruit>

   if (typeof(TFruit) == typeof(Apple))
       query = query.WithAppleEagerLoading();
   else 
       query = query.WithEagerLoading();

   return query.SingleOrDefault(x => x.FruitId == fruitId);
}

Which isn't nice - considering i have around 20 derived types.

Can anyone offer an alternative as to what i'm attempting to do?

Was it helpful?

Solution

Extension method resolution happens at compile time. The query variable is of type IQueryable<TFruit>, so the compiler picks the most specific method that matches WithEagerLoading<Fruit>. It can't pick the apple one because it only knows that TFruit is some kind of Fruit. It picks it once, and permanently.

What you're suggesting would require it to dynamically decide which extension method to use based on the type at runtime, or to compile separate versions of IQueryable<TFruit> that resolve methods differently based on specific values of TFruit.

Edit to answer additional question

Well, the special casing isn't super horrible, because you can use a switch statement. But I agree, not ideal if you have a lot of types. In terms of delegating to subclasses, I'd adjust Paul's answer a bit:

abstract class FruitRepository : IRepository<T> where TFruit : Fruit
{
   public TFruit FindByID(int fruitID)
   {
       //query stuff here

       query = AddEagerLoading(query)
                  .WithEagerLoading();
    }

    //this could also be abstract to prevent you from forgetting
    public virtual IQueryable<TFruit> AddEagerLoading(IQueryable<TFruit> query)
    {
        return query;
    }
}

and then

class AppleRepository : FruitRepository<Apple>
{
    public override AddEagerLoading(IQueryable<Apple> query)
    {
        return query.EagerLoad(x => x.AppleBrands);
    }
}

So that way you have the minimal code per subclass.

OTHER TIPS

I have had a similar problem where I wanted to effectively change the precedence of a method so that it would resolve a 'specialised' version first.

You can achieve this without changing your calling code, but the solution might not be popular as it uses runtime reflection and code-generation. I'm just going to chuck it out there anyway (I try and stick up at least one answer a day on SO!).

Note that this code represents an abstract pattern that would need to be adapted to your scenario.

public class Base
{
  public string BaseString { get; set; }
}

public class Derived : Base
{
  public string DerivedString { get; set; }
}

public static class SO4870831Extensions
{
  private static Dictionary<Type, Action<Base>> _helpers = 
    new Dictionary<Type,Action<Base>>();

  public static void Extension<TBase>(this TBase instance) 
    where TBase :Base
  {
    //see if we have a helper for the absolute type of the instance
    var derivedhelper = ResolveHelper<TBase>(instance);

    if (derivedhelper != null)
      derivedhelper(instance);
    else
      ExtensionHelper(instance);
  }

  public static void ExtensionHelper(this Base instance)
  {
    Console.WriteLine("Base string: {0}", 
      instance.BaseString ?? "[null]");
  }

  /// <summary>
  /// By Default this method is resolved dynamically, but is also 
  /// available explicitly.
  /// </summary>
  /// <param name="instance"></param>
  public static void ExtensionHelper(this Derived instance)
  {
    Console.WriteLine("Derived string: {0}", 
      instance.DerivedString ?? "[null]");
    //call the 'base' version - need the cast to avoid Stack Overflow(!)
    ((Base)instance).ExtensionHelper();
  }

  private static Action<Base> ResolveHelper<TBase>(TBase instance) 
    where TBase : Base
  {
    Action<Base> toReturn = null;
    Type instanceType = instance.GetType();
    if (_helpers.TryGetValue(instance.GetType(), out toReturn))
      return toReturn;  //could be null - that's fine

    //see if we can find a method in this class for that type
    //this could become more complicated, for example, reflecting
    //the type itself, or using attributes for richer metadata
    MethodInfo helperInfo = typeof(SO4870831Extensions).GetMethod(
      "BaseExtensionHelper", 
      BindingFlags.Public | BindingFlags.Static, 
      null, 
      new Type[] { instanceType }, 
      null);

    if (helperInfo != null)
    {
      ParameterExpression p1 = Expression.Parameter(typeof(Base), "p1");
      toReturn =
        Expression.Lambda<Action<Base>>(
        /* body */
          Expression.Call(
            helperInfo,
            Expression.Convert(p1, instanceType)),
        /* param */
          p1).Compile();
      _helpers.Add(instanceType, toReturn);
    }
    else
      //cache the null lookup so we don't expend energy doing it again
      _helpers.Add(instanceType, null);
    return toReturn;
  }
}
/// <summary>
/// Summary description for UnitTest1
/// </summary>
[TestClass]
public class UnitTest1
{
  [TestMethod]
  public void TestMethod1()
  {
    var a = new Base() { BaseString = "Base Only" };
    var b = new Derived() { DerivedString = "Derived", BaseString = "Base" };

    a.Extension();
    //Console output reads:
    //"Base String: Base Only"
    b.Extension();
    //Console output reads:
    //"Derived String: Derived"
    //"Base String: Base"
  }

I'm not saying that this pattern is better than finding a polymorphic solution that uses more traditional pattern provided by the language - but it's a solution :)

This pattern can be applied to most extension methods - but in its current form you'd have to repeat the pattern for each extension method that you'd want to write. Equally if those extensions require ref/out parameters it would be trickier.

As I also say in my comments you might consider changing the lookup for the method to support being defined in the instance type itself (keeps relevant code together).

You'd have to do a bit of work to make this work correctly for your IQueryable - I'm sorry I haven't made this solution more directly relevant to your scenario, it's just easier to mock up a test solution that takes most of the generic hell out!

Ultimately you need to find a way to introduce some polymorphism - you want special behavior for loading apples that extends the basic behavior for loading fruit. Sometimes the easiest way to do this is to make repository classes, such as:

class Repository<T> : IRepository<T>
{

   public virtual T FindById(int id)
   { ... }
}

class FruitRepository<T> : Repository<T> where T : Fruit
{
   public override T FindById(int id)
   {  ... }
}

class AppleRepository : FruitRepository<Apple>
{
   public override T FindById(int id)
   {  ... }
}

Now FindByID doesn't need to have a generic parameter at the method level, it just uses the generic parameter at the class level. You can then have FruitRepository and AppleRepository override FindByID as appropriate. Your usage code would be a little different in that you have to make sure the repository instance you have is appropriate for finding apples.

If you are using an IoC container, you might register that when something requests an IRepository<Apple> that it returns an instance of AppleRepository. Then you would use it as so:

// ideally you would resolve this via constructor injection, but whatever.
var repository = container.Resolve<IRepository<Apple>>(); 
var apple = repository.FindByID(1);

And if you are not using an IoC container... well.. you should be :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top