使自定义类 IQueryable

本文关键字:IQueryable 自定义 | 更新日期: 2023-09-27 18:32:39

我一直在使用VS2010的TFS API,并且不得不查询FieldCollection,我发现LINQ不支持它,所以我想创建一个自定义类以使LINQ可以查询字段和FieldCollection,所以我找到了一个基本模板并尝试实现它

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
public class WorkItemFieldCollection : IQueryable<Field>, IQueryProvider
{
    private List<Field> _fieldList = new List<Field>();
    #region Constructors
    /// <summary>
    /// This constructor is called by the client to create the data source.
    /// </summary>
    public WorkItemFieldCollection(FieldCollection fieldCollection)
    {
        foreach (Field field in fieldCollection)
        {
            _fieldList.Add(field);
        }
    }
    #endregion Constructors
    #region IQueryable Members
    Type IQueryable.ElementType
    {
        get { return typeof(Field); }
    }
    System.Linq.Expressions.Expression IQueryable.Expression
    {
        get { return Expression.Constant(this); }
    }
    IQueryProvider IQueryable.Provider
    {
        get { return this; }
    }
    #endregion IQueryable Members
    #region IEnumerable<Field> Members
    IEnumerator<Field> IEnumerable<Field>.GetEnumerator()
    {
        return (this as IQueryable).Provider.Execute<IEnumerator<Field>>(_expression);
    }
    private IList<Field> _field = new List<Field>();
    private Expression _expression = null;
    #endregion IEnumerable<Field> Members
    #region IEnumerable Members
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return (IEnumerator<Field>)(this as IQueryable).GetEnumerator();
    }
    private void ProcessExpression(Expression expression)
    {
        if (expression.NodeType == ExpressionType.Equal)
        {
            ProcessEqualResult((BinaryExpression)expression);
        }
        if (expression is UnaryExpression)
        {
            UnaryExpression uExp = expression as UnaryExpression;
            ProcessExpression(uExp.Operand);
        }
        else if (expression is LambdaExpression)
        {
            ProcessExpression(((LambdaExpression)expression).Body);
        }
        else if (expression is ParameterExpression)
        {
            if (((ParameterExpression)expression).Type == typeof(Field))
            {
                _field = GetFields();
            }
        }
    }
    private void ProcessEqualResult(BinaryExpression expression)
    {
        if (expression.Right.NodeType == ExpressionType.Constant)
        {
            string name = (String)((ConstantExpression)expression.Right).Value;
            ProceesItem(name);
        }
    }
    private void ProceesItem(string name)
    {
        IList<Field> filtered = new List<Field>();
        foreach (Field field in GetFields())
        {
            if (string.Compare(field.Name, name, true) == 0)
            {
                filtered.Add(field);
            }
        }
        _field = filtered;
    }
    private object GetValue(BinaryExpression expression)
    {
        if (expression.Right.NodeType == ExpressionType.Constant)
        {
            return ((ConstantExpression)expression.Right).Value;
        }
        return null;
    }
    private IList<Field> GetFields()
    {
        return _fieldList;
    }
    #endregion IEnumerable Members
    #region IQueryProvider Members
    IQueryable<S> IQueryProvider.CreateQuery<S>(System.Linq.Expressions.Expression expression)
    {
        if (typeof(S) != typeof(Field))
            throw new Exception("Only " + typeof(Field).FullName + " objects are supported.");
        this._expression = expression;
        return (IQueryable<S>)this;
    }
    IQueryable IQueryProvider.CreateQuery(System.Linq.Expressions.Expression expression)
    {
        return (IQueryable<Field>)(this as IQueryProvider).CreateQuery<Field>(expression);
    }
    TResult IQueryProvider.Execute<TResult>(System.Linq.Expressions.Expression expression)
    {
        MethodCallExpression methodcall = _expression as MethodCallExpression;
        foreach (var param in methodcall.Arguments)
        {
            ProcessExpression(param);
        }
        return (TResult)_field.GetEnumerator();
    }
    object IQueryProvider.Execute(System.Linq.Expressions.Expression expression)
    {
        return (this as IQueryProvider).Execute<IEnumerator<Field>>(expression);
    }
    #endregion IQueryProvider Members
}

它似乎可以编译并被 LINQ 识别,但我在 CreateQuery 方法中不断收到错误,因为它传入字符串而不是字段

    IQueryable<S> IQueryProvider.CreateQuery<S>(System.Linq.Expressions.Expression expression)
    {
        if (typeof(S) != typeof(Field))
            throw new Exception("Only " + typeof(Field).FullName + " objects are supported.");
        this._expression = expression;
        return (IQueryable<S>)this;
    }

这是我使用的 Linq 查询...columnFilterList 是 List,fields 是我的自定义 FieldCollection 类,见上文。

  foreach (var name in columnFilterList)
   {
        var fieldName = (from x in fields where x.Name == name select x.Name).First
   }

....我确定这是一个简单的错误...有人可以告诉我我做错了什么...谢谢

使自定义类 IQueryable

如果您希望某个对象可供 LINQ 使用,请实现 IEnumerable<T>IQueryable<T>对于 LINQ to Objects 来说是矫枉过正的。它旨在将表达式转换为另一种形式。

或者如果你愿意,你可以这样做

FieldCollection someFieldCollection = ...
IEnumerable<Field> fields = someFieldCollections.Cast<Field>();

在围绕现有 IEnumerable 集合类型包装和构建类的情况下,即 List<Field>

您可能只使用一组公开IQueryable<Field>接口的"转发函数"包装器:

public class WorkItemFieldCollection : IEnumerable<Field>, IQueryable<Field>
{
    ...
    #region Implementation of IQueryable<Field>
    public Type ElementType
    {
        get
        {
            return this._fieldList.AsQueryable().ElementType;
        }
    }
    public Expression Expression
    {
        get
        {
            return this._fieldList.AsQueryable().Expression;
        }
    }
    public IQueryProvider Provider
    {
        get
        {
            return this._fieldList.AsQueryable().Provider;
        }
    }
    #endregion
    ...
}

您现在可以直接查询您的WorkItemFieldCollection

var workItemFieldCollection = new WorkItemFieldCollection(...);
var Fuzz = "someStringId";
var workFieldItem = workItemFieldCollection.FirstOrDefault( c => c.Buzz == Fuzz );