C# 实现IQueryable包装器以转换结果对象

C# 实现IQueryable包装器以转换结果对象,c#,.net,linq,entity-framework,iqueryable,C#,.net,Linq,Entity Framework,Iqueryable,更新2013-08-22: 在看了“构建IQueryable提供程序系列”(感谢链接!)之后,我有了进一步的了解。我相应地更新了代码。但它仍然没有完全发挥作用。如果我正确理解本教程,则在请求多个元素时会调用GetEnumerator(例如,通过对queryable的ToList()调用或任何聚合函数)。因此,包装器的GetEnumerator实现所要做的就是调用提供者上的Execute,并传递queryable的表达式。在另一种情况下,如果只请求一个元素,则直接调用Execute。queryab

更新2013-08-22:

在看了“构建IQueryable提供程序系列”(感谢链接!)之后,我有了进一步的了解。我相应地更新了代码。但它仍然没有完全发挥作用。如果我正确理解本教程,则在请求多个元素时会调用
GetEnumerator
(例如,通过对queryable的
ToList()
调用或任何聚合函数)。因此,包装器的
GetEnumerator
实现所要做的就是调用提供者上的
Execute
,并传递queryable的表达式。在另一种情况下,如果只请求一个元素,则直接调用
Execute
。queryable的表达式还反映了它是针对单个元素还是针对多个元素。这是正确的吗

不幸的是,现在在对源查询提供程序调用
Execute
时,我收到一个InvalidOperationException,它说“序列包含多个元素”。这是什么意思?我只是传递表达式,没有任何翻译,因为上面提到的类型都是相同的。代码中带有
IEnumerable
的翻译位可能是不完整的,但现在我甚至没有说到这一点


我试图实现一个简单的IQueryable包装器,使用一个底层IQueryable作为数据源,为每个结果对象调用一个转换函数

我认为这将是相对琐碎的,因为包装器唯一要做的就是翻译。然而,我无法让我的实现工作

请看下面我得到的。对于某些查询,它正在工作,但我在某个点收到StackOverflowException InvalidOperationException。我想这是由于我的查询表和查询提供程序之间的循环关联造成的。但我不明白如何正确地实施这一点

这里是我的问题和想法:

一,。为什么iQueryTable有一个提供者,它又返回一个iQueryTable?这不是要求无限递归吗

二,。为什么仅仅实现IEnumerator还不够?例如,为什么FirstOrDefault不使用枚举器来获取元素?调试应用程序GetEnumerator()时,FirstOrDefault()没有在我的queryable上调用它

三,。由于枚举数并非在所有情况下都使用,因此调用转换函数的正确点在哪里?QueryProvider的执行方法似乎是正确的位置。但在某些情况下,我还需要枚举器中的翻译调用吗?更新:我知道我意识到我需要提供自己的
IEnumerable
实现,提供
translatingnumerator
,并从我的
Execute
方法返回此枚举。为了获取枚举器
GetEnumerator
调用
Execute
(见下文)。请求枚举数的LINQ代码似乎确保表达式实际返回一个
IEnumerable

代码的一些旁注:

  • 翻译源类型名为TDatabaseEntity,翻译目标类型名为TBusinessEntity

  • 我本质上提供了一个iQueryTable,它获取从底层iQueryTable检索到的结果对象,并将它们转换为TBusinessEntity类型的对象

  • 我知道这个表达也需要翻译。但是,我将此放在一边,因为在我的实际应用程序中,我对TBusinessEntity和TDatabaseEntity使用相同的类型,所以表达式可以直接传递

  • 尽管结果对象的类型相同,但仍然需要将其转换为其他实例。更新:我的翻译层已经在我的应用程序中工作,并且还负责相关的实体。这只是“实现一个IQueryable包装器”的事情,我一直在做

  • 恐怕整个实现都不正确——代码中的TODO只是我自己的注释

背景:我正在数据访问层中实现我自己对从DbContext接收的实体的分离,以防止我的业务层与实际实体接触——由于EF的一些缺陷和其他要求,我不能直接使用EF分离的实体

谢谢你的帮助

可查询的实现
内部类TranslatingQueryable:IQueryable
{
专用只读IQueryProvider\u提供程序;
私有只读可读取源;
内部TranslatingQueryable(TranslatingQueryProvider提供程序,IQueryable源)
{
Guard.ThrowIfArgumentNull(提供者,“提供者”);
Guard.ThrowIfArgumentNull(源,“源”);
_提供者=提供者;
_来源=来源;
}
内部TranslatingQueryable(Func translateFunc,IQueryable数据库Queryable)
:此(新TranslatingQueryProvider(translateFunc、databaseQueryable.Provider)、databaseQueryable)
{
}
公共IEnumerator GetEnumerator()
{
return((IEnumerable)Provider.Execute(Expression)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return((IEnumerable)Provider.Execute(Expression)).GetEnumerator();
}
公开表达
{
得到
{
返回_source.Expression;
}
}
公共类型ElementType
{
得到
{
返回类型(t业务实体);
}
}
公共IQueryProvider提供程序
{
得到
{
返回提供程序;
}
}
}
IQueryProvider实现
公共类TranslatingQueryProvider:IQueryProvider
{
私有只读函数_translateFunc;
专用只读IQueryProvider\u数据库查询提供程序;
公共TranslatingQueryProvider(Func translateFunc,IQueryProvider数据库QueryProvider)
{
internal class TranslatingQueryable<TDatabaseEntity, TBusinessEntity> : IQueryable<TBusinessEntity>
{
    private readonly IQueryProvider _provider;
    private readonly IQueryable<TDatabaseEntity> _source;

    internal TranslatingQueryable(TranslatingQueryProvider provider, IQueryable<TDatabaseEntity> source)
    {
        Guard.ThrowIfArgumentNull(provider, "provider");
        Guard.ThrowIfArgumentNull(source, "source");

        _provider = provider;
        _source = source;
    }

    internal TranslatingQueryable(Func<object, object> translateFunc, IQueryable<TDatabaseEntity> databaseQueryable)
        : this(new TranslatingQueryProvider(translateFunc, databaseQueryable.Provider), databaseQueryable)
    {
    }

    public IEnumerator<TBusinessEntity> GetEnumerator()
    {
        return ((IEnumerable<TBusinessEntity>)Provider.Execute(Expression)).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable)Provider.Execute(Expression)).GetEnumerator();
    }

    public Expression Expression
    {
        get
        {
            return _source.Expression;
        }
    }

    public Type ElementType
    {
        get
        {
            return typeof(TBusinessEntity);
        }
    }

    public IQueryProvider Provider
    {
        get
        {
            return _provider;
        }
    }
}
public class TranslatingQueryProvider : IQueryProvider
{
    private readonly Func<object, object> _translateFunc;
    private readonly IQueryProvider _databaseQueryProvider;

    public TranslatingQueryProvider(Func<object, object> translateFunc, IQueryProvider databaseQueryProvider)
    {
        _translateFunc = translateFunc;
        _databaseQueryProvider = databaseQueryProvider;
    }

    public IQueryable CreateQuery(Expression expression)
    {
        var databaseQueryable = _databaseQueryProvider.CreateQuery<object>(expression);

        return new TranslatingQueryable<object, object>(this, databaseQueryable);
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        var databaseQueryable = _databaseQueryProvider.CreateQuery<object>(expression);

        return new TranslatingQueryable<object, TElement>(this, databaseQueryable);
    }

    public object Execute(Expression expression)
    {
        return Execute<object>(expression);
    }

    public TResult Execute<TResult>(Expression expression)
    {
        // TODO This call throws an InvalidOperationException if an enumeration is requested
        var databaseResult = _databaseQueryProvider.Execute<TResult>(expression);

        var databaseEnumerable = databaseResult as IEnumerable;
        if (databaseEnumerable != null)
        {
            if (typeof(TResult).IsAssignableFrom(typeof(IEnumerable)))
            {
                throw new InvalidOperationException();
            }

            return (TResult)(object)new TranslatingEnumerable(databaseEnumerable, _translateFunc);
        }
        else
        {
            return (TResult)_translateFunc(databaseResult);
        }
    }

    private class TranslatingEnumerable : IEnumerable
    {
        private readonly TranslatingEnumerator _enumerator;

        public TranslatingEnumerable(IEnumerable databaseEnumerable, Func<object, object> translateFunc)
        {
            _enumerator = new TranslatingEnumerator(translateFunc, databaseEnumerable.GetEnumerator());
        }

        public IEnumerator GetEnumerator()
        {
            return _enumerator;
        }
    }
}
internal class TranslatingEnumerator : IEnumerator
{
    private readonly Func<object, object> _translateFunc;
    private readonly IEnumerator _databaseEnumerator;

    internal TranslatingEnumerator(Func<object, object> translateFunc, IEnumerator databaseEnumerator)
    {
        _translateFunc = translateFunc;
        _databaseEnumerator = databaseEnumerator;
    }

    public bool MoveNext()
    {
        return _databaseEnumerator.MoveNext();
    }

    public void Reset()
    {
        _databaseEnumerator.Reset();
    }

    public object Current
    {
        get
        {
            return _translateFunc(_databaseEnumerator.Current);
        }
    }

    object IEnumerator.Current
    {
        get
        {
            return Current;
        }
    }
}