BasicExpandProvider.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / 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

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK