gpt4 book ai didi

c# - 实现IQueryable包装器来转换结果对象

转载 作者:可可西里 更新时间:2023-11-01 03:11:08 26 4
gpt4 key购买 nike

更新2013-08-22:

看完“构建IQueryable提供程序系列”(感谢链接!)之后,我走了一些。我相应地更新了代码。它仍然没有完全正常工作。如果我正确理解了本教程,则在请求多个元素的情况下会调用GetEnumerator(例如,通过可查询的ToList()调用或任何聚合函数)。因此,包装程序的所有GetEnumerator实现都需要在提供程序上调用Execute并传递可查询对象的表达式。在其他情况下,如果仅请求单个元素,则直接调用Execute。查询对象的表达式还反射(reflect)了它是用于单个还是多个元素。这样对吗?

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

我正在尝试使用一个简单的IQueryable作为数据源来实现一个简单的IQueryable包装器,该IQueryable包装器为每个结果对象调用转换函数。

我认为这将是相对琐碎的,因为包装程序唯一要做的就是翻译。但是我无法实现我的实现。

到目前为止,请参阅下文。对于某些查询,它可以正常工作,但有时会收到StackOverflowException InvalidOperationException。我猜这是由于我的可查询对象和我的查询提供者之间的周期性关联而发生的。但是我不明白如何正确实现这一点。

这里是我的问题和想法:

1.为什么IQueryable具有提供者,而该提供者又又返回IQueryable?这不是要求无限递归吗?

2.为什么仅仅实现IEnumerator还不够?例如,为什么FirstOrDefault不使用枚举器获取元素?当我调试应用程序GetInumerator()时,FirstOrDefault()并未在我的可查询对象上调用它。

3.由于不是在每种情况下都使用枚举器,因此调用翻译函数的正确点在哪里? QueryProvider的Execute方法似乎是正确的地方。但是在某些情况下,我仍然需要Enumerator中的转换调用吗?更新:我知道我需要提供自己的IEnumerable实现并提供TranslatingEnumerator,并从Execute方法返回此可枚举的值。为了获得枚举器,GetEnumerator调用Execute(请参见下文)。请求枚举数的LINQ代码似乎确保该表达式实际上返回IEnumerable

代码的一些说明:

  • 转换源类型名为TDatabaseEntity,转换目标类型名为TBusinessEntity。
  • 我实质上是在提供一个IQueryable,该IQueryable将从底层IQueryable检索到的结果对象转换为TBusinessEntity类型的对象。
  • 我知道表达式也需要翻译。但是我将其搁置一旁,因为在我的实际应用程序中,我对TBusinessEntity和TDatabaseEntity使用相同的类型,因此可以直接传递Expression。
  • 尽管结果对象的类型相同,但仍然需要将其转换为其他实例。更新:我的翻译层已经在我的应用程序中正常工作,并且还负责相关实体。这只是我所坚持的“实现一个IQueryable包装器”。
  • 恐怕整个实现都不正确-代码中的TODO只是我自己的注释。

  • 背景:我正在实现自己从数据访问层中从DbContext接收到的实体的分离,以防止我的业务层与实际实体联系-由于EF的一些错误和我可以做到的其他要求, t直接使用EF分离实体。

    谢谢你的帮助!

    可查询的实现
    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;
    }
    }
    }

    IQueryProvider的实现
    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;
    }
    }
    }

    IEnumerator的实现
    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;
    }
    }
    }

    最佳答案

    好吧,这是我最好的回答

    Why does the IQueryable have a Provider which in turn returns an IQueryable again? Doesn't this call for endless recursion? You want to return an IQueryable for this instance


    SomeEnumerable.Where(x=>x.Field == something).Select(x=>x.SomeOtherField) 如果您熟悉链接,请考虑使用JQuery

    Why is it not enough to implement IEnumerator? Why does FirstOrDefault for instance not use the enumerator to get the element? When I debugged the application GetEnumerator() was not called by FirstOrDefault() on my queryable.



    由于IQueryable具有2个特殊属性,查询提供程序和查询表达式:

    What is the difference between IQueryable<T> and IEnumerable<T>?

    Since the enumerator is not used in every case, where is the correct point to call the translation function? The Execute-methods of the QueryProvider seemed to be the right place. But do I still need the translation call in the Enumerator for some cases?



    Execute是正确的位置,您将不得不解析表达式树,因为提供者不知道执行时该做什么。

    我还添加了此链接,当我实现自己的查询提供程序时,该链接对我有极大帮助
    http://blogs.msdn.com/b/mattwar/archive/2008/11/18/linq-links.aspx

    您可能能够避免的是使用本文中作者使用dbReader并将其转换为实际对象的相同方法,但是不是读者使用您的DBEntity并将其转换为您的BusinessEntity,但是我不确定是否这个有可能。因为每次添加linq子句(Select,Where ...),它都会创建该返回类型的新查询,因此,如果您有一个DBEntity“实体”的实体,并且您执行了objects.Select(x => x.someField)并且某些字段是int类型的,它现在是IQueryable,现在您的模型不起作用,因为它期望int并且正在获取DBEntitity

    关于c# - 实现IQueryable包装器来转换结果对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18365764/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com