0

When looking at the source code of EF Core, I see one Include function declared as follows:

public static IIncludableQueryable<TEntity, TProperty> Include<TEntity, TProperty>(
  [NotNull] this IQueryable<TEntity> source,
  [NotNull] Expression<Func<TEntity, TProperty>> navigationPropertyPath)
  where TEntity : class
{ }

And another one like this:

public static IQueryable<TEntity> Include<TEntity>(
  [NotNull] this IQueryable<TEntity> source,
  [NotNull, NotParameterized] string navigationPropertyPath)
  where TEntity : class
{ )

However, when actually calling those functions we can just call them without specifying types, why?

_context.Users.Include( u => u.PropertyFromAnotherTable)
              .Where( u => u.Age > 25 )
              .ToListAsync( cancellationToken );
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
Seb
  • 778
  • 1
  • 9
  • 27
  • Same applies to `Where`, `OrderBy`, `Select` and many others (basically all generic methods), so has nothing to do with EF Core `Include` / `ThenInclude`. – Ivan Stoev Nov 06 '19 at 20:18
  • Note that you don't specify a Type for Age either. Nothing special about `.Include()` here. – H H Nov 06 '19 at 20:21

1 Answers1

3

Because types are inferred from the context, i.e. the compiler sees thathe parameters you gave unambiguously define the generic type parameters.

You can witness a simpler version of this easily :

    public static void TestGeneric<T>(T myParam) { }

    public static void Example()
    {
        string testString = null;
        int testInt = 0;

        TestGeneric(testString);
        TestGeneric(testInt);
    }

Hover over both "TestGeneric" calls in Visual Studio : the first signature is TestGeneric<string>(string), the second is TestGeneric<int>(int) .

Note that the types are inferred on compilation (and thus, the generics version are still strongly and precisely typed)

There are many subtleties and limitations of this feature of the C# language. I don't have right now in memory all the details (see further reading section below).

For instance, if you play with different subclasses, the covariance and contravariance of the type parameters, etc... In some cases, you may encounter ambiguousness. The compiler will complain, and in this case, you'll still need to explicitly provide the type parameter(s).

My guess on C# mechanics in your case :

(not completely certain, feel free to comment !)

_context.Users.Include( u => u.PropertyFromAnotherTable)
     .Where( u => u.Age > 25 )
     .ToListAsync( cancellationToken );

First the TEntity type for Include<TEntity, TProperty> can easily be inferred because of the first argument of the extension method :

this IQueryable<TEntity> source,

since you call it on _context.Users, which is I presume something like DbSet<User> then it can be implicitly cast to IQueryable<User>, so TEntity could be User.

Then, it follows that u => u.PropertyFromAnotherTable should be of type Expression<Func<User, TProperty>> . So u is of type User, and since it has a unique property named PropertyFromAnotherTable, C# can then infer the type of TProperty as well.

Further reading :

Some more info here, to cite only one blog among many : http://joelabrahamsson.com/a-neat-little-type-inference-trick-with-c/

It mentions "C# In Depth" book from Jon Skeet, which I recommend if you're interested in the details I mentioned earlier, and in the evolution of the language.

Also, some interesting answers and links to blog from Eric Lippert in this Q&A are also worth reading for the mechanices : Why doesn't C# infer my generic types?

Pac0
  • 21,465
  • 8
  • 65
  • 74
  • Thanks! Exactly the answer I was looking for! I didn't realise it was a C# feature and had nothing to do with EF Core itself. I'm going to order this "C# In Depth" book right away. In case there are 2 types parameters, but the context only allows the inference of 1, I still have to specify both right? – Seb Nov 07 '19 at 05:11
  • Yes, I don't think there is a shortcut notation that allows you to provide only one if many type parameters are needed. FYI, you'll get this kind of error message if this step is failing : `Error CS0411 The type arguments for method 'A.TestGeneric(T, U)' cannot be inferred from the usage. Try specifying the type arguments explicitly.` – Pac0 Nov 07 '19 at 07:45
  • And indeed, Linq is only a set of extension methods _per se_, though the language evolved a bit to allow at least partyl for this nice notation to exist (and this process is descibed quite pleasantely and in enough details in Jon Skeet's book, by the way ;) ). – Pac0 Nov 07 '19 at 07:47