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 /// 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; } ///instance. 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 public IEnumerable ApplyExpansions(IQueryable queryable, ICollectionto 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. /// 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 IEnumerablesource; /// Initializes a new ExpandedEnumerable instance. /// Source for enumeration. public ExpandedEnumerable(IEnumerablesource) { 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 IEnumeratorGetEnumerator() { 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 IEnumeratore; /// Initializes a new ExpandedEnumerator instance. /// Source for enumeration. internal ExpandedEnumerator(IEnumeratorenumerator) { 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 Listchildren; /// Child nodes that have wrappers. private ListchildrenWithWrappers; /// Child nodes that have filters. private ListchildrenWithFilters; /// Child nodes that don't have wrappers. private ListchildrenWithoutWrappers; /// 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 /// Expand provider for nodes. /// Paths to expand. ///items for the specified paths. The root of the path trees. internal static ExpandSegmentNode CollectExpandPathsIntoTree( BasicExpandProvider provider, ICollectionexpandPaths) { 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, IEnumerableparameters) { 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 /// Name of property on the element type. /// Source expression for property. ///expression that binds an expression to the named property. /// /// A 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); } ///expression that binds an expression to the named property. /// 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 /// 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; } ///instance. 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 public IEnumerable ApplyExpansions(IQueryable queryable, ICollectionto 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. /// 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 IEnumerablesource; /// Initializes a new ExpandedEnumerable instance. /// Source for enumeration. public ExpandedEnumerable(IEnumerablesource) { 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 IEnumeratorGetEnumerator() { 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 IEnumeratore; /// Initializes a new ExpandedEnumerator instance. /// Source for enumeration. internal ExpandedEnumerator(IEnumeratorenumerator) { 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 Listchildren; /// Child nodes that have wrappers. private ListchildrenWithWrappers; /// Child nodes that have filters. private ListchildrenWithFilters; /// Child nodes that don't have wrappers. private ListchildrenWithoutWrappers; /// 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 /// Expand provider for nodes. /// Paths to expand. ///items for the specified paths. The root of the path trees. internal static ExpandSegmentNode CollectExpandPathsIntoTree( BasicExpandProvider provider, ICollectionexpandPaths) { 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, IEnumerableparameters) { 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 /// Name of property on the element type. /// Source expression for property. ///expression that binds an expression to the named property. /// /// A 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); } ///expression that binds an expression to the named property. /// 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
- XmlNodeWriter.cs
- DataAdapter.cs
- URLAttribute.cs
- PageCache.cs
- AnnotationAdorner.cs
- HtmlWindowCollection.cs
- QueueProcessor.cs
- GZipDecoder.cs
- filewebresponse.cs
- ScrollProviderWrapper.cs
- MDIClient.cs
- PrinterUnitConvert.cs
- VectorValueSerializer.cs
- PropertyOverridesDialog.cs
- DataGridViewAutoSizeColumnsModeEventArgs.cs
- StructuredType.cs
- SqlResolver.cs
- ListenerAdaptersInstallComponent.cs
- PictureBox.cs
- UriExt.cs
- SymbolTable.cs
- OptionUsage.cs
- SQLDateTime.cs
- Visual3D.cs
- DataRowComparer.cs
- AcceleratedTokenAuthenticator.cs
- TraceHandler.cs
- DefaultValueAttribute.cs
- XAMLParseException.cs
- OneToOneMappingSerializer.cs
- InternalRelationshipCollection.cs
- DataServiceExpressionVisitor.cs
- DataGridViewColumnCollectionDialog.cs
- CodeNamespaceImport.cs
- DataSet.cs
- SQLBytesStorage.cs
- SqlConnectionPoolProviderInfo.cs
- StylusButton.cs
- SoapDocumentServiceAttribute.cs
- XmlSchemaCollection.cs
- DescendentsWalker.cs
- ScrollItemProviderWrapper.cs
- AnimationClockResource.cs
- _OSSOCK.cs
- ResourceExpressionEditor.cs
- NegotiateStream.cs
- ComponentManagerBroker.cs
- AvTrace.cs
- ReadOnlyHierarchicalDataSource.cs
- OleCmdHelper.cs
- Int32CollectionConverter.cs
- DataServiceRequestException.cs
- TableItemProviderWrapper.cs
- ClientUrlResolverWrapper.cs
- IsolatedStorageFile.cs
- WebPartPersonalization.cs
- BitmapCodecInfoInternal.cs
- InvalidAsynchronousStateException.cs
- ToolTipAutomationPeer.cs
- GridViewSelectEventArgs.cs
- ReverseQueryOperator.cs
- SetIterators.cs
- Span.cs
- ListViewEditEventArgs.cs
- EntityCommand.cs
- QilStrConcat.cs
- WpfWebRequestHelper.cs
- Propagator.JoinPropagator.JoinPredicateVisitor.cs
- AppDomainUnloadedException.cs
- DictionaryEntry.cs
- FrugalMap.cs
- HttpCapabilitiesSectionHandler.cs
- XmlResolver.cs
- IISMapPath.cs
- BindUriHelper.cs
- ISAPIApplicationHost.cs
- MetaModel.cs
- TransportContext.cs
- CodePrimitiveExpression.cs
- VirtualizingStackPanel.cs
- StringKeyFrameCollection.cs
- DataGridViewTopRowAccessibleObject.cs
- ClientSideProviderDescription.cs
- EntityDataSourceDesignerHelper.cs
- SchemaNotation.cs
- DesignerAttribute.cs
- ReferencedCollectionType.cs
- DefaultTextStoreTextComposition.cs
- FilteredAttributeCollection.cs
- OledbConnectionStringbuilder.cs
- DbConnectionStringBuilder.cs
- XmlSerializationReader.cs
- InputScopeManager.cs
- ExpressionBuilderContext.cs
- MarkupExtensionSerializer.cs
- SerializationObjectManager.cs
- MimeReturn.cs
- DelegateInArgument.cs
- CurrentChangedEventManager.cs
- ObjectQueryState.cs