using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
public sealed class Subquery
var dbContextOptionsBuilder = new DbContextOptionsBuilder<EfContext>();
dbContextOptionsBuilder.UseInMemoryDatabase(Guid.NewGuid().ToString());
using var context = new EfContext(dbContextOptionsBuilder.Options);
IQueryable<Product> productQueryable = context.Products;
IQueryable<OrderItem> orderItemQueryable = context.OrderItems;
var maxQueryLinq2Ef = productQueryable.Where(p => p.Id == orderItemQueryable.Max(i => i.ProductId));
var maxResultLinq2Ef = maxQueryLinq2Ef.ToList();
var productQueryableExp = Expression.Constant(productQueryable);
var orderItemQueryableExp = Expression.Constant(orderItemQueryable);
var whereMethod = typeof(System.Linq.Queryable)
.Where(m => m.Name == nameof(System.Linq.Queryable.Where))
var genericArguments = m.GetGenericArguments();
var parameters = m.GetParameters();
if (genericArguments.Length == 1 && parameters.Length == 2)
var entity = genericArguments[0];
var predicate = parameters[1];
var predicateType = typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(entity, typeof(bool)));
return predicate.ParameterType == predicateType;
.MakeGenericMethod(typeof(Product));
var toListMethod = typeof(System.Linq.Enumerable)
.GetMethod(nameof(System.Linq.Enumerable.ToList))
.MakeGenericMethod(typeof(Product));
var productParameterExp = Expression.Parameter(typeof(Product), "p");
var productIdProperty = typeof(Product).GetProperty(nameof(Product.Id));
var productIdExp = Expression.MakeMemberAccess(productParameterExp, productIdProperty);
var oneValueExp = Expression.Constant(105);
var compareOneExp = Expression.MakeBinary(ExpressionType.Equal, productIdExp, oneValueExp);
var lambdaOneExp = Expression.Lambda<Func<Product, bool>>(compareOneExp, productParameterExp);
var whereOneExp = Expression.Call(whereMethod, productQueryableExp, lambdaOneExp);
var oneToListExp = Expression.Call(toListMethod, whereOneExp);
var oneResult = Expression.Lambda(oneToListExp).Compile().DynamicInvoke();
var orderItemParameterExp = Expression.Parameter(typeof(OrderItem), "i");
var orderItemProductIdProperty = typeof(OrderItem).GetProperty(nameof(OrderItem.ProductId));
var orderItemProductIdExp = Expression.MakeMemberAccess(orderItemParameterExp, orderItemProductIdProperty);
var maxMethod = typeof(System.Linq.Queryable)
.Where(m => m.Name == nameof(System.Linq.Queryable.Max))
var genericArguments = m.GetGenericArguments();
var parameters = m.GetParameters();
if (genericArguments.Length == 2 && parameters.Length == 2)
var entityType = genericArguments[0];
var resultType = genericArguments[1];
var predicate = parameters[1];
var predicateType = typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(entityType, resultType));
return predicate.ParameterType == predicateType;
.MakeGenericMethod(typeof(OrderItem), typeof(int));
var lambdaOrderItemProcutIdExp = Expression.Lambda<Func<OrderItem, int>>(orderItemProductIdExp, orderItemParameterExp);
var maxExp = Expression.Call(maxMethod, orderItemQueryableExp, lambdaOrderItemProcutIdExp);
var compareMaxExp = Expression.MakeBinary(ExpressionType.Equal, productIdExp, maxExp);
var lambdaMaxExp = Expression.Lambda<Func<Product, bool>>(compareMaxExp, productParameterExp);
var whereMaxExp = Expression.Call(whereMethod, productQueryableExp, lambdaMaxExp);
var toListMaxExp = Expression.Call(toListMethod, whereMaxExp);
var maxResult = Expression.Lambda(toListMaxExp).Compile().DynamicInvoke();
public class EfContext : DbContext
public EfContext(DbContextOptions options)
protected override void OnModelCreating(ModelBuilder modelBuilder)
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Product>().HasKey(x => x.Id);
modelBuilder.Entity<OrderItem>().HasKey(x => x.Id);
public DbSet<Product> Products { get; init; }
public DbSet<OrderItem> OrderItems { get; init; }
public sealed record OrderItem
public int Id { get; init; }
public int ProductId { get; init; }
public int Quantity { get; set; }
public sealed record Product
public int Id { get; init; }
public string Name { get; set; }