C#实体框架:使用自定义IQueryable实现枚举实体的问题

C#实体框架:使用自定义IQueryable实现枚举实体的问题,c#,entity-framework,linq,iqueryable,C#,Entity Framework,Linq,Iqueryable,我使用.NETFramework4.8和EntityFramework6.2 我有一个IOrderedQueryable实现: public sealed class EFQueryable<T> : IOrderedQueryable<T>, IDbAsyncEnumerable<T> { private static readonly Type _type = typeof(T); private readonly IEFQueryProv

我使用.NETFramework4.8和EntityFramework6.2

我有一个
IOrderedQueryable
实现:

public sealed class EFQueryable<T> : IOrderedQueryable<T>, IDbAsyncEnumerable<T>
{
    private static readonly Type _type = typeof(T);
    private readonly IEFQueryProvider _provider;

    public Type ElementType => _type;

    public Expression Expression { get; }

    public IQueryProvider Provider => _provider;

    public EFQueryable(IEFQueryProvider queryProvider)
    {
        _provider = queryProvider;
        Expression = Expression.Constant(this);
    }

    public EFQueryable(IEFQueryProvider queryProvider, Expression expression)
    {
        _provider = queryProvider;
        Expression = expression;
    }

    public IEnumerator<T> GetEnumerator() => _provider.GetEnumerator<T>(Expression);

    IEnumerator IEnumerable.GetEnumerator() => _provider.GetEnumerator(Expression);
}
public class EFQueryProvider<TEntity> : IEFQueryProvider where TEntity : class
{
    private readonly IDbContextFactory _dbContextFactory;

    public EFQueryProvider(IDbContextFactory dbContextFactory)
    {
        _dbContextFactory = dbContextFactory;
    }

    public IQueryable CreateQuery(Expression expression)
    {
        Type elementType = null;

        try
        {
            elementType = expression.Type.GetGenericArguments().First();
            return (IQueryable)Activator.CreateInstance(typeof(EFQueryable<>).MakeGenericType(elementType),
                BindingFlags.Instance | BindingFlags.NonPublic,
                null,
                new object[] { this, expression },
                null,
                null);
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to create instance of 'RsQueryable<{(elementType ?? expression.Type).Name}>'. {ex.Message}", ex);
        }
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        try
        {
            return new EFQueryable<TElement>(this, expression);
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to create instance of '{typeof(EFQueryable<TElement>).Name}'. {ex.Message}", ex);
        }
    }

    public object Execute(Expression expression)
    {
        try
        {
            using (var db = _dbContextFactory.CreateDbContext())
            {
                var queryable = db.Set<TEntity>().AsNoTracking();
                var convertedExpression = ChangeQueryableInExpression(expression, queryable);

                return queryable.Provider.Execute(convertedExpression);
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to query repository. {ex.Message}", ex);
        }
    }

    public TResult Execute<TResult>(Expression expression)
    {
        try
        {
            using (var db = _dbContextFactory.CreateDbContext())
            {
                var queryable = db.Set<TEntity>().AsNoTracking();
                var convertedExpression = ChangeQueryableInExpression(expression, queryable);

                return queryable.Provider.Execute<TResult>(convertedExpression);
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to query repository. {ex.Message}", ex);
        }
    }

    public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)
    {
        return Task.Run(() => Execute(expression), cancellationToken);
    }

    public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
    {
        return Task.Run(() => Execute<TResult>(expression), cancellationToken);
    }

    public IEnumerator<T> GetEnumerator<T>(Expression expression)
    {
        try
        {
            using (var db = _dbContextFactory.CreateDbContext())
            {
                var queryable = db.Set<TEntity>().AsNoTracking();
                var convertedExpression = ChangeQueryableInExpression(expression, queryable);
                return queryable.Provider.Execute<IEnumerable<T>>(convertedExpression).ToList().GetEnumerator();
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to query repository. {ex.Message}", ex);
        }
    }

    public IEnumerator GetEnumerator(Expression expression)
    {
        try
        {
            using (var db = _dbContextFactory.CreateDbContext())
            {
                var queryable = db.Set<TEntity>().AsNoTracking();
                var convertedExpression = ChangeQueryableInExpression(expression, queryable);

                return queryable.Provider.Execute<IEnumerable>(convertedExpression).Cast<object>().ToList().GetEnumerator();
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to query repository. {ex.Message}", ex);
        }
    }

    private static Expression ChangeQueryableInExpression(Expression expression, IQueryable<TEntity> queryable)
    {
        var visitor = new ExpressionQueryableSourceModifier(queryable.Expression);
        return visitor.Visit(expression);
    }

    private sealed class ExpressionQueryableSourceModifier : ExpressionVisitor
    {
        private readonly Expression _expression;
        private static readonly Type _queryableTypeToReplace = typeof(EFQueryable<TEntity>);

        public ExpressionQueryableSourceModifier(Expression expression)
        {
            _expression = expression;
        }

        protected override Expression VisitConstant(ConstantExpression node)
        {
            return node.Type == _queryableTypeToReplace ? _expression : base.VisitConstant(node);
        }
    }
}
及其实施:

public sealed class EFQueryable<T> : IOrderedQueryable<T>, IDbAsyncEnumerable<T>
{
    private static readonly Type _type = typeof(T);
    private readonly IEFQueryProvider _provider;

    public Type ElementType => _type;

    public Expression Expression { get; }

    public IQueryProvider Provider => _provider;

    public EFQueryable(IEFQueryProvider queryProvider)
    {
        _provider = queryProvider;
        Expression = Expression.Constant(this);
    }

    public EFQueryable(IEFQueryProvider queryProvider, Expression expression)
    {
        _provider = queryProvider;
        Expression = expression;
    }

    public IEnumerator<T> GetEnumerator() => _provider.GetEnumerator<T>(Expression);

    IEnumerator IEnumerable.GetEnumerator() => _provider.GetEnumerator(Expression);
}
public class EFQueryProvider<TEntity> : IEFQueryProvider where TEntity : class
{
    private readonly IDbContextFactory _dbContextFactory;

    public EFQueryProvider(IDbContextFactory dbContextFactory)
    {
        _dbContextFactory = dbContextFactory;
    }

    public IQueryable CreateQuery(Expression expression)
    {
        Type elementType = null;

        try
        {
            elementType = expression.Type.GetGenericArguments().First();
            return (IQueryable)Activator.CreateInstance(typeof(EFQueryable<>).MakeGenericType(elementType),
                BindingFlags.Instance | BindingFlags.NonPublic,
                null,
                new object[] { this, expression },
                null,
                null);
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to create instance of 'RsQueryable<{(elementType ?? expression.Type).Name}>'. {ex.Message}", ex);
        }
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        try
        {
            return new EFQueryable<TElement>(this, expression);
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to create instance of '{typeof(EFQueryable<TElement>).Name}'. {ex.Message}", ex);
        }
    }

    public object Execute(Expression expression)
    {
        try
        {
            using (var db = _dbContextFactory.CreateDbContext())
            {
                var queryable = db.Set<TEntity>().AsNoTracking();
                var convertedExpression = ChangeQueryableInExpression(expression, queryable);

                return queryable.Provider.Execute(convertedExpression);
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to query repository. {ex.Message}", ex);
        }
    }

    public TResult Execute<TResult>(Expression expression)
    {
        try
        {
            using (var db = _dbContextFactory.CreateDbContext())
            {
                var queryable = db.Set<TEntity>().AsNoTracking();
                var convertedExpression = ChangeQueryableInExpression(expression, queryable);

                return queryable.Provider.Execute<TResult>(convertedExpression);
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to query repository. {ex.Message}", ex);
        }
    }

    public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)
    {
        return Task.Run(() => Execute(expression), cancellationToken);
    }

    public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
    {
        return Task.Run(() => Execute<TResult>(expression), cancellationToken);
    }

    public IEnumerator<T> GetEnumerator<T>(Expression expression)
    {
        try
        {
            using (var db = _dbContextFactory.CreateDbContext())
            {
                var queryable = db.Set<TEntity>().AsNoTracking();
                var convertedExpression = ChangeQueryableInExpression(expression, queryable);
                return queryable.Provider.Execute<IEnumerable<T>>(convertedExpression).ToList().GetEnumerator();
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to query repository. {ex.Message}", ex);
        }
    }

    public IEnumerator GetEnumerator(Expression expression)
    {
        try
        {
            using (var db = _dbContextFactory.CreateDbContext())
            {
                var queryable = db.Set<TEntity>().AsNoTracking();
                var convertedExpression = ChangeQueryableInExpression(expression, queryable);

                return queryable.Provider.Execute<IEnumerable>(convertedExpression).Cast<object>().ToList().GetEnumerator();
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Unable to query repository. {ex.Message}", ex);
        }
    }

    private static Expression ChangeQueryableInExpression(Expression expression, IQueryable<TEntity> queryable)
    {
        var visitor = new ExpressionQueryableSourceModifier(queryable.Expression);
        return visitor.Visit(expression);
    }

    private sealed class ExpressionQueryableSourceModifier : ExpressionVisitor
    {
        private readonly Expression _expression;
        private static readonly Type _queryableTypeToReplace = typeof(EFQueryable<TEntity>);

        public ExpressionQueryableSourceModifier(Expression expression)
        {
            _expression = expression;
        }

        protected override Expression VisitConstant(ConstantExpression node)
        {
            return node.Type == _queryableTypeToReplace ? _expression : base.VisitConstant(node);
        }
    }
}
这是:

public IQueryable<TEntity> AsQueryable(Expression<Func<TEntity, bool>> where)
{
    return AsQueryable().Where(where);
}
SingleOrDefaultAsync
工作非常好。但是
ToList
甚至
ToListAsync
都不需要。我得到以下错误:

从物化的“Entities.Domain.Config”类型到“System.Collections.Generic.IEnumerable`1[Entities.Domain.Config]”类型的指定强制转换无效

你能帮我解决我的问题吗?我真的需要用那种方式使用AsQueryable

我想这可能是我的问题

Expression = Expression.Constant(this);
EFQueryable
构造函数中。但是我能用这个做什么呢


对于
AsQueryable().Where(expression)
我可以创建自己的扩展方法,但我不能为每个特定的用例创建扩展方法。我非常感谢任何帮助,使我的实施工作

有些奇怪。通过这个IQueryable实现,您想要实现什么?问这个问题是因为我知道这方面的几乎所有知识,并且没有看到这一实现探索的好处。@SvyatoslavDanyliv我在桌面上有很多遗留代码,它们使用DbContext作为存储库的构造函数参数。在进行一些小更改时,我需要重新编写通用存储库,因此它将DbContextFactory作为依赖项,而不是DbContext。遗留存储库具有这种奇怪的特性,可以将逻辑暴露在存储库之外。我现在不会写这样的代码,但我不能简单地重写数千行代码。所以我需要让它工作,我在.NET5和EFCore上尝试了相同的实现。它确实有效。所以问题在于我在.NETFramework应用程序中使用的EF6
Expression = Expression.Constant(this);