Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataWeb / Server / System / Data / Services / Providers / BasicExpandProvider.cs / 1 / BasicExpandProvider.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Provides a helper class to implement $expand functionality
// with filters by rewriting queries and implementing custom
// result enumerators.
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services.Providers
{
#region Namespaces.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Services.Client;
using System.Data.Services.Internal;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
#endregion Namespaces.
///
/// Provides a helper class to implement $expand functionality
/// with filters by rewriting queries and implementing custom
/// result enumerators.
///
internal class BasicExpandProvider : IExpandProvider
{
#region Private fields.
/// Whether all values are automatically expanded in the model.
private readonly bool expanded;
/// Full provider.
private readonly IDataServiceProvider provider;
#endregion Private fields.
/// Initializes a new instance.
/// Full provider.
/// Whether all values are automatically expanded in the model.
internal BasicExpandProvider(IDataServiceProvider provider, bool expanded)
{
Debug.Assert(provider != null, "provider != null");
this.provider = provider;
this.expanded = expanded;
}
/// Provider for metadata.
internal IDataServiceProvider Provider
{
get { return this.provider; }
}
/// Applies expansions to the specified .
/// object to expand.
/// A list of paths to expand.
///
/// An object of the same type as the given ,
/// with the results including the specified .
///
///
/// This method may modify the to indicate which expansions
/// are included.
///
/// The returned may implement the
/// interface to provide enumerable objects for the expansions; otherwise, the expanded
/// information is expected to be found directly in the enumerated objects.
///
public IEnumerable ApplyExpansions(IQueryable queryable, ICollection expandPaths)
{
ExpandSegmentNode root = ExpandSegmentNode.CollectExpandPathsIntoTree(this, expandPaths);
root.AssignTypeForExpected(queryable.ElementType, false /* singleResult */);
IQueryable query = root.BuildProjectionQuery(queryable);
Type resultEnumerable = typeof(ExpandedEnumerable<,>).MakeGenericType(queryable.ElementType, query.ElementType);
object[] args = new object[] { query };
return (IEnumerable)Activator.CreateInstance(resultEnumerable, args);
}
#region Inner types.
/// Enumerable element for results with expanded properties.
/// Type for enumeration.
/// Wrapper type with expanded properties.
internal class ExpandedEnumerable : IEnumerable where TWrapper : IExpandedResult
{
/// Source enumeration with wrapped properties.
private IEnumerable source;
/// Initializes a new ExpandedEnumerable instance.
/// Source for enumeration.
public ExpandedEnumerable(IEnumerable source)
{
Debug.Assert(source != null, "source != null");
this.source = source;
}
/// Gets an enumerator object for results.
/// An enumerator object for results.
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
/// Gets an enumerator object for results.
/// An enumerator object for results.
public IEnumerator GetEnumerator()
{
IEnumerator enumerator = this.source.GetEnumerator();
return new ExpandedEnumerator(enumerator);
}
}
/// Use this class to enumerate elements that can be expanded.
/// Type for enumeration.
/// Wrapper type with expanded properties.
internal class ExpandedEnumerator : IEnumerator, IExpandedResult where TWrapper : IExpandedResult
{
/// Enumerator for wrapper instances.
private IEnumerator e;
/// Initializes a new ExpandedEnumerator instance.
/// Source for enumeration.
internal ExpandedEnumerator(IEnumerator enumerator)
{
WebUtil.CheckArgumentNull(enumerator, "enumerator");
this.e = enumerator;
}
/// Element with expanded properties.
public object ExpandedElement
{
get { return this.Current; }
}
/// Element with expanded properties.
public TResult Current
{
get { return (TResult)this.e.Current.ExpandedElement; }
}
/// Element with expanded properties.
object IEnumerator.Current
{
get { return this.Current; }
}
/// Gets an expanded property for the specified .
/// Name of property to return.
/// The expanded property value with the specified .
public object GetExpandedPropertyValue(string name)
{
return this.e.Current.GetExpandedPropertyValue(name);
}
/// Moves to the next element.
/// true if an element is available after the move; false otherwise.
public bool MoveNext()
{
return this.e.MoveNext();
}
/// Resets the enumerator to the beginning of the sequence.
public void Reset()
{
throw Error.NotImplemented();
}
/// Releases resources.
public void Dispose()
{
this.e.Dispose();
}
}
/// Use this class to build a tree structure over a list of paths.
[DebuggerDisplay("ExpandSegmentNode {Segment}")]
internal class ExpandSegmentNode
{
#region Private fields.
/// Expansion segment for this node.
private readonly ExpandSegment Segment;
/// Provider with metadata for operations.
private readonly BasicExpandProvider ExpandProvider;
/// All child nodes for this segment.
private List children;
/// Child nodes that have wrappers.
private List childrenWithWrappers;
/// Child nodes that have filters.
private List childrenWithFilters;
/// Child nodes that don't have wrappers.
private List childrenWithoutWrappers;
/// The element type that will be projected from this segment.
private Type elementType;
///
/// The model (never wrapped) type to be used in an strongly-typed IEnumerable for this segment.
///
private Type enumeratedType;
/// Whether there are any filters within this node. MaxResults are considered filters for processing.
private bool hasFilterWithin;
/// Whether this segment represents a single element rather than a collection.
private bool singleResult;
/// Whether this segment should be projected into a wrapper.
///
/// Segments require a wrapper because the model doesn't auto-wire
/// up (as in the EDM case), or because they have children with
/// filters (and thus they need to project filtered children),
/// or because they have wrapped children (and thus they need
/// to project the custom projected type).
///
private bool requiresWrapper;
#endregion Private fields.
/// Initalizes a new .
/// Segment being described, possibly null.
/// Provider for expansion flags and metadata.
internal ExpandSegmentNode(ExpandSegment segment, BasicExpandProvider provider)
{
this.Segment = segment;
this.ExpandProvider = provider;
}
/// Type to be projected for this node in the model (possibly wrapped).
internal Type ProjectedType
{
get
{
if (this.singleResult)
{
return this.elementType;
}
else
{
return typeof(IEnumerable<>).MakeGenericType(this.elementType);
}
}
}
/// Description to initialize the wrapper with.
internal string WrapperDescription
{
get
{
StringBuilder result = new StringBuilder();
foreach (var child in this.children)
{
if (result.Length > 0)
{
result.Append(',');
}
result.Append(child.Segment.Name);
}
return result.ToString();
}
}
/// Builds a tree of items for the specified paths.
/// Expand provider for nodes.
/// Paths to expand.
/// The root of the path trees.
internal static ExpandSegmentNode CollectExpandPathsIntoTree(
BasicExpandProvider provider,
ICollection expandPaths)
{
Debug.Assert(provider != null, "provider != null");
Debug.Assert(expandPaths != null, "expandPaths != null");
ExpandSegmentNode root = new ExpandSegmentNode(null, provider);
foreach (ExpandSegmentCollection expandPath in expandPaths)
{
// Add each path to the root.
ExpandSegmentNode node = root;
foreach (ExpandSegment segment in expandPath)
{
ExpandSegmentNode child = node.FindChildByName(segment.Name);
if (child == null)
{
child = new ExpandSegmentNode(segment, provider);
node.AddChild(child);
}
node = child;
}
}
return root;
}
/// Adds a child node to this segment node.
/// Child to add.
internal void AddChild(ExpandSegmentNode child)
{
Debug.Assert(child != null, "child != null");
if (this.children == null)
{
this.children = new List(1);
}
this.children.Add(child);
}
/// ----ings type information given the specified type.
/// Type expected for enumeration.
/// true if a single result is expected of this node; false otherwise.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "enumeratedType", Justification = "1:1 mapping")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "singleResult", Justification = "1:1 mapping")]
internal void AssignTypeForExpected(Type enumeratedType, bool singleResult)
{
this.enumeratedType = enumeratedType;
this.singleResult = singleResult;
if (this.children == null)
{
this.elementType = this.enumeratedType;
this.hasFilterWithin = this.Segment.HasFilterOrMaxResults;
}
else
{
this.childrenWithWrappers = new List();
this.childrenWithoutWrappers = new List();
this.childrenWithFilters = new List();
foreach (ExpandSegmentNode child in this.children)
{
Debug.Assert(child.Segment != null, "child.Segment != null -- only the root has a null segment");
string name = child.Segment.Name;
ResourceProperty property = this.ExpandProvider.Provider.TryResolvePropertyName(enumeratedType, name);
Debug.Assert(property != null, "property != null -- otherwise this shouldn't have passed validation");
Type segmentType = property.ResourceType.Type;
child.AssignTypeForExpected(segmentType, property.Kind != ResourcePropertyKind.ResourceSetReference);
if (child.requiresWrapper)
{
this.childrenWithWrappers.Add(child);
}
else
{
this.childrenWithoutWrappers.Add(child);
}
if (child.hasFilterWithin)
{
this.childrenWithFilters.Add(child);
}
}
this.hasFilterWithin =
(this.Segment != null && this.Segment.HasFilterOrMaxResults) ||
this.childrenWithFilters.Count > 0;
this.requiresWrapper =
this.childrenWithWrappers.Count > 0 || // We wrap to project custom types.
this.childrenWithFilters.Count > 0 || // We wrap to project filtered results.
!this.ExpandProvider.expanded; // We wrap to implement expansion.
if (this.requiresWrapper)
{
this.SetWrapperElementType();
}
else
{
this.elementType = this.enumeratedType;
}
}
}
/// Finds a child node by name.
/// Name of node to find.
/// The child node if found; null otherwise.
internal ExpandSegmentNode FindChildByName(string name)
{
Debug.Assert(name != null, "name != null");
if (this.children != null)
{
foreach (var c in this.children)
{
if (c.Segment.Name == name)
{
return c;
}
}
}
return null;
}
/// Builds the projection to apply expansion for the specified .
/// Query to build for.
/// A new query with the expansions performed inside.
internal IQueryable BuildProjectionQuery(IQueryable query)
{
Debug.Assert(query != null, "query != null");
Debug.Assert(this.Segment == null, "this.Segment == null -- otherwise BuildProjectionQuery is being called on a non-root node.");
MethodCallExpression call = (MethodCallExpression)this.BuildProjectionExpression(query.Expression);
Debug.Assert(
call.Method.Name == "Select",
call.Method.Name + " == 'Select' -- otherwise last expression must be a .Select projection.");
LambdaExpression selector = (LambdaExpression)call.Arguments[1];
return RequestUriProcessor.InvokeSelectForTypes(query, this.elementType, selector);
}
///
/// Creates a LambdaExpression and can be used when the delegate type is not known at compile time.
///
/// A Type that represents a delegate type.
/// An Expression to set the Body property equal to.
/// An IEnumerable<T> that contains ParameterExpression objects to use to populate the Parameters collection.
///
/// An object that represents a lambda expression which has the NodeType property equal to Lambda and the
/// Body and Parameters properties set to the specified values.
///
private static LambdaExpression BuildLambdaExpression(Type delegateType, Expression body, IEnumerable parameters)
{
Debug.Assert(delegateType != null, "delegateType != null");
Debug.Assert(body != null, "body != null");
Type[] lambdaTypes = new Type[] { typeof(Expression), typeof(IEnumerable) };
MethodInfo methodInfo = typeof(Expression).GetMethod("Lambda", BindingFlags.Public | BindingFlags.Static, null, lambdaTypes, null);
methodInfo = methodInfo.MakeGenericMethod(new Type[] { delegateType });
return (LambdaExpression)methodInfo.Invoke(null, new object[] { body, parameters });
}
/// Checks whether the specified method is the IEnumerable.Select() with Func`T,T2.
/// Method to check.
/// true if this is the method; false otherwise.
private static bool IsMethodEnumerableSelect(MethodInfo m)
{
return IsNamedMethodSecondArgumentPredicate(m, "Select");
}
/// Checks whether the specified method is the IEnumerable.Where() with Func`T,bool.
/// Method to check.
/// true if this is the method; false otherwise.
private static bool IsMethodEnumerableWhere(MethodInfo m)
{
return IsNamedMethodSecondArgumentPredicate(m, "Where");
}
/// Checks whether the specified method takes a Func`T,bool as its second argument.
/// Method to check.
/// Expected name of method.
/// true if this is the method; false otherwise.
private static bool IsNamedMethodSecondArgumentPredicate(MethodInfo m, string name)
{
Debug.Assert(m != null, "m != null");
Debug.Assert(!String.IsNullOrEmpty(name), "!String.IsNullOrEmpty(name)");
if (m.Name == name)
{
ParameterInfo[] p = m.GetParameters();
if (p != null &&
p.Length == 2 &&
p[0].ParameterType.IsGenericType &&
p[1].ParameterType.IsGenericType)
{
Type functionParameter = p[1].ParameterType;
return functionParameter.IsGenericType && functionParameter.GetGenericArguments().Length == 2;
}
}
return false;
}
/// Applies the segment filter to the specified expression.
/// Expression to apply filter on.
/// The expression with the filter applied.
private Expression ApplySegmentFilter(Expression expression)
{
// Collections filter with .Where, and singletons conditionally evalute to null.
Type expressionElementType =
this.singleResult ?
expression.Type :
ReflectionServiceProvider.GetIEnumerableElement(expression.Type);
if (this.Segment != null)
{
if (this.singleResult && this.Segment.HasFilter)
{
LambdaExpression filterLambda = (LambdaExpression)this.Segment.Filter;
expression = RequestQueryProcessor.ComposePropertyNavigation(
expression, filterLambda, this.ExpandProvider.Provider.NullPropagationRequired);
}
else if (!this.singleResult)
{
// The filter should be applied before the .Take operation.
if (this.Segment.HasFilter)
{
MethodInfo[] methods = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public);
MethodInfo method = methods.Where(IsMethodEnumerableWhere).Single();
method = method.MakeGenericMethod(expressionElementType);
Expression[] arguments = new Expression[] { expression, this.Segment.Filter };
expression = Expression.Call(null, method, arguments);
}
if (this.Segment.MaxResultsExpected != Int32.MaxValue)
{
MethodInfo method = typeof(Enumerable).GetMethod("Take", BindingFlags.Static | BindingFlags.Public);
method = method.MakeGenericMethod(expressionElementType);
ConstantExpression count = Expression.Constant(this.Segment.MaxResultsExpected + 1, typeof(int));
Expression[] arguments = new Expression[] { expression, count };
expression = Expression.Call(null, method, arguments);
}
}
}
return expression;
}
/// Builds the projection for this segment.
///
/// Expected expression for this segment if it had no filtering or wrapping.
///
///
/// The expression for this segment, possibly a filter and/or wrapped version of
/// .
///
private Expression BuildProjectionExpression(Expression expression)
{
Debug.Assert(expression != null, "expression != null");
expression = this.ApplySegmentFilter(expression);
Type expressionElementType =
this.singleResult ?
expression.Type :
ReflectionServiceProvider.GetIEnumerableElement(expression.Type);
// If this segment requires a wrapper, project its child properties by passing
// the expression for the property as the parameter.
if (this.requiresWrapper)
{
Expression parameter = this.singleResult ? expression : Expression.Parameter(expressionElementType, "p");
MemberBinding[] bindings = new MemberBinding[this.children.Count + 2];
bindings[0] = this.BindByName("ExpandedElement", parameter);
bindings[1] = this.BindByName("Description", Expression.Constant(this.WrapperDescription));
for (int i = 0; i < this.children.Count; i++)
{
ExpandSegmentNode node = this.children[i];
Expression source = node.BuildProjectionExpression(Expression.Property(parameter, node.Segment.Name));
bindings[i + 2] = this.BindByName("ProjectedProperty" + i.ToString(System.Globalization.CultureInfo.InvariantCulture), source);
}
Expression body = Expression.MemberInit(Expression.New(this.elementType), bindings);
if (this.singleResult)
{
expression = body;
}
else
{
Type[] typeArguments = new Type[] { parameter.Type, body.Type };
Type delegateType = typeof(Func<,>).MakeGenericType(typeArguments);
LambdaExpression selector = BuildLambdaExpression(delegateType, body, new ParameterExpression[] { (ParameterExpression)parameter });
Expression[] arguments = new Expression[] { expression, selector };
MethodInfo[] methods = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public);
MethodInfo method = methods.Where(IsMethodEnumerableSelect).Single();
method = method.MakeGenericMethod(expressionElementType, this.elementType);
expression = Expression.Call(null, method, arguments);
}
}
return expression;
}
///
/// Creates a expression that binds an expression to the named property.
///
/// Name of property on the element type.
/// Source expression for property.
///
/// A expression that binds an expression to the named property.
///
private MemberAssignment BindByName(string propertyName, Expression source)
{
Debug.Assert(propertyName != null, "propertyName != null");
Debug.Assert(source != null, "source != null");
MemberInfo member = this.elementType.GetProperty(propertyName);
return Expression.Bind(member, source);
}
/// Sets the wrapper element type on this node.
private void SetWrapperElementType()
{
// Build a wrapper of the required cardinality.
Debug.Assert(this.children.Count > 0, "this.children > 0 -- otherwise we shouldn't be here");
Debug.Assert(this.requiresWrapper, "this.requiresWrapper -- otherwise no need to call SetWrapperElementType");
Type[] typeArguments = new Type[this.children.Count + 1];
typeArguments[0] = this.enumeratedType;
for (int i = 0; i < this.children.Count; i++)
{
typeArguments[i + 1] = this.children[i].ProjectedType;
}
switch (this.children.Count)
{
case 1:
this.elementType = typeof(ExpandedWrapper<,>).MakeGenericType(typeArguments);
break;
case 2:
this.elementType = typeof(ExpandedWrapper<,,>).MakeGenericType(typeArguments);
break;
case 3:
this.elementType = typeof(ExpandedWrapper<,,,>).MakeGenericType(typeArguments);
break;
case 4:
this.elementType = typeof(ExpandedWrapper<,,,,>).MakeGenericType(typeArguments);
break;
case 5:
this.elementType = typeof(ExpandedWrapper<,,,,,>).MakeGenericType(typeArguments);
break;
case 6:
this.elementType = typeof(ExpandedWrapper<,,,,,,>).MakeGenericType(typeArguments);
break;
case 7:
this.elementType = typeof(ExpandedWrapper<,,,,,,,>).MakeGenericType(typeArguments);
break;
case 8:
this.elementType = typeof(ExpandedWrapper<,,,,,,,,>).MakeGenericType(typeArguments);
break;
case 9:
this.elementType = typeof(ExpandedWrapper<,,,,,,,,,>).MakeGenericType(typeArguments);
break;
case 10:
this.elementType = typeof(ExpandedWrapper<,,,,,,,,,,>).MakeGenericType(typeArguments);
break;
case 11:
this.elementType = typeof(ExpandedWrapper<,,,,,,,,,,,>).MakeGenericType(typeArguments);
break;
case 12:
this.elementType = typeof(ExpandedWrapper<,,,,,,,,,,,,>).MakeGenericType(typeArguments);
break;
default:
throw DataServiceException.CreateBadRequestError(
Strings.BasicExpandProvider_UnsupportedExpandBreadth(this.children.Count));
}
}
}
#endregion Inner types.
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Provides a helper class to implement $expand functionality
// with filters by rewriting queries and implementing custom
// result enumerators.
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services.Providers
{
#region Namespaces.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Services.Client;
using System.Data.Services.Internal;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
#endregion Namespaces.
///
/// Provides a helper class to implement $expand functionality
/// with filters by rewriting queries and implementing custom
/// result enumerators.
///
internal class BasicExpandProvider : IExpandProvider
{
#region Private fields.
/// Whether all values are automatically expanded in the model.
private readonly bool expanded;
/// Full provider.
private readonly IDataServiceProvider provider;
#endregion Private fields.
/// Initializes a new instance.
/// Full provider.
/// Whether all values are automatically expanded in the model.
internal BasicExpandProvider(IDataServiceProvider provider, bool expanded)
{
Debug.Assert(provider != null, "provider != null");
this.provider = provider;
this.expanded = expanded;
}
/// Provider for metadata.
internal IDataServiceProvider Provider
{
get { return this.provider; }
}
/// Applies expansions to the specified .
/// object to expand.
/// A list of paths to expand.
///
/// An object of the same type as the given ,
/// with the results including the specified .
///
///
/// This method may modify the to indicate which expansions
/// are included.
///
/// The returned may implement the
/// interface to provide enumerable objects for the expansions; otherwise, the expanded
/// information is expected to be found directly in the enumerated objects.
///
public IEnumerable ApplyExpansions(IQueryable queryable, ICollection expandPaths)
{
ExpandSegmentNode root = ExpandSegmentNode.CollectExpandPathsIntoTree(this, expandPaths);
root.AssignTypeForExpected(queryable.ElementType, false /* singleResult */);
IQueryable query = root.BuildProjectionQuery(queryable);
Type resultEnumerable = typeof(ExpandedEnumerable<,>).MakeGenericType(queryable.ElementType, query.ElementType);
object[] args = new object[] { query };
return (IEnumerable)Activator.CreateInstance(resultEnumerable, args);
}
#region Inner types.
/// Enumerable element for results with expanded properties.
/// Type for enumeration.
/// Wrapper type with expanded properties.
internal class ExpandedEnumerable : IEnumerable where TWrapper : IExpandedResult
{
/// Source enumeration with wrapped properties.
private IEnumerable source;
/// Initializes a new ExpandedEnumerable instance.
/// Source for enumeration.
public ExpandedEnumerable(IEnumerable source)
{
Debug.Assert(source != null, "source != null");
this.source = source;
}
/// Gets an enumerator object for results.
/// An enumerator object for results.
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
/// Gets an enumerator object for results.
/// An enumerator object for results.
public IEnumerator GetEnumerator()
{
IEnumerator enumerator = this.source.GetEnumerator();
return new ExpandedEnumerator(enumerator);
}
}
/// Use this class to enumerate elements that can be expanded.
/// Type for enumeration.
/// Wrapper type with expanded properties.
internal class ExpandedEnumerator : IEnumerator, IExpandedResult where TWrapper : IExpandedResult
{
/// Enumerator for wrapper instances.
private IEnumerator e;
/// Initializes a new ExpandedEnumerator instance.
/// Source for enumeration.
internal ExpandedEnumerator(IEnumerator enumerator)
{
WebUtil.CheckArgumentNull(enumerator, "enumerator");
this.e = enumerator;
}
/// Element with expanded properties.
public object ExpandedElement
{
get { return this.Current; }
}
/// Element with expanded properties.
public TResult Current
{
get { return (TResult)this.e.Current.ExpandedElement; }
}
/// Element with expanded properties.
object IEnumerator.Current
{
get { return this.Current; }
}
/// Gets an expanded property for the specified .
/// Name of property to return.
/// The expanded property value with the specified .
public object GetExpandedPropertyValue(string name)
{
return this.e.Current.GetExpandedPropertyValue(name);
}
/// Moves to the next element.
/// true if an element is available after the move; false otherwise.
public bool MoveNext()
{
return this.e.MoveNext();
}
/// Resets the enumerator to the beginning of the sequence.
public void Reset()
{
throw Error.NotImplemented();
}
/// Releases resources.
public void Dispose()
{
this.e.Dispose();
}
}
/// Use this class to build a tree structure over a list of paths.
[DebuggerDisplay("ExpandSegmentNode {Segment}")]
internal class ExpandSegmentNode
{
#region Private fields.
/// Expansion segment for this node.
private readonly ExpandSegment Segment;
/// Provider with metadata for operations.
private readonly BasicExpandProvider ExpandProvider;
/// All child nodes for this segment.
private List children;
/// Child nodes that have wrappers.
private List childrenWithWrappers;
/// Child nodes that have filters.
private List childrenWithFilters;
/// Child nodes that don't have wrappers.
private List childrenWithoutWrappers;
/// The element type that will be projected from this segment.
private Type elementType;
///
/// The model (never wrapped) type to be used in an strongly-typed IEnumerable for this segment.
///
private Type enumeratedType;
/// Whether there are any filters within this node. MaxResults are considered filters for processing.
private bool hasFilterWithin;
/// Whether this segment represents a single element rather than a collection.
private bool singleResult;
/// Whether this segment should be projected into a wrapper.
///
/// Segments require a wrapper because the model doesn't auto-wire
/// up (as in the EDM case), or because they have children with
/// filters (and thus they need to project filtered children),
/// or because they have wrapped children (and thus they need
/// to project the custom projected type).
///
private bool requiresWrapper;
#endregion Private fields.
/// Initalizes a new .
/// Segment being described, possibly null.
/// Provider for expansion flags and metadata.
internal ExpandSegmentNode(ExpandSegment segment, BasicExpandProvider provider)
{
this.Segment = segment;
this.ExpandProvider = provider;
}
/// Type to be projected for this node in the model (possibly wrapped).
internal Type ProjectedType
{
get
{
if (this.singleResult)
{
return this.elementType;
}
else
{
return typeof(IEnumerable<>).MakeGenericType(this.elementType);
}
}
}
/// Description to initialize the wrapper with.
internal string WrapperDescription
{
get
{
StringBuilder result = new StringBuilder();
foreach (var child in this.children)
{
if (result.Length > 0)
{
result.Append(',');
}
result.Append(child.Segment.Name);
}
return result.ToString();
}
}
/// Builds a tree of items for the specified paths.
/// Expand provider for nodes.
/// Paths to expand.
/// The root of the path trees.
internal static ExpandSegmentNode CollectExpandPathsIntoTree(
BasicExpandProvider provider,
ICollection expandPaths)
{
Debug.Assert(provider != null, "provider != null");
Debug.Assert(expandPaths != null, "expandPaths != null");
ExpandSegmentNode root = new ExpandSegmentNode(null, provider);
foreach (ExpandSegmentCollection expandPath in expandPaths)
{
// Add each path to the root.
ExpandSegmentNode node = root;
foreach (ExpandSegment segment in expandPath)
{
ExpandSegmentNode child = node.FindChildByName(segment.Name);
if (child == null)
{
child = new ExpandSegmentNode(segment, provider);
node.AddChild(child);
}
node = child;
}
}
return root;
}
/// Adds a child node to this segment node.
/// Child to add.
internal void AddChild(ExpandSegmentNode child)
{
Debug.Assert(child != null, "child != null");
if (this.children == null)
{
this.children = new List(1);
}
this.children.Add(child);
}
/// ----ings type information given the specified type.
/// Type expected for enumeration.
/// true if a single result is expected of this node; false otherwise.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "enumeratedType", Justification = "1:1 mapping")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "singleResult", Justification = "1:1 mapping")]
internal void AssignTypeForExpected(Type enumeratedType, bool singleResult)
{
this.enumeratedType = enumeratedType;
this.singleResult = singleResult;
if (this.children == null)
{
this.elementType = this.enumeratedType;
this.hasFilterWithin = this.Segment.HasFilterOrMaxResults;
}
else
{
this.childrenWithWrappers = new List();
this.childrenWithoutWrappers = new List();
this.childrenWithFilters = new List();
foreach (ExpandSegmentNode child in this.children)
{
Debug.Assert(child.Segment != null, "child.Segment != null -- only the root has a null segment");
string name = child.Segment.Name;
ResourceProperty property = this.ExpandProvider.Provider.TryResolvePropertyName(enumeratedType, name);
Debug.Assert(property != null, "property != null -- otherwise this shouldn't have passed validation");
Type segmentType = property.ResourceType.Type;
child.AssignTypeForExpected(segmentType, property.Kind != ResourcePropertyKind.ResourceSetReference);
if (child.requiresWrapper)
{
this.childrenWithWrappers.Add(child);
}
else
{
this.childrenWithoutWrappers.Add(child);
}
if (child.hasFilterWithin)
{
this.childrenWithFilters.Add(child);
}
}
this.hasFilterWithin =
(this.Segment != null && this.Segment.HasFilterOrMaxResults) ||
this.childrenWithFilters.Count > 0;
this.requiresWrapper =
this.childrenWithWrappers.Count > 0 || // We wrap to project custom types.
this.childrenWithFilters.Count > 0 || // We wrap to project filtered results.
!this.ExpandProvider.expanded; // We wrap to implement expansion.
if (this.requiresWrapper)
{
this.SetWrapperElementType();
}
else
{
this.elementType = this.enumeratedType;
}
}
}
/// Finds a child node by name.
/// Name of node to find.
/// The child node if found; null otherwise.
internal ExpandSegmentNode FindChildByName(string name)
{
Debug.Assert(name != null, "name != null");
if (this.children != null)
{
foreach (var c in this.children)
{
if (c.Segment.Name == name)
{
return c;
}
}
}
return null;
}
/// Builds the projection to apply expansion for the specified .
/// Query to build for.
/// A new query with the expansions performed inside.
internal IQueryable BuildProjectionQuery(IQueryable query)
{
Debug.Assert(query != null, "query != null");
Debug.Assert(this.Segment == null, "this.Segment == null -- otherwise BuildProjectionQuery is being called on a non-root node.");
MethodCallExpression call = (MethodCallExpression)this.BuildProjectionExpression(query.Expression);
Debug.Assert(
call.Method.Name == "Select",
call.Method.Name + " == 'Select' -- otherwise last expression must be a .Select projection.");
LambdaExpression selector = (LambdaExpression)call.Arguments[1];
return RequestUriProcessor.InvokeSelectForTypes(query, this.elementType, selector);
}
///
/// Creates a LambdaExpression and can be used when the delegate type is not known at compile time.
///
/// A Type that represents a delegate type.
/// An Expression to set the Body property equal to.
/// An IEnumerable<T> that contains ParameterExpression objects to use to populate the Parameters collection.
///
/// An object that represents a lambda expression which has the NodeType property equal to Lambda and the
/// Body and Parameters properties set to the specified values.
///
private static LambdaExpression BuildLambdaExpression(Type delegateType, Expression body, IEnumerable parameters)
{
Debug.Assert(delegateType != null, "delegateType != null");
Debug.Assert(body != null, "body != null");
Type[] lambdaTypes = new Type[] { typeof(Expression), typeof(IEnumerable) };
MethodInfo methodInfo = typeof(Expression).GetMethod("Lambda", BindingFlags.Public | BindingFlags.Static, null, lambdaTypes, null);
methodInfo = methodInfo.MakeGenericMethod(new Type[] { delegateType });
return (LambdaExpression)methodInfo.Invoke(null, new object[] { body, parameters });
}
/// Checks whether the specified method is the IEnumerable.Select() with Func`T,T2.
/// Method to check.
/// true if this is the method; false otherwise.
private static bool IsMethodEnumerableSelect(MethodInfo m)
{
return IsNamedMethodSecondArgumentPredicate(m, "Select");
}
/// Checks whether the specified method is the IEnumerable.Where() with Func`T,bool.
/// Method to check.
/// true if this is the method; false otherwise.
private static bool IsMethodEnumerableWhere(MethodInfo m)
{
return IsNamedMethodSecondArgumentPredicate(m, "Where");
}
/// Checks whether the specified method takes a Func`T,bool as its second argument.
/// Method to check.
/// Expected name of method.
/// true if this is the method; false otherwise.
private static bool IsNamedMethodSecondArgumentPredicate(MethodInfo m, string name)
{
Debug.Assert(m != null, "m != null");
Debug.Assert(!String.IsNullOrEmpty(name), "!String.IsNullOrEmpty(name)");
if (m.Name == name)
{
ParameterInfo[] p = m.GetParameters();
if (p != null &&
p.Length == 2 &&
p[0].ParameterType.IsGenericType &&
p[1].ParameterType.IsGenericType)
{
Type functionParameter = p[1].ParameterType;
return functionParameter.IsGenericType && functionParameter.GetGenericArguments().Length == 2;
}
}
return false;
}
/// Applies the segment filter to the specified expression.
/// Expression to apply filter on.
/// The expression with the filter applied.
private Expression ApplySegmentFilter(Expression expression)
{
// Collections filter with .Where, and singletons conditionally evalute to null.
Type expressionElementType =
this.singleResult ?
expression.Type :
ReflectionServiceProvider.GetIEnumerableElement(expression.Type);
if (this.Segment != null)
{
if (this.singleResult && this.Segment.HasFilter)
{
LambdaExpression filterLambda = (LambdaExpression)this.Segment.Filter;
expression = RequestQueryProcessor.ComposePropertyNavigation(
expression, filterLambda, this.ExpandProvider.Provider.NullPropagationRequired);
}
else if (!this.singleResult)
{
// The filter should be applied before the .Take operation.
if (this.Segment.HasFilter)
{
MethodInfo[] methods = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public);
MethodInfo method = methods.Where(IsMethodEnumerableWhere).Single();
method = method.MakeGenericMethod(expressionElementType);
Expression[] arguments = new Expression[] { expression, this.Segment.Filter };
expression = Expression.Call(null, method, arguments);
}
if (this.Segment.MaxResultsExpected != Int32.MaxValue)
{
MethodInfo method = typeof(Enumerable).GetMethod("Take", BindingFlags.Static | BindingFlags.Public);
method = method.MakeGenericMethod(expressionElementType);
ConstantExpression count = Expression.Constant(this.Segment.MaxResultsExpected + 1, typeof(int));
Expression[] arguments = new Expression[] { expression, count };
expression = Expression.Call(null, method, arguments);
}
}
}
return expression;
}
/// Builds the projection for this segment.
///
/// Expected expression for this segment if it had no filtering or wrapping.
///
///
/// The expression for this segment, possibly a filter and/or wrapped version of
/// .
///
private Expression BuildProjectionExpression(Expression expression)
{
Debug.Assert(expression != null, "expression != null");
expression = this.ApplySegmentFilter(expression);
Type expressionElementType =
this.singleResult ?
expression.Type :
ReflectionServiceProvider.GetIEnumerableElement(expression.Type);
// If this segment requires a wrapper, project its child properties by passing
// the expression for the property as the parameter.
if (this.requiresWrapper)
{
Expression parameter = this.singleResult ? expression : Expression.Parameter(expressionElementType, "p");
MemberBinding[] bindings = new MemberBinding[this.children.Count + 2];
bindings[0] = this.BindByName("ExpandedElement", parameter);
bindings[1] = this.BindByName("Description", Expression.Constant(this.WrapperDescription));
for (int i = 0; i < this.children.Count; i++)
{
ExpandSegmentNode node = this.children[i];
Expression source = node.BuildProjectionExpression(Expression.Property(parameter, node.Segment.Name));
bindings[i + 2] = this.BindByName("ProjectedProperty" + i.ToString(System.Globalization.CultureInfo.InvariantCulture), source);
}
Expression body = Expression.MemberInit(Expression.New(this.elementType), bindings);
if (this.singleResult)
{
expression = body;
}
else
{
Type[] typeArguments = new Type[] { parameter.Type, body.Type };
Type delegateType = typeof(Func<,>).MakeGenericType(typeArguments);
LambdaExpression selector = BuildLambdaExpression(delegateType, body, new ParameterExpression[] { (ParameterExpression)parameter });
Expression[] arguments = new Expression[] { expression, selector };
MethodInfo[] methods = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public);
MethodInfo method = methods.Where(IsMethodEnumerableSelect).Single();
method = method.MakeGenericMethod(expressionElementType, this.elementType);
expression = Expression.Call(null, method, arguments);
}
}
return expression;
}
///
/// Creates a expression that binds an expression to the named property.
///
/// Name of property on the element type.
/// Source expression for property.
///
/// A expression that binds an expression to the named property.
///
private MemberAssignment BindByName(string propertyName, Expression source)
{
Debug.Assert(propertyName != null, "propertyName != null");
Debug.Assert(source != null, "source != null");
MemberInfo member = this.elementType.GetProperty(propertyName);
return Expression.Bind(member, source);
}
/// Sets the wrapper element type on this node.
private void SetWrapperElementType()
{
// Build a wrapper of the required cardinality.
Debug.Assert(this.children.Count > 0, "this.children > 0 -- otherwise we shouldn't be here");
Debug.Assert(this.requiresWrapper, "this.requiresWrapper -- otherwise no need to call SetWrapperElementType");
Type[] typeArguments = new Type[this.children.Count + 1];
typeArguments[0] = this.enumeratedType;
for (int i = 0; i < this.children.Count; i++)
{
typeArguments[i + 1] = this.children[i].ProjectedType;
}
switch (this.children.Count)
{
case 1:
this.elementType = typeof(ExpandedWrapper<,>).MakeGenericType(typeArguments);
break;
case 2:
this.elementType = typeof(ExpandedWrapper<,,>).MakeGenericType(typeArguments);
break;
case 3:
this.elementType = typeof(ExpandedWrapper<,,,>).MakeGenericType(typeArguments);
break;
case 4:
this.elementType = typeof(ExpandedWrapper<,,,,>).MakeGenericType(typeArguments);
break;
case 5:
this.elementType = typeof(ExpandedWrapper<,,,,,>).MakeGenericType(typeArguments);
break;
case 6:
this.elementType = typeof(ExpandedWrapper<,,,,,,>).MakeGenericType(typeArguments);
break;
case 7:
this.elementType = typeof(ExpandedWrapper<,,,,,,,>).MakeGenericType(typeArguments);
break;
case 8:
this.elementType = typeof(ExpandedWrapper<,,,,,,,,>).MakeGenericType(typeArguments);
break;
case 9:
this.elementType = typeof(ExpandedWrapper<,,,,,,,,,>).MakeGenericType(typeArguments);
break;
case 10:
this.elementType = typeof(ExpandedWrapper<,,,,,,,,,,>).MakeGenericType(typeArguments);
break;
case 11:
this.elementType = typeof(ExpandedWrapper<,,,,,,,,,,,>).MakeGenericType(typeArguments);
break;
case 12:
this.elementType = typeof(ExpandedWrapper<,,,,,,,,,,,,>).MakeGenericType(typeArguments);
break;
default:
throw DataServiceException.CreateBadRequestError(
Strings.BasicExpandProvider_UnsupportedExpandBreadth(this.children.Count));
}
}
}
#endregion Inner types.
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XComponentModel.cs
- ParameterExpression.cs
- RegexFCD.cs
- ElementHost.cs
- Validator.cs
- ChameleonKey.cs
- PrinterUnitConvert.cs
- InstanceData.cs
- NamespaceCollection.cs
- MemberMaps.cs
- X500Name.cs
- DrawingCollection.cs
- ImageIndexConverter.cs
- XmlSchemaType.cs
- ManualResetEvent.cs
- Assert.cs
- Image.cs
- Internal.cs
- ImageSourceConverter.cs
- SafeHandle.cs
- DesignTimeTemplateParser.cs
- XmlReaderSettings.cs
- initElementDictionary.cs
- EmbeddedMailObjectsCollection.cs
- ResourceAssociationType.cs
- WindowsToolbarAsMenu.cs
- IODescriptionAttribute.cs
- ListenerConnectionDemuxer.cs
- MissingFieldException.cs
- KerberosRequestorSecurityToken.cs
- WebPartConnectionsConnectVerb.cs
- SettingsPropertyValue.cs
- KnownTypeHelper.cs
- Version.cs
- HandlerMappingMemo.cs
- FilterException.cs
- XPathSelectionIterator.cs
- mactripleDES.cs
- StrongNameKeyPair.cs
- HttpHandlerActionCollection.cs
- ClipboardData.cs
- XsdDuration.cs
- Journal.cs
- X509Certificate2Collection.cs
- TimeEnumHelper.cs
- ApplicationTrust.cs
- NamespaceTable.cs
- securestring.cs
- TextEditor.cs
- GridViewCommandEventArgs.cs
- RequestCacheManager.cs
- SqlDataSourceStatusEventArgs.cs
- TemplateControlCodeDomTreeGenerator.cs
- ObjRef.cs
- MembershipSection.cs
- MarshalByRefObject.cs
- SqlEnums.cs
- StringStorage.cs
- DataView.cs
- SecureConversationSecurityTokenParameters.cs
- Vector3DAnimationBase.cs
- FixedDocumentPaginator.cs
- Msec.cs
- Debug.cs
- SmtpTransport.cs
- InternalSafeNativeMethods.cs
- PersistenceTypeAttribute.cs
- InputManager.cs
- WebSysDefaultValueAttribute.cs
- Exceptions.cs
- XamlParser.cs
- ArgumentNullException.cs
- InputScopeConverter.cs
- CompensatableTransactionScopeActivityDesigner.cs
- ObjectTypeMapping.cs
- ETagAttribute.cs
- Stacktrace.cs
- ListSortDescription.cs
- JoinElimination.cs
- ParentQuery.cs
- XPathPatternBuilder.cs
- ButtonPopupAdapter.cs
- DataFieldConverter.cs
- WinFormsSecurity.cs
- RightsManagementPermission.cs
- DataGridViewCellCancelEventArgs.cs
- XmlILIndex.cs
- MemoryMappedFileSecurity.cs
- ListItemCollection.cs
- List.cs
- ImportCatalogPart.cs
- EventLogPermission.cs
- GeneralTransform3D.cs
- FixedTextSelectionProcessor.cs
- ProfilePropertyMetadata.cs
- ObjectReaderCompiler.cs
- _Events.cs
- SelectedCellsCollection.cs
- CapacityStreamGeometryContext.cs
- OpenTypeLayout.cs