制作自定义类IQueryable

时间:2013-09-24 12:50:25

标签: c# linq

我一直在使用VS2010的TFS API,并且不得不查询我发现LINQ不支持的FieldCollection,所以我想创建一个自定义类,使LINQ可以查询Field和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,字段是我的自定义FieldCollection类,见上文。

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

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

2 个答案:

答案 0 :(得分:4)

如果希望LINQ可以使用对象,请实现IEnumerable<T>。 LINQ to Objects对IQueryable<T>来说太过分了。它旨在将表达式转换为另一种形式。

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

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

答案 1 :(得分:0)

在围绕现有IEnumerable Collection类型(即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 );