DataServiceConfiguration.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataWeb / Server / System / Data / Services / DataServiceConfiguration.cs / 3 / 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.Diagnostics;
    using System.Linq;
    using System.Linq.Expressions; 
    using System.Reflection;
 
    #endregion Namespaces. 

    /// Use this class to manage the configuration data for a data service. 
    internal 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 IDataServiceProvider provider; 

        /// Rights used for unspecified resource containers.
        private EntitySetRights rightsForUnspecifiedResourceContainer;
 
        /// Rights used for unspecified service operations.
        private ServiceOperationRights rightsForUnspecifiedServiceOperation; 
 
        /// A lookup of containers to their rights.
        private Dictionary resourceRights; 

        /// A lookup of service operations to their rights.
        private Dictionary serviceRights;
 
        /// A list of known types.
        private List knownTypes; 
 
        /// Whether verbose errors should be returned by default.
        private bool useVerboseErrors; 

        #endregion Private fields.

        #region Constructor. 

        ///  
        /// Initializes a new  with 
        /// the specified .
        ///  
        /// Non-null provider for this configuration.
        internal DataServiceConfiguration(IDataServiceProvider provider)
        {
            Debug.Assert(provider != null, "provider != null"); 
            this.provider = provider;
            this.resourceRights = new Dictionary(); 
            this.serviceRights = new Dictionary(); 
            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; 
        }
 
        #endregion Constructor.

        #region IDataServiceConfiguration implementation.
 
        /// 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 { 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; 
            }
        }

        /// Sets the access rights on the specified resource container. 
        /// 
        /// Name of resource container to set; '*' to indicate all 
        /// resource containers 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
            { 
                ResourceContainer container = this.provider.TryResolveContainerName(name); 
                if (container == null)
                { 
                    throw new ArgumentException(Strings.DataServiceConfiguration_ResourceNameNotFound(name), "name");
                }

                this.resourceRights[container] = 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"); 
            ServiceOperation serviceOperation = this.provider.TryResolveServiceOperation(name);
            if (name == "*") 
            { 
                this.rightsForUnspecifiedServiceOperation = rights;
            } 
            else
            {
                if (serviceOperation == null)
                { 
                    throw new ArgumentException(Strings.DataServiceConfiguration_ServiceNameNotFound(name), "name");
                } 
 
                this.serviceRights[serviceOperation] = 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);
        } 

        #endregion IDataServiceConfiguration implementation. 
 
        #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, ResourceContainer container)
        { 
            Debug.Assert(service != null, "service != null"); 
            Debug.Assert(container != null, "container != null");
            MethodInfo[] methods = container.ReadAuthorizationMethods; 
            if (methods == null || methods.Length == 0)
            {
                return null;
            } 

            LambdaExpression filter = null; 
            for (int i = 0; i < methods.Length; i++) 
            {
                Expression predicate = (Expression)methods[i].Invoke(service.Instance, WebUtil.EmptyObjectArray); 
                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 container to compose with. 
        /// Query to compose.
        /// The resulting composed query. 
        internal static IQueryable ComposeResourceContainer(IDataService service, ResourceContainer container, IQueryable query) 
        {
            Debug.Assert(service != null, "service != null"); 
            Debug.Assert(container != null, "container != null");
            Debug.Assert(query != null, "query != null");

            MethodInfo[] methods = container.ReadAuthorizationMethods; 
            if (methods != null)
            { 
                for (int i = 0; i < methods.Length; i++) 
                {
                    Expression predicate = (Expression)methods[i].Invoke(service.Instance, WebUtil.EmptyObjectArray); 
                    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;
        }
 
        /// Applies the configuration to the current provider.
        /// Instance of the data source for the provider. 
        /// This method should be called exactly once. 
        internal void ApplyToProvider(object dataSourceInstance)
        { 
            Debug.Assert(this.provider != null, "this.provider != null");
            Debug.Assert(this.resourceRights != null, "this.resourceRights != null - otherwise, ApplyToProvider has been called multiple times.");

            this.provider.ApplyConfiguration(this, dataSourceInstance); 
            this.resourceRights = null;
            this.serviceRights = null; 
            this.knownTypes = null; 
        }
 
        /// Checks whether this request has the specified rights.
        /// Container to check.
        /// Required rights.
        /// Thrown if  aren't available. 
        internal void CheckResourceRights(ResourceContainer container, EntitySetRights requiredRights)
        { 
            Debug.Assert(container != null, "container != null"); 
            Debug.Assert(requiredRights != EntitySetRights.None, "requiredRights != EntitySetRights.None");
 
            EntitySetRights effectiveRights = this.GetResourceContainerRights(container);
            if ((requiredRights & effectiveRights) == 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 void CheckResourceRightsForRead(ResourceContainer container, bool singleResult)
        { 
            Debug.Assert(container != null, "container != null");
            EntitySetRights requiredRights = singleResult ? EntitySetRights.ReadSingle : EntitySetRights.ReadMultiple; 
            this.CheckResourceRights(container, requiredRights); 
        }
 
        /// Checks whether this request has the specified rights.
        /// Operation to check.
        /// Required rights.
        /// Thrown if  aren't available. 
        internal void CheckServiceRights(ServiceOperation operation, ServiceOperationRights requiredRights)
        { 
            Debug.Assert(operation != null, "operation != null"); 
            Debug.Assert(requiredRights != ServiceOperationRights.None, "requiredRights != EntitySetRights.None");
 
            ServiceOperationRights effectiveRights = this.GetServiceOperationRights(operation);
            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 void CheckServiceRights(ServiceOperation operation, bool singleResult)
        { 
            Debug.Assert(operation != null, "operation != null");
 
            if (operation.ResultKind != ServiceOperationResultKind.Nothing) 
            {
                ServiceOperationRights requiredRights = singleResult ? ServiceOperationRights.ReadSingle : ServiceOperationRights.ReadMultiple; 
                this.CheckServiceRights(operation, requiredRights);
            }
        }
 
        /// Gets a string with methods allowed on the target for the .
        /// Description with target. 
        /// A string with methods allowed on the description; possibly null. 
        internal string GetAllowedMethods(RequestDescription description)
        { 
            Debug.Assert(description != null, "description != null");
            Debug.Assert(
                description.TargetKind != RequestTargetKind.Nothing,
                "description.TargetKind != RequestTargetKind.Nothing - 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();
                ResourceContainer container = description.SegmentInfos[index].TargetContainer;
                return this.GetAllowedMethods(container, description.IsSingleResult);
            } 
        }
 
        ///  
        /// Gets a string representation of allowed methods on the container (with the specified target cardinality),
        /// suitable for an 'Allow' header. 
        /// 
        /// Targetted container, possibly null.
        /// true if a single entity is checked for rights; false otherwise.
        /// A value for an 'Allow' header; null if  is null. 
        internal string GetAllowedMethods(ResourceContainer container, bool single)
        { 
            if (container == null) 
            {
                return null; 
            }
            else
            {
                System.Text.StringBuilder result = new System.Text.StringBuilder(); 
                EntitySetRights rights = this.GetResourceContainerRights(container);
                if (single) 
                { 
                    AppendRight(rights, EntitySetRights.ReadSingle, XmlConstants.HttpMethodGet, result);
                    AppendRight(rights, EntitySetRights.WriteDelete, XmlConstants.HttpMethodDelete, result); 
                    AppendRight(rights, EntitySetRights.WriteReplace, XmlConstants.HttpMethodPut, result);
                    AppendRight(rights, EntitySetRights.WriteMerge, XmlConstants.HttpMethodMerge, 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 GetResourceContainerRights(ResourceContainer container)
        { 
            Debug.Assert(container != null, "container != null");

            EntitySetRights result;
            if (this.resourceRights == null) 
            {
                result = container.Rights; 
            } 
            else
            { 
                if (!this.resourceRights.TryGetValue(container, 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 operation) 
        {
            Debug.Assert(operation != null, "operation != null"); 
 
            ServiceOperationRights result;
            if (this.serviceRights == null) 
            {
                result = operation.Rights;
            }
            else 
            {
                if (!this.serviceRights.TryGetValue(operation, out result)) 
                { 
                    result = this.rightsForUnspecifiedServiceOperation;
                } 
            }

            return result;
        } 

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

        ///  
        /// Invokes the static service initialization methods on the
        /// specified type family. 
        ///  
        /// Type of service to initialize for.
        internal 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[] methods = type.GetMethods(flags);
                for (int i = 0; i < methods.Length; i++) 
                {
                    MethodInfo method = methods[i];
                    Debug.Assert(method.IsStatic, "method.IsStatic");
                    if (method.Name == XmlConstants.ClrServiceInitializationMethodName && 
                        method.ReturnType == typeof(void))
                    { 
                        ParameterInfo[] parameters = method.GetParameters(); 
                        if (parameters.Length == 1 &&
                            parameters[0].ParameterType == typeof(IDataServiceConfiguration) && 
                            !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. 
        internal void RegisterCallbacks(Type type)
        {
            Debug.Assert(type != null, "type != 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)
                    { 
                        ResourceContainer container = this.provider.TryResolveContainerName(attribute.EntitySetName); 
                        if (container == null)
                        { 
                            string message = Strings.DataService_AttributeEntitySetNotFound(
                                attribute.EntitySetName,
                                method.Name,
                                type.FullName); 
                            throw new InvalidOperationException(message);
                        } 
 
                        CheckQueryInterceptorSignature(type, method, container);
                        if (!method.IsAbstract) 
                        {
                            container.AddReadAuthorizationMethod(method);
                        }
                    } 

                    ChangeInterceptorAttribute[] changeAttributes = (ChangeInterceptorAttribute[]) 
                        method.GetCustomAttributes(typeof(ChangeInterceptorAttribute), true /* inherit */); 
                    foreach (ChangeInterceptorAttribute attribute in changeAttributes)
                    { 
                        ResourceContainer container = this.provider.TryResolveContainerName(attribute.EntitySetName);
                        if (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.ElementType)) 
                        {
                            string message = Strings.DataService_AuthorizationParameterNotAssignable( 
                                parameters[0].Name,
                                method.Name,
                                type.FullName,
                                elementParameterType.FullName, 
                                container.ElementType);
                            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)
                        { 
                            container.AddWriteAuthorizationMethod(method);
                        } 
                    } 
                }
 
                type = type.BaseType;
            }
        }
 
        /// 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;
        } 
 
        #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, ResourceContainer 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.ElementType, 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.ElementType, 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)); 
            } 
        }
 
        /// 
        /// 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.
//---------------------------------------------------------------------- 
// 
//      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.Diagnostics;
    using System.Linq;
    using System.Linq.Expressions; 
    using System.Reflection;
 
    #endregion Namespaces. 

    /// Use this class to manage the configuration data for a data service. 
    internal 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 IDataServiceProvider provider; 

        /// Rights used for unspecified resource containers.
        private EntitySetRights rightsForUnspecifiedResourceContainer;
 
        /// Rights used for unspecified service operations.
        private ServiceOperationRights rightsForUnspecifiedServiceOperation; 
 
        /// A lookup of containers to their rights.
        private Dictionary resourceRights; 

        /// A lookup of service operations to their rights.
        private Dictionary serviceRights;
 
        /// A list of known types.
        private List knownTypes; 
 
        /// Whether verbose errors should be returned by default.
        private bool useVerboseErrors; 

        #endregion Private fields.

        #region Constructor. 

        ///  
        /// Initializes a new  with 
        /// the specified .
        ///  
        /// Non-null provider for this configuration.
        internal DataServiceConfiguration(IDataServiceProvider provider)
        {
            Debug.Assert(provider != null, "provider != null"); 
            this.provider = provider;
            this.resourceRights = new Dictionary(); 
            this.serviceRights = new Dictionary(); 
            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; 
        }
 
        #endregion Constructor.

        #region IDataServiceConfiguration implementation.
 
        /// 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 { 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; 
            }
        }

        /// Sets the access rights on the specified resource container. 
        /// 
        /// Name of resource container to set; '*' to indicate all 
        /// resource containers 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
            { 
                ResourceContainer container = this.provider.TryResolveContainerName(name); 
                if (container == null)
                { 
                    throw new ArgumentException(Strings.DataServiceConfiguration_ResourceNameNotFound(name), "name");
                }

                this.resourceRights[container] = 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"); 
            ServiceOperation serviceOperation = this.provider.TryResolveServiceOperation(name);
            if (name == "*") 
            { 
                this.rightsForUnspecifiedServiceOperation = rights;
            } 
            else
            {
                if (serviceOperation == null)
                { 
                    throw new ArgumentException(Strings.DataServiceConfiguration_ServiceNameNotFound(name), "name");
                } 
 
                this.serviceRights[serviceOperation] = 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);
        } 

        #endregion IDataServiceConfiguration implementation. 
 
        #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, ResourceContainer container)
        { 
            Debug.Assert(service != null, "service != null"); 
            Debug.Assert(container != null, "container != null");
            MethodInfo[] methods = container.ReadAuthorizationMethods; 
            if (methods == null || methods.Length == 0)
            {
                return null;
            } 

            LambdaExpression filter = null; 
            for (int i = 0; i < methods.Length; i++) 
            {
                Expression predicate = (Expression)methods[i].Invoke(service.Instance, WebUtil.EmptyObjectArray); 
                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 container to compose with. 
        /// Query to compose.
        /// The resulting composed query. 
        internal static IQueryable ComposeResourceContainer(IDataService service, ResourceContainer container, IQueryable query) 
        {
            Debug.Assert(service != null, "service != null"); 
            Debug.Assert(container != null, "container != null");
            Debug.Assert(query != null, "query != null");

            MethodInfo[] methods = container.ReadAuthorizationMethods; 
            if (methods != null)
            { 
                for (int i = 0; i < methods.Length; i++) 
                {
                    Expression predicate = (Expression)methods[i].Invoke(service.Instance, WebUtil.EmptyObjectArray); 
                    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;
        }
 
        /// Applies the configuration to the current provider.
        /// Instance of the data source for the provider. 
        /// This method should be called exactly once. 
        internal void ApplyToProvider(object dataSourceInstance)
        { 
            Debug.Assert(this.provider != null, "this.provider != null");
            Debug.Assert(this.resourceRights != null, "this.resourceRights != null - otherwise, ApplyToProvider has been called multiple times.");

            this.provider.ApplyConfiguration(this, dataSourceInstance); 
            this.resourceRights = null;
            this.serviceRights = null; 
            this.knownTypes = null; 
        }
 
        /// Checks whether this request has the specified rights.
        /// Container to check.
        /// Required rights.
        /// Thrown if  aren't available. 
        internal void CheckResourceRights(ResourceContainer container, EntitySetRights requiredRights)
        { 
            Debug.Assert(container != null, "container != null"); 
            Debug.Assert(requiredRights != EntitySetRights.None, "requiredRights != EntitySetRights.None");
 
            EntitySetRights effectiveRights = this.GetResourceContainerRights(container);
            if ((requiredRights & effectiveRights) == 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 void CheckResourceRightsForRead(ResourceContainer container, bool singleResult)
        { 
            Debug.Assert(container != null, "container != null");
            EntitySetRights requiredRights = singleResult ? EntitySetRights.ReadSingle : EntitySetRights.ReadMultiple; 
            this.CheckResourceRights(container, requiredRights); 
        }
 
        /// Checks whether this request has the specified rights.
        /// Operation to check.
        /// Required rights.
        /// Thrown if  aren't available. 
        internal void CheckServiceRights(ServiceOperation operation, ServiceOperationRights requiredRights)
        { 
            Debug.Assert(operation != null, "operation != null"); 
            Debug.Assert(requiredRights != ServiceOperationRights.None, "requiredRights != EntitySetRights.None");
 
            ServiceOperationRights effectiveRights = this.GetServiceOperationRights(operation);
            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 void CheckServiceRights(ServiceOperation operation, bool singleResult)
        { 
            Debug.Assert(operation != null, "operation != null");
 
            if (operation.ResultKind != ServiceOperationResultKind.Nothing) 
            {
                ServiceOperationRights requiredRights = singleResult ? ServiceOperationRights.ReadSingle : ServiceOperationRights.ReadMultiple; 
                this.CheckServiceRights(operation, requiredRights);
            }
        }
 
        /// Gets a string with methods allowed on the target for the .
        /// Description with target. 
        /// A string with methods allowed on the description; possibly null. 
        internal string GetAllowedMethods(RequestDescription description)
        { 
            Debug.Assert(description != null, "description != null");
            Debug.Assert(
                description.TargetKind != RequestTargetKind.Nothing,
                "description.TargetKind != RequestTargetKind.Nothing - 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();
                ResourceContainer container = description.SegmentInfos[index].TargetContainer;
                return this.GetAllowedMethods(container, description.IsSingleResult);
            } 
        }
 
        ///  
        /// Gets a string representation of allowed methods on the container (with the specified target cardinality),
        /// suitable for an 'Allow' header. 
        /// 
        /// Targetted container, possibly null.
        /// true if a single entity is checked for rights; false otherwise.
        /// A value for an 'Allow' header; null if  is null. 
        internal string GetAllowedMethods(ResourceContainer container, bool single)
        { 
            if (container == null) 
            {
                return null; 
            }
            else
            {
                System.Text.StringBuilder result = new System.Text.StringBuilder(); 
                EntitySetRights rights = this.GetResourceContainerRights(container);
                if (single) 
                { 
                    AppendRight(rights, EntitySetRights.ReadSingle, XmlConstants.HttpMethodGet, result);
                    AppendRight(rights, EntitySetRights.WriteDelete, XmlConstants.HttpMethodDelete, result); 
                    AppendRight(rights, EntitySetRights.WriteReplace, XmlConstants.HttpMethodPut, result);
                    AppendRight(rights, EntitySetRights.WriteMerge, XmlConstants.HttpMethodMerge, 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 GetResourceContainerRights(ResourceContainer container)
        { 
            Debug.Assert(container != null, "container != null");

            EntitySetRights result;
            if (this.resourceRights == null) 
            {
                result = container.Rights; 
            } 
            else
            { 
                if (!this.resourceRights.TryGetValue(container, 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 operation) 
        {
            Debug.Assert(operation != null, "operation != null"); 
 
            ServiceOperationRights result;
            if (this.serviceRights == null) 
            {
                result = operation.Rights;
            }
            else 
            {
                if (!this.serviceRights.TryGetValue(operation, out result)) 
                { 
                    result = this.rightsForUnspecifiedServiceOperation;
                } 
            }

            return result;
        } 

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

        ///  
        /// Invokes the static service initialization methods on the
        /// specified type family. 
        ///  
        /// Type of service to initialize for.
        internal 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[] methods = type.GetMethods(flags);
                for (int i = 0; i < methods.Length; i++) 
                {
                    MethodInfo method = methods[i];
                    Debug.Assert(method.IsStatic, "method.IsStatic");
                    if (method.Name == XmlConstants.ClrServiceInitializationMethodName && 
                        method.ReturnType == typeof(void))
                    { 
                        ParameterInfo[] parameters = method.GetParameters(); 
                        if (parameters.Length == 1 &&
                            parameters[0].ParameterType == typeof(IDataServiceConfiguration) && 
                            !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. 
        internal void RegisterCallbacks(Type type)
        {
            Debug.Assert(type != null, "type != 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)
                    { 
                        ResourceContainer container = this.provider.TryResolveContainerName(attribute.EntitySetName); 
                        if (container == null)
                        { 
                            string message = Strings.DataService_AttributeEntitySetNotFound(
                                attribute.EntitySetName,
                                method.Name,
                                type.FullName); 
                            throw new InvalidOperationException(message);
                        } 
 
                        CheckQueryInterceptorSignature(type, method, container);
                        if (!method.IsAbstract) 
                        {
                            container.AddReadAuthorizationMethod(method);
                        }
                    } 

                    ChangeInterceptorAttribute[] changeAttributes = (ChangeInterceptorAttribute[]) 
                        method.GetCustomAttributes(typeof(ChangeInterceptorAttribute), true /* inherit */); 
                    foreach (ChangeInterceptorAttribute attribute in changeAttributes)
                    { 
                        ResourceContainer container = this.provider.TryResolveContainerName(attribute.EntitySetName);
                        if (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.ElementType)) 
                        {
                            string message = Strings.DataService_AuthorizationParameterNotAssignable( 
                                parameters[0].Name,
                                method.Name,
                                type.FullName,
                                elementParameterType.FullName, 
                                container.ElementType);
                            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)
                        { 
                            container.AddWriteAuthorizationMethod(method);
                        } 
                    } 
                }
 
                type = type.BaseType;
            }
        }
 
        /// 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;
        } 
 
        #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, ResourceContainer 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.ElementType, 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.ElementType, 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)); 
            } 
        }
 
        /// 
        /// 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