BaseServiceProvider.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataWeb / Server / System / Data / Services / Providers / BaseServiceProvider.cs / 1 / BaseServiceProvider.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Abstract Class which contains the common code for ObjectContextServiceProvider
//      and ReflectionServiceProvider 
//  
//
// @owner  [....] 
//---------------------------------------------------------------------

namespace System.Data.Services.Providers
{ 
    #region Namespaces.
 
    using System; 
    using System.Collections;
    using System.Collections.Generic; 
    using System.Data.Services.Caching;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq; 
    using System.Reflection;
    using System.ServiceModel.Web; 
    using System.Xml; 

    #endregion Namespaces. 

    /// Provides a reflection-based  implementation.
    internal abstract class BaseServiceProvider : IDataServiceProvider, IDisposable, IUpdatable, IExpandProvider
    { 
        /// Bindings Flags to be used for reflection.
        protected const BindingFlags ResourceContainerBindingFlags = WebUtil.PublicInstanceBindingFlags; 
 
        /// Instance from which data is provided.
        private object instance; 

        /// Metadata to be used by the service provider.
        private MetadataCacheItem metadata;
 
        /// 
        /// Initializes a new System.Data.Services.BaseServiceProvider instance. 
        ///  
        /// Metadata for this provider.
        /// instance of the data source provider. 
        protected BaseServiceProvider(MetadataCacheItem metadata, object dataSourceInstance)
        {
            WebUtil.CheckArgumentNull(metadata, "metadata");
            WebUtil.CheckArgumentNull(dataSourceInstance, "dataSourceInstance"); 
            this.metadata = metadata;
            this.instance = dataSourceInstance; 
        } 

        /// Returns the instance from which data is provided. 
        public object CurrentDataSource
        {
            [DebuggerStepThrough]
            get 
            {
                // Many debuggers will try to display this property, and we don't want to trigger an assertion. 
                Debug.Assert( 
                    System.Diagnostics.Debugger.IsAttached || this.instance != null,
                    "this.instance != null -- otherwise CurrentDataSource is accessed before initialization or after disposal."); 
                return this.instance;
            }
        }
 
        /// Gets a value indicating whether null propagation is required in expression trees.
        public abstract bool NullPropagationRequired 
        { 
            get;
        } 

        /// 
        /// Provides a name for the context in which all resource containers are.
        ///  
        public abstract string ResourceContextName
        { 
            get; 
        }
 
        /// Returns the list of entity sets.
        protected IDictionary EntitySets
        {
            [DebuggerStepThrough] 
            get { return this.metadata.EntitySets; }
        } 
 
        /// Returns all known service operations.
        protected IEnumerable ServiceOperations 
        {
            [DebuggerStepThrough]
            get { return this.metadata.ServiceOperations.Values; }
        } 

        /// Target type for the data provider  
        protected Type Type 
        {
            [DebuggerStepThrough] 
            get { return this.metadata.Type; }
        }

        /// Returns the list of resource types. 
        protected IEnumerable Types
        { 
            [DebuggerStepThrough] 
            get { return this.metadata.TypeCache.Values; }
        } 

        /// Returns true if the metadata is compatible with Edm V1 Schema otherwise false
        protected bool CompatibleWithV1Schema
        { 
            get { return this.metadata.EdmVersion1Schema; }
        } 
 
        /// Cache of resource properties per type.
        private Dictionary TypeCache 
        {
            [DebuggerStepThrough]
            get { return this.metadata.TypeCache; }
        } 

        /// Applies expansions to the specified . 
        ///  object to expand. 
        /// A collection of ordered  paths.
        ///  
        /// An  object of the same type as the given ,
        /// with the results including the specified .
        /// 
        ///  
        /// This method may modify the  to indicate which expansions
        /// are included. 
        /// 
        /// The returned  may implement the 
        /// interface to provide enumerable objects for the expansions; otherwise, the expanded 
        /// information is expected to be found directly in the enumerated objects.
        /// 
        public abstract IEnumerable ApplyExpansions(IQueryable queryable, ICollection expandPaths);
 
        /// 
        /// Get the container name corresponding to the given navigation property 
        /// The declaring type container name refers to the entity set that the declaring type belongs to 
        /// 
        /// name of the entity container that the declaring type belongs to 
        /// declaring type
        /// resource navigation property
        /// name of the container that this property refers to
        public abstract ResourceContainer GetContainer(string declaringTypeContainerName, Type declaringResourceType, ResourceProperty resourceProperty); 

        /// Gets the name of the container that holds this resource type. This method is called for open types only 
        /// Resource to get container for. 
        /// 
        /// The name of the container for the specified resource; null if it cannot 
        /// be determined.
        /// 
        public abstract ResourceContainer GetContainerForResourceType(Type resourceType);
 
        /// Gets the metadata document for this provider.
        /// Writer to which metadata XML should be written. 
        public abstract void GetMetadata(XmlWriter writer); 

        ///  
        /// Get the list of property names that form the ETag for the given resource instance
        /// 
        /// name of the container to which the resource belongs t
        /// clr type of the resource whose etag properties need to be fetched 
        /// list of property names that form the ETag for the given resource instance
        public abstract ICollection GetETagProperties(string containerName, Type resourceClrType); 
 
        #region IUpdatable Members
 
        /// 
        /// Creates the resource of the given type and belonging to the given container
        /// 
        /// container name to which the resource needs to be added 
        /// full type name i.e. Namespace qualified type name of the resource
        /// object representing a resource of given type and belonging to the given container 
        public abstract object CreateResource(string containerName, string fullTypeName); 

        ///  
        /// Gets the resource of the given type that the query points to
        /// 
        /// query pointing to a particular resource
        /// full type name i.e. Namespace qualified type name of the resource 
        /// object representing a resource of given type and as referenced by the query
        public abstract object GetResource(IQueryable query, string fullTypeName); 
 
        /// 
        /// Checks whether the specified  is ordered. 
        /// 
        /// Type to check.
        /// true if the type may be ordered; false otherwise.
        ///  
        /// The ordering may still fail at runtime; this method is currently
        /// used for cleaner error messages only. 
        ///  
        public virtual bool GetTypeIsOrdered(Type type)
        { 
            Debug.Assert(type != null, "type != null");
            return type == typeof(object) || WebUtil.IsPrimitiveType(type);
        }
 
        /// 
        /// Resets the value of the given resource to its default value 
        ///  
        /// resource whose value needs to be reset
        /// same resource with its value reset 
        public abstract object ResetResource(object resource);

        /// 
        /// Sets the value of the given property on the target object 
        /// 
        /// target object which defines the property 
        /// name of the property whose value needs to be updated 
        /// value of the property
        public abstract void SetValue(object targetResource, string propertyName, object propertyValue); 

        /// 
        /// Gets the value of the given property on the target object
        ///  
        /// target object which defines the property
        /// name of the property whose value needs to be updated 
        /// the value of the property for the given target resource 
        public abstract object GetValue(object targetResource, string propertyName);
 
        /// 
        /// Sets the value of the given reference property on the target object
        /// 
        /// target object which defines the property 
        /// name of the property whose value needs to be updated
        /// value of the property 
        public abstract void SetReference(object targetResource, string propertyName, object propertyValue); 

        ///  
        /// Adds the given value to the collection
        /// 
        /// target object which defines the property
        /// name of the property whose value needs to be updated 
        /// value of the property which needs to be added
        public abstract void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded); 
 
        /// 
        /// Removes the given value from the collection 
        /// 
        /// target object which defines the property
        /// name of the property whose value needs to be updated
        /// value of the property which needs to be removed 
        public abstract void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved);
 
        ///  
        /// Delete the given resource
        ///  
        /// resource that needs to be deleted
        public abstract void DeleteResource(object targetResource);

        ///  
        /// Saves all the pending changes made till now
        ///  
        public abstract void SaveChanges(); 

        ///  
        /// Returns the actual instance of the resource represented by the given resource object
        /// 
        /// object representing the resource whose instance needs to be fetched
        /// The actual instance of the resource represented by the given resource object 
        public abstract object ResolveResource(object resource);
 
        ///  
        /// Revert all the pending changes.
        ///  
        public abstract void ClearChanges();

        #endregion
 
        #region IDataServiceProvider Members
 
        ///  
        /// Applies the settings specified in the given .
        ///  
        /// Configuration to apply.
        /// Instance of the data source for the provider.
        void IDataServiceProvider.ApplyConfiguration(DataServiceConfiguration configuration, object dataSourceInstance)
        { 
            Debug.Assert(configuration != null, "configuration != null");
 
            this.PopulateMetadataForUserSpecifiedTypes(configuration.GetKnownTypes(), this.TypeCache, this.EntitySets.Values); 
            this.ApplyRights(configuration);
            this.CheckConfigurationConsistency(dataSourceInstance); 
            this.RemoveHiddenResourceContainers();
            this.RemoveHiddenServiceOperations();
            this.RemoveHiddenProperties();
            this.RemoveHiddenTypes(); 
            this.CheckServiceOperationContainers();
            this.DisallowDerivedEntityTypesWithNavProperties(); 
        } 

        /// Disposes of the underlying . 
        void IDataServiceProvider.DisposeDataSource()
        {
            WebUtil.Dispose(this.instance);
            this.instance = null; 
        }
 
        /// Gets all available containers. 
        /// An enumerable object with all available containers.
        IEnumerable IDataServiceProvider.GetContainers() 
        {
            return this.EntitySets.Values;
        }
 
        /// 
        /// Returns the IQueryable that represents the container. 
        ///  
        /// Name of the resource container.
        ///  
        /// An IQueryable that represents the container; null if there is
        /// no container for the specified name.
        /// 
        IQueryable IDataServiceProvider.ResolveContainerName(string containerName) 
        {
            ResourceContainer resourceContainer = ((IDataServiceProvider)this).TryResolveContainerName(containerName); 
            if (resourceContainer != null) 
            {
                return this.GetResourceContainerInstance(resourceContainer); 
            }
            else
            {
                return null; 
            }
        } 
 
        ///  Gets the root type of the given resource type.
        /// ResourceType to get least derived type for. 
        /// The least derived type for the specified .
        ResourceType IDataServiceProvider.GetRootType(ResourceType resourceType)
        {
            if (resourceType == null) 
            {
                return resourceType; 
            } 

            while (resourceType.BaseType != null) 
            {
                resourceType = resourceType.BaseType;
            }
 
            return resourceType;
        } 
 
        /// Gets the  for the specified .
        ///  to map into a . 
        /// The  that  maps to for this provider.
        ResourceType IDataServiceProvider.GetResourceType(Type type)
        {
            Debug.Assert(type != null, "type != null"); 

            return this.GetType(type); 
        } 

        /// Gets the kind of resource that the type is. 
        ///  to check.
        /// The kind of resource that the specified  is.
        ResourceTypeKind IDataServiceProvider.GetResourceTypeKind(Type type)
        { 
            Debug.Assert(type != null, "type != null");
 
            ResourceType resourceType = this.GetType(type); 
            Debug.Assert(resourceType != null, "resourceType != null -- otherwise the specified Type '" + type + "' isn't part of the known type system.");
            return resourceType.ResourceTypeKind; 
        }

        /// Given the specified name, tries to find a resource container.
        /// Name of the resource container to resolve. 
        /// Resolved resource container, possibly null.
        ResourceContainer IDataServiceProvider.TryResolveContainerName(string name) 
        { 
            ResourceContainer resourceContainer;
            if (this.EntitySets.TryGetValue(name, out resourceContainer)) 
            {
                return resourceContainer;
            }
            else 
            {
                return null; 
            } 
        }
 
        /// Given the specified CLR type, finds the named property.
        /// Type to get property for.
        /// Name of property to resolve.
        /// Resolved property, possibly null. 
        /// 
        ///  should be a resource type or a complex type. 
        ///  
        ResourceProperty IDataServiceProvider.TryResolvePropertyName(Type type, string propertyName)
        { 
            Debug.Assert(type != null, "type != null");
            Debug.Assert(propertyName != null, "propertyName != null");

            // Look for a known type. 
            ResourceType resourceType;
            if (this.TypeCache.TryGetValue(type, out resourceType)) 
            { 
                return resourceType.TryResolvePropertyName(propertyName);
            } 

            // The property wasn't found.
            return null;
        } 

        /// Given the specified name, tries to find a service operation. 
        /// Name of the service operation to resolve. 
        /// Resolved operation, possibly null.
        ServiceOperation IDataServiceProvider.TryResolveServiceOperation(string name) 
        {
            Debug.Assert(name != null, "name != null");

            ServiceOperation result; 
            if (this.metadata.ServiceOperations.TryGetValue(name, out result))
            { 
                return result; 
            }
            else 
            {
                return null;
            }
        } 

        /// Given the specified name, tries to find a type. 
        /// Name of the type to resolve. 
        /// Resolved resource type, possibly null.
        ResourceType IDataServiceProvider.TryResolveTypeName(string name) 
        {
            Debug.Assert(name != null, "name != null");
            Debug.Assert(this.metadata != null, "this.metadata != null");
            Debug.Assert(this.TypeCache != null, "this.TypeCache != null"); 
            foreach (ResourceType t in this.TypeCache.Values)
            { 
                if (t.FullName == name) 
                {
                    return t; 
                }
            }

            return null; 
        }
        #endregion //IDataServiceProvider Members 
 
        /// Releases the current data source object as necessary.
        void IDisposable.Dispose() 
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        } 

        ///  
        /// Returns the type of the IEnumerable if the type implements IEnumerable interface; null otherwise. 
        /// 
        /// type that needs to be checked 
        /// Element type if the type implements IEnumerable, else returns null
        internal static Type GetIEnumerableElement(Type type)
        {
            return GetGenericInterfaceElementType(type, IEnumerableTypeFilter); 
        }
 
        ///  
        /// Returns the "T" in the IQueryable of T implementation of type.
        ///  
        /// Type to check.
        /// filter against which the type is checked
        /// 
        /// The element type for the generic IQueryable interface of the type, 
        /// or null if it has none or if it's ambiguous.
        ///  
        internal static Type GetGenericInterfaceElementType(Type type, TypeFilter typeFilter) 
        {
            Debug.Assert(type != null, "type != null"); 
            Debug.Assert(!type.IsGenericTypeDefinition, "!type.IsGenericTypeDefinition");

            if (typeFilter(type, null))
            { 
                return type.GetGenericArguments()[0];
            } 
 
            Type[] queriables = type.FindInterfaces(typeFilter, null);
            if (queriables != null && queriables.Length == 1) 
            {
                return queriables[0].GetGenericArguments()[0];
            }
            else 
            {
                return null; 
            } 
        }
 
        /// Adds service operations based on methods of the specified type.
        /// Type with methods to add.
        internal void AddOperationsFromType(Type type)
        { 
            Debug.Assert(type != null, "type != null");
            foreach (MethodInfo methodInfo in type.GetMethods(WebUtil.PublicInstanceBindingFlags | BindingFlags.FlattenHierarchy)) 
            { 
                if (methodInfo.GetCustomAttributes(typeof(WebGetAttribute), true).Length != 0)
                { 
                    this.AddServiceOperation(methodInfo, false);
                }
                else if (methodInfo.GetCustomAttributes(typeof(WebInvokeAttribute), true).Length != 0)
                { 
                    this.AddServiceOperation(methodInfo, true);
                } 
            } 
        }
 
        /// Populates the metadata for the given provider.
        internal void PopulateMetadata()
        {
            // Add the primitive types to the type cache. 
            foreach (ResourceType resourceType in WebUtil.GetPrimitiveTypes())
            { 
                this.TypeCache.Add(resourceType.Type, resourceType); 
            }
 
            this.PopulateMetadata(this.TypeCache, this.EntitySets);

#if ASTORIA_CONTAINMENT
            this.ApplyAccessPaths(); 
#endif
        } 
 
        #region Protected methods.
 
        /// 
        /// Writes the edmx elements for the metadata document.
        /// 
        /// xml writer into which the metadata document needs to be written. 
        protected static void WriteTopLevelSchemaElements(XmlWriter writer)
        { 
            writer.WriteStartElement(XmlConstants.EdmxNamespacePrefix, XmlConstants.EdmxElement, XmlConstants.EdmxNamespace); 
            writer.WriteAttributeString(XmlConstants.EdmxVersion, XmlConstants.EdmxVersionValue);
            writer.WriteStartElement(XmlConstants.EdmxNamespacePrefix, XmlConstants.EdmxDataServicesElement, XmlConstants.EdmxNamespace); 
        }

        /// 
        /// Writes the lop level schema element 
        /// 
        /// xml writer into which the schema node definition is written to 
        /// namespace of the schema 
        /// true if the metadata is compatible with the edm v1 schema
        protected static void WriteSchemaElement(XmlWriter writer, string schemaNamespace, bool compatibleWithV1Schema) 
        {
            if (compatibleWithV1Schema)
            {
                writer.WriteStartElement(XmlConstants.Schema, XmlConstants.EdmV1Namespace); 
            }
            else 
            { 
                writer.WriteStartElement(XmlConstants.Schema, XmlConstants.EdmV2Namespace);
            } 

            writer.WriteAttributeString(XmlConstants.Namespace, schemaNamespace);
            writer.WriteAttributeString(XmlConstants.XmlnsNamespacePrefix, XmlConstants.DataWebNamespacePrefix, null, XmlConstants.DataWebNamespace);
            writer.WriteAttributeString(XmlConstants.XmlnsNamespacePrefix, XmlConstants.DataWebMetadataNamespacePrefix, null, XmlConstants.DataWebMetadataNamespace); 
        }
 
        ///  
        /// Returns the type of the IQueryable if the type implements IQueryable interface
        ///  
        /// clr type on which IQueryable check needs to be performed.
        /// Element type if the property type implements IQueryable, else returns null
        protected static Type GetIQueryableElement(Type type)
        { 
            return GetGenericInterfaceElementType(type, IQueryableTypeFilter);
        } 
 
        /// Writes an attribute in the dataweb metadata namespace.
        /// XmlWriter in which the attribute needs to be written 
        /// Attribute name.
        /// Attribute value.
        protected static void WriteDataWebMetadata(XmlWriter writer, string name, string value)
        { 
            Debug.Assert(writer != null, "writer != null");
            Debug.Assert(name != null, "name != null"); 
            Debug.Assert(value != null, "value != null"); 
            writer.WriteAttributeString(XmlConstants.DataWebMetadataNamespacePrefix, name, XmlConstants.DataWebMetadataNamespace, value);
        } 

        /// Checks that the applied configuration is consistent.
        /// Instance of the data source for the provider.
        /// At this point in initialization, metadata trimming hasn't taken place. 
        protected virtual void CheckConfigurationConsistency(object dataSourceInstance)
        { 
        } 

        /// Releases the current data source object as necessary. 
        /// 
        /// Whether this method is called from an explicit call to Dispose by
        /// the consumer, rather than during finalization.
        ///  
        protected virtual void Dispose(bool disposing)
        { 
            ((IDataServiceProvider)this).DisposeDataSource(); 
        }
 
#if ASTORIA_CONTAINMENT

        /// 
        /// Returns an object that can enumerate all  
        /// instances that apply to this model.
        ///  
        ///  
        /// An object that can enumerate all 
        /// instances that apply to this model. 
        /// 
        protected abstract IEnumerable EnumerateAccessPathAttributes();

#endif 

        ///  
        /// Writes the service operations as FunctionImports in the specified . 
        /// 
        /// Writer to which CSDL is being written to. 
        /// Enumeration of service operations to be written.
        protected void WriteServiceOperations(
            XmlWriter writer,
            IEnumerable serviceOperations) 
        {
            Debug.Assert(writer != null, "writer != null"); 
 
            foreach (ServiceOperation operation in serviceOperations)
            { 
                string returnTypeString;
                string entitySetName = null;
                if (operation.ResultKind == ServiceOperationResultKind.Nothing)
                { 
                    returnTypeString = null;
                } 
                else 
                {
                    ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(operation.ResultType); 
                    returnTypeString = resourceType.FullName;
                    if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
                    {
                        ResourceContainer container = operation.EntitySet; 
                        Debug.Assert(
                            container != null, 
                            "this.TryFindAnyContainerForType(operation.ResultType, out container) -- otherwise, we didn't trim operation '" + operation.Name + "' based on its type having no container."); 
                        entitySetName = container.Name;
                    } 

                    if (operation.ResultKind != ServiceOperationResultKind.QueryWithSingleResult &&
                        operation.ResultKind != ServiceOperationResultKind.DirectValue)
                    { 
                        Debug.Assert(
                            operation.ResultKind == ServiceOperationResultKind.Enumeration || 
                            operation.ResultKind == ServiceOperationResultKind.QueryWithMultipleResults, 
                            operation.ResultKind + " == Enumeration or QueryWithMultipleResults");
                        returnTypeString = String.Format(CultureInfo.InvariantCulture, XmlConstants.EdmCollectionTypeFormat, returnTypeString); 
                    }
                }

                writer.WriteStartElement(XmlConstants.EdmFunctionImportElementName); 
                writer.WriteAttributeString(XmlConstants.Name, operation.Name);
 
                if (entitySetName != null) 
                {
                    writer.WriteAttributeString(XmlConstants.EdmEntitySetAttributeName, entitySetName); 
                }

                if (returnTypeString != null)
                { 
                    writer.WriteAttributeString(XmlConstants.EdmReturnTypeAttributeName, returnTypeString);
                } 
 
                string verb = operation.Invoke ? XmlConstants.HttpMethodPost : XmlConstants.HttpMethodGet;
                BaseServiceProvider.WriteDataWebMetadata(writer, XmlConstants.ServiceOperationHttpMethodName, verb); 

                if (!String.IsNullOrEmpty(operation.MimeType))
                {
                    BaseServiceProvider.WriteDataWebMetadata(writer, XmlConstants.DataWebMimeTypeAttributeName, operation.MimeType); 
                }
 
                foreach (ServiceOperationParameter parameter in operation.Parameters) 
                {
                    writer.WriteStartElement(XmlConstants.EdmParameterElementName); 
                    writer.WriteAttributeString(XmlConstants.Name, parameter.Name);
                    writer.WriteAttributeString(XmlConstants.Type, WebUtil.GetEdmTypeName(parameter.Type));
                    writer.WriteAttributeString(XmlConstants.EdmModeAttributeName, XmlConstants.EdmModeInValue);
                    writer.WriteEndElement(); 
                }
 
                writer.WriteEndElement(); 
            }
        } 

        /// 
        /// Creates the object query for the given resource container and returns it
        ///  
        /// resource container for which IQueryable instance needs to be created
        /// returns the IQueryable instance for the given resource container 
        protected abstract IQueryable GetResourceContainerInstance(ResourceContainer resourceContainer); 

        ///  
        /// Populates the metadata for the given provider
        /// 
        /// list of known types
        /// list of entity sets 
        protected abstract void PopulateMetadata(
            IDictionary knownTypes, IDictionary entitySets); 
 
        /// 
        /// Populate types for metadata specified by the provider 
        /// 
        /// list of types specified by the provider
        /// list of already known types
        /// list of entity sets as specified in the data source type 
        protected abstract void PopulateMetadataForUserSpecifiedTypes(IEnumerable userSpecifiedTypes, IDictionary knownTypes, IEnumerable entitySets);
 
        ///  
        /// Populate metadata for the given clr type.
        ///  
        /// type whose metadata needs to be loaded.
        /// list of already known resource types.
        /// list of entity sets as specified in the data source.
        /// resource type containing metadata for the given clr type. 
        protected abstract ResourceType PopulateMetadataForType(Type type, IDictionary knownTypes, IEnumerable entitySets);
 
        #endregion Protected methods. 

        /// Filter callback for finding IQueryable implementations. 
        /// Type to inspect.
        /// Filter criteria.
        /// true if the specified type is an IQueryable of T; false otherwise.
        private static bool IQueryableTypeFilter(Type m, object filterCriteria) 
        {
            Debug.Assert(m != null, "m != null"); 
            return m.IsGenericType && m.GetGenericTypeDefinition() == typeof(IQueryable<>); 
        }
 
        /// Filter callback for finding IEnumerable implementations.
        /// Type to inspect.
        /// Filter criteria.
        /// true if the specified type is an IEnumerable of T; false otherwise. 
        private static bool IEnumerableTypeFilter(Type m, object filterCriteria)
        { 
            Debug.Assert(m != null, "m != null"); 
            return m.IsGenericType && m.GetGenericTypeDefinition() == typeof(IEnumerable<>);
        } 

        /// 
        /// Removes the entries from  in the
        /// specified . 
        /// 
        /// Type of dictionary key to remove. 
        /// Type of dictionary value to remove. 
        /// Dictionary from which entries should be removed.
        /// Keys for entries to remove from . 
        private static void RemoveKeys(IDictionary dictionary, IEnumerable keys)
        {
            Debug.Assert(dictionary != null, "dictionary != null");
            Debug.Assert(keys != null, "keys != null"); 

            foreach (TKey key in keys) 
            { 
                bool removed = dictionary.Remove(key);
                Debug.Assert(removed, "removed - key " + key.ToString() + " should be present."); 
            }
        }

        ///  
        /// Marks complex types from the specified 
        /// by adding them to the given , 
        /// and adding them to the  
        /// to be examined later if this is the first time they are seen.
        ///  
        /// Set of types that have been marked.
        /// Types that need to be examined further.
        /// Type with properties to mark.
        private static void MarkComplexTypes(HashSet markedTypes, Stack pendingTypes, ResourceType type) 
        {
            foreach (ResourceProperty property in type.Properties) 
            { 
                if (property.TypeKind == ResourceTypeKind.ComplexType)
                { 
                    Debug.Assert(property.ResourceType != null, "property.ResourceType != null");
                    Debug.Assert(property.ResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType, "property.ResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType");
                    bool added = markedTypes.Add(property.ResourceType);
                    if (added) 
                    {
                        pendingTypes.Push(property.ResourceType); 
                    } 
                }
            } 
        }

        /// 
        /// Adds a new  based on the specified  
        /// instance.
        ///  
        /// Method to expose as a service operation. 
        /// Whether invocation is required.
        private void AddServiceOperation(MethodInfo method, bool invoke) 
        {
            Debug.Assert(method != null, "method != null");
            Debug.Assert(!method.IsAbstract, "!method.IsAbstract - if method is abstract, the type is abstract - already checked");
 
            if (this.metadata.ServiceOperations.ContainsKey(method.Name))
            { 
                throw new InvalidOperationException(Strings.BaseServiceProvider_OverloadingNotSupported(this.Type, method)); 
            }
 
            bool hasSingleResult = SingleResultAttribute.MethodHasSingleResult(method);
            ServiceOperationResultKind resultKind;
            Type resultType;
            ResourceType resourceType; 
            if (method.ReturnType == typeof(void))
            { 
                resultKind = ServiceOperationResultKind.Nothing; 
                resultType = typeof(void);
                resourceType = null; 
                this.metadata.EdmVersion1Schema = false;
            }
            else
            { 
                resourceType = this.GetType(method.ReturnType);
                if (resourceType != null) 
                { 
                    resultKind = ServiceOperationResultKind.DirectValue;
                    resultType = method.ReturnType; 
                    this.metadata.EdmVersion1Schema = false;
                }
                else
                { 
                    Type queryableElement = GetGenericInterfaceElementType(method.ReturnType, IQueryableTypeFilter);
                    if (queryableElement != null) 
                    { 
                        resultKind = hasSingleResult ?
                            ServiceOperationResultKind.QueryWithSingleResult : 
                            ServiceOperationResultKind.QueryWithMultipleResults;
                        resultType = queryableElement;
                    }
                    else 
                    {
                        Type enumerableElement = GetIEnumerableElement(method.ReturnType); 
                        if (enumerableElement != null) 
                        {
                            if (hasSingleResult) 
                            {
                                throw new InvalidOperationException(Strings.BaseServiceProvider_IEnumerableAlwaysMultiple(this.Type, method));
                            }
 
                            resultKind = ServiceOperationResultKind.Enumeration;
                            resultType = enumerableElement; 
                        } 
                        else
                        { 
                            resultType = method.ReturnType;
                            resultKind = ServiceOperationResultKind.DirectValue;
                            this.metadata.EdmVersion1Schema = false;
                        } 
                    }
 
                    resourceType = this.PopulateMetadataForType(resultType, this.TypeCache, this.EntitySets.Values); 
                    if (resourceType == null)
                    { 
                        throw new InvalidOperationException(Strings.BaseServiceProvider_UnsupportedReturnType(method, method.ReturnType));
                    }

                    if (hasSingleResult || 
                        (!hasSingleResult &&
                        (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType || 
                         resourceType.ResourceTypeKind == ResourceTypeKind.Primitive))) 
                    {
                        this.metadata.EdmVersion1Schema = false; 
                    }
                }
            }
 
            string mimeType = MimeTypeAttribute.GetMemberMimeType(method);
            ParameterInfo[] parametersInfo = method.GetParameters(); 
            ServiceOperationParameter[] parameters = new ServiceOperationParameter[parametersInfo.Length]; 
            for (int i = 0; i < parameters.Length; i++)
            { 
                if (parametersInfo[i].IsOut || parametersInfo[i].IsRetval)
                {
                    throw new InvalidOperationException(Strings.BaseServiceProvider_ParameterNotIn(method, parametersInfo[i]));
                } 

                if (!WebUtil.IsPrimitiveType(parametersInfo[i].ParameterType)) 
                { 
                    throw new InvalidOperationException(
                        Strings.BaseServiceProvider_ParameterTypeNotSupported(method, parametersInfo[i], parametersInfo[i].ParameterType)); 
                }

                string parameterName = parametersInfo[i].Name ?? "p" + i.ToString(CultureInfo.InvariantCulture);
                parameters[i] = new ServiceOperationParameter(parameterName, parametersInfo[i].ParameterType); 
            }
 
            ServiceOperation operation = new ServiceOperation(method, resultKind, resultType, mimeType, invoke, parameters); 
            this.metadata.ServiceOperations.Add(method.Name, operation);
            if (resourceType != null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) 
            {
                operation.EntitySet = this.TryFindSingleContainerForType(resultType);
                if (operation.EntitySet == null)
                { 
                    throw new InvalidOperationException(
                        Strings.BaseServiceProvider_ServiceOperationMissingSingleEntitySet(method, resourceType.FullName)); 
                } 
            }
        } 

#if ASTORIA_CONTAINMENT

        /// Applies access path attributes to the model. 
        private void ApplyAccessPaths()
        { 
            foreach (AccessPathAttribute path in this.EnumerateAccessPathAttributes()) 
            {
                Debug.Assert(path != null, "path != null -- otherwise EnumerateAccessPathAttributes is incorrect."); 
                Debug.Assert(
                    path.AnnotatedContainer != null,
                    "path.AnnotatedContainer -- otherwise EnumerateAccessPathAttributes is incorrect.");
 
                // When an access path is inferred from schema, full validation hasn't taken place. Validation
                // errors for inferred values from here on should be ignored, with the result that the proposed 
                // access path doesn't take effect. 
                Exception exception = path.Validate(this);
                if (exception != null) 
                {
                    if (path.InferredFromSchema)
                    {
                        continue; 
                    }
                    else 
                    { 
                        throw exception;
                    } 
                }

                // Mark the NavigationProperty with the appropriate values.
                IDataServiceProvider provider = (IDataServiceProvider)this; 
                ResourceContainer child = path.AnnotatedContainer;
                ResourceContainer parent = provider.TryResolveContainerName(path.Parent); 
                ResourceProperty parentProperty = parent.ResourceType.TryResolvePropertyName(path.ParentNavigationProperty); 
                if (parentProperty.ContainmentTarget != null)
                { 
                    throw new InvalidOperationException(
                        Strings.BaseServiceProvider_AccessPathTargetAlreadySpecified(
                            path.ParentNavigationProperty, path.Parent, parentProperty.ContainmentTarget.Name, child.Name));
                } 

                if (parentProperty.Kind != ResourcePropertyKind.ResourceSetReference) 
                { 
                    // Because it's a to-one reference doesn't actually add any syntactic sugar, we skip this case.
                    if (path.InferredFromSchema) 
                    {
                        continue;
                    }
                    else 
                    {
                        throw new InvalidOperationException( 
                            Strings.BaseServiceProvider_AccessPathNotThroughCollection(path.ParentNavigationProperty)); 
                    }
                } 

                parentProperty.SetupContainmentTarget(path.InternalChildKeyMapping, path.InternalParentKeyMapping, child);

                // If the path is canonical, mark the resource container. 
                if (path.Canonical)
                { 
                    CanonicalAccessPathAttribute canonical = (CanonicalAccessPathAttribute)path; 
                    if (child.ContainmentCanonicalParent != null)
                    { 
                        throw new InvalidOperationException(
                            Strings.BaseServiceProvider_CanonicalPathTargetAlreadySpecified(
                                path.ParentNavigationProperty,
                                path.Parent, 
                                child.Name,
                                child.ContainmentCanonicalProperty.Name, 
                                child.ContainmentCanonicalParent.Name)); 
                    }
 
                    child.SetupCanonicalAccessPath(parent, parentProperty, canonical.TopLevelAccess);
                }
            }
        } 

#endif 
 
        /// 
        /// Sets the Rights properties as specified on the given 
        /// .
        /// 
        /// Configuration to apply.
        private void ApplyRights(DataServiceConfiguration configuration) 
        {
            Debug.Assert(configuration != null, "configuration != null"); 
            foreach (ResourceContainer container in this.EntitySets.Values) 
            {
                container.Rights = configuration.GetResourceContainerRights(container); 
            }

            foreach (ServiceOperation operation in this.metadata.ServiceOperations.Values)
            { 
                operation.Rights = configuration.GetServiceOperationRights(operation);
            } 
        } 

        /// Checks that every service operation has a result type with a container. 
        private void CheckServiceOperationContainers()
        {
            foreach (ServiceOperation operation in this.metadata.ServiceOperations.Values)
            { 
                if (operation.ResultKind == ServiceOperationResultKind.Nothing)
                { 
                    continue; 
                }
 
                ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(operation.ResultType);
                if (resourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
                {
                    continue; 
                }
 
                ResourceContainer container; 
                bool containerFound = this.TryFindAnyContainerForType(operation.ResultType, out container);
                if (!containerFound) 
                {
                    throw new InvalidOperationException(
                        Strings.BaseServiceProvider_ServiceOperationTypeHasNoContainer(operation.Name, operation.ResultType));
                } 
            }
        } 
 
        /// 
        /// Returns the resource type for the corresponding clr type 
        /// 
        /// clrType whose corresponding resource type needs to be returned
        /// Returns the resource type
        private ResourceType GetType(Type type) 
        {
            Debug.Assert(type != null, "type != null"); 
 
            // Check for the type directly first
            ResourceType resourceType; 
            if (this.TypeCache.TryGetValue(type, out resourceType))
            {
                return resourceType;
            } 

            // check for nullable types 
            Type elementType = Nullable.GetUnderlyingType(type); 
            if (elementType != null && this.TypeCache.TryGetValue(elementType, out resourceType))
            { 
                return resourceType;
            }

            return null; 
        }
 
        ///  
        /// Removes from metadata resource containers that are not visible to
        /// service consumers. 
        /// 
        private void RemoveHiddenResourceContainers()
        {
            List entriesToRemove = new List(); 
            foreach (KeyValuePair container in this.EntitySets)
            { 
                if (container.Value.IsHidden) 
                {
                    entriesToRemove.Add(container.Key); 
                }
            }

            RemoveKeys(this.EntitySets, entriesToRemove); 
        }
 
        ///  
        /// Removes from metadata service operations that are not visible to
        /// service consumers. 
        /// 
        private void RemoveHiddenServiceOperations()
        {
            List entriesToRemove = new List(); 
            foreach (KeyValuePair operation in this.metadata.ServiceOperations)
            { 
                if (operation.Value.IsHidden) 
                {
                    entriesToRemove.Add(operation.Key); 
                }
            }

            RemoveKeys(this.metadata.ServiceOperations, entriesToRemove); 
        }
 
        ///  
        /// Removes from metadata propertiers that are not visible to
        /// service consumers (those that references hidden resource 
        /// containers).
        /// 
        private void RemoveHiddenProperties()
        { 
            foreach (ResourceType type in this.Types)
            { 
                ResourceType typeEvaluated = type; 
                while (typeEvaluated != null)
                { 
                    List properties = typeEvaluated.PropertiesDeclaredInThisType;
                    Debug.Assert(
                        properties == typeEvaluated.PropertiesDeclaredInThisType,
                        "ResourceType.PropertiesDeclaredInThisType must by a mutable by-reference value for RemoveHiddenProperties to work correctly."); 
                    for (int i = properties.Count - 1; i >= 0; i--)
                    { 
                        ResourceProperty property = properties[i]; 
                        ResourceContainer container = property.ResourceContainer;
                        if (container != null && container.IsHidden) 
                        {
                            properties.RemoveAt(i);
                        }
                    } 

                    typeEvaluated = typeEvaluated.BaseType; 
                } 
            }
        } 

        /// 
        /// Removes types that are no longer visible after applying the service
        /// configuration. 
        /// 
        private void RemoveHiddenTypes() 
        { 
            this.RemoveHiddenReferenceTypes();
            this.RemoveHiddenComplexTypes(); 
        }

        /// 
        /// Removes from metadata reference types that are not accessible 
        /// through visible resource containers or visible service
        /// operations. 
        ///  
        private void RemoveHiddenReferenceTypes()
        { 
            // Remove reference types that are not ancestors nor descendants
            // of any entity sets nor service operations.
            List keysToRemove = new List();
            foreach (KeyValuePair entry in this.TypeCache) 
            {
                ResourceType candidateType = entry.Value; 
                if (candidateType.ResourceTypeKind != ResourceTypeKind.EntityType) 
                {
                    continue; 
                }

                bool visible = false;
                foreach (ResourceContainer container in this.EntitySets.Values) 
                {
                    Debug.Assert(!container.IsHidden, "!container.IsHidden - all hidden container should have been removed before invoking RemoveHiddenTypes"); 
                    Type containerType = container.ElementType; 
                    if (containerType.IsAssignableFrom(candidateType.Type) ||
                        candidateType.Type.IsAssignableFrom(containerType)) 
                    {
                        visible = true;
                        break;
                    } 
                }
 
                if (!visible) 
                {
                    foreach (ServiceOperation operation in this.ServiceOperations) 
                    {
                        Debug.Assert(!operation.IsHidden, "!operation.IsHidden - all hidden service operations should have been removed before invoking RemoveHiddenTypes");
                        Type operationType = operation.ResultType;
                        if (operationType != null && 
                            (operationType.IsAssignableFrom(candidateType.Type) ||
                             candidateType.Type.IsAssignableFrom(operationType))) 
                        { 
                            visible = true;
                            break; 
                        }
                    }

                    if (!visible) 
                    {
                        keysToRemove.Add(entry.Key); 
                    } 
                }
            } 

            RemoveKeys(this.TypeCache, keysToRemove);
        }
 
        /// 
        /// Removes from metadata reference types that are not accessible 
        /// through visible types. 
        /// 
        private void RemoveHiddenComplexTypes() 
        {
            // Make a graph of all available complex types starting from visible
            // reference types, then remove those missing.
            HashSet visibleComplexTypes = new HashSet(EqualityComparer.Default); 
            Stack pendingComplexTypes = new Stack();
            foreach (ResourceType type in this.Types) 
            { 
                // If there is a entity or a complex type which is open, we can't detect
                // which complex types to hide. Hence we should just return and assume that 
                // all complex types are getting used.
#if ASTORIA_OPEN_OBJECT
                if (type.IsOpenType)
                { 
                    return;
                } 
#endif 

                if (type.ResourceTypeKind == ResourceTypeKind.EntityType) 
                {
                    MarkComplexTypes(visibleComplexTypes, pendingComplexTypes, type);
                }
            } 

            // If the complex type is returned by a service operation, it should be visible. 
            foreach (ServiceOperation serviceOperation in this.ServiceOperations) 
            {
                if (serviceOperation.ResultKind != ServiceOperationResultKind.Nothing) 
                {
                    ResourceType resourceType = this.TypeCache[serviceOperation.ResultType];
                    Debug.Assert(resourceType != null, "resourceType != null");
                    if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) 
                    {
                        if (visibleComplexTypes.Add(resourceType)) 
                        { 
                            pendingComplexTypes.Push(resourceType);
                        } 
                    }
                }
            }
 
            while (pendingComplexTypes.Count > 0)
            { 
                ResourceType type = pendingComplexTypes.Pop(); 
                Debug.Assert(type.ResourceTypeKind == ResourceTypeKind.ComplexType, "type.ResourceTypeKind == ResourceTypeKind.ComplexType");
                MarkComplexTypes(visibleComplexTypes, pendingComplexTypes, type); 
            }

            List keysToRemove = new List();
            foreach (KeyValuePair entry in this.TypeCache) 
            {
                if (entry.Value.ResourceTypeKind == ResourceTypeKind.ComplexType && 
                    !visibleComplexTypes.Contains(entry.Value)) 
                {
                    keysToRemove.Add(entry.Key); 
                }
            }

            RemoveKeys(this.TypeCache, keysToRemove); 
        }
 
        ///  
        /// Looks for the first resource container that the specified 
        /// could belong to. 
        /// 
        /// Type to look for.
        /// After the method returns, the container to which the type could belong.
        /// true if a container was found; false otherwise. 
        private bool TryFindAnyContainerForType(Type type, out ResourceContainer container)
        { 
            Debug.Assert(type != null, "type != null"); 

            foreach (ResourceContainer c in this.EntitySets.Values) 
            {
                if (c.ElementType.IsAssignableFrom(type))
                {
                    container = c; 
                    return true;
                } 
            } 

            container = default(ResourceContainer); 
            return false;
        }

        ///  
        /// Looks for a single resource container that the specified 
        /// could belong to. 
        ///  
        /// Type to look for.
        /// The single container that can hold the specified type; null otherwise. 
        private ResourceContainer TryFindSingleContainerForType(Type type)
        {
            Debug.Assert(type != null, "type != null");
 
            ResourceContainer result = null;
            foreach (ResourceContainer c in this.EntitySets.Values) 
            { 
                if (c.ElementType.IsAssignableFrom(type))
                { 
                    if (result != null)
                    {
                        return null;
                    } 

                    result = c; 
                } 
            }
 
            return result;
        }

        ///  
        /// Figure out all the disallowed entity types for each entity set in the system.
        ///  
        private void DisallowDerivedEntityTypesWithNavProperties() 
        {
            // Perform this check for entity sets whose root types takes part in inheritance 
            foreach (ResourceContainer entitySet in this.EntitySets.Values.Where(es => es.ResourceType.HasDerivedTypes))
            {
                foreach (ResourceType resourceType in this.Types)
                { 
                    // only takes leaf nodes into account - to make it slightly more performant
                    // check for sub-class 
                    if (!resourceType.HasDerivedTypes && resourceType.Type.IsSubclassOf(entitySet.ResourceType.Type)) 
                    {
                        // walk up the hierarchy and then down, adding types which are disallowed 
                        Queue hierarchyChain = new Queue();
                        ResourceType currentType = resourceType;
                        do
                        { 
                            hierarchyChain.Enqueue(currentType);
                            currentType = currentType.BaseType; 
                        } 
                        while (currentType != entitySet.ResourceType);
 
                        // If one of the type in the hierarchy chain has navigation property, then
                        // all the sub-types need to be added to the entity set as disallowed types.
                        bool hasNavigationProperties = false;
                        while (hierarchyChain.Count != 0) 
                        {
                            currentType = hierarchyChain.Dequeue(); 
                            if (hasNavigationProperties || currentType.HasNavigationProperties()) 
                            {
                                entitySet.AddDisallowedDerivedType(currentType); 
                                hasNavigationProperties = true;
                            }
                        }
                    } 
                }
            } 
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Abstract Class which contains the common code for ObjectContextServiceProvider
//      and ReflectionServiceProvider 
//  
//
// @owner  [....] 
//---------------------------------------------------------------------

namespace System.Data.Services.Providers
{ 
    #region Namespaces.
 
    using System; 
    using System.Collections;
    using System.Collections.Generic; 
    using System.Data.Services.Caching;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq; 
    using System.Reflection;
    using System.ServiceModel.Web; 
    using System.Xml; 

    #endregion Namespaces. 

    /// Provides a reflection-based  implementation.
    internal abstract class BaseServiceProvider : IDataServiceProvider, IDisposable, IUpdatable, IExpandProvider
    { 
        /// Bindings Flags to be used for reflection.
        protected const BindingFlags ResourceContainerBindingFlags = WebUtil.PublicInstanceBindingFlags; 
 
        /// Instance from which data is provided.
        private object instance; 

        /// Metadata to be used by the service provider.
        private MetadataCacheItem metadata;
 
        /// 
        /// Initializes a new System.Data.Services.BaseServiceProvider instance. 
        ///  
        /// Metadata for this provider.
        /// instance of the data source provider. 
        protected BaseServiceProvider(MetadataCacheItem metadata, object dataSourceInstance)
        {
            WebUtil.CheckArgumentNull(metadata, "metadata");
            WebUtil.CheckArgumentNull(dataSourceInstance, "dataSourceInstance"); 
            this.metadata = metadata;
            this.instance = dataSourceInstance; 
        } 

        /// Returns the instance from which data is provided. 
        public object CurrentDataSource
        {
            [DebuggerStepThrough]
            get 
            {
                // Many debuggers will try to display this property, and we don't want to trigger an assertion. 
                Debug.Assert( 
                    System.Diagnostics.Debugger.IsAttached || this.instance != null,
                    "this.instance != null -- otherwise CurrentDataSource is accessed before initialization or after disposal."); 
                return this.instance;
            }
        }
 
        /// Gets a value indicating whether null propagation is required in expression trees.
        public abstract bool NullPropagationRequired 
        { 
            get;
        } 

        /// 
        /// Provides a name for the context in which all resource containers are.
        ///  
        public abstract string ResourceContextName
        { 
            get; 
        }
 
        /// Returns the list of entity sets.
        protected IDictionary EntitySets
        {
            [DebuggerStepThrough] 
            get { return this.metadata.EntitySets; }
        } 
 
        /// Returns all known service operations.
        protected IEnumerable ServiceOperations 
        {
            [DebuggerStepThrough]
            get { return this.metadata.ServiceOperations.Values; }
        } 

        /// Target type for the data provider  
        protected Type Type 
        {
            [DebuggerStepThrough] 
            get { return this.metadata.Type; }
        }

        /// Returns the list of resource types. 
        protected IEnumerable Types
        { 
            [DebuggerStepThrough] 
            get { return this.metadata.TypeCache.Values; }
        } 

        /// Returns true if the metadata is compatible with Edm V1 Schema otherwise false
        protected bool CompatibleWithV1Schema
        { 
            get { return this.metadata.EdmVersion1Schema; }
        } 
 
        /// Cache of resource properties per type.
        private Dictionary TypeCache 
        {
            [DebuggerStepThrough]
            get { return this.metadata.TypeCache; }
        } 

        /// Applies expansions to the specified . 
        ///  object to expand. 
        /// A collection of ordered  paths.
        ///  
        /// An  object of the same type as the given ,
        /// with the results including the specified .
        /// 
        ///  
        /// This method may modify the  to indicate which expansions
        /// are included. 
        /// 
        /// The returned  may implement the 
        /// interface to provide enumerable objects for the expansions; otherwise, the expanded 
        /// information is expected to be found directly in the enumerated objects.
        /// 
        public abstract IEnumerable ApplyExpansions(IQueryable queryable, ICollection expandPaths);
 
        /// 
        /// Get the container name corresponding to the given navigation property 
        /// The declaring type container name refers to the entity set that the declaring type belongs to 
        /// 
        /// name of the entity container that the declaring type belongs to 
        /// declaring type
        /// resource navigation property
        /// name of the container that this property refers to
        public abstract ResourceContainer GetContainer(string declaringTypeContainerName, Type declaringResourceType, ResourceProperty resourceProperty); 

        /// Gets the name of the container that holds this resource type. This method is called for open types only 
        /// Resource to get container for. 
        /// 
        /// The name of the container for the specified resource; null if it cannot 
        /// be determined.
        /// 
        public abstract ResourceContainer GetContainerForResourceType(Type resourceType);
 
        /// Gets the metadata document for this provider.
        /// Writer to which metadata XML should be written. 
        public abstract void GetMetadata(XmlWriter writer); 

        ///  
        /// Get the list of property names that form the ETag for the given resource instance
        /// 
        /// name of the container to which the resource belongs t
        /// clr type of the resource whose etag properties need to be fetched 
        /// list of property names that form the ETag for the given resource instance
        public abstract ICollection GetETagProperties(string containerName, Type resourceClrType); 
 
        #region IUpdatable Members
 
        /// 
        /// Creates the resource of the given type and belonging to the given container
        /// 
        /// container name to which the resource needs to be added 
        /// full type name i.e. Namespace qualified type name of the resource
        /// object representing a resource of given type and belonging to the given container 
        public abstract object CreateResource(string containerName, string fullTypeName); 

        ///  
        /// Gets the resource of the given type that the query points to
        /// 
        /// query pointing to a particular resource
        /// full type name i.e. Namespace qualified type name of the resource 
        /// object representing a resource of given type and as referenced by the query
        public abstract object GetResource(IQueryable query, string fullTypeName); 
 
        /// 
        /// Checks whether the specified  is ordered. 
        /// 
        /// Type to check.
        /// true if the type may be ordered; false otherwise.
        ///  
        /// The ordering may still fail at runtime; this method is currently
        /// used for cleaner error messages only. 
        ///  
        public virtual bool GetTypeIsOrdered(Type type)
        { 
            Debug.Assert(type != null, "type != null");
            return type == typeof(object) || WebUtil.IsPrimitiveType(type);
        }
 
        /// 
        /// Resets the value of the given resource to its default value 
        ///  
        /// resource whose value needs to be reset
        /// same resource with its value reset 
        public abstract object ResetResource(object resource);

        /// 
        /// Sets the value of the given property on the target object 
        /// 
        /// target object which defines the property 
        /// name of the property whose value needs to be updated 
        /// value of the property
        public abstract void SetValue(object targetResource, string propertyName, object propertyValue); 

        /// 
        /// Gets the value of the given property on the target object
        ///  
        /// target object which defines the property
        /// name of the property whose value needs to be updated 
        /// the value of the property for the given target resource 
        public abstract object GetValue(object targetResource, string propertyName);
 
        /// 
        /// Sets the value of the given reference property on the target object
        /// 
        /// target object which defines the property 
        /// name of the property whose value needs to be updated
        /// value of the property 
        public abstract void SetReference(object targetResource, string propertyName, object propertyValue); 

        ///  
        /// Adds the given value to the collection
        /// 
        /// target object which defines the property
        /// name of the property whose value needs to be updated 
        /// value of the property which needs to be added
        public abstract void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded); 
 
        /// 
        /// Removes the given value from the collection 
        /// 
        /// target object which defines the property
        /// name of the property whose value needs to be updated
        /// value of the property which needs to be removed 
        public abstract void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved);
 
        ///  
        /// Delete the given resource
        ///  
        /// resource that needs to be deleted
        public abstract void DeleteResource(object targetResource);

        ///  
        /// Saves all the pending changes made till now
        ///  
        public abstract void SaveChanges(); 

        ///  
        /// Returns the actual instance of the resource represented by the given resource object
        /// 
        /// object representing the resource whose instance needs to be fetched
        /// The actual instance of the resource represented by the given resource object 
        public abstract object ResolveResource(object resource);
 
        ///  
        /// Revert all the pending changes.
        ///  
        public abstract void ClearChanges();

        #endregion
 
        #region IDataServiceProvider Members
 
        ///  
        /// Applies the settings specified in the given .
        ///  
        /// Configuration to apply.
        /// Instance of the data source for the provider.
        void IDataServiceProvider.ApplyConfiguration(DataServiceConfiguration configuration, object dataSourceInstance)
        { 
            Debug.Assert(configuration != null, "configuration != null");
 
            this.PopulateMetadataForUserSpecifiedTypes(configuration.GetKnownTypes(), this.TypeCache, this.EntitySets.Values); 
            this.ApplyRights(configuration);
            this.CheckConfigurationConsistency(dataSourceInstance); 
            this.RemoveHiddenResourceContainers();
            this.RemoveHiddenServiceOperations();
            this.RemoveHiddenProperties();
            this.RemoveHiddenTypes(); 
            this.CheckServiceOperationContainers();
            this.DisallowDerivedEntityTypesWithNavProperties(); 
        } 

        /// Disposes of the underlying . 
        void IDataServiceProvider.DisposeDataSource()
        {
            WebUtil.Dispose(this.instance);
            this.instance = null; 
        }
 
        /// Gets all available containers. 
        /// An enumerable object with all available containers.
        IEnumerable IDataServiceProvider.GetContainers() 
        {
            return this.EntitySets.Values;
        }
 
        /// 
        /// Returns the IQueryable that represents the container. 
        ///  
        /// Name of the resource container.
        ///  
        /// An IQueryable that represents the container; null if there is
        /// no container for the specified name.
        /// 
        IQueryable IDataServiceProvider.ResolveContainerName(string containerName) 
        {
            ResourceContainer resourceContainer = ((IDataServiceProvider)this).TryResolveContainerName(containerName); 
            if (resourceContainer != null) 
            {
                return this.GetResourceContainerInstance(resourceContainer); 
            }
            else
            {
                return null; 
            }
        } 
 
        ///  Gets the root type of the given resource type.
        /// ResourceType to get least derived type for. 
        /// The least derived type for the specified .
        ResourceType IDataServiceProvider.GetRootType(ResourceType resourceType)
        {
            if (resourceType == null) 
            {
                return resourceType; 
            } 

            while (resourceType.BaseType != null) 
            {
                resourceType = resourceType.BaseType;
            }
 
            return resourceType;
        } 
 
        /// Gets the  for the specified .
        ///  to map into a . 
        /// The  that  maps to for this provider.
        ResourceType IDataServiceProvider.GetResourceType(Type type)
        {
            Debug.Assert(type != null, "type != null"); 

            return this.GetType(type); 
        } 

        /// Gets the kind of resource that the type is. 
        ///  to check.
        /// The kind of resource that the specified  is.
        ResourceTypeKind IDataServiceProvider.GetResourceTypeKind(Type type)
        { 
            Debug.Assert(type != null, "type != null");
 
            ResourceType resourceType = this.GetType(type); 
            Debug.Assert(resourceType != null, "resourceType != null -- otherwise the specified Type '" + type + "' isn't part of the known type system.");
            return resourceType.ResourceTypeKind; 
        }

        /// Given the specified name, tries to find a resource container.
        /// Name of the resource container to resolve. 
        /// Resolved resource container, possibly null.
        ResourceContainer IDataServiceProvider.TryResolveContainerName(string name) 
        { 
            ResourceContainer resourceContainer;
            if (this.EntitySets.TryGetValue(name, out resourceContainer)) 
            {
                return resourceContainer;
            }
            else 
            {
                return null; 
            } 
        }
 
        /// Given the specified CLR type, finds the named property.
        /// Type to get property for.
        /// Name of property to resolve.
        /// Resolved property, possibly null. 
        /// 
        ///  should be a resource type or a complex type. 
        ///  
        ResourceProperty IDataServiceProvider.TryResolvePropertyName(Type type, string propertyName)
        { 
            Debug.Assert(type != null, "type != null");
            Debug.Assert(propertyName != null, "propertyName != null");

            // Look for a known type. 
            ResourceType resourceType;
            if (this.TypeCache.TryGetValue(type, out resourceType)) 
            { 
                return resourceType.TryResolvePropertyName(propertyName);
            } 

            // The property wasn't found.
            return null;
        } 

        /// Given the specified name, tries to find a service operation. 
        /// Name of the service operation to resolve. 
        /// Resolved operation, possibly null.
        ServiceOperation IDataServiceProvider.TryResolveServiceOperation(string name) 
        {
            Debug.Assert(name != null, "name != null");

            ServiceOperation result; 
            if (this.metadata.ServiceOperations.TryGetValue(name, out result))
            { 
                return result; 
            }
            else 
            {
                return null;
            }
        } 

        /// Given the specified name, tries to find a type. 
        /// Name of the type to resolve. 
        /// Resolved resource type, possibly null.
        ResourceType IDataServiceProvider.TryResolveTypeName(string name) 
        {
            Debug.Assert(name != null, "name != null");
            Debug.Assert(this.metadata != null, "this.metadata != null");
            Debug.Assert(this.TypeCache != null, "this.TypeCache != null"); 
            foreach (ResourceType t in this.TypeCache.Values)
            { 
                if (t.FullName == name) 
                {
                    return t; 
                }
            }

            return null; 
        }
        #endregion //IDataServiceProvider Members 
 
        /// Releases the current data source object as necessary.
        void IDisposable.Dispose() 
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        } 

        ///  
        /// Returns the type of the IEnumerable if the type implements IEnumerable interface; null otherwise. 
        /// 
        /// type that needs to be checked 
        /// Element type if the type implements IEnumerable, else returns null
        internal static Type GetIEnumerableElement(Type type)
        {
            return GetGenericInterfaceElementType(type, IEnumerableTypeFilter); 
        }
 
        ///  
        /// Returns the "T" in the IQueryable of T implementation of type.
        ///  
        /// Type to check.
        /// filter against which the type is checked
        /// 
        /// The element type for the generic IQueryable interface of the type, 
        /// or null if it has none or if it's ambiguous.
        ///  
        internal static Type GetGenericInterfaceElementType(Type type, TypeFilter typeFilter) 
        {
            Debug.Assert(type != null, "type != null"); 
            Debug.Assert(!type.IsGenericTypeDefinition, "!type.IsGenericTypeDefinition");

            if (typeFilter(type, null))
            { 
                return type.GetGenericArguments()[0];
            } 
 
            Type[] queriables = type.FindInterfaces(typeFilter, null);
            if (queriables != null && queriables.Length == 1) 
            {
                return queriables[0].GetGenericArguments()[0];
            }
            else 
            {
                return null; 
            } 
        }
 
        /// Adds service operations based on methods of the specified type.
        /// Type with methods to add.
        internal void AddOperationsFromType(Type type)
        { 
            Debug.Assert(type != null, "type != null");
            foreach (MethodInfo methodInfo in type.GetMethods(WebUtil.PublicInstanceBindingFlags | BindingFlags.FlattenHierarchy)) 
            { 
                if (methodInfo.GetCustomAttributes(typeof(WebGetAttribute), true).Length != 0)
                { 
                    this.AddServiceOperation(methodInfo, false);
                }
                else if (methodInfo.GetCustomAttributes(typeof(WebInvokeAttribute), true).Length != 0)
                { 
                    this.AddServiceOperation(methodInfo, true);
                } 
            } 
        }
 
        /// Populates the metadata for the given provider.
        internal void PopulateMetadata()
        {
            // Add the primitive types to the type cache. 
            foreach (ResourceType resourceType in WebUtil.GetPrimitiveTypes())
            { 
                this.TypeCache.Add(resourceType.Type, resourceType); 
            }
 
            this.PopulateMetadata(this.TypeCache, this.EntitySets);

#if ASTORIA_CONTAINMENT
            this.ApplyAccessPaths(); 
#endif
        } 
 
        #region Protected methods.
 
        /// 
        /// Writes the edmx elements for the metadata document.
        /// 
        /// xml writer into which the metadata document needs to be written. 
        protected static void WriteTopLevelSchemaElements(XmlWriter writer)
        { 
            writer.WriteStartElement(XmlConstants.EdmxNamespacePrefix, XmlConstants.EdmxElement, XmlConstants.EdmxNamespace); 
            writer.WriteAttributeString(XmlConstants.EdmxVersion, XmlConstants.EdmxVersionValue);
            writer.WriteStartElement(XmlConstants.EdmxNamespacePrefix, XmlConstants.EdmxDataServicesElement, XmlConstants.EdmxNamespace); 
        }

        /// 
        /// Writes the lop level schema element 
        /// 
        /// xml writer into which the schema node definition is written to 
        /// namespace of the schema 
        /// true if the metadata is compatible with the edm v1 schema
        protected static void WriteSchemaElement(XmlWriter writer, string schemaNamespace, bool compatibleWithV1Schema) 
        {
            if (compatibleWithV1Schema)
            {
                writer.WriteStartElement(XmlConstants.Schema, XmlConstants.EdmV1Namespace); 
            }
            else 
            { 
                writer.WriteStartElement(XmlConstants.Schema, XmlConstants.EdmV2Namespace);
            } 

            writer.WriteAttributeString(XmlConstants.Namespace, schemaNamespace);
            writer.WriteAttributeString(XmlConstants.XmlnsNamespacePrefix, XmlConstants.DataWebNamespacePrefix, null, XmlConstants.DataWebNamespace);
            writer.WriteAttributeString(XmlConstants.XmlnsNamespacePrefix, XmlConstants.DataWebMetadataNamespacePrefix, null, XmlConstants.DataWebMetadataNamespace); 
        }
 
        ///  
        /// Returns the type of the IQueryable if the type implements IQueryable interface
        ///  
        /// clr type on which IQueryable check needs to be performed.
        /// Element type if the property type implements IQueryable, else returns null
        protected static Type GetIQueryableElement(Type type)
        { 
            return GetGenericInterfaceElementType(type, IQueryableTypeFilter);
        } 
 
        /// Writes an attribute in the dataweb metadata namespace.
        /// XmlWriter in which the attribute needs to be written 
        /// Attribute name.
        /// Attribute value.
        protected static void WriteDataWebMetadata(XmlWriter writer, string name, string value)
        { 
            Debug.Assert(writer != null, "writer != null");
            Debug.Assert(name != null, "name != null"); 
            Debug.Assert(value != null, "value != null"); 
            writer.WriteAttributeString(XmlConstants.DataWebMetadataNamespacePrefix, name, XmlConstants.DataWebMetadataNamespace, value);
        } 

        /// Checks that the applied configuration is consistent.
        /// Instance of the data source for the provider.
        /// At this point in initialization, metadata trimming hasn't taken place. 
        protected virtual void CheckConfigurationConsistency(object dataSourceInstance)
        { 
        } 

        /// Releases the current data source object as necessary. 
        /// 
        /// Whether this method is called from an explicit call to Dispose by
        /// the consumer, rather than during finalization.
        ///  
        protected virtual void Dispose(bool disposing)
        { 
            ((IDataServiceProvider)this).DisposeDataSource(); 
        }
 
#if ASTORIA_CONTAINMENT

        /// 
        /// Returns an object that can enumerate all  
        /// instances that apply to this model.
        ///  
        ///  
        /// An object that can enumerate all 
        /// instances that apply to this model. 
        /// 
        protected abstract IEnumerable EnumerateAccessPathAttributes();

#endif 

        ///  
        /// Writes the service operations as FunctionImports in the specified . 
        /// 
        /// Writer to which CSDL is being written to. 
        /// Enumeration of service operations to be written.
        protected void WriteServiceOperations(
            XmlWriter writer,
            IEnumerable serviceOperations) 
        {
            Debug.Assert(writer != null, "writer != null"); 
 
            foreach (ServiceOperation operation in serviceOperations)
            { 
                string returnTypeString;
                string entitySetName = null;
                if (operation.ResultKind == ServiceOperationResultKind.Nothing)
                { 
                    returnTypeString = null;
                } 
                else 
                {
                    ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(operation.ResultType); 
                    returnTypeString = resourceType.FullName;
                    if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
                    {
                        ResourceContainer container = operation.EntitySet; 
                        Debug.Assert(
                            container != null, 
                            "this.TryFindAnyContainerForType(operation.ResultType, out container) -- otherwise, we didn't trim operation '" + operation.Name + "' based on its type having no container."); 
                        entitySetName = container.Name;
                    } 

                    if (operation.ResultKind != ServiceOperationResultKind.QueryWithSingleResult &&
                        operation.ResultKind != ServiceOperationResultKind.DirectValue)
                    { 
                        Debug.Assert(
                            operation.ResultKind == ServiceOperationResultKind.Enumeration || 
                            operation.ResultKind == ServiceOperationResultKind.QueryWithMultipleResults, 
                            operation.ResultKind + " == Enumeration or QueryWithMultipleResults");
                        returnTypeString = String.Format(CultureInfo.InvariantCulture, XmlConstants.EdmCollectionTypeFormat, returnTypeString); 
                    }
                }

                writer.WriteStartElement(XmlConstants.EdmFunctionImportElementName); 
                writer.WriteAttributeString(XmlConstants.Name, operation.Name);
 
                if (entitySetName != null) 
                {
                    writer.WriteAttributeString(XmlConstants.EdmEntitySetAttributeName, entitySetName); 
                }

                if (returnTypeString != null)
                { 
                    writer.WriteAttributeString(XmlConstants.EdmReturnTypeAttributeName, returnTypeString);
                } 
 
                string verb = operation.Invoke ? XmlConstants.HttpMethodPost : XmlConstants.HttpMethodGet;
                BaseServiceProvider.WriteDataWebMetadata(writer, XmlConstants.ServiceOperationHttpMethodName, verb); 

                if (!String.IsNullOrEmpty(operation.MimeType))
                {
                    BaseServiceProvider.WriteDataWebMetadata(writer, XmlConstants.DataWebMimeTypeAttributeName, operation.MimeType); 
                }
 
                foreach (ServiceOperationParameter parameter in operation.Parameters) 
                {
                    writer.WriteStartElement(XmlConstants.EdmParameterElementName); 
                    writer.WriteAttributeString(XmlConstants.Name, parameter.Name);
                    writer.WriteAttributeString(XmlConstants.Type, WebUtil.GetEdmTypeName(parameter.Type));
                    writer.WriteAttributeString(XmlConstants.EdmModeAttributeName, XmlConstants.EdmModeInValue);
                    writer.WriteEndElement(); 
                }
 
                writer.WriteEndElement(); 
            }
        } 

        /// 
        /// Creates the object query for the given resource container and returns it
        ///  
        /// resource container for which IQueryable instance needs to be created
        /// returns the IQueryable instance for the given resource container 
        protected abstract IQueryable GetResourceContainerInstance(ResourceContainer resourceContainer); 

        ///  
        /// Populates the metadata for the given provider
        /// 
        /// list of known types
        /// list of entity sets 
        protected abstract void PopulateMetadata(
            IDictionary knownTypes, IDictionary entitySets); 
 
        /// 
        /// Populate types for metadata specified by the provider 
        /// 
        /// list of types specified by the provider
        /// list of already known types
        /// list of entity sets as specified in the data source type 
        protected abstract void PopulateMetadataForUserSpecifiedTypes(IEnumerable userSpecifiedTypes, IDictionary knownTypes, IEnumerable entitySets);
 
        ///  
        /// Populate metadata for the given clr type.
        ///  
        /// type whose metadata needs to be loaded.
        /// list of already known resource types.
        /// list of entity sets as specified in the data source.
        /// resource type containing metadata for the given clr type. 
        protected abstract ResourceType PopulateMetadataForType(Type type, IDictionary knownTypes, IEnumerable entitySets);
 
        #endregion Protected methods. 

        /// Filter callback for finding IQueryable implementations. 
        /// Type to inspect.
        /// Filter criteria.
        /// true if the specified type is an IQueryable of T; false otherwise.
        private static bool IQueryableTypeFilter(Type m, object filterCriteria) 
        {
            Debug.Assert(m != null, "m != null"); 
            return m.IsGenericType && m.GetGenericTypeDefinition() == typeof(IQueryable<>); 
        }
 
        /// Filter callback for finding IEnumerable implementations.
        /// Type to inspect.
        /// Filter criteria.
        /// true if the specified type is an IEnumerable of T; false otherwise. 
        private static bool IEnumerableTypeFilter(Type m, object filterCriteria)
        { 
            Debug.Assert(m != null, "m != null"); 
            return m.IsGenericType && m.GetGenericTypeDefinition() == typeof(IEnumerable<>);
        } 

        /// 
        /// Removes the entries from  in the
        /// specified . 
        /// 
        /// Type of dictionary key to remove. 
        /// Type of dictionary value to remove. 
        /// Dictionary from which entries should be removed.
        /// Keys for entries to remove from . 
        private static void RemoveKeys(IDictionary dictionary, IEnumerable keys)
        {
            Debug.Assert(dictionary != null, "dictionary != null");
            Debug.Assert(keys != null, "keys != null"); 

            foreach (TKey key in keys) 
            { 
                bool removed = dictionary.Remove(key);
                Debug.Assert(removed, "removed - key " + key.ToString() + " should be present."); 
            }
        }

        ///  
        /// Marks complex types from the specified 
        /// by adding them to the given , 
        /// and adding them to the  
        /// to be examined later if this is the first time they are seen.
        ///  
        /// Set of types that have been marked.
        /// Types that need to be examined further.
        /// Type with properties to mark.
        private static void MarkComplexTypes(HashSet markedTypes, Stack pendingTypes, ResourceType type) 
        {
            foreach (ResourceProperty property in type.Properties) 
            { 
                if (property.TypeKind == ResourceTypeKind.ComplexType)
                { 
                    Debug.Assert(property.ResourceType != null, "property.ResourceType != null");
                    Debug.Assert(property.ResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType, "property.ResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType");
                    bool added = markedTypes.Add(property.ResourceType);
                    if (added) 
                    {
                        pendingTypes.Push(property.ResourceType); 
                    } 
                }
            } 
        }

        /// 
        /// Adds a new  based on the specified  
        /// instance.
        ///  
        /// Method to expose as a service operation. 
        /// Whether invocation is required.
        private void AddServiceOperation(MethodInfo method, bool invoke) 
        {
            Debug.Assert(method != null, "method != null");
            Debug.Assert(!method.IsAbstract, "!method.IsAbstract - if method is abstract, the type is abstract - already checked");
 
            if (this.metadata.ServiceOperations.ContainsKey(method.Name))
            { 
                throw new InvalidOperationException(Strings.BaseServiceProvider_OverloadingNotSupported(this.Type, method)); 
            }
 
            bool hasSingleResult = SingleResultAttribute.MethodHasSingleResult(method);
            ServiceOperationResultKind resultKind;
            Type resultType;
            ResourceType resourceType; 
            if (method.ReturnType == typeof(void))
            { 
                resultKind = ServiceOperationResultKind.Nothing; 
                resultType = typeof(void);
                resourceType = null; 
                this.metadata.EdmVersion1Schema = false;
            }
            else
            { 
                resourceType = this.GetType(method.ReturnType);
                if (resourceType != null) 
                { 
                    resultKind = ServiceOperationResultKind.DirectValue;
                    resultType = method.ReturnType; 
                    this.metadata.EdmVersion1Schema = false;
                }
                else
                { 
                    Type queryableElement = GetGenericInterfaceElementType(method.ReturnType, IQueryableTypeFilter);
                    if (queryableElement != null) 
                    { 
                        resultKind = hasSingleResult ?
                            ServiceOperationResultKind.QueryWithSingleResult : 
                            ServiceOperationResultKind.QueryWithMultipleResults;
                        resultType = queryableElement;
                    }
                    else 
                    {
                        Type enumerableElement = GetIEnumerableElement(method.ReturnType); 
                        if (enumerableElement != null) 
                        {
                            if (hasSingleResult) 
                            {
                                throw new InvalidOperationException(Strings.BaseServiceProvider_IEnumerableAlwaysMultiple(this.Type, method));
                            }
 
                            resultKind = ServiceOperationResultKind.Enumeration;
                            resultType = enumerableElement; 
                        } 
                        else
                        { 
                            resultType = method.ReturnType;
                            resultKind = ServiceOperationResultKind.DirectValue;
                            this.metadata.EdmVersion1Schema = false;
                        } 
                    }
 
                    resourceType = this.PopulateMetadataForType(resultType, this.TypeCache, this.EntitySets.Values); 
                    if (resourceType == null)
                    { 
                        throw new InvalidOperationException(Strings.BaseServiceProvider_UnsupportedReturnType(method, method.ReturnType));
                    }

                    if (hasSingleResult || 
                        (!hasSingleResult &&
                        (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType || 
                         resourceType.ResourceTypeKind == ResourceTypeKind.Primitive))) 
                    {
                        this.metadata.EdmVersion1Schema = false; 
                    }
                }
            }
 
            string mimeType = MimeTypeAttribute.GetMemberMimeType(method);
            ParameterInfo[] parametersInfo = method.GetParameters(); 
            ServiceOperationParameter[] parameters = new ServiceOperationParameter[parametersInfo.Length]; 
            for (int i = 0; i < parameters.Length; i++)
            { 
                if (parametersInfo[i].IsOut || parametersInfo[i].IsRetval)
                {
                    throw new InvalidOperationException(Strings.BaseServiceProvider_ParameterNotIn(method, parametersInfo[i]));
                } 

                if (!WebUtil.IsPrimitiveType(parametersInfo[i].ParameterType)) 
                { 
                    throw new InvalidOperationException(
                        Strings.BaseServiceProvider_ParameterTypeNotSupported(method, parametersInfo[i], parametersInfo[i].ParameterType)); 
                }

                string parameterName = parametersInfo[i].Name ?? "p" + i.ToString(CultureInfo.InvariantCulture);
                parameters[i] = new ServiceOperationParameter(parameterName, parametersInfo[i].ParameterType); 
            }
 
            ServiceOperation operation = new ServiceOperation(method, resultKind, resultType, mimeType, invoke, parameters); 
            this.metadata.ServiceOperations.Add(method.Name, operation);
            if (resourceType != null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) 
            {
                operation.EntitySet = this.TryFindSingleContainerForType(resultType);
                if (operation.EntitySet == null)
                { 
                    throw new InvalidOperationException(
                        Strings.BaseServiceProvider_ServiceOperationMissingSingleEntitySet(method, resourceType.FullName)); 
                } 
            }
        } 

#if ASTORIA_CONTAINMENT

        /// Applies access path attributes to the model. 
        private void ApplyAccessPaths()
        { 
            foreach (AccessPathAttribute path in this.EnumerateAccessPathAttributes()) 
            {
                Debug.Assert(path != null, "path != null -- otherwise EnumerateAccessPathAttributes is incorrect."); 
                Debug.Assert(
                    path.AnnotatedContainer != null,
                    "path.AnnotatedContainer -- otherwise EnumerateAccessPathAttributes is incorrect.");
 
                // When an access path is inferred from schema, full validation hasn't taken place. Validation
                // errors for inferred values from here on should be ignored, with the result that the proposed 
                // access path doesn't take effect. 
                Exception exception = path.Validate(this);
                if (exception != null) 
                {
                    if (path.InferredFromSchema)
                    {
                        continue; 
                    }
                    else 
                    { 
                        throw exception;
                    } 
                }

                // Mark the NavigationProperty with the appropriate values.
                IDataServiceProvider provider = (IDataServiceProvider)this; 
                ResourceContainer child = path.AnnotatedContainer;
                ResourceContainer parent = provider.TryResolveContainerName(path.Parent); 
                ResourceProperty parentProperty = parent.ResourceType.TryResolvePropertyName(path.ParentNavigationProperty); 
                if (parentProperty.ContainmentTarget != null)
                { 
                    throw new InvalidOperationException(
                        Strings.BaseServiceProvider_AccessPathTargetAlreadySpecified(
                            path.ParentNavigationProperty, path.Parent, parentProperty.ContainmentTarget.Name, child.Name));
                } 

                if (parentProperty.Kind != ResourcePropertyKind.ResourceSetReference) 
                { 
                    // Because it's a to-one reference doesn't actually add any syntactic sugar, we skip this case.
                    if (path.InferredFromSchema) 
                    {
                        continue;
                    }
                    else 
                    {
                        throw new InvalidOperationException( 
                            Strings.BaseServiceProvider_AccessPathNotThroughCollection(path.ParentNavigationProperty)); 
                    }
                } 

                parentProperty.SetupContainmentTarget(path.InternalChildKeyMapping, path.InternalParentKeyMapping, child);

                // If the path is canonical, mark the resource container. 
                if (path.Canonical)
                { 
                    CanonicalAccessPathAttribute canonical = (CanonicalAccessPathAttribute)path; 
                    if (child.ContainmentCanonicalParent != null)
                    { 
                        throw new InvalidOperationException(
                            Strings.BaseServiceProvider_CanonicalPathTargetAlreadySpecified(
                                path.ParentNavigationProperty,
                                path.Parent, 
                                child.Name,
                                child.ContainmentCanonicalProperty.Name, 
                                child.ContainmentCanonicalParent.Name)); 
                    }
 
                    child.SetupCanonicalAccessPath(parent, parentProperty, canonical.TopLevelAccess);
                }
            }
        } 

#endif 
 
        /// 
        /// Sets the Rights properties as specified on the given 
        /// .
        /// 
        /// Configuration to apply.
        private void ApplyRights(DataServiceConfiguration configuration) 
        {
            Debug.Assert(configuration != null, "configuration != null"); 
            foreach (ResourceContainer container in this.EntitySets.Values) 
            {
                container.Rights = configuration.GetResourceContainerRights(container); 
            }

            foreach (ServiceOperation operation in this.metadata.ServiceOperations.Values)
            { 
                operation.Rights = configuration.GetServiceOperationRights(operation);
            } 
        } 

        /// Checks that every service operation has a result type with a container. 
        private void CheckServiceOperationContainers()
        {
            foreach (ServiceOperation operation in this.metadata.ServiceOperations.Values)
            { 
                if (operation.ResultKind == ServiceOperationResultKind.Nothing)
                { 
                    continue; 
                }
 
                ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(operation.ResultType);
                if (resourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
                {
                    continue; 
                }
 
                ResourceContainer container; 
                bool containerFound = this.TryFindAnyContainerForType(operation.ResultType, out container);
                if (!containerFound) 
                {
                    throw new InvalidOperationException(
                        Strings.BaseServiceProvider_ServiceOperationTypeHasNoContainer(operation.Name, operation.ResultType));
                } 
            }
        } 
 
        /// 
        /// Returns the resource type for the corresponding clr type 
        /// 
        /// clrType whose corresponding resource type needs to be returned
        /// Returns the resource type
        private ResourceType GetType(Type type) 
        {
            Debug.Assert(type != null, "type != null"); 
 
            // Check for the type directly first
            ResourceType resourceType; 
            if (this.TypeCache.TryGetValue(type, out resourceType))
            {
                return resourceType;
            } 

            // check for nullable types 
            Type elementType = Nullable.GetUnderlyingType(type); 
            if (elementType != null && this.TypeCache.TryGetValue(elementType, out resourceType))
            { 
                return resourceType;
            }

            return null; 
        }
 
        ///  
        /// Removes from metadata resource containers that are not visible to
        /// service consumers. 
        /// 
        private void RemoveHiddenResourceContainers()
        {
            List entriesToRemove = new List(); 
            foreach (KeyValuePair container in this.EntitySets)
            { 
                if (container.Value.IsHidden) 
                {
                    entriesToRemove.Add(container.Key); 
                }
            }

            RemoveKeys(this.EntitySets, entriesToRemove); 
        }
 
        ///  
        /// Removes from metadata service operations that are not visible to
        /// service consumers. 
        /// 
        private void RemoveHiddenServiceOperations()
        {
            List entriesToRemove = new List(); 
            foreach (KeyValuePair operation in this.metadata.ServiceOperations)
            { 
                if (operation.Value.IsHidden) 
                {
                    entriesToRemove.Add(operation.Key); 
                }
            }

            RemoveKeys(this.metadata.ServiceOperations, entriesToRemove); 
        }
 
        ///  
        /// Removes from metadata propertiers that are not visible to
        /// service consumers (those that references hidden resource 
        /// containers).
        /// 
        private void RemoveHiddenProperties()
        { 
            foreach (ResourceType type in this.Types)
            { 
                ResourceType typeEvaluated = type; 
                while (typeEvaluated != null)
                { 
                    List properties = typeEvaluated.PropertiesDeclaredInThisType;
                    Debug.Assert(
                        properties == typeEvaluated.PropertiesDeclaredInThisType,
                        "ResourceType.PropertiesDeclaredInThisType must by a mutable by-reference value for RemoveHiddenProperties to work correctly."); 
                    for (int i = properties.Count - 1; i >= 0; i--)
                    { 
                        ResourceProperty property = properties[i]; 
                        ResourceContainer container = property.ResourceContainer;
                        if (container != null && container.IsHidden) 
                        {
                            properties.RemoveAt(i);
                        }
                    } 

                    typeEvaluated = typeEvaluated.BaseType; 
                } 
            }
        } 

        /// 
        /// Removes types that are no longer visible after applying the service
        /// configuration. 
        /// 
        private void RemoveHiddenTypes() 
        { 
            this.RemoveHiddenReferenceTypes();
            this.RemoveHiddenComplexTypes(); 
        }

        /// 
        /// Removes from metadata reference types that are not accessible 
        /// through visible resource containers or visible service
        /// operations. 
        ///  
        private void RemoveHiddenReferenceTypes()
        { 
            // Remove reference types that are not ancestors nor descendants
            // of any entity sets nor service operations.
            List keysToRemove = new List();
            foreach (KeyValuePair entry in this.TypeCache) 
            {
                ResourceType candidateType = entry.Value; 
                if (candidateType.ResourceTypeKind != ResourceTypeKind.EntityType) 
                {
                    continue; 
                }

                bool visible = false;
                foreach (ResourceContainer container in this.EntitySets.Values) 
                {
                    Debug.Assert(!container.IsHidden, "!container.IsHidden - all hidden container should have been removed before invoking RemoveHiddenTypes"); 
                    Type containerType = container.ElementType; 
                    if (containerType.IsAssignableFrom(candidateType.Type) ||
                        candidateType.Type.IsAssignableFrom(containerType)) 
                    {
                        visible = true;
                        break;
                    } 
                }
 
                if (!visible) 
                {
                    foreach (ServiceOperation operation in this.ServiceOperations) 
                    {
                        Debug.Assert(!operation.IsHidden, "!operation.IsHidden - all hidden service operations should have been removed before invoking RemoveHiddenTypes");
                        Type operationType = operation.ResultType;
                        if (operationType != null && 
                            (operationType.IsAssignableFrom(candidateType.Type) ||
                             candidateType.Type.IsAssignableFrom(operationType))) 
                        { 
                            visible = true;
                            break; 
                        }
                    }

                    if (!visible) 
                    {
                        keysToRemove.Add(entry.Key); 
                    } 
                }
            } 

            RemoveKeys(this.TypeCache, keysToRemove);
        }
 
        /// 
        /// Removes from metadata reference types that are not accessible 
        /// through visible types. 
        /// 
        private void RemoveHiddenComplexTypes() 
        {
            // Make a graph of all available complex types starting from visible
            // reference types, then remove those missing.
            HashSet visibleComplexTypes = new HashSet(EqualityComparer.Default); 
            Stack pendingComplexTypes = new Stack();
            foreach (ResourceType type in this.Types) 
            { 
                // If there is a entity or a complex type which is open, we can't detect
                // which complex types to hide. Hence we should just return and assume that 
                // all complex types are getting used.
#if ASTORIA_OPEN_OBJECT
                if (type.IsOpenType)
                { 
                    return;
                } 
#endif 

                if (type.ResourceTypeKind == ResourceTypeKind.EntityType) 
                {
                    MarkComplexTypes(visibleComplexTypes, pendingComplexTypes, type);
                }
            } 

            // If the complex type is returned by a service operation, it should be visible. 
            foreach (ServiceOperation serviceOperation in this.ServiceOperations) 
            {
                if (serviceOperation.ResultKind != ServiceOperationResultKind.Nothing) 
                {
                    ResourceType resourceType = this.TypeCache[serviceOperation.ResultType];
                    Debug.Assert(resourceType != null, "resourceType != null");
                    if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType) 
                    {
                        if (visibleComplexTypes.Add(resourceType)) 
                        { 
                            pendingComplexTypes.Push(resourceType);
                        } 
                    }
                }
            }
 
            while (pendingComplexTypes.Count > 0)
            { 
                ResourceType type = pendingComplexTypes.Pop(); 
                Debug.Assert(type.ResourceTypeKind == ResourceTypeKind.ComplexType, "type.ResourceTypeKind == ResourceTypeKind.ComplexType");
                MarkComplexTypes(visibleComplexTypes, pendingComplexTypes, type); 
            }

            List keysToRemove = new List();
            foreach (KeyValuePair entry in this.TypeCache) 
            {
                if (entry.Value.ResourceTypeKind == ResourceTypeKind.ComplexType && 
                    !visibleComplexTypes.Contains(entry.Value)) 
                {
                    keysToRemove.Add(entry.Key); 
                }
            }

            RemoveKeys(this.TypeCache, keysToRemove); 
        }
 
        ///  
        /// Looks for the first resource container that the specified 
        /// could belong to. 
        /// 
        /// Type to look for.
        /// After the method returns, the container to which the type could belong.
        /// true if a container was found; false otherwise. 
        private bool TryFindAnyContainerForType(Type type, out ResourceContainer container)
        { 
            Debug.Assert(type != null, "type != null"); 

            foreach (ResourceContainer c in this.EntitySets.Values) 
            {
                if (c.ElementType.IsAssignableFrom(type))
                {
                    container = c; 
                    return true;
                } 
            } 

            container = default(ResourceContainer); 
            return false;
        }

        ///  
        /// Looks for a single resource container that the specified 
        /// could belong to. 
        ///  
        /// Type to look for.
        /// The single container that can hold the specified type; null otherwise. 
        private ResourceContainer TryFindSingleContainerForType(Type type)
        {
            Debug.Assert(type != null, "type != null");
 
            ResourceContainer result = null;
            foreach (ResourceContainer c in this.EntitySets.Values) 
            { 
                if (c.ElementType.IsAssignableFrom(type))
                { 
                    if (result != null)
                    {
                        return null;
                    } 

                    result = c; 
                } 
            }
 
            return result;
        }

        ///  
        /// Figure out all the disallowed entity types for each entity set in the system.
        ///  
        private void DisallowDerivedEntityTypesWithNavProperties() 
        {
            // Perform this check for entity sets whose root types takes part in inheritance 
            foreach (ResourceContainer entitySet in this.EntitySets.Values.Where(es => es.ResourceType.HasDerivedTypes))
            {
                foreach (ResourceType resourceType in this.Types)
                { 
                    // only takes leaf nodes into account - to make it slightly more performant
                    // check for sub-class 
                    if (!resourceType.HasDerivedTypes && resourceType.Type.IsSubclassOf(entitySet.ResourceType.Type)) 
                    {
                        // walk up the hierarchy and then down, adding types which are disallowed 
                        Queue hierarchyChain = new Queue();
                        ResourceType currentType = resourceType;
                        do
                        { 
                            hierarchyChain.Enqueue(currentType);
                            currentType = currentType.BaseType; 
                        } 
                        while (currentType != entitySet.ResourceType);
 
                        // If one of the type in the hierarchy chain has navigation property, then
                        // all the sub-types need to be added to the entity set as disallowed types.
                        bool hasNavigationProperties = false;
                        while (hierarchyChain.Count != 0) 
                        {
                            currentType = hierarchyChain.Dequeue(); 
                            if (hasNavigationProperties || currentType.HasNavigationProperties()) 
                            {
                                entitySet.AddDisallowedDerivedType(currentType); 
                                hasNavigationProperties = true;
                            }
                        }
                    } 
                }
            } 
        } 
    }
} 

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