I have been working with the TFS API for VS2010 and had to query FieldCollection which I found isn't supported by LINQ so I wanted to create a custom class to make the Field and FieldCollection queryable by LINQ so I found a basic 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 to compile and was recognized by LINQ but i keep getting an error in the CreateQuery method because it passes in string and not 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 use... columnFilterList is List and fields is my custom FieldCollection class see above.
foreach (var name in columnFilterList)
{
var fieldName = (from x in fields where x.Name == name select x.Name).First
}
....I sure it is a simple mistake...could someone tell me what I am doing wrong...thanks
If you want an object to be usable by LINQ, implement IEnumerable<T>
. IQueryable<T>
is overkill for LINQ to Objects. It is designed for converting the expressions into another form.
Or if you want, you can do this
FieldCollection someFieldCollection = ...
IEnumerable<Field> fields = someFieldCollections.Cast<Field>();
In your case of wrapping and building a class around an existing IEnumerable Collection type i.e. List<Field>
,
you might just use a set of "forward function" wrappers that expose 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
...
}
You can now directly query your WorkItemFieldCollection
:
var workItemFieldCollection = new WorkItemFieldCollection(...);
var Fuzz = "someStringId";
var workFieldItem = workItemFieldCollection.FirstOrDefault( c => c.Buzz == Fuzz );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With