Creating an IQueryable Custom Class

I am working with the TFS API for VS2010 and had to request a FieldCollection, which I found was not supported by LINQ, so I wanted to create my own class to make the field and FieldCollection available for LINQ, so I found the base template and tried to implement it

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 } 

It appeared for compilation and was recognized by LINQ, but I continue to receive an error in the CreateQuery method, because it is passed in a string, not in a field

  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; } 

here is the Linq query I'm using ... columnFilterList is a List and fields are my own FieldCollection class as shown above.

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

.... I'm sure this is a simple mistake ... can someone tell me what I'm doing wrong ... thanks

+6
source share
2 answers

If you want the object to be able to use LINQ, do IEnumerable<T> . IQueryable<T> is redundant for LINQ for objects. It is designed to convert expressions to another form.

Or if you want, you can do it

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

In your case, wrapping and building a class around an existing IEnumerable collection type, i.e. List<Field> ,

you can simply use the set of "forward functions" that open the IQueryable<Field> interface:

 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 ... } 

Now you can directly request your WorkItemFieldCollection :

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

Source: https://habr.com/ru/post/954500/


All Articles