6

Assume we have this model :

public abstract class AbstractTableReferentielEntity {}
public class EstimationTauxReussite : AbstractTableReferentielEntity { }

I created a extension method for all classes that inherit from AbstractTableReferentielEntity.

public static EntityItemViewModel ToEntityItem<T>(this T entity)
    where T : AbstractTableReferentielEntity {}

But for one specific type of AbstractTableReferentielEntity (like EstimationTauxReussite), I would like to perform a specific action, so I created a second extension method.

 public static EntityItemViewModel ToEntityItem(this EstimationTauxReussite entity) {}

All extensions methods are declared in the same namespace.

After that, I retrieve some data from a DB with Entity Framework :

protected List<EntityItemViewModel> GetAllActifEntityItem<T>()
    where T : AbstractTableReferentielEntity
{
    return Context
        .Set<T>()
        .Where(item => item.IsActif)
        .Select(item => item.ToEntityItem())
        .ToList();
}

It compiles.

When T at runtime is a EstimationTauxReussite type, it goes into the wrong method ToEntityItem when I call Select(item => item.ToEntityItem()). It doesn't go into the most specific extension method. Any ideas ?

Konamiman
  • 49,681
  • 17
  • 108
  • 138
Christophe Gigax
  • 3,211
  • 4
  • 25
  • 37
  • An extension method should never take priority, so this is odd behaviour. – NibblyPig Jul 22 '15 at 09:57
  • Did you do any changes to the database? If so please update the entity framework. I have also face same kind of problem when I do changes on database. – Nivs Jul 22 '15 at 09:58
  • @Nivs, it has nothing to do with Entity Framework or the database, it's just because extension methods are just static methods and so they don't participate in polymorphism. – Thomas Levesque Jul 22 '15 at 10:15

3 Answers3

3

The reason is that extension methods are "syntactic sugar", ie they are a compiler trick. Your line:

.Select(item => item.ToEntityItem())

is effectively converted by the compiler to:

.Select(item => StaticClassWithExtensionMethod.ToEntityItem(item))

and then turned into IL. This means that the type of item has to be determined at compile-time, not runtime. So the AbstractTableReferentielEntity version of the extension method is used as that's the one tht matches the type at compile time.

David Arno
  • 42,717
  • 16
  • 86
  • 131
1

If i have access to sources of AbstractTableReferentielEntity and EstimationTauxReussite classes i would remake them in following way

  1. Add virtual ToEntityItem method to AbstractTableReferentielEntity class
  2. Override it in EstimationTauxReussite class
  3. Delete both extension methods

Now Select(item => item.ToEntityItem()) should pick method depends on input object

Disappointed
  • 1,100
  • 1
  • 9
  • 21
1

That's because extension methods are just syntactic sugar for static methods. The method to call is resolved at compile time based on the compile-time type of the argument, there is no virtual dispatch involved.

In your GetAllActifEntityItem method, the compiler only knows that T is a AbstractTableReferentielEntity, so it resolves the ToEntityItem method based on that. The fact that it will actually be called with EstimationTauxReussite for T is irrelevant.

A possible workaround would be to make ToEntityItem a virtual member method of AbstractTableReferentielEntity, and override it in EstimationTauxReussite. This way, virtual dispatch will occur as expected and the correct method will be called.

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758