DataServiceConfiguration.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / DataServiceConfiguration.cs / 1305376 / DataServiceConfiguration.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Provides an implementation of the IDataServiceConfiguration
//      interface. 
//  
//
// @owner  [....] 
//---------------------------------------------------------------------

namespace System.Data.Services
{ 
    #region Namespaces.
 
    using System; 
    using System.Collections.Generic;
    using System.Data.Services.Client; 
    using System.Data.Services.Providers;
    using System.Data.Services.Common;
    using System.Diagnostics;
    using System.Linq; 
    using System.Linq.Expressions;
    using System.Reflection; 
    using System.Net; 
    #endregion Namespaces.
 
    /// Use this class to manage the configuration data for a data service.
    public sealed class DataServiceConfiguration : IDataServiceConfiguration
    {
        #region Private fields. 

        /// Whether this configuration has been sealed. 
        private bool configurationSealed; 

        /// Maximum number of change sets and query operations in a batch. 
        private int maxBatchCount;

        /// Maximum number of changes in a change set.
        private int maxChangeSetCount; 

        /// Maximum number of segments to be expanded allowed in a request. 
        private int maxExpandCount; 

        /// Maximum number of segments in a single $expand path. 
        private int maxExpandDepth;

        /// Maximum number of elements in each returned collection (top-level or expanded).
        private int maxResultsPerCollection; 

        /// maximum number of objects that can be referred in a single insert request. 
        private int maxObjectCountOnInsert; 

        /// The provider for the web service. 
        private IDataServiceMetadataProvider provider;

        /// Rights used for unspecified resource sets.
        private EntitySetRights rightsForUnspecifiedResourceContainer; 

        /// Rights used for unspecified service operations. 
        private ServiceOperationRights rightsForUnspecifiedServiceOperation; 

        /// Page size for unspecified resource sets 
        private int defaultPageSize;

        /// 
        /// A lookup of containers to their rights. 
        /// For IDSP there is no guarantee that the provider will always return the same metadata instance.  We should
        /// use the name instead of the instance as key since the configuration is cached across requests. 
        ///  
        private Dictionary resourceRights;
 
        /// 
        /// A lookup of service operations to their rights.
        /// For IDSP there is no guarantee that the provider will always return the same metadata instance.  We should
        /// use the name instead of the instance as key since the configuration is cached across requests. 
        /// 
        private Dictionary serviceRights; 
 
        /// 
        /// A lookup of resource sets to their page sizes. 
        /// For IDSP there is no guarantee that the provider will always return the same metadata instance.  We should
        /// use the name instead of the instance as key since the configuration is cached across requests.
        /// 
        private Dictionary pageSizes; 

        /// A list of known types. 
        private List knownTypes; 

        /// Whether verbose errors should be returned by default. 
        private bool useVerboseErrors;

        /// Holds configuration of service behavior
        private DataServiceBehavior dataServiceBehavior; 

        ///  
        /// Perform type conversion from the type specified in the payload to the actual property type. 
        /// 
        private bool typeConversion; 

        /// 
        /// A lookup of resource sets to the corresponding QueryInterceptors.
        /// For IDSP there is no guarantee that the provider will always return the same metadata instance.  We should 
        /// use the name instead of the instance as key since the configuration is cached across requests.
        ///  
        private Dictionary> readAuthorizationMethods; 

        ///  
        /// A lookup of resource sets to the corresponding ChangeInterceptors.
        /// For IDSP there is no guarantee that the provider will always return the same metadata instance.  We should
        /// use the name instead of the instance as key since the configuration is cached across requests.
        ///  
        private Dictionary> writeAuthorizationMethods;
 
        /// This is set to true if EnableAccess("*") is called.  False otherwise. 
        private bool accessEnabledForAllResourceTypes;
 
        /// List of fully qualified type names that were marked as visible by calling EnableAccess().
        private HashSet accessEnabledResourceTypes;

        #endregion Private fields. 

        #region Constructor. 
 
        /// 
        /// Initializes a new  with 
        /// the specified .
        /// 
        /// Non-null provider for this configuration.
        internal DataServiceConfiguration(IDataServiceMetadataProvider provider) 
        {
            Debug.Assert(provider != null, "provider != null"); 
            this.provider = provider; 
            this.resourceRights = new Dictionary(EqualityComparer.Default);
            this.serviceRights = new Dictionary(EqualityComparer.Default); 
            this.pageSizes = new Dictionary(EqualityComparer.Default);
            this.rightsForUnspecifiedResourceContainer = EntitySetRights.None;
            this.rightsForUnspecifiedServiceOperation = ServiceOperationRights.None;
            this.knownTypes = new List(); 
            this.maxBatchCount = Int32.MaxValue;
            this.maxChangeSetCount = Int32.MaxValue; 
            this.maxExpandCount = Int32.MaxValue; 
            this.maxExpandDepth = Int32.MaxValue;
            this.maxResultsPerCollection = Int32.MaxValue; 
            this.maxObjectCountOnInsert = Int32.MaxValue;
            this.readAuthorizationMethods = new Dictionary>(EqualityComparer.Default);
            this.writeAuthorizationMethods = new Dictionary>(EqualityComparer.Default);
            this.accessEnabledResourceTypes = new HashSet(EqualityComparer.Default); 
            this.dataServiceBehavior = new DataServiceBehavior();
 
            // default value is true since in V1, we always did the type conversion 
            // and this configuration settings was introduced in V2
            this.typeConversion = true; 
        }

        #endregion Constructor.
 
        #region Public Properties
 
        ///  
        /// Specifies whether the data service runtime should do type conversion from the payload type
        /// to the actual property type in POST/PUT/MERGE requests. 
        /// 
        public bool EnableTypeConversion
        {
            get 
            {
                return this.typeConversion; 
            } 

            set 
            {
                this.CheckNotSealed();
                this.typeConversion = value;
            } 
        }
 
        /// Maximum number of change sets and query operations in a batch. 
        public int MaxBatchCount
        { 
            get { return this.maxBatchCount; }
            set { this.maxBatchCount = this.CheckNonNegativeProperty(value, "MaxBatchCount"); }
        }
 
        /// Maximum number of changes in a change set.
        public int MaxChangesetCount 
        { 
            get { return this.maxChangeSetCount; }
            set { this.maxChangeSetCount = this.CheckNonNegativeProperty(value, "MaxChangesetCount"); } 
        }

        /// Maximum number of segments to be expanded allowed in a request.
        public int MaxExpandCount 
        {
            get { return this.maxExpandCount; } 
            set { this.maxExpandCount = this.CheckNonNegativeProperty(value, "MaxExpandCount"); } 
        }
 
        /// Maximum number of segments in a single $expand path.
        public int MaxExpandDepth
        {
            get { return this.maxExpandDepth; } 
            set { this.maxExpandDepth = this.CheckNonNegativeProperty(value, "MaxExpandDepth"); }
        } 
 
        /// Maximum number of elements in each returned collection (top-level or expanded).
        public int MaxResultsPerCollection 
        {
            get
            {
                return this.maxResultsPerCollection; 
            }
 
            set 
            {
                if (this.IsPageSizeDefined) 
                {
                    throw new InvalidOperationException(Strings.DataService_SDP_PageSizeWithMaxResultsPerCollection);
                }
 
                this.maxResultsPerCollection = this.CheckNonNegativeProperty(value, "MaxResultsPerCollection");
            } 
        } 

        /// Maximum number of objects that can be referred in a single POST request. 
        public int MaxObjectCountOnInsert
        {
            get { return this.maxObjectCountOnInsert; }
            set { this.maxObjectCountOnInsert = this.CheckNonNegativeProperty(value, "MaxObjectCountOnInsert"); } 
        }
 
        /// Gets or sets whether verbose errors should be used by default. 
        /// 
        /// This property sets the default for the whole service; individual responses may behave differently 
        /// depending on the value of the VerboseResponse property of the arguments to the HandleException
        /// method on the  class.
        /// 
        public bool UseVerboseErrors 
        {
            get 
            { 
                return this.useVerboseErrors;
            } 

            set
            {
                this.CheckNotSealed(); 
                this.useVerboseErrors = value;
            } 
        } 

        ///  
        /// Gets settings that define service behavior.
        /// 
        public DataServiceBehavior DataServiceBehavior
        { 
            get
            { 
                return this.dataServiceBehavior; 
            }
        } 

        #endregion Public Properties

        #region Internal Properties 

        /// True if all resource types have been made visible by calling EnableAccess("*").  False otherwise. 
        internal bool AccessEnabledForAllResourceTypes 
        {
            [DebuggerStepThrough] 
            get { return this.accessEnabledForAllResourceTypes; }
        }

        #endregion Internal Properties 

        #region Private Properties 
 
        /// 
        /// Whether size of a page has been defined. 
        /// 
        private bool IsPageSizeDefined
        {
            get 
            {
                return this.pageSizes.Count > 0 || this.defaultPageSize > 0; 
            } 
        }
 
        #endregion

        #region Public Methods
 
        /// Sets the access rights on the specified resource set.
        ///  
        /// Name of resource set to set; '*' to indicate all 
        /// resource sets not otherwise specified.
        ///  
        /// Rights to be granted to this resource.
        public void SetEntitySetAccessRule(string name, EntitySetRights rights)
        {
            this.CheckNotSealed(); 
            if (name == null)
            { 
                throw Error.ArgumentNull("name"); 
            }
 
            WebUtil.CheckResourceContainerRights(rights, "rights");
            if (name == "*")
            {
                this.rightsForUnspecifiedResourceContainer = rights; 
            }
            else 
            { 
                ResourceSet container;
                if (!this.provider.TryResolveResourceSet(name, out container) || container == null) 
                {
                    throw new ArgumentException(Strings.DataServiceConfiguration_ResourceSetNameNotFound(name), "name");
                }
 
                this.resourceRights[container.Name] = rights;
            } 
        } 

        /// Sets the access rights on the specified service operation. 
        /// 
        /// Name of service operation to set; '*' to indicate all
        /// service operations not otherwise specified.
        ///  
        /// Rights to be granted to this operation.
        public void SetServiceOperationAccessRule(string name, ServiceOperationRights rights) 
        { 
            this.CheckNotSealed();
            if (name == null) 
            {
                throw new ArgumentNullException("name");
            }
 
            WebUtil.CheckServiceOperationRights(rights, "rights");
            if (name == "*") 
            { 
                this.rightsForUnspecifiedServiceOperation = rights;
            } 
            else
            {
                ServiceOperation serviceOperation;
                if (!this.provider.TryResolveServiceOperation(name, out serviceOperation) || serviceOperation == null) 
                {
                    throw new ArgumentException(Strings.DataServiceConfiguration_ServiceNameNotFound(name), "name"); 
                } 

                this.serviceRights[serviceOperation.Name] = rights; 
            }
        }

        ///  
        /// Add the type to the list of known types. If there is a type that needs to be added since
        /// Astoria runtime can't detect it using the default set of rules, providers can add them using 
        /// this method 
        /// 
        /// type which needs to be added to the known types collection 
        public void RegisterKnownType(Type type)
        {
            this.CheckNotSealed();
            this.knownTypes.Add(type); 
        }
 
        /// Sets the page size per entity set 
        /// Name of entity set, '*' to indicate those for which page size is not explicitly specified
        /// Page size for the resource set(s) specified in  
        public void SetEntitySetPageSize(String name, int size)
        {
            WebUtil.CheckArgumentNull(name, "name");
            if (size < 0) 
            {
                throw new ArgumentOutOfRangeException("size", size, Strings.DataService_SDP_PageSizeMustbeNonNegative(size, name)); 
            } 

            // Treat a page size of Int32.MaxValue to be the same as not setting the page size. 
            if (size == Int32.MaxValue)
            {
                size = 0;
            } 

            if (this.MaxResultsPerCollection != Int32.MaxValue) 
            { 
                throw new InvalidOperationException(Strings.DataService_SDP_PageSizeWithMaxResultsPerCollection);
            } 

            this.CheckNotSealed();

            if (name == "*") 
            {
                this.defaultPageSize = size; 
            } 
            else
            { 
                ResourceSet container;
                if (!this.provider.TryResolveResourceSet(name, out container) || container == null)
                {
                    throw new ArgumentException(Strings.DataServiceConfiguration_ResourceSetNameNotFound(name), "name"); 
                }
 
                this.pageSizes[container.Name] = size; 
            }
        } 

        /// 
        /// This method is used to register a type with the Astoria runtime which may be returned in the �open properties� of
        /// an open type such that the type is visible in $metadata output and usable with CRUD operations. 
        ///
        /// The typename parameter must be a namespace qualified type name (format: <namespace>.<typename>). 
        /// The name provided must be as it would show up in a CSDL document (ie. model types, not CLR types) 
        ///
        /// The types registered via calls to EnableAccess will be additive to those implicitly made accessible via 
        /// DSC.SetEntitySetAccessRule(�) invocations
        ///  � Note: The Astoria runtime layer won�t be able to determine if a typename specified maps to an Entity Type,
        ///    Complex Type, etc until it actually obtains type info (entity types, complex types, etc) from the underlying provider
        ///  � �*� can be used as the value of �typename�, which will be interpreted as matching all types 
        ///
        /// When Astoria enumerates types or needs to obtain a type (Complex Types, Entity Types) from the underlying provider 
        /// it will first determine if the type should be visible (show in $metadata and accessible via operations exposed by the 
        /// service) as per the standard v1 checks (ie. driven by SetEntitySetAccessRule calls). If the type is not visible via V1
        /// rules, then we consult the set of types registered via EnableAccess(<typename>) invocations.  If the type was 
        /// included in such a call then the type is visible via $metadata and can be accessed via CRUD ops, etc.
        ///
        /// If a type is not made visible via one of the mechanisms above, then:
        ///   � That type must not be included a response to a $metadata request 
        ///   � Instances of the type must not be returned to the client as the response of a request to the data service.
        ///     If such a type instance would be required the service MUST fail the request.  Failure semantics are covered 
        ///     in the area of the specification which covers request/response semantics with respect to open types. 
        ///
        /// Invoking this method multiple times with the same type name is allowed and considered a �NO OP�. 
        /// 
        /// The namespace qualified complex type name to be made visible via the data service
        public void EnableTypeAccess(string typeName)
        { 
            WebUtil.CheckStringArgumentNull(typeName, "typeName");
            this.CheckNotSealed(); 
 
            if (typeName == "*")
            { 
                this.accessEnabledForAllResourceTypes = true;
            }
            else
            { 
                ResourceType resourceType;
                if (!this.provider.TryResolveResourceType(typeName, out resourceType) || resourceType == null) 
                { 
                    throw new ArgumentException(Strings.DataServiceConfiguration_ResourceTypeNameNotFound(typeName), "typeName");
                } 

                if (resourceType.ResourceTypeKind != ResourceTypeKind.ComplexType)
                {
                    throw new ArgumentException(Strings.DataServiceConfiguration_NotComplexType(typeName), "typeName"); 
                }
 
                Debug.Assert(resourceType.FullName == typeName, "resourceType.FullName == typeName"); 
                this.accessEnabledResourceTypes.Add(typeName);
            } 
        }

        #endregion Public Methods
 
        #region Internal methods.
 
        /// Composes all query interceptors into a single expression. 
        /// Web service instance.
        /// Container for which interceptors should run. 
        /// An expression the filter for query interceptors, possibly null.
        internal static Expression ComposeQueryInterceptors(IDataService service, ResourceSetWrapper container)
        {
            Debug.Assert(service != null, "service != null"); 
            Debug.Assert(container != null, "container != null");
            MethodInfo[] methods = container.QueryInterceptors; 
            if (methods == null || methods.Length == 0) 
            {
                return null; 
            }

            LambdaExpression filter = null;
            for (int i = 0; i < methods.Length; i++) 
            {
                Expression predicate = null; 
                try 
                {
                    predicate = (Expression)methods[i].Invoke(service.Instance, WebUtil.EmptyObjectArray); 
                }
                catch (TargetInvocationException tie)
                {
                    ErrorHandler.HandleTargetInvocationException(tie); 
                    throw;
                } 
 
                if (predicate == null)
                { 
                    throw new InvalidOperationException(Strings.DataService_AuthorizationReturnedNullQuery(methods[i].Name, methods[i].DeclaringType.FullName));
                }

                Debug.Assert(predicate is LambdaExpression, "predicate is LambdaExpression -- otherwise signature check missed something."); 
                if (filter == null)
                { 
                    filter = (LambdaExpression)predicate; 
                }
                else 
                {
                    ParameterExpression parameter = filter.Parameters[0];
                    Expression adjustedPredicate = ParameterReplacerVisitor.Replace(
                        ((LambdaExpression)predicate).Body,             // expression 
                        ((LambdaExpression)predicate).Parameters[0],    // oldParameter
                        parameter);                                     // newParameter 
                    filter = Expression.Lambda(Expression.And(filter.Body, adjustedPredicate), parameter); 
                }
            } 

            return filter;
        }
 
        /// 
        /// Composes the specified  for the 
        /// given  with authorization 
        /// callbacks.
        ///  
        /// Data service on which to invoke method.
        /// resource set to compose with.
        /// Query to compose.
        /// The resulting composed query. 
        internal static IQueryable ComposeResourceContainer(IDataService service, ResourceSetWrapper container, IQueryable query)
        { 
            Debug.Assert(service != null, "service != null"); 
            Debug.Assert(container != null, "container != null");
            Debug.Assert(query != null, "query != null"); 

            MethodInfo[] methods = container.QueryInterceptors;
            if (methods != null)
            { 
                for (int i = 0; i < methods.Length; i++)
                { 
                    Expression predicate = null; 
                    try
                    { 
                        predicate = (Expression)methods[i].Invoke(service.Instance, WebUtil.EmptyObjectArray);
                    }
                    catch (TargetInvocationException tie)
                    { 
                        ErrorHandler.HandleTargetInvocationException(tie);
                        throw; 
                    } 

                    if (predicate == null) 
                    {
                        throw new InvalidOperationException(Strings.DataService_AuthorizationReturnedNullQuery(methods[i].Name, methods[i].DeclaringType.FullName));
                    }
 
                    Debug.Assert(predicate is LambdaExpression, "predicate is LambdaExpression -- otherwise signature check missed something.");
                    query = RequestUriProcessor.InvokeWhereForType(query, (LambdaExpression)predicate); 
                } 
            }
 
            return query;
        }

        /// Checks whether this request has the specified rights. 
        /// Container to check.
        /// Required rights. 
        /// Thrown if  aren't available. 
        internal static void CheckResourceRights(ResourceSetWrapper container, EntitySetRights requiredRights)
        { 
            Debug.Assert(container != null, "container != null");
            Debug.Assert(requiredRights != EntitySetRights.None, "requiredRights != EntitySetRights.None");

            if ((requiredRights & container.Rights) == 0) 
            {
                throw DataServiceException.CreateForbidden(); 
            } 
        }
 
        /// Checks whether this request has the specified reading rights.
        /// Container to check.
        /// Whether a single or multiple resources are requested.
        /// Thrown if  aren't available. 
        internal static void CheckResourceRightsForRead(ResourceSetWrapper container, bool singleResult)
        { 
            Debug.Assert(container != null, "container != null"); 
            EntitySetRights requiredRights = singleResult ? EntitySetRights.ReadSingle : EntitySetRights.ReadMultiple;
            CheckResourceRights(container, requiredRights); 
        }

        /// Checks whether this request has the specified rights.
        /// Operation to check. 
        /// Required rights.
        /// Thrown if  aren't available. 
        internal static void CheckServiceRights(ServiceOperationWrapper operation, ServiceOperationRights requiredRights) 
        {
            Debug.Assert(operation != null, "operation != null"); 
            Debug.Assert(requiredRights != ServiceOperationRights.None, "requiredRights != EntitySetRights.None");

            ServiceOperationRights effectiveRights = operation.Rights;
            if ((requiredRights & effectiveRights) == 0) 
            {
                throw DataServiceException.CreateForbidden(); 
            } 
        }
 
        /// Checks whether this request has the specified rights.
        /// Operation to check.
        /// Whether a single or multiple resources are requested.
        /// Thrown if  aren't available. 
        internal static void CheckServiceRights(ServiceOperationWrapper operation, bool singleResult)
        { 
            Debug.Assert(operation != null, "operation != null"); 

            if (operation.ResultKind != ServiceOperationResultKind.Void) 
            {
                ServiceOperationRights requiredRights = singleResult ? ServiceOperationRights.ReadSingle : ServiceOperationRights.ReadMultiple;
                CheckServiceRights(operation, requiredRights);
            } 
        }
 
        /// Gets a string with methods allowed on the target for the . 
        /// configuration object which has the data
        /// Description with target. 
        /// A string with methods allowed on the description; possibly null.
        internal static string GetAllowedMethods(DataServiceConfiguration configuration, RequestDescription description)
        {
            Debug.Assert(description != null, "description != null"); 
            Debug.Assert(
                description.TargetKind != RequestTargetKind.Nothing, 
                "description.TargetKind != RequestTargetKind.Void - otherwise it hasn't been determined yet"); 
            Debug.Assert(
                description.TargetKind != RequestTargetKind.VoidServiceOperation, 
                "description.TargetKind != RequestTargetKind.VoidServiceOperation - this method is only for containers");
            if (description.TargetKind == RequestTargetKind.Metadata ||
                description.TargetKind == RequestTargetKind.ServiceDirectory)
            { 
                return XmlConstants.HttpMethodGet;
            } 
            else if (description.TargetKind == RequestTargetKind.Batch) 
            {
                return XmlConstants.HttpMethodPost; 
            }
            else
            {
                int index = description.GetIndexOfTargetEntityResource(); 
                Debug.Assert(index >= 0 && index < description.SegmentInfos.Length, "index >=0 && index 
        /// Gets a string representation of allowed methods on the container (with the specified target cardinality),
        /// suitable for an 'Allow' header. 
        /// 
        /// configuration object which has the data 
        /// Targetted container, possibly null. 
        /// Description with target.
        /// A value for an 'Allow' header; null if  is null. 
        internal static string GetAllowedMethods(DataServiceConfiguration configuration, ResourceSetWrapper container, RequestDescription description)
        {
            if (container == null)
            { 
                return null;
            } 
            else 
            {
                System.Text.StringBuilder result = new System.Text.StringBuilder(); 
                EntitySetRights rights = configuration.GetResourceSetRights(container.ResourceSet);
                if (description.IsSingleResult)
                {
                    AppendRight(rights, EntitySetRights.ReadSingle, XmlConstants.HttpMethodGet, result); 
                    AppendRight(rights, EntitySetRights.WriteReplace, XmlConstants.HttpMethodPut, result);
                    if (description.TargetKind != RequestTargetKind.MediaResource) 
                    { 
                        AppendRight(rights, EntitySetRights.WriteMerge, XmlConstants.HttpMethodMerge, result);
                        AppendRight(rights, EntitySetRights.WriteDelete, XmlConstants.HttpMethodDelete, result); 
                    }
                }
                else
                { 
                    AppendRight(rights, EntitySetRights.ReadMultiple, XmlConstants.HttpMethodGet, result);
                    AppendRight(rights, EntitySetRights.WriteAppend, XmlConstants.HttpMethodPost, result); 
                } 

                return result.ToString(); 
            }
        }

        /// Gets the effective rights on the specified container. 
        /// Container to get rights for.
        /// The effective rights as per this configuration. 
        internal EntitySetRights GetResourceSetRights(ResourceSet container) 
        {
            Debug.Assert(container != null, "container != null"); 
            Debug.Assert(this.resourceRights != null, "this.resourceRights != null");

            EntitySetRights result;
            if (!this.resourceRights.TryGetValue(container.Name, out result)) 
            {
                result = this.rightsForUnspecifiedResourceContainer; 
            } 

            return result; 
        }

        /// Gets the effective rights on the specified operation.
        /// Operation to get rights for. 
        /// The effective rights as per this configuration.
        internal ServiceOperationRights GetServiceOperationRights(ServiceOperation serviceOperation) 
        { 
            Debug.Assert(serviceOperation != null, "operation != null");
            Debug.Assert(this.serviceRights != null, "this.serviceRights != null"); 

            ServiceOperationRights result;
            if (!this.serviceRights.TryGetValue(serviceOperation.Name, out result))
            { 
                result = this.rightsForUnspecifiedServiceOperation;
            } 
 
            return result;
        } 

        /// Gets the page size per entity set
        /// Entity set for which to get the page size
        /// Page size for the  
        internal int GetResourceSetPageSize(ResourceSet container)
        { 
            Debug.Assert(container != null, "container != null"); 
            Debug.Assert(this.pageSizes != null, "this.pageSizes != null");
 
            int pageSize;
            if (!this.pageSizes.TryGetValue(container.Name, out pageSize))
            {
                pageSize = this.defaultPageSize; 
            }
 
            return pageSize; 
        }
 
        /// Returns the list of types registered by the data service.
        /// The list of types as registered by the data service
        internal IEnumerable GetKnownTypes()
        { 
            return this.knownTypes;
        } 
 
        /// Get the list of access enabled resourceType names.
        /// List of namespace qualified resourceType names that were marked as visible by calling EnableAccess(). 
        internal IEnumerable GetAccessEnabledResourceTypes()
        {
            Debug.Assert(this.accessEnabledResourceTypes != null, "this.accessEnabledResourceTypes != null");
            return this.accessEnabledResourceTypes; 
        }
 
        ///  
        /// Initializes the DataServiceConfiguration instance by:
        /// 1. Invokes the static service initialization methods on the specified type family. 
        /// 2. Register authorization callbacks specified on the given .
        /// 
        /// Type of service to initialize for.
        internal void Initialize(Type type) 
        {
            Debug.Assert(type != null, "type != null"); 
            this.InvokeStaticInitialization(type); 
            this.RegisterCallbacks(type);
        } 

        /// 
        /// Returns the list of QueryInterceptors for the given resource set
        ///  
        /// resource set instance
        /// List of QueryInterceptors for the resource set, null if there is none defined for the resource set. 
        internal MethodInfo[] GetReadAuthorizationMethods(ResourceSet resourceSet) 
        {
            Debug.Assert(resourceSet != null, "resourceSet != null"); 

            List methods;
            if (this.readAuthorizationMethods.TryGetValue(resourceSet.Name, out methods))
            { 
                return methods.ToArray();
            } 
 
            return null;
        } 

        /// 
        /// Returns the list of ChangeInterceptors for the given resource set
        ///  
        /// resource set instance
        /// List of ChangeInterceptors for the resource set, null if there is none defined for the resource set. 
        internal MethodInfo[] GetWriteAuthorizationMethods(ResourceSet resourceSet) 
        {
            Debug.Assert(resourceSet != null, "resourceSet != null"); 

            List methods;
            if (this.writeAuthorizationMethods.TryGetValue(resourceSet.Name, out methods))
            { 
                return methods.ToArray();
            } 
 
            return null;
        } 

        /// Seals this configuration instance and prevents further changes.
        /// 
        /// This method should be called after the configuration has been set up and before it's placed on the 
        /// metadata cache for sharing.
        ///  
        internal void Seal() 
        {
            Debug.Assert(!this.configurationSealed, "!configurationSealed - otherwise .Seal is invoked multiple times"); 
            this.configurationSealed = true;
            this.provider = null;
        }
 
        /// 
        /// Validated if server options used by the service are compatible with MaxProtocolVersion 
        ///  
        /// Whether friendly feeds are compatible with V1.
        internal void ValidateServerOptions(bool friendlyFeedsV1Compatible) 
        {
            Debug.Assert(this.configurationSealed, "Configuration must be sealed to validate server options");

            if (this.DataServiceBehavior.MaxProtocolVersion == DataServiceProtocolVersion.V1) 
            {
                if (!friendlyFeedsV1Compatible) 
                { 
                    throw new InvalidOperationException(Strings.DataServiceConfiguration_FriendlyFeedsWithKeepInContentFalseNotSupportedInV1Server);
                } 

                if (this.IsPageSizeDefined)
                {
                    throw new InvalidOperationException(Strings.DataServiceConfiguration_ServerPagingNotSupportedInV1Server); 
                }
            } 
        } 

        ///  
        /// Validates that the versions of features used in the request are less then or equal than the configured
        /// MaxProtocolVersion.
        /// 
        /// Request description 
        internal void ValidateMaxProtocolVersion(RequestDescription requestDescription)
        { 
            Debug.Assert( 
                Enum.GetValues(typeof(DataServiceProtocolVersion)).Length == 2,
                "This method has to be modified when adding/removing DataServiceProtocolVersion values."); 

            Version maxProtocolVersion =
                new Version(this.DataServiceBehavior.MaxProtocolVersion == DataServiceProtocolVersion.V1 ? 1 : 2, 0);
 
            if (requestDescription.MaxFeatureVersion > maxProtocolVersion)
            { 
                throw DataServiceException.CreateBadRequestError(Strings.DataServiceConfiguration_V2ResponseForV1Server); 
            }
 
            // As a cross check let's verify if the version of the response is not higher than the configured max protocol version.
            Debug.Assert(maxProtocolVersion >= requestDescription.ResponseVersion, "ResponseVersion > MaxProtocolVersion");
        }
 
        #endregion Internal methods.
 
        #region Private methods. 

        ///  
        /// Appends the  of a right if the  right is enabled
        /// on .
        /// 
        /// Rights to be checked. 
        /// Right being looked for.
        /// Name of right to append. 
        /// Comma-separated list of right names to append to. 
        private static void AppendRight(EntitySetRights entitySetRights, EntitySetRights test, string name, System.Text.StringBuilder builder)
        { 
            Debug.Assert(builder != null, "builder != null");
            if (0 != (entitySetRights & test))
            {
                if (builder.Length > 0) 
                {
                    builder.Append(", "); 
                } 

                builder.Append(name); 
            }
        }

        /// Checks that the specified  has a correct signature. 
        /// Service type.
        /// Method to check. 
        /// Container associated with the interceptor. 
        private static void CheckQueryInterceptorSignature(Type type, MethodInfo method, ResourceSet container)
        { 
            Debug.Assert(type != null, "type != null");
            Debug.Assert(method != null, "method != null");
            Debug.Assert(container != null, "container != null");
 
            ParameterInfo[] parameters = method.GetParameters();
            if (parameters.Length != 0) 
            { 
                throw new InvalidOperationException(Strings.DataService_QueryInterceptorIncorrectParameterCount(method.Name, type.FullName, parameters.Length));
            } 

            Type lambdaType = typeof(Func<,>).MakeGenericType(container.ResourceType.InstanceType, typeof(bool));
            Type expectedReturnType = typeof(Expression<>).MakeGenericType(lambdaType);
            Type returnType = method.ReturnType; 
            if (returnType == typeof(void))
            { 
                throw new InvalidOperationException(Strings.DataService_AuthorizationMethodVoid(method.Name, type.FullName, expectedReturnType)); 
            }
            else if (!expectedReturnType.IsAssignableFrom(returnType)) 
            {
                Type nullableLambdaType = typeof(Func<,>).MakeGenericType(container.ResourceType.InstanceType, typeof(bool?));
                if (!(typeof(Expression<>).MakeGenericType(nullableLambdaType).IsAssignableFrom(returnType)))
                { 
                    throw new InvalidOperationException(
                        Strings.DataService_AuthorizationReturnTypeNotAssignable(method.Name, type.FullName, returnType.FullName, expectedReturnType.FullName)); 
                } 
            }
        } 

        /// Verifies that the specified  is not an [out] parameter.
        /// Method with parameter to check.
        /// Parameter to check. 
        private static void CheckParameterIsNotOut(MethodInfo method, ParameterInfo parameter)
        { 
            Debug.Assert(method != null, "method != null"); 
            Debug.Assert(parameter != null, "parameter != null");
 
            if (parameter.IsOut)
            {
                throw new InvalidOperationException(Strings.DataService_ParameterIsOut(method.DeclaringType.FullName, method.Name, parameter.Name));
            } 
        }
 
        ///  
        /// Invokes the static service initialization methods on the
        /// specified type family. 
        /// 
        /// Type of service to initialize for.
        private void InvokeStaticInitialization(Type type)
        { 
            Debug.Assert(type != null, "type != null");
 
            // Build a stack going from most-specific to least-specific type. 
            BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public;
            while (type != null) 
            {
                MethodInfo method = type.GetMethod(XmlConstants.ClrServiceInitializationMethodName, flags, null, new Type[] { typeof(IDataServiceConfiguration) }, null);
                if (method == null)
                { 
                    method = type.GetMethod(XmlConstants.ClrServiceInitializationMethodName, flags, null, new Type[] { typeof(DataServiceConfiguration) }, null);
                } 
 
                if (method != null && method.ReturnType == typeof(void))
                { 
                    Debug.Assert(method.IsStatic, "method.IsStatic");
                    Debug.Assert(method.Name == XmlConstants.ClrServiceInitializationMethodName, "Making sure that the method name is as expected");

                    ParameterInfo[] parameters = method.GetParameters(); 
                    if (parameters.Length == 1 && !parameters[0].IsOut)
                    { 
                        object[] initializeParameters = new object[] { this }; 
                        try
                        { 
                            method.Invoke(null, initializeParameters);
                        }
                        catch (TargetInvocationException exception)
                        { 
                            ErrorHandler.HandleTargetInvocationException(exception);
                            throw; 
                        } 

                        return; 
                    }
                }

                type = type.BaseType; 
            }
        } 
 
        /// 
        /// Register authorization callbacks specified on the given 
        /// .
        /// 
        /// Type of web data service to check.
        private void RegisterCallbacks(Type type) 
        {
            Debug.Assert(type != null, "type != null"); 
            Debug.Assert(this.provider != null, "this.provider != null"); 

            BindingFlags flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public; 
            while (type != null)
            {
                MethodInfo[] methods = type.GetMethods(flags);
                for (int i = 0; i < methods.Length; i++) 
                {
                    MethodInfo method = methods[i]; 
                    QueryInterceptorAttribute[] queryAttributes = (QueryInterceptorAttribute[]) 
                        method.GetCustomAttributes(typeof(QueryInterceptorAttribute), true /* inherit */);
                    foreach (QueryInterceptorAttribute attribute in queryAttributes) 
                    {
                        ResourceSet container;
                        if (!this.provider.TryResolveResourceSet(attribute.EntitySetName, out container) || container == null)
                        { 
                            string message = Strings.DataService_AttributeEntitySetNotFound(
                                attribute.EntitySetName, 
                                method.Name, 
                                type.FullName);
                            throw new InvalidOperationException(message); 
                        }

                        CheckQueryInterceptorSignature(type, method, container);
                        if (!method.IsAbstract) 
                        {
                            if (!this.readAuthorizationMethods.ContainsKey(container.Name)) 
                            { 
                                this.readAuthorizationMethods[container.Name] = new List();
                            } 

                            this.readAuthorizationMethods[container.Name].Add(method);
                        }
                    } 

                    ChangeInterceptorAttribute[] changeAttributes = (ChangeInterceptorAttribute[]) 
                        method.GetCustomAttributes(typeof(ChangeInterceptorAttribute), true /* inherit */); 
                    foreach (ChangeInterceptorAttribute attribute in changeAttributes)
                    { 
                        ResourceSet container;
                        if (!this.provider.TryResolveResourceSet(attribute.EntitySetName, out container) || container == null)
                        {
                            string message = Strings.DataService_AttributeEntitySetNotFound( 
                                attribute.EntitySetName,
                                method.Name, 
                                type.FullName); 
                            throw new InvalidOperationException(message);
                        } 

                        // Check the signature.
                        ParameterInfo[] parameters = method.GetParameters();
                        if (parameters.Length != 2) 
                        {
                            string message = Strings.DataService_ChangeInterceptorIncorrectParameterCount( 
                                method.Name, 
                                type.FullName,
                                parameters.Length); 
                            throw new InvalidOperationException(message);
                        }

                        CheckParameterIsNotOut(method, parameters[0]); 
                        CheckParameterIsNotOut(method, parameters[1]);
                        Type elementParameterType = parameters[0].ParameterType; 
                        if (!elementParameterType.IsAssignableFrom(container.ResourceType.InstanceType)) 
                        {
                            string message = Strings.DataService_AuthorizationParameterNotAssignable( 
                                parameters[0].Name,
                                method.Name,
                                type.FullName,
                                elementParameterType.FullName, 
                                container.ResourceType.InstanceType);
                            throw new InvalidOperationException(message); 
                        } 

                        Type actionParameterType = parameters[1].ParameterType; 
                        if (actionParameterType != typeof(UpdateOperations))
                        {
                            string message = Strings.DataService_AuthorizationParameterNotResourceAction(
                                parameters[1].Name, 
                                method.Name,
                                type.FullName, 
                                typeof(UpdateOperations).FullName); 
                            throw new InvalidOperationException(message);
                        } 

                        Type returnType = method.ReturnType;
                        if (returnType != typeof(void))
                        { 
                            string message = Strings.DataService_AuthorizationMethodNotVoid(
                                method.Name, 
                                type.FullName, 
                                returnType.FullName);
                            throw new InvalidOperationException(message); 
                        }

                        if (!method.IsAbstract)
                        { 
                            if (!this.writeAuthorizationMethods.ContainsKey(container.Name))
                            { 
                                this.writeAuthorizationMethods[container.Name] = new List(); 
                            }
 
                            this.writeAuthorizationMethods[container.Name].Add(method);
                        }
                    }
                } 

                type = type.BaseType; 
            } 
        }
 
        /// 
        /// Checks that the specified  for the named property is not negative and that the
        /// configuration isn't sealed.
        ///  
        /// Value to check.
        /// Parameter name. 
        /// The  to set. 
        /// 
        /// This method is typically used in properties with the following pattern: 
        /// public int Foo { get {... } set { this.foo = this.CheckNonNegativeProperty(value, "Foo"); } }
        /// 
        private int CheckNonNegativeProperty(int value, string propertyName)
        { 
            this.CheckNotSealed();
            if (value < 0) 
            { 
                throw new ArgumentOutOfRangeException("value", value, Strings.PropertyRequiresNonNegativeNumber(propertyName));
            } 

            return value;
        }
 
        /// Checks that this configuration hasn't been sealed yet.
        private void CheckNotSealed() 
        { 
            if (this.configurationSealed)
            { 
                string message = Strings.DataServiceConfiguration_NoChangesAllowed(XmlConstants.ClrServiceInitializationMethodName);
                throw new InvalidOperationException(message);
            }
        } 

        #endregion Private methods. 
    } 
}

// 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