File: System\Data\Services\Providers\ObjectContextServiceProvider.cs
Project: ndp\fx\src\DataWeb\Server\System.Data.Services.csproj (System.Data.Services)
//---------------------------------------------------------------------
// <copyright file="ObjectContextServiceProvider.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// <summary>
//      Provides the interface definition for web data service
//      data sources.
// </summary>
//
// @owner  Microsoft
//---------------------------------------------------------------------
 
namespace System.Data.Services.Providers
{
    #region Namespaces.
 
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.EntityClient;
    using System.Data.Metadata.Edm;
    using System.Data.Objects;
    using System.Data.Objects.DataClasses;
    using System.Data.Services.Caching;
    using System.Data.Services.Common;
    using System.Data.Services.Serializers;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Xml;
    using System.Xml.Linq;
 
    #endregion Namespaces.
 
    /// <summary>
    /// Provides a reflection-based provider implementation.
    /// </summary>
    [DebuggerDisplay("ObjectContextServiceProvider: {Type}")]
    internal partial class ObjectContextServiceProvider : BaseServiceProvider, IDataServiceUpdateProvider
    {
        #region Private fields.
 
        /// <summary>
        /// List of objects that we need to be replaced. The key value indicates the current instance
        /// that will be replaced during SaveChanges. All the property changes are expected to happen
        /// on the value instance. At the time of SaveChanges, all the changes applied to the Value 
        /// instance are then applied to the instance present in Key and then it is saved.
        /// Since EF will always return the same reference for same key value by looking up the first
        /// level cache, we can assume reference equality for the objects thus obtained.
        /// </summary>
        private Dictionary<object, object> objectsToBeReplaced = new Dictionary<object, object>(ReferenceEqualityComparer<object>.Instance);
 
        /// <summary>List of cspace types for which ospace metadata couldn't be found.</summary>
        private List<StructuralType> typesWithoutOSpaceMetadata;
 
        #endregion Private fields.
 
        /// <summary>
        /// Initializes a new System.Data.Services.ReflectionServiceProvider instance.
        /// </summary>
        /// <param name="metadata">Metadata for this provider.</param>
        /// <param name="dataServiceInstance">instance of the data service.</param>
        internal ObjectContextServiceProvider(MetadataCacheItem metadata, object dataServiceInstance)
            : base(metadata, dataServiceInstance)
        {
            this.typesWithoutOSpaceMetadata = new List<StructuralType>();
        }
 
        /// <summary>Gets a value indicating whether null propagation is required in expression trees.</summary>
        public override bool IsNullPropagationRequired
        {
            get { return false; }
        }
 
        /// <summary>Namespace name for the EDM container.</summary>
        public override string ContainerNamespace
        {
            get { return this.Type.Namespace; }
        }
 
        /// <summary>Name of the EDM container</summary>
        public override string ContainerName
        {
            get { return this.ObjectContext.DefaultContainerName; }
        }
 
        /// <summary>Strongly-types instance being reflected upon.</summary>
        private ObjectContext ObjectContext
        {
            get
            {
                return (ObjectContext)this.CurrentDataSource;
            }
        }
 
        /// <summary>
        /// Gets the ResourceAssociationSet instance when given the source association end.
        /// </summary>
        /// <param name="resourceSet">Resource set of the source association end.</param>
        /// <param name="resourceType">Resource type of the source association end.</param>
        /// <param name="resourceProperty">Resource property of the source association end.</param>
        /// <returns>ResourceAssociationSet instance.</returns>
        public override ResourceAssociationSet GetResourceAssociationSet(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
        {
            Debug.Assert(resourceSet != null, "resourceSet != null");
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(resourceProperty != null, "resourceProperty != null");
            Debug.Assert(resourceType == DataServiceProviderWrapper.GetDeclaringTypeForProperty(resourceType, resourceProperty), "resourceType should be the declaring type for resourceProperty");
 
            // Get source set
            EntitySet sourceEntitySet = this.GetEntitySet(resourceSet.Name);
            Debug.Assert(sourceEntitySet != null, "entitySet != null -- GetEntitySet should never return null");
 
            // Get the source type
            EntityType sourceEntityType = this.ObjectContext.MetadataWorkspace.GetItem<EntityType>(resourceType.FullName, DataSpace.CSpace);
            Debug.Assert(sourceEntityType != null, "entityType != null");
 
            // Get source navigation property
            NavigationProperty sourceNavigationProperty;
            sourceEntityType.NavigationProperties.TryGetValue(resourceProperty.Name, false /*ignoreCase*/, out sourceNavigationProperty);
            Debug.Assert(sourceNavigationProperty != null, "navigationProperty != null");
            Debug.Assert(sourceEntityType == (EntityType)sourceNavigationProperty.DeclaringType, "sourceEntityType == (EntityType)sourceNavigationProperty.DeclaringType");
 
            ResourceAssociationSet result = null;
            foreach (AssociationSet associationSet in sourceEntitySet.EntityContainer.BaseEntitySets.OfType<AssociationSet>())
            {
                if (associationSet.ElementType == sourceNavigationProperty.RelationshipType)
                {
                    // from AssociationSetEnd
                    AssociationSetEnd setEnd = associationSet.AssociationSetEnds[sourceNavigationProperty.FromEndMember.Name];
                    if (setEnd.EntitySet == sourceEntitySet)
                    {
                        // from ResourceAssociationSetEnd
                        ResourceAssociationSetEnd thisAssociationSetEnd = new ResourceAssociationSetEnd(resourceSet, resourceType, resourceProperty);
 
                        // to AssociationSetEnd
                        setEnd = associationSet.AssociationSetEnds[sourceNavigationProperty.ToEndMember.Name];
 
                        // Get the target resource set
                        EntitySet targetEntitySet = setEnd.EntitySet;
                        string targetEntitySetName = GetEntitySetName(targetEntitySet.Name, targetEntitySet.EntityContainer.Name, this.ObjectContext.DefaultContainerName == targetEntitySet.EntityContainer.Name);
                        ResourceSet targetResourceSet;
                        ((IDataServiceMetadataProvider)this).TryResolveResourceSet(targetEntitySetName, out targetResourceSet);
                        Debug.Assert(targetResourceSet != null, "targetResourceSet != null");
 
                        // Get the target resource type
                        EntityType targetEntityType = (EntityType)((RefType)sourceNavigationProperty.ToEndMember.TypeUsage.EdmType).ElementType;
                        ResourceType targetResourceType;
                        ((IDataServiceMetadataProvider)this).TryResolveResourceType(targetEntityType.FullName, out targetResourceType);
                        Debug.Assert(targetResourceType != null, "targetResourceType != null");
 
                        // Get the target resource property
                        ResourceProperty targetResourceProperty = null;
                        foreach (NavigationProperty navProperty in targetEntityType.NavigationProperties)
                        {
                            if (navProperty.ToEndMember == sourceNavigationProperty.FromEndMember)
                            {
                                targetResourceProperty = targetResourceType.TryResolvePropertyName(navProperty.Name);
                                break;
                            }
                        }
 
                        // to ResourceAssociationSetEnd
                        ResourceAssociationSetEnd relatedAssociationSetEnd = new ResourceAssociationSetEnd(
                            targetResourceSet,
                            targetResourceType,
                            (resourceType == targetResourceType && resourceProperty == targetResourceProperty) ? null : targetResourceProperty);
 
                        result = new ResourceAssociationSet(associationSet.Name, thisAssociationSetEnd, relatedAssociationSetEnd);
                        break;
                    }
                }
            }
 
            return result;
        }
 
        /// <summary>
        /// Returns the collection of open properties name and value for the given resource instance.
        /// </summary>
        /// <param name="target">instance of the resource.</param>
        /// <returns>Returns the collection of open properties name and value for the given resource instance. Currently not supported for ObjectContext provider.</returns>
        public override IEnumerable<KeyValuePair<string, object>> GetOpenPropertyValues(object target)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Gets the value of the open property.
        /// </summary>
        /// <param name="target">instance of the resource type.</param>
        /// <param name="propertyName">name of the property.</param>
        /// <returns>the value of the open property. Currently this is not supported for ObjectContext providers.</returns>
        public override object GetOpenPropertyValue(object target, string propertyName)
        {
            throw new NotImplementedException();
        }
 
        /// <summary>
        /// Returns the requested service
        /// </summary>
        /// <param name="serviceType">type of service you are requesting for.</param>
        /// <returns>returns the instance of the requested service.</returns>
        public override object GetService(Type serviceType)
        {
            if (serviceType == typeof(IDataServiceUpdateProvider))
            {
                return this;
            }
 
            return base.GetService(serviceType);
        }
 
        /// <summary>Applies expansions and projections to the specified <paramref name="source"/>.</summary>
        /// <param name="source"><see cref="IQueryable"/> object to expand and apply projections to.</param>
        /// <param name="projection">The root node of the tree which describes
        /// the projections and expansions to be applied to the <paramref name="source"/>.</param>
        /// <returns>
        /// An <see cref="IQueryable"/> object, with the results including 
        /// the expansions and projections specified in <paramref name="projection"/>. 
        /// </returns>
        /// <remarks>
        /// The returned <see cref="IQueryable"/> may implement the <see cref="IExpandedResult"/> interface 
        /// to provide enumerable objects for the expansions; otherwise, the expanded
        /// information is expected to be found directly in the enumerated objects. If paging is 
        /// requested by providing a non-empty list in <paramref name="projection"/>.OrderingInfo then
        /// it is expected that the topmost <see cref="IExpandedResult"/> would have a $skiptoken property 
        /// which will be an <see cref="IExpandedResult"/> in itself and each of it's sub-properties will
        /// be named SkipTokenPropertyXX where XX represents numbers in increasing order starting from 0. Each of 
        /// SkipTokenPropertyXX properties will be used to generated the $skiptoken to support paging.
        /// If projections are required, the provider may choose to return <see cref="IQueryable"/>
        /// which returns instances of <see cref="IProjectedResult"/>. In that case property values are determined
        /// by calling the <see cref="IProjectedResult.GetProjectedPropertyValue"/> method instead of
        /// accessing properties of the returned object directly.
        /// If both expansion and projections are required, the provider may choose to return <see cref="IQueryable"/>
        /// of <see cref="IExpandedResult"/> which in turn returns <see cref="IProjectedResult"/> from its
        /// <see cref="IExpandedResult.ExpandedElement"/> property.
        /// </remarks>
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining | System.Runtime.CompilerServices.MethodImplOptions.NoOptimization)]
        public override IQueryable ApplyProjections(
            IQueryable source,
            ProjectionNode projection)
        {
            Debug.Assert(projection is RootProjectionNode, "We always get the special root node.");
            RootProjectionNode rootNode = (RootProjectionNode)projection;
            Debug.Assert(rootNode.OrderingInfo != null, "We always get non-null OrderingInfo");
 
            // Start by adding all the containers for the root type, in case the query originates from
            // a service operation and we don't know what the origin is.
            HashSet<ResourceSet> containers = new HashSet<ResourceSet>(EqualityComparer<ResourceSet>.Default);
            foreach (var set in this.EntitySets)
            {
                if (set.Value.ResourceType.InstanceType.IsAssignableFrom(source.ElementType))
                {
                    containers.Add(set.Value);
                }
            }
 
            // when we are not expanding ObjectQueries (maybe IQueryable from from service ops), 
            // then we should use basic expand provider
            bool useBasicExpandProvider = !(typeof(ObjectQuery).IsAssignableFrom(source.GetType()));
 
            useBasicExpandProvider |= ShouldUseBasicExpandProvider(rootNode, containers, 0);
 
            // Any use of ordering, skip, top settings or projections excludes the possibility of using Include method
            if (useBasicExpandProvider || rootNode.OrderingInfo.OrderingExpressions.Count > 0 ||
                rootNode.SkipCount.HasValue || rootNode.TakeCount.HasValue || rootNode.ProjectionsSpecified)
            {
                return new BasicExpandProvider(this.ProviderWrapper, false, false).ApplyProjections(source, projection);
            }
 
            MethodInfo includeMethod = typeof(ObjectContextServiceProvider).GetMethod("Include", BindingFlags.Static | BindingFlags.NonPublic);
            includeMethod = includeMethod.MakeGenericMethod(source.ElementType);
 
            return VisitDottedExpandPaths<IQueryable>(
                rootNode,
                (queryable, dottedPath) => (IQueryable)includeMethod.Invoke(null, new object[] { queryable, dottedPath }),
                source,
                new List<string>());
        }
 
        /// <summary>
        /// Writes the metadata in EDMX format into the specified <paramref name="xmlWriter"/>.
        /// </summary>
        /// <param name="xmlWriter">Writer to which metadata XML should be written.</param>
        /// <param name="provider">Data provider from which metadata should be gathered.</param>
        /// <param name="service">Data service instance.</param>
        public void GetMetadata(XmlWriter xmlWriter, DataServiceProviderWrapper provider, IDataService service)
        {
            Debug.Assert(xmlWriter != null, "xmlWriter != null");
            InitializeObjectItemCollection(this.ObjectContext, this.Type.Assembly);
 
            var metadataManager = new MetadataManager(this.ObjectContext.MetadataWorkspace, this.GetDefaultEntityContainer(), provider, service);
            MetadataEdmSchemaVersion metadataEdmSchemaVersion = this.EdmSchemaVersion;
            string dataServiceVersion = MetadataSerializer.GetVersionsForMetadata(provider.Types, ref metadataEdmSchemaVersion);
            MetadataSerializer.WriteTopLevelSchemaElements(xmlWriter, dataServiceVersion);
            bool entityContainerDefinitionWritten = false;
 
            Double currentVersion = ((EdmItemCollection)this.ObjectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSpace)).EdmVersion;
            if (currentVersion == 2.0)
            {
                metadataEdmSchemaVersion = MetadataEdmSchemaVersion.Version2Dot0;
            }
 
            // Write all the types in their respective namespaces
            foreach (KeyValuePair<string, HashSet<EdmType>> typesInNamespace in metadataManager.NamespaceAlongWithTypes)
            {
                MetadataSerializer.WriteSchemaElement(xmlWriter, typesInNamespace.Key, metadataEdmSchemaVersion);
 
                // Write the entity container in the same namespace as that of the type
                if (!entityContainerDefinitionWritten && typesInNamespace.Key == this.Type.Namespace)
                {
                    WriteEntityContainers(xmlWriter, this.GetDefaultEntityContainer(), provider, metadataManager);
                    entityContainerDefinitionWritten = true;
                }
 
                WriteEdmTypes(xmlWriter, typesInNamespace.Value, metadataManager);
                xmlWriter.WriteEndElement();
            }
 
            // If the entity container is in a different namespace than the types, then write the entity container definition
            // in a different namespace
            if (!entityContainerDefinitionWritten)
            {
                MetadataSerializer.WriteSchemaElement(xmlWriter, this.Type.Namespace, metadataEdmSchemaVersion);
                WriteEntityContainers(xmlWriter, this.GetDefaultEntityContainer(), provider, metadataManager);
                xmlWriter.WriteEndElement();
            }
 
            // These end elements balance the elements written out in WriteTopLevelSchemaElements
            xmlWriter.WriteEndElement();
            xmlWriter.WriteEndElement();
            xmlWriter.Flush();
        }
 
        #region IUpdatable Members
 
        /// <summary>
        /// Creates the resource of the given type and belonging to the given container
        /// </summary>
        /// <param name="containerName">container name to which the resource needs to be added</param>
        /// <param name="fullTypeName">full type name i.e. Namespace qualified type name of the resource</param>
        /// <returns>object representing a resource of given type and belonging to the given container</returns>
        public object CreateResource(string containerName, string fullTypeName)
        {
            ResourceType resourceType;
            ((IDataServiceMetadataProvider)this).TryResolveResourceType(fullTypeName, out resourceType);
            Debug.Assert(resourceType != null, "resourceType != null");
 
            if (resourceType.InstanceType.IsAbstract)
            {
                throw DataServiceException.CreateBadRequestError(Strings.CannotCreateInstancesOfAbstractType(resourceType.FullName));
            }
 
            object resource;
            if (containerName != null)
            {
                Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resourceType.ResourceTypeKind == ResourceTypeKind.EntityType - expecting an entity type");
                resource = CreateObject(this.ObjectContext, resourceType.InstanceType);
                this.ObjectContext.AddObject(containerName, resource);
            }
            else
            {
                // When the container name is null, it means we are trying to create a instance of complex types.
                Debug.Assert(resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType, "resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType - expecting a complex type");
                resource = resourceType.ConstructorDelegate();
            }
 
            return resource;
        }
 
        /// <summary>
        /// Gets the resource of the given type that the query points to
        /// </summary>
        /// <param name="query">query pointing to a particular resource</param>
        /// <param name="fullTypeName">full type name i.e. Namespace qualified type name of the resource</param>
        /// <returns>object representing a resource of given type and as referenced by the query</returns>
        public object GetResource(IQueryable query, string fullTypeName)
        {
            Debug.Assert(query != null, "query != null");
 
            ObjectQuery objectQuery = query as ObjectQuery;
            Debug.Assert(objectQuery != null, "objectQuery != null - otherwise we're passed an IQueryable we didn't produce");
 
            objectQuery.MergeOption = MergeOption.AppendOnly;
            object result = null;
            foreach (object resource in objectQuery)
            {
                if (result != null)
                {
                    throw new InvalidOperationException(Strings.SingleResourceExpected);
                }
 
                result = resource;
            }
 
            if (result != null && fullTypeName != null)
            {
                ResourceType resourceType = this.GetSingleResource(result);
                Debug.Assert(resourceType != null, "the result must return a known type");
                if (resourceType.FullName != fullTypeName)
                {
                    throw DataServiceException.CreateBadRequestError(Strings.TargetElementTypeOfTheUriSpecifiedDoesNotMatchWithTheExpectedType(resourceType.FullName, fullTypeName));
                }
            }
 
            return result;
        }
 
        /// <summary>
        /// Resets the value of the given resource to its default value
        /// </summary>
        /// <param name="resource">resource whose value needs to be reset</param>
        /// <returns>same resource with its value reset</returns>
        public object ResetResource(object resource)
        {
            Debug.Assert(resource != null, "resource != null");
 
            ResourceType resourceType = this.GetSingleResource(resource);
            if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
            {
                // For entity types, do the following:
                // create a new instance of the same type and set the key values on it
                object newInstance = CreateObject(this.ObjectContext, resourceType.InstanceType);
 
                // set the key value on the new instance
                foreach (ResourceProperty property in resourceType.KeyProperties)
                {
                    object propertyValue = ((IDataServiceQueryProvider)this).GetPropertyValue(resource, property);
                    resourceType.SetValue(newInstance, propertyValue, property);
                }
                
                // When reset resource, we return the old instance since it's the one
                // EF actually attached to.
                // but all property modification will be done on the newInstance
                // upon save changes, the modifications on newInstance will be merged with
                // the old instance (property by property).
                this.objectsToBeReplaced.Add(resource, newInstance);
 
                ObjectStateEntry objectStateEntry = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(resource);
                if (objectStateEntry.State == EntityState.Added)
                {
                    // in case when the resource is been added, and we PUT to that resource
                    // we'll actually insert the newInstance and detach the old one. 
                    // So we need to return the newInstance instead.
                    this.ObjectContext.AddObject(this.GetEntitySetName(objectStateEntry), newInstance);
                    return newInstance;
                }                
            }
            else if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
            {
                // For complex types, just return a brand new instance.
                return resourceType.ConstructorDelegate();
            }
 
            return resource;
        }
 
        /// <summary>
        /// Sets the value of the given property on the target object
        /// </summary>
        /// <param name="targetResource">target object which defines the property</param>
        /// <param name="propertyName">name of the property whose value needs to be updated</param>
        /// <param name="propertyValue">value of the property</param>
        public void SetValue(object targetResource, string propertyName, object propertyValue)
        {
            ResourceType resourceType = this.GetSingleResource(targetResource);
            Debug.Assert(resourceType != null, "resourceType != null");
            ResourceProperty resourceProperty = resourceType.TryResolvePropertyName(propertyName);
            Debug.Assert(resourceProperty != null, "resourceProperty != null");
 
            // is target Resource going to be replaced?
            // See comment in ResetResources
            object replacedTarget;
            if (this.objectsToBeReplaced.TryGetValue(targetResource, out replacedTarget))
            {
                resourceType.SetValue(replacedTarget, propertyValue, resourceProperty);
            }
            else
            {
                resourceType.SetValue(targetResource, propertyValue, resourceProperty);
            }
        }
 
        /// <summary>
        /// Gets the value of the given property on the target object
        /// </summary>
        /// <param name="targetResource">target object which defines the property</param>
        /// <param name="propertyName">name of the property whose value needs to be updated</param>
        /// <returns>the value of the property for the given target resource</returns>
        public object GetValue(object targetResource, string propertyName)
        {
            ResourceType resourceType = this.GetSingleResource(targetResource);
            Debug.Assert(resourceType != null, "resourceType != null");
            ResourceProperty resourceProperty = resourceType.TryResolvePropertyName(propertyName);
            Debug.Assert(resourceProperty != null, "resourceProperty != null");
            object resource = ((IDataServiceQueryProvider)this).GetPropertyValue(targetResource, resourceProperty);
            return resource;
        }
 
        /// <summary>
        /// Sets the value of the given reference property on the target object
        /// </summary>
        /// <param name="targetResource">target object which defines the property</param>
        /// <param name="propertyName">name of the property whose value needs to be updated</param>
        /// <param name="propertyValue">value of the property</param>
        public void SetReference(object targetResource, string propertyName, object propertyValue)
        {
            this.UpdateRelationship(targetResource, propertyName, propertyValue, null);
        }
 
        /// <summary>
        /// Adds the given value to the collection
        /// </summary>
        /// <param name="targetResource">target object which defines the property</param>
        /// <param name="propertyName">name of the property whose value needs to be updated</param>
        /// <param name="resourceToBeAdded">value of the property which needs to be added</param>
        public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
        {
            this.UpdateRelationship(targetResource, propertyName, resourceToBeAdded, true /*addRelationship*/);
        }
 
        /// <summary>
        /// Removes the given value from the collection
        /// </summary>
        /// <param name="targetResource">target object which defines the property</param>
        /// <param name="propertyName">name of the property whose value needs to be updated</param>
        /// <param name="resourceToBeRemoved">value of the property which needs to be removed</param>
        public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
        {
            this.UpdateRelationship(targetResource, propertyName, resourceToBeRemoved, false /*addRelationship*/);
        }
 
        /// <summary>
        /// Delete the given resource
        /// </summary>
        /// <param name="resource">resource that needs to be deleted</param>
        public void DeleteResource(object resource)
        {
            this.ObjectContext.DeleteObject(resource);
        }
 
        /// <summary>
        /// Saves all the pending changes made till now
        /// </summary>
        public void SaveChanges()
        {
            // handle the resource which need to be replaced.
            foreach (KeyValuePair<object, object> objectToBeReplaced in this.objectsToBeReplaced)
            {
                ObjectStateEntry objectStateEntry = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(objectToBeReplaced.Key);
                string entitySetName = this.GetEntitySetName(objectStateEntry);
                if (objectStateEntry.State == EntityState.Added)
                {
                    // In the case of batching if we do a PUT after POST in the same changeset, we need to detach and re-add.
                    this.ObjectContext.Detach(objectToBeReplaced.Key);
                    this.ObjectContext.AddObject(entitySetName, objectToBeReplaced.Value);
                }
                else
                {
#pragma warning disable 618
                    // Apply property changes as specified in the new object.
                    this.ObjectContext.ApplyPropertyChanges(entitySetName, objectToBeReplaced.Value);
#pragma warning restore 618
                }
            }
 
            // clear all these once we have processed all the entities that need to be replaced.
            this.objectsToBeReplaced.Clear();
 
            try
            {
                // Save Changes
                this.ObjectContext.SaveChanges();
            }
            catch (OptimisticConcurrencyException e)
            {
                throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch, e);
            }
        }
 
        /// <summary>
        /// Returns the actual instance of the resource represented by the given resource object
        /// </summary>
        /// <param name="resource">object representing the resource whose instance needs to be fetched</param>
        /// <returns>The actual instance of the resource represented by the given resource object</returns>
        public object ResolveResource(object resource)
        {
            Debug.Assert(resource != null, "resource != null");
            return resource;
        }
 
        /// <summary>
        /// Revert all the pending changes.
        /// </summary>
        public void ClearChanges()
        {
            // Detach all the existing entries in the object context
            foreach (ObjectStateEntry entry in this.ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged))
            {
                // entry.State != Entry.Detached: Also, if they are stub entries (no entity object
                // associated with the entry yet - these get automatically populated when we query the related entity),
                // they also get automatically detached once we detach the associated entity).
                // Both Entity and IsRelationship property throws if the entry is detached.
                // !entry.IsRelationship: We just need to remove the key entries.
                // While detaching the key entries, the relationship entries associated 
                // with the key entries also get detached. 
                // entry.Entity != null:  Since they are no ordering gaurantees, the stub key entries 
                // can come first, we need to skip these.
                if (entry.State != EntityState.Detached && !entry.IsRelationship && entry.Entity != null)
                {
                    this.ObjectContext.Detach(entry.Entity);
                }
            }
 
            // clear the list of objects that need to be special handled during save changes for replace semantics
            this.objectsToBeReplaced.Clear();
        }
 
        #endregion
 
        #region IConcurrencyProvider Methods
 
        /// <summary>
        /// Set the etag values for the given resource.
        /// </summary>
        /// <param name="resource">resource for which etag values need to be set.</param>
        /// <param name="checkForEquality">true if we need to compare the property values for equality. If false, then we need to compare values for non-equality.</param>
        /// <param name="concurrencyValues">list of the etag property names, along with their values.</param>
        public void SetConcurrencyValues(object resource, bool? checkForEquality, IEnumerable<KeyValuePair<string, object>> concurrencyValues)
        {
            // Now this method will need to check for cases when etag are specified
            if (checkForEquality == null)
            {
                ResourceType resourceType = this.GetResourceType(resource);
                throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotPerformOperationWithoutETag(resourceType.FullName));
            }
 
            Debug.Assert(checkForEquality.Value, "If-None-Match header is currently not supported for Update/Delete operations");
            ObjectStateEntry objectStateEntry = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(resource);
            Debug.Assert(objectStateEntry != null, "ObjectStateEntry must be found");
 
            OriginalValueRecord originalValues = objectStateEntry.GetUpdatableOriginalValues();
            foreach (KeyValuePair<string, object> etag in concurrencyValues)
            {
                int propertyOrdinal = originalValues.GetOrdinal(etag.Key);
                originalValues.SetValue(propertyOrdinal, etag.Value);
            }
        }
 
        #endregion IConcurrencyProvider Methods
 
        /// <summary>
        /// Trasnslates content kind to string for csdl
        /// </summary>
        /// <param name="contentKind">ContentKind</param>
        /// <returns>String corresponding to contentKind</returns>
        internal static String MapSyndicationTextContentKindToEpmContentKind(SyndicationTextContentKind contentKind)
        {
            switch (contentKind)
            {
                case SyndicationTextContentKind.Plaintext:
                    return XmlConstants.SyndContentKindPlaintext;
                case SyndicationTextContentKind.Html:
                    return XmlConstants.SyndContentKindHtml;
                default:
                    Debug.Assert(contentKind == SyndicationTextContentKind.Xhtml, "Unexpected syndication text content kind");
                    return XmlConstants.SyndContentKindXHtml;
            }
        }
 
        /// <summary>
        /// Translates syndication item property to string for csdl
        /// </summary>
        /// <param name="property">Syndication property to translate</param>
        /// <returns>TargetPath corresponding to SyndicationItemProperty</returns>
        internal static String MapSyndicationPropertyToEpmTargetPath(SyndicationItemProperty property)
        {
            switch (property)
            {
                case SyndicationItemProperty.AuthorEmail:
                    return XmlConstants.SyndAuthorEmail;
                case SyndicationItemProperty.AuthorName:
                    return XmlConstants.SyndAuthorName;
                case SyndicationItemProperty.AuthorUri:
                    return XmlConstants.SyndAuthorUri;
                case SyndicationItemProperty.ContributorEmail:
                    return XmlConstants.SyndContributorEmail;
                case SyndicationItemProperty.ContributorName:
                    return XmlConstants.SyndContributorName;
                case SyndicationItemProperty.ContributorUri:
                    return XmlConstants.SyndContributorUri;
                case SyndicationItemProperty.Updated:
                    return XmlConstants.SyndUpdated;
                case SyndicationItemProperty.Published:
                    return XmlConstants.SyndPublished;
                case SyndicationItemProperty.Rights:
                    return XmlConstants.SyndRights;
                case SyndicationItemProperty.Summary:
                    return XmlConstants.SyndSummary;
                default:
                    Debug.Assert(property == SyndicationItemProperty.Title, "Unexpected SyndicationItemProperty value");
                    return XmlConstants.SyndTitle;
            }
        }
 
        /// <summary>
        /// Checks whether the provider implements IUpdatable.
        /// </summary>
        /// <returns>returns true if the provider implements IUpdatable. otherwise returns false.</returns>
        internal override bool ImplementsIUpdatable()
        {
            return true;
        }
 
        /// <summary>
        /// Get the list of etag property names given the entity set name and the instance of the resource
        /// </summary>
        /// <param name="containerName">name of the entity set</param>
        /// <param name="resourceType">Type of the resource whose etag properties need to be fetched</param>
        /// <returns>list of etag property names</returns>
        internal IList<ResourceProperty> GetETagProperties(string containerName, ResourceType resourceType)
        {
            Debug.Assert(!String.IsNullOrEmpty(containerName), "container name must not be empty");
            Debug.Assert(resourceType != null, "Type cannot be null");
 
            EntitySetBase entitySet = this.GetEntitySet(containerName);
            EntityType entityType = this.ObjectContext.MetadataWorkspace.GetItem<EntityType>(resourceType.FullName, DataSpace.CSpace);
            Debug.Assert(entityType != null, "entityType != null");
            List<ResourceProperty> etagProperties = new List<ResourceProperty>();
 
            // Workspace associated directly with the ObjectContext has metadata only about OSpace, CSpace and OCSpace.
            // Since GetRequiredOriginalValueMembers depends on mapping information (CSSpace metadata),
            // we need to make sure we call this API on a workspace which has information about the CS Mapping.
            // Hence getting workspace from the underlying Entity connection.
            MetadataWorkspace workspace = ((EntityConnection)this.ObjectContext.Connection).GetMetadataWorkspace();
 
            #pragma warning disable 618
 
            foreach (EdmMember member in workspace.GetRequiredOriginalValueMembers(entitySet, entityType))
            {
                ResourceProperty property = resourceType.TryResolvePropertyName(member.Name);
                Debug.Assert(property != null, "property != null");
                Debug.Assert(property.TypeKind == ResourceTypeKind.Primitive, "property.TypeKind == ResourceTypeKind.Primitive");
 
                // Ignore key properties if they are part of etag, since the uri already has the key information
                // and it makes no sense to duplicate them in etag
                if (!property.IsOfKind(ResourcePropertyKind.Key))
                {
                    etagProperties.Add(property);
                }
            }
 
            #pragma warning restore 618
 
            return etagProperties;
        }
 
        /// <summary>Checks that the applied configuration is consistent.</summary>
        /// <param name='dataSourceInstance'>Instance of the data source for the provider.</param>
        /// <param name="configuration">Data service configuration instance with access right info.</param>
        /// <remarks>At this point in initialization, metadata trimming hasn't taken place.</remarks>
        protected override void CheckConfigurationConsistency(object dataSourceInstance, DataServiceConfiguration configuration)
        {
            base.CheckConfigurationConsistency(dataSourceInstance, configuration);
 
            // Check that rights are consistent in MEST scenarios.
            //
            // Strictly we should only check for consistent visibility
            // for all entity sets of a given type, however the current
            // metadata design doesn't differentiate between resource 
            // container types and resource set instances on
            // associations, and therefore all rights are checked at 
            // the resource type level, which forces this check to have 
            // consistent rights.
            //
            // The only exception could be references which are not connected
            // (technically those that are not targets, but in EDM all
            // associations are two-way). These can have entity sets
            // with different rights, enforced at the container level.
 
            // Discover connected types.
            HashSet<ResourceType> connectedTypes = new HashSet<ResourceType>(EqualityComparer<ResourceType>.Default);
            foreach (ResourceType type in ((IDataServiceMetadataProvider)this).Types)
            {
                foreach (ResourceProperty property in type.PropertiesDeclaredOnThisType)
                {
                    if (property.TypeKind == ResourceTypeKind.EntityType)
                    {
                        connectedTypes.Add(property.ResourceType);
                    }
                }
            }
 
            // Discover containers of same type with conflicting rights.
            Dictionary<ResourceType, ResourceSet> typeRights = new Dictionary<ResourceType, ResourceSet>(ReferenceEqualityComparer<ResourceType>.Instance);
            foreach (KeyValuePair<string, ResourceSet> containerEntry in this.EntitySets)
            {
                Debug.Assert(containerEntry.Key != null, "containerEntry.Key != null");
                Debug.Assert(containerEntry.Value != null, "containerEntry.Value != null");
 
                ResourceType resourceType = containerEntry.Value.ResourceType;
 
                // Disregard types that are not connected to any other types.
                if (!connectedTypes.Contains(resourceType))
                {
                    continue;
                }
 
                ResourceSet previouslyFoundContainer;
                if (typeRights.TryGetValue(resourceType, out previouslyFoundContainer))
                {
                    EntitySetRights containerRights = configuration.GetResourceSetRights(containerEntry.Value);
                    EntitySetRights previouslyFoundContainerRights = configuration.GetResourceSetRights(previouslyFoundContainer);
                    if (containerRights != previouslyFoundContainerRights)
                    {
                        throw new InvalidOperationException(Strings.ObjectContext_DifferentContainerRights(
                            previouslyFoundContainer.Name,
                            previouslyFoundContainerRights,
                            containerEntry.Value.Name,
                            containerRights));
                    }
                }
                else
                {
                    typeRights.Add(resourceType, containerEntry.Value);
                }
            }
 
            CheckNavigationPropertiesBound(dataSourceInstance);
        }
 
        /// <summary>
        /// Populates metadata from the given object context
        /// </summary>
        /// <param name="knownTypes">dictionary of already known types</param>
        /// <param name="childTypes">list of already known types and their immediate children</param>
        /// <param name="entitySets">list of already known entity sets</param>
        protected override void PopulateMetadata(
            IDictionary<Type, ResourceType> knownTypes,
            IDictionary<ResourceType, List<ResourceType>> childTypes,
            IDictionary<string, ResourceSet> entitySets)
        {
            Debug.Assert(knownTypes != null, "knownTypes != null");
            Debug.Assert(entitySets != null, "entitySets != null");
            Debug.Assert(this.ObjectContext != null, "this.ObjectContext != null");
 
            InitializeObjectItemCollection(this.ObjectContext, this.Type.Assembly);
            MetadataWorkspace metadataWorkspace = this.ObjectContext.MetadataWorkspace;
 
            // Create Resource types for all the top level entity types and complexTypes
            foreach (StructuralType edmType in metadataWorkspace.GetItems<StructuralType>(DataSpace.CSpace))
            {
                if (edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType ||
                    edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType)
                {
                    // Populates metadata for the given types and all its base types
                    if (PopulateTypeMetadata(metadataWorkspace, edmType, knownTypes, childTypes) == null)
                    {
                        this.typesWithoutOSpaceMetadata.Add(edmType);
                    }
                }
            }
 
            foreach (EntityContainer entityContainer in metadataWorkspace.GetItems<EntityContainer>(DataSpace.CSpace))
            {
                bool defaultEntityContainer = entityContainer.Name == this.ObjectContext.DefaultContainerName;
 
                // Get the list of entity sets (Ignore the relationship sets, since we won't allow that to be queried directly
                foreach (EntitySetBase entitySetBase in entityContainer.BaseEntitySets)
                {
                    // Ignore all the association sets for the type being, since we are caching only entity sets
                    if (entitySetBase.BuiltInTypeKind != BuiltInTypeKind.EntitySet)
                    {
                        continue;
                    }
 
                    EntitySet entitySet = (EntitySet)entitySetBase;
                    Type elementType = GetClrTypeForCSpaceType(metadataWorkspace, entitySet.ElementType);
                    ResourceType resourceType = knownTypes[elementType];
                    string entitySetName = GetEntitySetName(entitySet.Name, entitySet.EntityContainer.Name, defaultEntityContainer);
                    ResourceSet resourceContainer = new ResourceSet(entitySetName, resourceType);
                    entitySets.Add(entitySetName, resourceContainer);
                }
            }
 
            // Now go and populate the member information for each resource type
            foreach (ResourceType resourceType in knownTypes.Values)
            {
                if (resourceType.ResourceTypeKind == ResourceTypeKind.Primitive)
                {
                    continue;
                }
 
                PopulateMemberMetadata(resourceType, metadataWorkspace, knownTypes);
                this.GetEpmInfoForResourceType(metadataWorkspace, resourceType, resourceType);
                resourceType.EpmInfoInitialized = true;
            }
        }
 
        /// <summary>
        /// Creates the object query for the given resource set and returns it
        /// </summary>
        /// <param name="resourceContainer">resource set for which IQueryable instance needs to be created</param>
        /// <returns>returns the IQueryable instance for the given resource set</returns>
        protected override IQueryable GetResourceContainerInstance(ResourceSet resourceContainer)
        {
            Debug.Assert(resourceContainer != null, "resourceContainer != null");
            ObjectQuery result = this.InternalGetResourceContainerInstance(resourceContainer);
            result.MergeOption = MergeOption.NoTracking;
            return result;
        }
 
        /// <summary>
        /// Populate types for metadata specified by the provider
        /// </summary>
        /// <param name="userSpecifiedTypes">list of types specified by the provider</param>
        /// <param name="knownTypes">list of already known types</param>
        /// <param name="childTypes">list of already known types and their immediate children</param>
        /// <param name="entitySets">list of entity sets as specified in the data source type</param>
        protected override void PopulateMetadataForUserSpecifiedTypes(
            IEnumerable<Type> userSpecifiedTypes,
            IDictionary<Type, ResourceType> knownTypes,
            IDictionary<ResourceType, List<ResourceType>> childTypes,
            IEnumerable<ResourceSet> entitySets)
        {
            foreach (Type type in userSpecifiedTypes)
            {
                if (this.PopulateMetadataForType(type, knownTypes, childTypes, entitySets) == null)
                {
                    throw new InvalidOperationException(Strings.BadProvider_InvalidTypeSpecified(type.FullName));
                }
            }
 
            // If there is a type in the model, for which we couldn't load the metadata, we should throw.
            if (this.typesWithoutOSpaceMetadata.Count != 0)
            {
                throw new InvalidOperationException(Strings.ObjectContext_UnableToLoadMetadataForType(this.typesWithoutOSpaceMetadata[0].FullName));
            }
 
            this.typesWithoutOSpaceMetadata = null;
        }
 
        /// <summary>
        /// Populate metadata for the given clr type.
        /// </summary>
        /// <param name="type">type whose metadata needs to be loaded.</param>
        /// <param name="knownTypes">list of already known resource types.</param>
        /// <param name="childTypes">list of already known types and their immediate children</param>
        /// <param name="entitySets">list of entity sets as specified in the data source.</param>
        /// <returns>resource type containing metadata for the given clr type.</returns>
        protected override ResourceType PopulateMetadataForType(
            Type type,
            IDictionary<Type, ResourceType> knownTypes,
            IDictionary<ResourceType, List<ResourceType>> childTypes,
            IEnumerable<ResourceSet> entitySets)
        {
            Debug.Assert(!WebUtil.IsPrimitiveType(type), "Why are we trying to load metadata for a primitive type?");
 
            ResourceType resourceType;
            if (!knownTypes.TryGetValue(type, out resourceType))
            {
                InitializeObjectItemCollection(this.ObjectContext, type.Assembly);
                ObjectItemCollection objectItemCollection = (ObjectItemCollection)this.ObjectContext.MetadataWorkspace.GetItemCollection(DataSpace.OSpace);
                StructuralType ospaceType, cspaceType;
                if (objectItemCollection.TryGetItem<StructuralType>(type.FullName, out ospaceType))
                {
                    if (this.ObjectContext.MetadataWorkspace.TryGetEdmSpaceType(ospaceType, out cspaceType))
                    {
                        ResourceType baseType = null;
                        if (cspaceType.BaseType != null)
                        {
                            baseType = this.PopulateMetadataForType(type.BaseType, knownTypes, childTypes, entitySets);
                        }
 
                        resourceType = CreateResourceType(cspaceType, type, baseType, knownTypes, childTypes);
                        this.typesWithoutOSpaceMetadata.Remove(cspaceType);
                    }
                }
            }
 
            return resourceType;
        }
 
        /// <summary>
        /// Returns the resource type for the corresponding clr type.
        /// </summary>
        /// <param name="type">clrType whose corresponding resource type needs to be returned</param>
        /// <returns>Returns the resource type</returns>
        protected override ResourceType ResolveNonPrimitiveType(System.Type type)
        {
            Type actualType = ObjectContext.GetObjectType(type);
            return base.ResolveNonPrimitiveType(actualType);
        }
 
        /// <summary>
        /// Checks that all navigation properties are bound to some association set for every entity set.
        /// </summary>
        /// <param name='dataSourceInstance'>Instance of the data source for the provider.</param>
        private static void CheckNavigationPropertiesBound(object dataSourceInstance)
        {
            // For every navigation property, ensure that all of the EntitySets that can
            // take their EntityType have an AssociationSet of the appropriate Association type.
            Debug.Assert(dataSourceInstance != null, "dataSourceInstance != null");
            MetadataWorkspace workspace = ((ObjectContext)dataSourceInstance).MetadataWorkspace;
            foreach (EntityType type in workspace.GetItems<EntityType>(DataSpace.CSpace))
            {
                foreach (NavigationProperty navigationProperty in type.NavigationProperties)
                {
                    foreach (EntitySet entitySet in GetEntitySetsForType(workspace, type))
                    {
                        IEnumerable<EntitySet> entitySetsWithAssocation = GetEntitySetsWithAssociationSets(
                            workspace,
                            navigationProperty.RelationshipType,
                            navigationProperty.FromEndMember);
                        if (!entitySetsWithAssocation.Contains(entitySet))
                        {
                            throw new InvalidOperationException(Strings.ObjectContext_NavigationPropertyUnbound(
                                navigationProperty.Name,
                                type.FullName,
                                entitySet.Name));
                        }
                    }
                }
            }
        }
 
        /// <summary>Gets the CLR type mapped to the specified C-Space type.</summary>
        /// <param name="workspace">Workspace in which the type is defined.</param>
        /// <param name="edmType">C-Space type whose matching clr type needs to be looked up.</param>
        /// <returns>The resolved <see cref="Type"/> for the given <paramref name="edmType"/>.</returns>
        private static Type GetClrTypeForCSpaceType(MetadataWorkspace workspace, StructuralType edmType)
        {
            Debug.Assert(workspace != null, "workspace != null");
            Debug.Assert(edmType != null, "edmType != null");
            Debug.Assert(
               edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType || edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType,
               "Must be entityType or complexType");
 
            StructuralType ospaceType;
            if (workspace.TryGetObjectSpaceType(edmType, out ospaceType))
            {
                ObjectItemCollection objectItemCollection = (ObjectItemCollection)workspace.GetItemCollection(DataSpace.OSpace);
                return objectItemCollection.GetClrType(ospaceType);
            }
 
            return null;
        }
 
        /// <summary>
        /// Gets all <see cref="EntitySet"/> instance that may hold an entity of type <paramref name="type"/>.
        /// </summary>
        /// <param name="workspace">Workspace with metadata.</param>
        /// <param name="type">Entity type to get entity sets for.</param>
        /// <returns>An enumeration of <see cref="EntitySet"/> instances that can hold <paramref name="type"/>.</returns>
        private static IEnumerable<EntitySet> GetEntitySetsForType(MetadataWorkspace workspace, EntityType type)
        {
            Debug.Assert(type != null, "type != null");
            Debug.Assert(workspace != null, "workspace != null");
            foreach (EntityContainer container in workspace.GetItems<EntityContainer>(DataSpace.CSpace))
            {
                foreach (EntitySet entitySet in container.BaseEntitySets.OfType<EntitySet>())
                {
                    if (IsAssignableFrom(entitySet.ElementType, type))
                    {
                        yield return entitySet;
                    }
                }
            }
        }
 
        /// <summary>
        /// Gets all entity sets that participate as members for the specified <paramref name="associationType"/>.
        /// </summary>
        /// <param name="workspace">Workspace with metadata.</param>
        /// <param name="associationType">Type of assocation to check.</param>
        /// <param name="member">Member of association to check.</param>
        /// <returns>
        /// All <see cref="EntitySet"/> instances that are are on the <paramref name="member"/> role for 
        /// some association of <paramref name="associationType"/>.
        /// </returns>
        private static IEnumerable<EntitySet> GetEntitySetsWithAssociationSets(
            MetadataWorkspace workspace,
            RelationshipType associationType,
            RelationshipEndMember member)
        {
            Debug.Assert(workspace != null, "workspace != null");
            Debug.Assert(associationType != null, "associationType != null");
            Debug.Assert(member != null, "member != null");
            foreach (EntityContainer container in workspace.GetItems<EntityContainer>(DataSpace.CSpace))
            {
                foreach (AssociationSet associationSet in container.BaseEntitySets.OfType<AssociationSet>())
                {
                    if (associationSet.ElementType == associationType)
                    {
                        foreach (AssociationSetEnd end in associationSet.AssociationSetEnds)
                        {
                            if (end.CorrespondingAssociationEndMember == member)
                            {
                                yield return end.EntitySet;
                            }
                        }
                    }
                }
            }
        }
 
        /// <summary>
        /// Checks the <paramref name="type"/> and all of its base types for the HasStream attribute.
        /// </summary>
        /// <param name="type">Type to check</param>
        /// <returns>True if HasStream="true" stream property is defined on the given entity type or any of its base types.</returns>
        private static bool GetDefaultStreamPropertyFromEntityTypeHierarchy(StructuralType type)
        {            
            while (type != null)
            {
                if (GetEntityTypeDefaultStreamProperty(type))
                {
                    return true;
                }
 
                type = type.BaseType as StructuralType;
            }
 
            return false;
        }
 
        /// <summary>Reads the HasStream attribute from the specified <paramref name="type"/>.</summary>
        /// <param name="type">Type to read attribute from.</param>
        /// <returns>True if HasStream="true" stream property is defined for the entity type.</returns>
        private static bool GetEntityTypeDefaultStreamProperty(StructuralType type)
        {
            Debug.Assert(type != null, "type != null");
 
            const string HasStreamAttribute = XmlConstants.DataWebMetadataNamespace + ":" + XmlConstants.DataWebAccessHasStreamAttribute;
            bool result = false;
            MetadataProperty property;
            if (type.MetadataProperties.TryGetValue(HasStreamAttribute, false /* ignoreCase */, out property))
            {
                string text = (string)property.Value;
                if (String.IsNullOrEmpty(text))
                {
                    throw new InvalidOperationException(Strings.ObjectContext_HasStreamAttributeEmpty(type.Name));
                }
 
                if (text != XmlConstants.DataWebAccessDefaultStreamPropertyValue)
                {
                    // In the future we might support multiple stream properties. For now we only support $default.
                    throw new NotSupportedException(Strings.ObjectContext_UnsupportedStreamProperty(text, type.Name));
                }
 
                result = true;
            }
 
            return result;
        }
 
        /// <summary>Sets the MIME type, if specified for the specified member.</summary>
        /// <param name="resourceProperty">resource property whose mime type needs to be updated.</param>
        /// <param name="csdlMember">C-Space member for which we need to find the C-Space mime type attribute.</param>
        private static void SetMimeTypeForMappedMember(ResourceProperty resourceProperty, EdmMember csdlMember)
        {
            const string MimePropertyName = XmlConstants.DataWebMetadataNamespace + ":" + XmlConstants.DataWebMimeTypeAttributeName;
            MetadataProperty property;
            if (csdlMember.MetadataProperties.TryGetValue(MimePropertyName, false /* ignoreCase */, out property))
            {
                string mimeType = (string)property.Value;
                resourceProperty.MimeType = mimeType;
            }
        }
 
        /// <summary>Generic method to invoke an Include method on an ObjectQuery source.</summary>
        /// <typeparam name="T">Element type of the source.</typeparam>
        /// <param name="query">Source query.</param>
        /// <param name="dottedPath">Path to include.</param>
        /// <returns>A new query that includes <paramref name="dottedPath"/> in <paramref name="query"/>.</returns>
        private static ObjectQuery<T> Include<T>(IQueryable query, string dottedPath)
        {
            Debug.Assert(query != null, "query != null");
            Debug.Assert(dottedPath != null, "dottedPath != null");
 
            ObjectQuery<T> typedQuery = (ObjectQuery<T>)query;
            return typedQuery.Include(dottedPath);
        }
 
        /// <summary>Checks whether <paramref name="derivedType"/> may be assigned to <paramref name="baseType"/>.</summary>
        /// <param name="baseType">Type to check assignment to.</param>
        /// <param name="derivedType">Type to check assignment from.</param>
        /// <returns>
        /// true if an instance of <paramref name="derivedType" /> can be assigned to a variable of 
        /// <paramref name="baseType"/>; false otherwise.
        /// </returns>
        private static bool IsAssignableFrom(EntityType baseType, EntityType derivedType)
        {
            while (derivedType != null)
            {
                if (derivedType == baseType)
                {
                    return true;
                }
 
                derivedType = (EntityType)derivedType.BaseType;
            }
 
            return false;
        }
 
        /// <summary>Checks whether the specified type is a known primitive type.</summary>
        /// <param name="type">Type to check.</param>
        /// <returns>true if the specified type is known to be a primitive type; false otherwise.</returns>
        private static bool IsPrimitiveType(EdmType type)
        {
            Debug.Assert(type != null, "type != null");
            if (type.BuiltInTypeKind != BuiltInTypeKind.PrimitiveType)
            {
                return false;
            }
            else
            {
                Debug.Assert(
                    WebUtil.IsPrimitiveType(((PrimitiveType)type).ClrEquivalentType),
                    "WebUtil.IsPrimitiveType(((PrimitiveType)type).ClrEquivalentType) - all EDM primitive types are Astoria primitive types");
                return true;
            }
        }
 
        /// <summary>Joins the list of segment identifiers by dots.</summary>
        /// <param name='segments'>List of segments to join.</param>
        /// <returns>A string with the identifiers joined by dots.</returns>
        private static string JoinIdentifiers(List<string> segments)
        {
            Debug.Assert(segments != null, "segments != null");
            
            int capacity = 0;
            foreach (string segment in segments)
            {
                capacity += segment.Length;
            }
 
            capacity += segments.Count - 1;
 
            StringBuilder builder = new StringBuilder(capacity);
            foreach (string segment in segments)
            {
                if (builder.Length > 0)
                {
                    builder.Append('.');
                }
 
                builder.Append(segment);
            }
 
            return builder.ToString();
        }
 
        /// <summary>Walks all the expansion paths in the subtree of the <paramref name="expandedNode"/>
        /// and will execute the <paramref name="action"/> for each of the unique paths with the path
        /// serialized as a dotted string as its argument.</summary>
        /// <typeparam name="T">The type of the state which is modified by the actions.</typeparam>
        /// <param name="expandedNode">The root node to start from.</param>
        /// <param name="action">The action to execute for each unique path.</param>
        /// <param name="state">The state object to pass to the action.</param>
        /// <param name="pathSegments">List of path segments so far (including the <paramref name="expandedNode"/>)</param>
        /// <returns>The modified state as a result to call to all the actions.</returns>
        private static T VisitDottedExpandPaths<T>(ExpandedProjectionNode expandedNode, Func<T, string, T> action, T state, List<string> pathSegments)
        {
            bool foundChildExpansionNode = false;
            Debug.Assert(
                expandedNode.PropertyName.Length == 0 || pathSegments[pathSegments.Count - 1] == expandedNode.PropertyName,
                "The last segment doesn't match the node the function was called on.");
 
            foreach (ProjectionNode node in expandedNode.Nodes)
            {
                ExpandedProjectionNode childExpandedNode = node as ExpandedProjectionNode;
                if (childExpandedNode != null)
                {
                    foundChildExpansionNode = true;
 
                    pathSegments.Add(childExpandedNode.PropertyName);
                    state = VisitDottedExpandPaths(childExpandedNode, action, state, pathSegments);
                    pathSegments.RemoveAt(pathSegments.Count - 1);
                }
            }
 
            if (!foundChildExpansionNode && pathSegments.Count > 0)
            {
                state = action(state, JoinIdentifiers(pathSegments));
            }
 
            return state;
        }
 
        /// <summary>
        /// Populates the metadata for the given type and its base type
        /// </summary>
        /// <param name="workspace">metadata workspace containing all the metadata information</param>
        /// <param name="edmType"> type whose metadata needs to be populated </param>
        /// <param name="knownTypes">list of known types </param>
        /// <param name="childTypes">list of already known types and their immediate children</param>
        /// <returns>returns the resource type corresponding to the given edmType</returns>
        private static ResourceType PopulateTypeMetadata(
            MetadataWorkspace workspace,
            StructuralType edmType,
            IDictionary<Type, ResourceType> knownTypes,
            IDictionary<ResourceType, List<ResourceType>> childTypes)
        {
            Debug.Assert(
                edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType ||
                edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType, 
                "type must be entity or complex type");
 
            ResourceType resourceType = null;
            Type clrType = GetClrTypeForCSpaceType(workspace, edmType);
            if (clrType != null && !knownTypes.TryGetValue(clrType, out resourceType))
            {
                ResourceType baseType = null;
                if (edmType.BaseType != null)
                {
                    baseType = PopulateTypeMetadata(workspace, (StructuralType)edmType.BaseType, knownTypes, childTypes);
                }
 
                resourceType = CreateResourceType(edmType, clrType, baseType, knownTypes, childTypes);
            }
 
            return resourceType;
        }
 
        /// <summary>
        /// Creates a new instance of resource type given the cspace structural type and mapping clr type.
        /// </summary>
        /// <param name="cspaceType">cspace structural type.</param>
        /// <param name="clrType">mapping clr type for the given structural type.</param>
        /// <param name="baseResourceType">the base resource type for the given resource type.</param>
        /// <param name="knownTypes">list of already known resource types.</param>
        /// <param name="childTypes">list of already known types and their immediate children</param>
        /// <returns>the new resource type instance created for the given cspace type.</returns>
        private static ResourceType CreateResourceType(
            StructuralType cspaceType,
            Type clrType,
            ResourceType baseResourceType,
            IDictionary<Type, ResourceType> knownTypes,
            IDictionary<ResourceType, List<ResourceType>> childTypes)
        {
            ResourceTypeKind resourceTypeKind = cspaceType.BuiltInTypeKind == BuiltInTypeKind.EntityType ? ResourceTypeKind.EntityType : ResourceTypeKind.ComplexType;
            
            // We do not support open types in Object Context provider yet.
            ResourceType resourceType = new ResourceType(clrType, resourceTypeKind, baseResourceType, cspaceType.NamespaceName, cspaceType.Name, clrType.IsAbstract);
            if (GetDefaultStreamPropertyFromEntityTypeHierarchy(cspaceType))
            {
                resourceType.IsMediaLinkEntry = true;
            }
 
            knownTypes.Add(clrType, resourceType);
            childTypes.Add(resourceType, null);
            if (baseResourceType != null)
            {
                Debug.Assert(childTypes.ContainsKey(baseResourceType), "childTypes.ContainsKey(baseResourceType)");
                if (childTypes[baseResourceType] == null)
                {
                    childTypes[baseResourceType] = new List<ResourceType>();
                }
 
                childTypes[baseResourceType].Add(resourceType);
            }
 
            return resourceType;
        }
 
        /// <summary>
        /// Populates the member metadata for the given type
        /// </summary>
        /// <param name="resourceType">resource type whose member metadata needs to be filled</param>
        /// <param name="workspace">workspace containing the metadata information</param>
        /// <param name="knownTypes">list of already known types</param>
        private static void PopulateMemberMetadata(
            ResourceType resourceType,
            MetadataWorkspace workspace,
            IDictionary<Type, ResourceType> knownTypes)
        {
            Debug.Assert(resourceType != null, "resourceType != null");
            Debug.Assert(workspace != null, "workspace != null");
 
            // Find the type from the OSpace
            StructuralType edmType = workspace.GetItem<StructuralType>(resourceType.FullName, DataSpace.CSpace);
            foreach (EdmMember member in edmType.Members)
            {
                // only look at the instance members
                if (member.DeclaringType != edmType)
                {
                    continue;
                }
 
                ResourcePropertyKind kind = (ResourcePropertyKind)(-1);
                PropertyInfo propertyInfo = resourceType.InstanceType.GetProperty(member.Name);
                ResourceType propertyType = null;
                switch (member.TypeUsage.EdmType.BuiltInTypeKind)
                {
                    case BuiltInTypeKind.PrimitiveType:
                        propertyType = ResourceType.GetPrimitiveResourceType(propertyInfo.PropertyType);
 
                        if (propertyType == null)
                        {
                            throw new NotSupportedException(Strings.ObjectContext_PrimitiveTypeNotSupported(member.Name, edmType.Name, member.TypeUsage.EdmType.Name));
                        }
 
                        if (edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType &&
                            ((EntityType)edmType).KeyMembers.Contains(member))
                        {
                            kind = ResourcePropertyKind.Key | ResourcePropertyKind.Primitive;
                        }
                        else
                        {
                            kind = ResourcePropertyKind.Primitive;
                        }
 
                        break;
                    case BuiltInTypeKind.ComplexType:
                        kind = ResourcePropertyKind.ComplexType;
                        propertyType = knownTypes[propertyInfo.PropertyType];
                        break;
                    case BuiltInTypeKind.EntityType:
                        kind = ResourcePropertyKind.ResourceReference;
                        propertyType = knownTypes[propertyInfo.PropertyType];
                        break;
                    case BuiltInTypeKind.CollectionType:
                        kind = ResourcePropertyKind.ResourceSetReference;
                        Type propertyClrType = GetClrTypeForCSpaceType(workspace, (EntityType)((CollectionType)member.TypeUsage.EdmType).TypeUsage.EdmType);
                        Debug.Assert(!WebUtil.IsPrimitiveType(propertyClrType), "We don't support collections of primitives, we shouldn't see one here");
                        propertyType = knownTypes[propertyClrType];
                        break;
                    default:
                        Debug.Assert(false, "Invalid member type encountered on " + member.Name + " - " + member.TypeUsage.EdmType.BuiltInTypeKind);
                        break;
                }
 
                Debug.Assert(propertyType != null, "propertyType != null");
                ResourceProperty resourceProperty = new ResourceProperty(propertyInfo.Name, kind, propertyType);
                SetMimeTypeForMappedMember(resourceProperty, member);
                resourceType.AddProperty(resourceProperty);
            }
        }
 
        /// <summary>
        /// Finds a non-syndication mapping related property from the collection of extended properties from an EFx resource property
        /// </summary>
        /// <param name="metadataExtendedProperties">Collection of metadata extended properties of <paramref name="memberName"/></param>        
        /// <param name="currentlyAllowed">Flag indicating whether the extended property should exist in the current context</param>
        /// <param name="epmPropertyName">Name of the property of resource type</param>
        /// <param name="typeName">Type to which the property belongs</param>
        /// <param name="memberName">Name of the member whose extended properties we are searching from</param>
        /// <returns>The corresponding MetadataProperty object if found, null otherwise</returns>
        private static MetadataProperty GetNonSyndicationExtendedProperty(
            IEnumerable<MetadataProperty> metadataExtendedProperties, 
            bool currentlyAllowed, 
            String epmPropertyName,
            String typeName, 
            String memberName)
        {
            MetadataProperty epmProperty = FindSingletonExtendedProperty(metadataExtendedProperties, epmPropertyName, typeName, memberName);
            if (epmProperty != null)
            {
                if (!currentlyAllowed)
                {
                    throw new InvalidOperationException(memberName == null ? 
                                    Strings.ObjectContext_InvalidAttributeForNonSyndicationItemsType(epmPropertyName, typeName) :
                                    Strings.ObjectContext_InvalidAttributeForNonSyndicationItemsMember(epmPropertyName, memberName, typeName));
                }
            }
 
            return epmProperty;
        }
 
        /// <summary>Gets the NavigationProperty for the member of a set type if available.</summary>
        /// <param name="set">Set for which to return navigation property.</param>
        /// <param name="member">Relationship end member for the navigation property.</param>
        /// <returns>The NavigationProperty for the member of a set type if available; null otherwise.</returns>
        private static NavigationProperty PropertyForEnd(EntitySet set, RelationshipEndMember member)
        {
            Debug.Assert(set != null, "set != null");
            Debug.Assert(member != null, "member != null");
            foreach (NavigationProperty p in set.ElementType.NavigationProperties)
            {
                if (p.FromEndMember == member)
                {
                    return p;
                }
            }
            
            return null;
        }
 
        /// <summary>Creates a string array with property names.</summary>
        /// <param name="properties">Properties to include in name.</param>
        /// <returns>A string array with property names.</returns>
        private static string[] PropertyNamesToStrings(ReadOnlyMetadataCollection<EdmProperty> properties)
        {
            Debug.Assert(properties != null, "properties != null");
            string[] result = new string[properties.Count];
            for (int i = 0; i < properties.Count; i++)
            {
                result[i] = properties[i].Name;
            }
            
            return result;
        }
 
        /// <summary>
        /// Write the child element of the referential constraint
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="nodeName">name of the xmlnode : Principal or Dependent</param>
        /// <param name="roleName">role name</param>
        /// <param name="properties">list of properties</param>
        private static void WriteReferentialConstraintChildElement(
            XmlWriter xmlWriter,
            string nodeName,
            string roleName,
            ReadOnlyMetadataCollection<EdmProperty> properties)
        {
            // Write the principal role
            xmlWriter.WriteStartElement(nodeName);
            xmlWriter.WriteAttributeString(XmlConstants.Role, roleName);
 
            foreach (EdmProperty property in properties)
            {
                xmlWriter.WriteStartElement(XmlConstants.PropertyRef);
                xmlWriter.WriteAttributeString(XmlConstants.Name, property.Name);
                xmlWriter.WriteEndElement();
            }
 
            xmlWriter.WriteEndElement();
        }
 
        /// <summary>
        /// Write the metadata for the given facets in the given xml writer
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="typeUsage">typeusage whose metadata needs to be written</param>
        private static void WriteFacets(XmlWriter xmlWriter, TypeUsage typeUsage)
        {
            foreach (Facet facet in typeUsage.Facets)
            {
                if (facet.Value != null)
                {
                    WriteFacetValue(xmlWriter, facet.Name, facet.Value);
                }
            }
        }
 
        /// <summary>
        /// Write the metadata for the given facet
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="facetName">name of the facet</param>
        /// <param name="facetValue">value of the facet</param>
        private static void WriteFacetValue(XmlWriter xmlWriter, string facetName, object facetValue)
        {
            Type facetValueType = facetValue.GetType();
 
            // We need to special case the boolean facets, since ToString returns True and False as values
            // and for xml, they need to be lower case
            if (facetValueType == typeof(bool))
            {
                if ((bool)facetValue)
                {
                    xmlWriter.WriteAttributeString(facetName, XmlConstants.XmlTrueLiteral);
                }
                else
                {
                    xmlWriter.WriteAttributeString(facetName, XmlConstants.XmlFalseLiteral);
                }
            }
            else if (facetValueType == typeof(int))
            {
                xmlWriter.WriteAttributeString(facetName, XmlConvert.ToString((int)facetValue));
            }
            else if (facetValueType.IsEnum)
            {
                xmlWriter.WriteAttributeString(facetName, facetValue.ToString());
            }
            else if (facetValueType == typeof(byte))
            {
                xmlWriter.WriteAttributeString(facetName, XmlConvert.ToString((byte)facetValue));
            }
            else if (facetValueType == typeof(DateTime))
            {
                xmlWriter.WriteAttributeString(facetName, XmlConvert.ToString((DateTime)facetValue, "yyyy-MM-dd HH:mm:ss.fffZ"));
            }
            else
            {
                xmlWriter.WriteAttributeString(facetName, facetValue.ToString());
            }
        }
 
        /// <summary>
        /// Write the user annotations to the csdl
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="metadataProperties">list of metadata properties from which extended ones needs to be written</param>
        /// <param name="writeCustomElements">if true, this function writes out the custom elements, otherwise it writes the custom attributes to the given xml writer.</param>
        private static void WriteUserDefinedAnnotations(
            XmlWriter xmlWriter,
            ReadOnlyMetadataCollection<MetadataProperty> metadataProperties,
            bool writeCustomElements)
        {
            Debug.Assert(xmlWriter != null, "xmlWriter != null");
            Debug.Assert(metadataProperties != null, "metadataProperties != null");
            Debug.Assert(
                (!writeCustomElements && xmlWriter.WriteState == WriteState.Element) ||
                (writeCustomElements && xmlWriter.WriteState == WriteState.Element || xmlWriter.WriteState == WriteState.Content), 
                "For custom attributes, the write state must be element. For custom elements, writer can be in element or content state");
 
            foreach (MetadataProperty metadataProperty in metadataProperties)
            {
                if (metadataProperty.PropertyKind == PropertyKind.Extended)
                {
                    // Temporary fix for beta1 to ignore the store generated pattern attribute, since
                    // that causes V1 code gen to fail
                    if (metadataProperty.Name.Equals(XmlConstants.StoreGeneratedPattern))
                    {
                        continue;
                    }
 
                    int index = metadataProperty.Name.LastIndexOf(":", StringComparison.Ordinal);
                    Debug.Assert(index != -1, "empty space is a reserved namespace for edm. So this should never be the case");
                    string xmlNamespace = metadataProperty.Name.Substring(0, index);
                    string attributeName = metadataProperty.Name.Substring(index + 1);
 
                    if (!writeCustomElements && (metadataProperty.Value == null || metadataProperty.Value.GetType() != typeof(XElement)))
                    {
                        xmlWriter.WriteAttributeString(attributeName, xmlNamespace, (string)metadataProperty.Value);
                    }
                    else if (writeCustomElements && metadataProperty.Value != null && metadataProperty.Value.GetType() == typeof(XElement))
                    {
                        XElement annotation = (XElement)metadataProperty.Value;
                        annotation.WriteTo(xmlWriter);
                    }
                }
            }
        }
 
        /// <summary>
        /// Returns the entity set name for the given entity set. If this entity set belongs to the default container name,
        /// then it returns the entity set name, otherwise qualifies it with the entitycontainer name
        /// </summary>
        /// <param name="entitySetName">entity set name</param>
        /// <param name="entityContainerName">entity container name</param>
        /// <param name="containedInDefaultEntityContainer">true if the given entity set belongs to the default entity container</param>
        /// <returns>returns the entity set name</returns>
        private static string GetEntitySetName(string entitySetName, string entityContainerName, bool containedInDefaultEntityContainer)
        {
            if (containedInDefaultEntityContainer)
            {
                return entitySetName;
            }
            else
            {
                return entityContainerName + "." + entitySetName;
            }
        }
 
        /// <summary>
        /// Returns the escaped entity set name for the given entity set. If this entity set belongs to the default container name,
        /// then it returns the escaped entity set name, otherwise it escapes both the container and set name
        /// </summary>
        /// <param name="qualifiedEntitySetName">qualified entity set name whose name needs to be escaped</param>
        /// <returns>returns the escaped entityset name</returns>
        private static string GetEscapedEntitySetName(string qualifiedEntitySetName)
        {
            int indexOfLastPeriod = qualifiedEntitySetName.LastIndexOf('.');
 
            if (-1 == indexOfLastPeriod)
            {
                return "[" + qualifiedEntitySetName + "]";
            }
            else
            {
                return "[" + qualifiedEntitySetName.Substring(0, indexOfLastPeriod) + "].[" + qualifiedEntitySetName.Substring(indexOfLastPeriod + 1) + "]";
            }
        }
 
        /// <summary>
        /// Returns the edm schema multiplicity value for the given multiplicity enum
        /// </summary>
        /// <param name="multiplicity">enum multiplicity value</param>
        /// <returns>returns edm schema multiplicity value</returns>
        private static string GetMultiplicity(RelationshipMultiplicity multiplicity)
        {
            string multiplicityValue;
            if (RelationshipMultiplicity.Many == multiplicity)
            {
                multiplicityValue = XmlConstants.Many;
            }
            else if (RelationshipMultiplicity.One == multiplicity)
            {
                multiplicityValue = XmlConstants.One;
            }
            else
            {
                Debug.Assert(
                    RelationshipMultiplicity.ZeroOrOne == multiplicity,
                    "Invalid value for multiplicity encountered");
                multiplicityValue = XmlConstants.ZeroOrOne;
            }
 
            return multiplicityValue;
        }
 
        /// <summary>Initializes metadata for the given object context.</summary>
        /// <param name="objectContext">Instance of data source to use if pure static analysis isn't possible.</param>
        /// <param name="assembly">assembly whose metadata needs to be loaded.</param>
        private static void InitializeObjectItemCollection(ObjectContext objectContext, Assembly assembly)
        {
            objectContext.MetadataWorkspace.LoadFromAssembly(assembly);
        }
 
        /// <summary>
        /// Write the metadata for the given members in the given xml writer
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="declaringType">type whose members metadata needs to be written</param>
        /// <param name="metadataManager">metadata manager instance</param>
        private static void WriteMembers(XmlWriter xmlWriter, StructuralType declaringType, MetadataManager metadataManager)
        {
            foreach (EdmMember member in metadataManager.GetPropertiesDeclaredInThisType(declaringType))
            {
                if (member.BuiltInTypeKind == BuiltInTypeKind.EdmProperty)
                {
                    xmlWriter.WriteStartElement(XmlConstants.Property);
                    xmlWriter.WriteAttributeString(XmlConstants.Name, member.Name);
                    xmlWriter.WriteAttributeString(XmlConstants.Type, member.TypeUsage.EdmType.FullName);
                    WriteFacets(xmlWriter, member.TypeUsage);
                }
                else
                {
                    Debug.Assert(
                        BuiltInTypeKind.NavigationProperty == member.BuiltInTypeKind,
                        "Invalid Member type encountered");
                    NavigationProperty navProperty = (NavigationProperty)member;
                    xmlWriter.WriteStartElement(XmlConstants.NavigationProperty);
                    xmlWriter.WriteAttributeString(XmlConstants.Name, navProperty.Name);
                    xmlWriter.WriteAttributeString(XmlConstants.Relationship, navProperty.RelationshipType.FullName);
                    xmlWriter.WriteAttributeString(XmlConstants.FromRole, navProperty.FromEndMember.Name);
                    xmlWriter.WriteAttributeString(XmlConstants.ToRole, navProperty.ToEndMember.Name);
                }
 
                WriteUserDefinedAnnotations(xmlWriter, member.MetadataProperties, false /*writeCustomElements*/);
                WriteUserDefinedAnnotations(xmlWriter, member.MetadataProperties, true /*writeCustomElements*/);
                xmlWriter.WriteEndElement();
            }
        }
 
        /// <summary>
        /// Write the metadata for the given association set element in the given xml writer
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="associationSet">association set whose metadata needs to be written</param>
        private static void WriteAssociationSet(XmlWriter xmlWriter, AssociationSet associationSet)
        {
            xmlWriter.WriteStartElement(XmlConstants.AssociationSet);
            xmlWriter.WriteAttributeString(XmlConstants.Name, associationSet.Name);
            xmlWriter.WriteAttributeString(XmlConstants.Association, associationSet.ElementType.FullName);
 
            // Write the user defined annotations
            WriteUserDefinedAnnotations(xmlWriter, associationSet.MetadataProperties, false /*writeCustomElements*/);
            for (int i = 0; i < associationSet.AssociationSetEnds.Count; i++)
            {
                xmlWriter.WriteStartElement(XmlConstants.End);
                xmlWriter.WriteAttributeString(XmlConstants.Role, associationSet.AssociationSetEnds[i].Name);
                xmlWriter.WriteAttributeString(XmlConstants.EntitySet, associationSet.AssociationSetEnds[i].EntitySet.Name);
 
                // Write the user defined annotations
                WriteUserDefinedAnnotations(xmlWriter, associationSet.AssociationSetEnds[i].MetadataProperties, false /*writeCustomElements*/);
                WriteUserDefinedAnnotations(xmlWriter, associationSet.AssociationSetEnds[i].MetadataProperties, true /*writeCustomElements*/);
                xmlWriter.WriteEndElement();
            }
 
            WriteUserDefinedAnnotations(xmlWriter, associationSet.MetadataProperties, true /*writeCustomElements*/);
            xmlWriter.WriteEndElement();
        }
 
        /// <summary>
        /// Write the entity set element in the given xml writer
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="entitySet">entity set whose metadata needs to be written</param>
        private static void WriteEntitySet(XmlWriter xmlWriter, EntitySet entitySet)
        {
            xmlWriter.WriteStartElement(XmlConstants.EntitySet);
            xmlWriter.WriteAttributeString(XmlConstants.Name, entitySet.Name);
            xmlWriter.WriteAttributeString(XmlConstants.EntityType, entitySet.ElementType.FullName);
 
            // Write the user defined annotations
            WriteUserDefinedAnnotations(xmlWriter, entitySet.MetadataProperties, false /*writeCustomElements*/);
            WriteUserDefinedAnnotations(xmlWriter, entitySet.MetadataProperties, true /*writeCustomElements*/);
            xmlWriter.WriteEndElement();
        }
 
        /// <summary>
        /// Writes the metadata for all the edmTypes
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="edmTypes">edmtypes whose metadata needs to be written</param>
        /// <param name="metadataManager">metadata manager instance</param>
        private static void WriteEdmTypes(XmlWriter xmlWriter, IEnumerable<EdmType> edmTypes, MetadataManager metadataManager)
        {
            // Top Level EdmTypes are EntityType, ComplexType and AssociationType.
            foreach (EdmType edmType in edmTypes)
            {
                switch (edmType.BuiltInTypeKind)
                {
                    case BuiltInTypeKind.EntityType:
                        WriteEntityType(xmlWriter, (EntityType)edmType, metadataManager);
                        break;
                    case BuiltInTypeKind.ComplexType:
                        WriteComplexType(xmlWriter, (ComplexType)edmType, metadataManager);
                        break;
                    case BuiltInTypeKind.AssociationType:
                        WriteAssociationType(xmlWriter, (AssociationType)edmType);
                        break;
                    default:
                        Debug.Assert(false, "Unexpected EdmType encountered");
                        break;
                }
            }
        }
 
        /// <summary>
        /// Write the metadata for the given EntityType element in the given xml writer
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="entityType">entity type whose metadata needs to be written</param>
        /// <param name="metadataManager">metadata manager instance</param>
        private static void WriteEntityType(XmlWriter xmlWriter, EntityType entityType, MetadataManager metadataManager)
        {
            xmlWriter.WriteStartElement(XmlConstants.EntityType);
            xmlWriter.WriteAttributeString(XmlConstants.Name, entityType.Name);
            if (entityType.Abstract)
            {
                xmlWriter.WriteAttributeString(XmlConstants.Abstract, XmlConstants.XmlTrueLiteral);
            }
 
            if (entityType.BaseType != null)
            {
                xmlWriter.WriteAttributeString(XmlConstants.BaseType, entityType.BaseType.FullName);
 
                // Write the user defined annotations
                WriteUserDefinedAnnotations(xmlWriter, entityType.MetadataProperties, false /*writeCustomElements*/);
            }
            else
            {
                // Write the user defined annotations
                WriteUserDefinedAnnotations(xmlWriter, entityType.MetadataProperties, false /*writeCustomElements*/);
 
                // If there is no base type, then the key must be defined on this entity type.
                xmlWriter.WriteStartElement(XmlConstants.Key);
                foreach (EdmMember member in entityType.KeyMembers)
                {
                    xmlWriter.WriteStartElement(XmlConstants.PropertyRef);
                    xmlWriter.WriteAttributeString(XmlConstants.Name, member.Name);
                    xmlWriter.WriteEndElement();
                }
 
                xmlWriter.WriteEndElement();
            }
 
            WriteMembers(xmlWriter, entityType, metadataManager);
            WriteUserDefinedAnnotations(xmlWriter, entityType.MetadataProperties, true /*writeCustomElements*/);
            xmlWriter.WriteEndElement();
        }
 
        /// <summary>
        /// Write the metadata for the given complex type element in the given xml writer
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="complexType">complex type whose metadata needs to be written</param>
        /// <param name="metadataManager">metadata manager instance</param>
        private static void WriteComplexType(XmlWriter xmlWriter, ComplexType complexType, MetadataManager metadataManager)
        {
            xmlWriter.WriteStartElement(XmlConstants.ComplexType);
            xmlWriter.WriteAttributeString(XmlConstants.Name, complexType.Name);
 
            // Write the user defined annotations
            WriteUserDefinedAnnotations(xmlWriter, complexType.MetadataProperties, false /*writeCustomElements*/);
 
            // Write the metadata for complex type properties
            WriteMembers(xmlWriter, complexType, metadataManager);
            WriteUserDefinedAnnotations(xmlWriter, complexType.MetadataProperties, true /*writeCustomElements*/);
            xmlWriter.WriteEndElement();
        }
 
        /// <summary>
        /// Write the metadata for the given association type element in the given xml writer
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="associationType">association type whose metadata needs to be written</param>
        private static void WriteAssociationType(XmlWriter xmlWriter, AssociationType associationType)
        {
            xmlWriter.WriteStartElement(XmlConstants.Association);
            xmlWriter.WriteAttributeString(XmlConstants.Name, associationType.Name);
 
            // Write the user defined annotations
            WriteUserDefinedAnnotations(xmlWriter, associationType.MetadataProperties, false /*writeCustomElements*/);
 
            foreach (AssociationEndMember end in associationType.RelationshipEndMembers)
            {
                xmlWriter.WriteStartElement(XmlConstants.End);
                xmlWriter.WriteAttributeString(XmlConstants.Role, end.Name);
 
                // The ends are of reference type
                xmlWriter.WriteAttributeString(XmlConstants.Type, ((RefType)end.TypeUsage.EdmType).ElementType.FullName);
 
                // Write the multiplicity value
                xmlWriter.WriteAttributeString(XmlConstants.Multiplicity, GetMultiplicity(end.RelationshipMultiplicity));
 
                // Write the user defined annotations (only attributes)
                WriteUserDefinedAnnotations(xmlWriter, end.MetadataProperties, false /*writeCustomElements*/);
 
                // write the action value
                if (OperationAction.None != end.DeleteBehavior)
                {
                    xmlWriter.WriteStartElement(XmlConstants.OnDelete);
                    xmlWriter.WriteAttributeString(XmlConstants.Action, end.DeleteBehavior.ToString());
                    xmlWriter.WriteEndElement();
                }
 
                WriteUserDefinedAnnotations(xmlWriter, end.MetadataProperties, true /*writeCustomElements*/);
                xmlWriter.WriteEndElement();
            }
 
            foreach (ReferentialConstraint referentialConstraint in associationType.ReferentialConstraints)
            {
                xmlWriter.WriteStartElement(XmlConstants.ReferentialConstraint);
 
                // Write the user defined annotations
                WriteUserDefinedAnnotations(xmlWriter, referentialConstraint.MetadataProperties, false /*writeCustomElements*/);
                WriteReferentialConstraintChildElement(xmlWriter, XmlConstants.Principal, referentialConstraint.FromRole.Name, referentialConstraint.FromProperties);
                WriteReferentialConstraintChildElement(xmlWriter, XmlConstants.Dependent, referentialConstraint.ToRole.Name, referentialConstraint.ToProperties);
                WriteUserDefinedAnnotations(xmlWriter, referentialConstraint.MetadataProperties, true /*writeCustomElements*/);
                xmlWriter.WriteEndElement();
            }
 
            WriteUserDefinedAnnotations(xmlWriter, associationType.MetadataProperties, true /*writeCustomElements*/);
            xmlWriter.WriteEndElement();
        }
 
        /// <summary>
        /// Writes all the entity container definition in the given xml writer.
        /// Also emits a special annotation for the default entity container so that
        /// client can figure out which one is the default entity container name
        /// </summary>
        /// <param name="xmlWriter">xmlWriter to which metadata needs to be written</param>
        /// <param name="defaultEntityContainer">default entity container</param>
        /// <param name="provider">Data provider from which metadata should be gathered.</param>
        /// <param name="metadataManager">metadata manager instance</param>
        private static void WriteEntityContainers(
            XmlWriter xmlWriter,
            EntityContainer defaultEntityContainer,
            DataServiceProviderWrapper provider,
            MetadataManager metadataManager)
        {
            foreach (EntityContainer container in metadataManager.EntityContainers)
            {
                xmlWriter.WriteStartElement(XmlConstants.EntityContainer);
                xmlWriter.WriteAttributeString(XmlConstants.Name, container.Name);
 
                // Write the user defined annotations
                WriteUserDefinedAnnotations(xmlWriter, container.MetadataProperties, false /*writeCustomElements*/);
                if (container == defaultEntityContainer)
                {
                    MetadataSerializer.WriteDataWebMetadata(
                        xmlWriter, XmlConstants.IsDefaultEntityContainerAttribute, XmlConstants.XmlTrueLiteral);
                    MetadataSerializer.WriteServiceOperations(xmlWriter, provider);
                }
 
                List<EntitySet> entitySets;
                if (metadataManager.EntitySets.TryGetValue(container, out entitySets))
                {
                    foreach (EntitySet entitySet in entitySets)
                    {
                        WriteEntitySet(xmlWriter, entitySet);
                    }
                }
 
                List<AssociationSet> associationSets;
                if (metadataManager.AssociationSets.TryGetValue(container, out associationSets))
                {
                    foreach (AssociationSet associationSet in associationSets)
                    {
                        WriteAssociationSet(xmlWriter, associationSet);
                    }
                }
 
                WriteUserDefinedAnnotations(xmlWriter, container.MetadataProperties, true /*writeCustomElements*/);
                xmlWriter.WriteEndElement();
            }
        }
 
        /// <summary>
        /// Returns true if the subtree of expansions rooted in the specified <paramref name="expandedNode"/>
        /// contains either a filter or paging/maxresult constraint or if it expands two properties
        /// pointing into the same resource set.
        /// </summary>
        /// <param name="expandedNode">The root of the expansions tree to inspect.</param>
        /// <param name="resourceSets">Already visited resource sets.</param>
        /// <param name="depth">The depth of the expandedNode. The method will throw if depth greater than 8 is reached.</param>
        /// <returns>True if BasicExpandProvider should be used to process a query with this tree
        /// or false otherwise.</returns>
        private static bool ShouldUseBasicExpandProvider(ExpandedProjectionNode expandedNode, HashSet<ResourceSet> resourceSets, int depth)
        {
            // Note that we have to walk the entire tree even though we already found a reason to use the BasicExpandProvider
            //   the reason is that we also check for the maximum depth of expansions.
            bool shouldUseBasicExpandProvider = false;
 
            if (depth > 8)
            {
                throw DataServiceException.CreateBadRequestError(Strings.ObjectContext_ExpandTooDeep);
            }
 
            foreach (ProjectionNode node in expandedNode.Nodes)
            {
                ExpandedProjectionNode childExpandedNode = node as ExpandedProjectionNode;
                if (childExpandedNode != null)
                {
                    shouldUseBasicExpandProvider |= childExpandedNode.HasFilterOrMaxResults;
 
                    // If we expand properties that come from a container we've already encountered,
                    // we should use the basic provider; otherwise the EF materializer will throw because
                    // tracking is turned off for querying.
                    Debug.Assert(
                        childExpandedNode.ResourceSetWrapper != null,
                        "expandedNode.ResourceSetWrapper != null -- otherwise we have an open property in EF or forgot to bind a segment");
                    shouldUseBasicExpandProvider |= !resourceSets.Add(childExpandedNode.ResourceSetWrapper.ResourceSet);
 
                    shouldUseBasicExpandProvider |= ShouldUseBasicExpandProvider(childExpandedNode, resourceSets, depth + 1);
                }
            }
 
            return shouldUseBasicExpandProvider;
        }
 
        /// <summary>
        /// Create a new instance of the given clrtype using ObjectContext.CreateObject method
        /// </summary>
        /// <param name="context">current object context instance.</param>
        /// <param name="clrType">clrType whose instance needs to be created.</param>
        /// <returns>the instance returned by ObjectContext.</returns>
        private static object CreateObject(ObjectContext context, Type clrType)
        {
            // this.ObjectContext.CreateObject<T>()
            MethodInfo createObjectMethod = context.GetType().GetMethod("CreateObject", BindingFlags.Public | BindingFlags.Instance);
            return createObjectMethod.MakeGenericMethod(clrType).Invoke(context, null);
        }
 
        /// <summary>
        /// Checks if the given association is a FK association with cardinality 1 to 1 or 0..1 to 1
        /// </summary>
        /// <param name="association">metadata for the association.</param>
        /// <returns>Returns true if the given association is a FK association with cardinality 1 to 1 or 0..1 to 1.</returns>
        private static bool IsOneToOneFKAssocation(AssociationType association)
        {
            // first check if the relationship type is a FK relationship. If no, then return , since updates are not supported only for FK relationships
            if (!association.IsForeignKey)
            {
                return false;
            }
 
            // check if the relationship is 1 to 1 or 1 to 0..1 relationship (FK relationships are not supported for 0..1 to 0..1 cardinality)
            // if its neither, we do support updates and there is no need to throw.
            if (association.RelationshipEndMembers[0].RelationshipMultiplicity == RelationshipMultiplicity.Many ||
                association.RelationshipEndMembers[1].RelationshipMultiplicity == RelationshipMultiplicity.Many)
            {
                return false;
            }
 
            return true;
        }
 
        /// <summary>
        /// Given a resource type, builds the EntityPropertyMappingInfo for each EntityPropertyMappingAttribute on it
        /// </summary>
        /// <param name="workspace">The EFx metadata workspace to which the resource type belongs</param>
        /// <param name="baseResourceType">Resouce type for which EntityPropertyMappingAttribute discovery is happening</param>
        /// <param name="descendentResourceType">The most inherited resource type in the hierarchy</param>
        private void GetEpmInfoForResourceType(MetadataWorkspace workspace, ResourceType baseResourceType, ResourceType descendentResourceType)
        {
            if (baseResourceType.BaseType != null)
            {
                this.GetEpmInfoForResourceType(workspace, baseResourceType.BaseType, descendentResourceType);
            }
 
            // Get epm information provided at the entity type declaration level
            StructuralType edmType = workspace.GetItem<StructuralType>(baseResourceType.FullName, DataSpace.CSpace);
            IEnumerable<MetadataProperty> extendedProperties = edmType.MetadataProperties.Where(mp => mp.PropertyKind == PropertyKind.Extended);
 
            foreach (EpmPropertyInformation propertyInformation in GetEpmPropertyInformation(extendedProperties, edmType.Name, null))
            {
                ResourceProperty redefinedProperty = this.GetResourcePropertyFromEpmPath(
                                                            baseResourceType,
                                                            propertyInformation.SourcePath);
                if (redefinedProperty == null)
                {
                    throw new InvalidOperationException(Strings.ObjectContext_UnknownPropertyNameInEpmAttributes(propertyInformation.SourcePath, baseResourceType.Name));
                }
 
                this.GetEpmInfoForResourceProperty(
                    propertyInformation,
                    descendentResourceType,
                    redefinedProperty,
                    baseResourceType);
            }
 
            // Get epm information provided at the entity type property level
            foreach (EdmMember member in edmType.Members.Where(m => m.DeclaringType == edmType))
            {
                ResourceProperty resourceProperty = baseResourceType.TryResolvePropertiesDeclaredOnThisTypeByName(member.Name);
                Debug.Assert(resourceProperty != null, "resourceProperty must not be null");
                IEnumerable<MetadataProperty> extendedMemberProperties = member.MetadataProperties.Where(mdp => mdp.PropertyKind == PropertyKind.Extended);
 
                foreach (EpmPropertyInformation propertyInformation in GetEpmPropertyInformation(extendedMemberProperties, edmType.Name, member.Name))
                {
                    ResourceProperty propertyToUse = resourceProperty;
 
                    if (resourceProperty.IsOfKind(ResourcePropertyKind.ComplexType) && propertyInformation.PathGiven)
                    {
                        propertyInformation.SourcePath = resourceProperty.Name + "/" + propertyInformation.SourcePath;
                        propertyToUse = this.GetResourcePropertyFromEpmPath(
                                                baseResourceType,
                                                propertyInformation.SourcePath);
                    }
 
                    this.GetEpmInfoForResourceProperty(
                        propertyInformation,
                        descendentResourceType,
                        propertyToUse,
                        baseResourceType);
                }
            }
        }
 
        /// <summary>
        /// Given a resource type and its resource proeperty builds the EntityPropertyMappingInfo for the EntityPropertyMappingAttribute on it
        /// </summary>
        /// <param name="propertyInformation">EPM information for current property</param>
        /// <param name="descendentResourceType">The most inherited resource type in the hierarchy</param>
        /// <param name="resourceProperty">Resource property for which to get the information</param>
        /// <param name="definingType">Type that defined this property</param>
        private void GetEpmInfoForResourceProperty(
            EpmPropertyInformation propertyInformation,
            ResourceType descendentResourceType,
            ResourceProperty resourceProperty,
            ResourceType definingType)
        {
            if (propertyInformation.IsAtom)
            {
                if (resourceProperty.IsOfKind(ResourcePropertyKind.ComplexType))
                {
                    throw new InvalidOperationException(Strings.ObjectContext_SyndicationMappingForComplexPropertiesNotAllowed);
                }
                else
                {
                    EntityPropertyMappingAttribute epmAttr = new EntityPropertyMappingAttribute(
                                        propertyInformation.SourcePath,
                                        propertyInformation.SyndicationItem,
                                        propertyInformation.ContentKind,
                                        propertyInformation.KeepInContent);
 
                    descendentResourceType.BuildEpmInfo(epmAttr, definingType, true);
                }
            }
            else
            {
                if (resourceProperty.IsOfKind(ResourcePropertyKind.ComplexType))
                {
                    foreach (EntityPropertyMappingAttribute epmAttr in this.GetEpmAttrsFromComplexProperty(
                                                                        resourceProperty,
                                                                        propertyInformation.SourcePath,
                                                                        propertyInformation.TargetPath,
                                                                        propertyInformation.NsPrefix,
                                                                        propertyInformation.NsUri,
                                                                        propertyInformation.KeepInContent))
                    {
                        descendentResourceType.BuildEpmInfo(epmAttr, definingType, true);
                    }
                }
                else
                {
                    EntityPropertyMappingAttribute epmAttr = new EntityPropertyMappingAttribute(
                                        propertyInformation.SourcePath,
                                        propertyInformation.TargetPath,
                                        propertyInformation.NsPrefix,
                                        propertyInformation.NsUri,
                                        propertyInformation.KeepInContent);
 
                    descendentResourceType.BuildEpmInfo(epmAttr, definingType, true);
                }
            }
        }
 
        /// <summary>
        /// Obtains the ResourceProperty corresponding to a given sourcePath
        /// </summary>
        /// <param name="baseResourceType">Resource type in which to look for property</param>
        /// <param name="sourcePath">Source Path</param>
        /// <returns>ResourceProperty object corresponding to the property given through source path</returns>
        private ResourceProperty GetResourcePropertyFromEpmPath(ResourceType baseResourceType, String sourcePath)
        {
            String[] propertyPath = sourcePath.Split('/');
 
            if (baseResourceType.TryResolvePropertiesDeclaredOnThisTypeByName(propertyPath[0]) == null)
            {
                return baseResourceType.BaseType != null ? this.GetResourcePropertyFromEpmPath(baseResourceType.BaseType, sourcePath) : null;
            }
            else
            {
                ResourceProperty resourceProperty = null;
                foreach (var pathSegment in propertyPath)
                {
                    resourceProperty = baseResourceType.TryResolvePropertiesDeclaredOnThisTypeByName(pathSegment);
 
                    // Property not declared in this type
                    if (resourceProperty == null)
                    {
                        throw new InvalidOperationException(Strings.EpmSourceTree_InaccessiblePropertyOnType(pathSegment, baseResourceType.Name));
                    }
 
                    baseResourceType = resourceProperty.ResourceType;
                }
 
                return resourceProperty;
            }
        }
 
        /// <summary>
        /// Returns a sequence of attributes corresponding to a complex type with recursion
        /// </summary>
        /// <param name="complexProperty">Complex typed property</param>
        /// <param name="epmSourcePath">Source path</param>
        /// <param name="epmTargetPath">Target path</param>
        /// <param name="epmNsPrefix">Namespace prefix</param>
        /// <param name="epmNsUri">Namespace Uri</param>
        /// <param name="epmKeepInContent">KeepInContent setting</param>
        /// <returns>Sequence of entity property mapping information for complex type properties</returns>
        private IEnumerable<EntityPropertyMappingAttribute> GetEpmAttrsFromComplexProperty(
            ResourceProperty complexProperty,
            String epmSourcePath,
            String epmTargetPath,
            String epmNsPrefix,
            String epmNsUri,
            bool epmKeepInContent)
        {
            foreach (ResourceProperty subProperty in complexProperty.ResourceType.Properties)
            {
                String sourcePath = epmSourcePath + "/" + subProperty.Name;
                String targetPath = epmTargetPath + "/" + subProperty.Name;
 
                if (subProperty.IsOfKind(ResourcePropertyKind.ComplexType))
                {
                    foreach (EntityPropertyMappingAttribute epmAttr in this.GetEpmAttrsFromComplexProperty(subProperty, sourcePath, targetPath, epmNsPrefix, epmNsUri, epmKeepInContent))
                    {
                        yield return epmAttr;
                    }
                }
                else
                {
                    yield return new EntityPropertyMappingAttribute(
                                        sourcePath,
                                        targetPath,
                                        epmNsPrefix,
                                        epmNsUri,
                                        epmKeepInContent);
                }
            }
        }
 
        /// <summary>
        /// Get the entity set metadata object given the qualified entity set name
        /// </summary>
        /// <param name="qualifiedEntitySetName">qualified entity set name i.e. if the entity set
        /// belongs to entity container other than the default one, then the entity container name should
        /// be part of the qualified name</param>
        /// <returns>the entity set metadata object</returns>
        private EntitySet GetEntitySet(string qualifiedEntitySetName)
        {
            Debug.Assert(
                !String.IsNullOrEmpty(qualifiedEntitySetName),
                "!String.IsNullOrEmpty(qualifiedEntitySetName) -- otherwise qualifiedEntitySetName didn't come from internal metadata");
 
            string entityContainerName;
            string entitySetName;
 
            // entity set name is fully qualified
            int index = qualifiedEntitySetName.LastIndexOf('.');
            if (index != -1)
            {
                entityContainerName = qualifiedEntitySetName.Substring(0, index);
                entitySetName = qualifiedEntitySetName.Substring(index + 1);
            }
            else
            {
                entityContainerName = this.ObjectContext.DefaultContainerName;
                entitySetName = qualifiedEntitySetName;
            }
 
            EntityContainer entityContainer = this.ObjectContext.MetadataWorkspace.GetEntityContainer(entityContainerName, DataSpace.CSpace);
            Debug.Assert(
                entityContainer != null, 
                "entityContainer != null -- otherwise entityContainerName '" + entityContainerName + "' didn't come from metadata");
 
            EntitySet entitySet = entityContainer.GetEntitySetByName(entitySetName, false /*ignoreCase*/);
            Debug.Assert(
                entitySet != null,
                "entitySet != null -- otherwise entitySetName '" + entitySetName + "' didn't come from metadata");
 
            return entitySet;
        }
 
        /// <summary>
        /// Get the default entity container
        /// </summary>
        /// <returns>returns the default entity container</returns>
        private EntityContainer GetDefaultEntityContainer()
        {
            EntityContainer entityContainer;
 
            if (String.IsNullOrEmpty(this.ObjectContext.DefaultContainerName))
            {
                return null;
            }
 
            if (!this.ObjectContext.MetadataWorkspace.TryGetEntityContainer(
                    this.ObjectContext.DefaultContainerName, DataSpace.CSpace, out entityContainer))
            {
                throw new InvalidOperationException(
                    Strings.ObjectContext_InvalidDefaultEntityContainerName(this.ObjectContext.DefaultContainerName));
            }
 
            return entityContainer;
        }
 
        /// <summary>Creates the object query for the given resource set and returns it.</summary>
        /// <param name="container">resource set for which a query instance needs to be created.</param>
        /// <returns>Returns the ObjectQuery instance for the given resource set.</returns>
        private ObjectQuery InternalGetResourceContainerInstance(ResourceSet container)
        {
            Debug.Assert(container != null, "container != null");
            if (container.ReadFromContextDelegate == null)
            {
                Type[] parameterTypes = new Type[] { typeof(object) };
                string escapedEntitySetName = GetEscapedEntitySetName(container.Name);
                MethodInfo genericMethod = typeof(ObjectContext).GetMethod("CreateQuery", WebUtil.PublicInstanceBindingFlags).MakeGenericMethod(container.ResourceType.InstanceType);
 
                // ((ObjectContext)arg0).CreateQuery("escapedEntitySetName", new ObjectParameter[0]);
                System.Reflection.Emit.DynamicMethod readerMethod = new System.Reflection.Emit.DynamicMethod("queryable_reader", typeof(IQueryable), parameterTypes, false);
                var generator = readerMethod.GetILGenerator();
                generator.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
                generator.Emit(System.Reflection.Emit.OpCodes.Castclass, typeof(ObjectContext));
                generator.Emit(System.Reflection.Emit.OpCodes.Ldstr, escapedEntitySetName);
                generator.Emit(System.Reflection.Emit.OpCodes.Ldc_I4_0);
                generator.Emit(System.Reflection.Emit.OpCodes.Newarr, typeof(ObjectParameter));
                generator.Emit(System.Reflection.Emit.OpCodes.Call, genericMethod);
                generator.Emit(System.Reflection.Emit.OpCodes.Ret);
                container.ReadFromContextDelegate = (Func<object, IQueryable>)readerMethod.CreateDelegate(typeof(Func<object, IQueryable>));
            }
 
            return (ObjectQuery)container.ReadFromContextDelegate(this.ObjectContext);
        }
 
        /// <summary>
        /// Returns the entity set name for the given object state entry.
        /// </summary>
        /// <param name="entry">object state entry for the object whose entity set name needs to be retreived.</param>
        /// <returns>entity set name for the given entity entry.</returns>
        private string GetEntitySetName(ObjectStateEntry entry)
        {
            return ObjectContextServiceProvider.GetEntitySetName(
                entry.EntitySet.Name,
                entry.EntitySet.EntityContainer.Name,
                entry.EntitySet.EntityContainer.Name == this.ObjectContext.DefaultContainerName);
        }
 
        /// <summary>
        /// Update the relationship for the given entity.
        /// </summary>
        /// <param name="targetResource">source entity.</param>
        /// <param name="propertyName">navigation property which needs to get updated.</param>
        /// <param name="propertyValue">target entity - the other end of the relationship.</param>
        /// <param name="addRelationship">null for reference properties, true if relationship needs to be added for collection properties, else false.</param>
        private void UpdateRelationship(object targetResource, string propertyName, object propertyValue, bool? addRelationship)
        {
            Debug.Assert(targetResource != null, "targetResource != null");
            Debug.Assert(!String.IsNullOrEmpty(propertyName), "!String.IsNullOrEmpty(propertyName)");
 
            ResourceType resourceType = this.GetSingleResource(targetResource);
            
            // Get the NavigationProperty metadata to get the association and ToRole metadata
            EntityType cspaceEntityType = this.ObjectContext.MetadataWorkspace.GetItem<EntityType>(resourceType.FullName, DataSpace.CSpace);
            NavigationProperty navProperty = cspaceEntityType.NavigationProperties[propertyName];
 
            // Get the resource type and resource property to find the type of the property
            ResourceProperty resourceProperty = resourceType.TryResolvePropertyName(propertyName);
            Debug.Assert(resourceProperty != null && resourceProperty.TypeKind == ResourceTypeKind.EntityType, "property must be a navigation property");
 
            // Get the relation end 
            var stateEntry = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(targetResource);
            IRelatedEnd relatedEnd = stateEntry.RelationshipManager.GetRelatedEnd(navProperty.RelationshipType.Name, navProperty.ToEndMember.Name);
 
            try
            {
                if (resourceProperty.Kind == ResourcePropertyKind.ResourceReference)
                {
                    Debug.Assert(addRelationship == null, "addRelationship == null for reference properties");
 
                    // For FK relationships with 1 to 1 or 1 to 0..1 cardinalities, we need to handle it in a different manner
                    if (IsOneToOneFKAssocation((AssociationType)navProperty.RelationshipType))
                    {
                        // If the parent entity is not in the added state and the other end is not yet loaded, then load it
                        if (stateEntry.State != EntityState.Added && !relatedEnd.IsLoaded)
                        {
                            relatedEnd.Load();
                        }
                    }
 
                    // if the property value is null, then just set the entity key to null. 
                    // if the key is null, then this will be a no-op, but if the key is not null, then it will
                    // delete the relationship
                    if (propertyValue == null)
                    {
                        ((EntityReference)relatedEnd).EntityKey = null;
                    }
                    else
                    {
                        EntityKey key = ((EntityReference)relatedEnd).EntityKey;
 
                        // If the key is null, that means there was no relationship and user is trying to set the relationship for the first time
                        // if there is a key, make sure the relationship is set to something new.
                        // The reason why we need to do a equality check is because when we set the EntityKey to null, it will put the dependent
                        // in deleted state, and setting the same entity key again, will try to add it. Setting the key to the same value is
                        // a very common pattern in the astoria client and hence we need to make sure that scenario works.
                        if (key == null ||
                            !key.Equals(this.ObjectContext.ObjectStateManager.GetObjectStateEntry(propertyValue).EntityKey))
                        {
                            // First set the reference property to null, in case there is an existing property, since relatedEnd.Add throws,
                            // if the reference property is not null. Then update the property to the new value, if required.
                            ((EntityReference)relatedEnd).EntityKey = null;
                            relatedEnd.Add(propertyValue);
                        }
                    }
                }
                else if (addRelationship == true)
                {
                    Debug.Assert(propertyValue != null, "propertyValue != null");
                    relatedEnd.Add(propertyValue);
                }
                else
                {
                    // The reason why we need to attach first, before removing is ObjectContext might not know about the relationship yet.
                    // Hence attaching establishes the relationship, and then removing marks it as delete.
                    Debug.Assert(propertyValue != null, "propertyValue != null");
                    relatedEnd.Attach(propertyValue);
                    relatedEnd.Remove(propertyValue);
                }
            }
            catch (InvalidOperationException exception)
            {
                throw DataServiceException.CreateBadRequestError(Strings.BadRequest_ErrorInSettingPropertyValue(propertyName), exception);
            }
        }
 
        /// <summary>
        /// Finds all visible resource sets, types, service ops, association sets, etc. from the given provider
        /// </summary>
        private sealed class MetadataManager
        {
            #region Private Fields
            
            /// <summary>data service provider instance</summary>
            private DataServiceProviderWrapper provider;
 
            /// <summary>EF workspace</summary>
            private MetadataWorkspace workspace;
 
            /// <summary>Default entity container</summary>
            private EntityContainer defaultEntityContainer;
 
            /// <summary>List of entity containers</summary>
            private List<EntityContainer> entityContainers = new List<EntityContainer>();
 
            /// <summary>List of namespace along with the types in that namespace</summary>
            private Dictionary<string, HashSet<EdmType>> edmTypes = new Dictionary<string, HashSet<EdmType>>(StringComparer.Ordinal);
 
            /// <summary>List of entity containers along with entity sets</summary>
            private Dictionary<EntityContainer, List<EntitySet>> entitySets = new Dictionary<EntityContainer, List<EntitySet>>(EqualityComparer<EntityContainer>.Default);
 
            /// <summary>List of entity containers along with association sets</summary>
            private Dictionary<EntityContainer, List<AssociationSet>> associationSets = new Dictionary<EntityContainer, List<AssociationSet>>(EqualityComparer<EntityContainer>.Default);
 
            /// <summary>Set to true if we see any visible MLE.</summary>
            private bool hasVisibleMediaLinkEntry;
 
            #endregion Private Fields
 
            #region Constructor
 
            /// <summary>Constructs a metadata manager instance</summary>
            /// <param name="workspace">workspace containing the metadata</param>
            /// <param name="defaultEntityContainer">default entity container</param>
            /// <param name="provider">data service provider instance to check type visibility</param>
            /// <param name="service">Data service instance.</param>
            internal MetadataManager(MetadataWorkspace workspace, EntityContainer defaultEntityContainer, DataServiceProviderWrapper provider, IDataService service)
            {
                Debug.Assert(workspace != null, "workspace != null");
                Debug.Assert(provider != null, "provider != null");
 
                this.workspace = workspace;
                this.defaultEntityContainer = defaultEntityContainer;
                this.provider = provider;
 
                // Add entity sets and association sets
                this.PopulateEntityAndAssociationSets();
 
                // Add entity types reachable from visible sets
                foreach (ResourceSetWrapper resourceSet in this.provider.ResourceSets)
                {
                    this.PopulateTypesForSet(resourceSet);
                }
 
                // Add resource types reachable from service operations
                foreach (ServiceOperationWrapper serviceOperation in this.provider.ServiceOperations)
                {
                    this.PopulateTypeForServiceOperation(serviceOperation);
                }
 
                // Add association types
                this.PopulateAssociationTypes();
 
                // If we have encountered a visible MLE type, we need to make sure there is a stream provider implementation
                // for this service or else we can end up with in-stream errors during runtime.
                if (this.hasVisibleMediaLinkEntry)
                {
                    // LoadStreamProvider will throw if we can't locate a stream provider implementation.
                    DataServiceStreamProviderWrapper.LoadStreamProvider(service);
                }
            }
 
            #endregion Constructor
 
            #region Properties
 
            /// <summary>Default entity container</summary>
            internal IEnumerable<EntityContainer> EntityContainers
            {
                get { return this.entityContainers; }
            }
 
            /// <summary>List of visible resource sets</summary>
            internal Dictionary<EntityContainer, List<EntitySet>> EntitySets
            {
                get { return this.entitySets; }
            }
 
            /// <summary>List of visible association sets</summary>
            internal Dictionary<EntityContainer, List<AssociationSet>> AssociationSets
            {
                get { return this.associationSets; }
            }
            
            /// <summary>Returns the list of namespace and the types in those namespaces.</summary>
            internal IEnumerable<KeyValuePair<string, HashSet<EdmType>>> NamespaceAlongWithTypes
            {
                get { return this.edmTypes; }
            }
 
            #endregion Properties
 
            #region Methods
 
            /// <summary>Gets the list of visible edm properties from a type.</summary>
            /// <param name="declaringType">Type in question.</param>
            /// <returns>List of visible edm properties.</returns>
            internal IEnumerable<EdmMember> GetPropertiesDeclaredInThisType(StructuralType declaringType)
            {
                foreach (EdmMember member in declaringType.Members)
                {
                    // Ignore members which are not defined on this type
                    if (member.DeclaringType != declaringType)
                    {
                        continue;
                    }
 
                    if (member.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty)
                    {
                        EdmType targetType = ((RefType)((NavigationProperty)member).ToEndMember.TypeUsage.EdmType).ElementType;
                        HashSet<EdmType> typesInNamespace;
                        string typeNamespace = targetType.NamespaceName;
                        if (!this.edmTypes.TryGetValue(typeNamespace, out typesInNamespace) ||
                            !typesInNamespace.Contains(targetType))
                        {
                            continue;
                        }
                    }
 
                    yield return member;
                }
            }
 
            /// <summary>
            /// Add association types
            /// </summary>
            private void PopulateAssociationTypes()
            {
                foreach (AssociationType associationType in this.workspace.GetItems<AssociationType>(DataSpace.CSpace))
                {
                    // Skip association types were any ends are not visible.
                    bool shouldAdd = true;
                    foreach (AssociationEndMember member in associationType.AssociationEndMembers)
                    {
                        EdmType endType = ((RefType)member.TypeUsage.EdmType).ElementType;
                        string typeNamespace = endType.NamespaceName;
                        HashSet<EdmType> typesInNamespace;
                        if (!this.edmTypes.TryGetValue(typeNamespace, out typesInNamespace) || !typesInNamespace.Contains(endType))
                        {
                            shouldAdd = false;
                            break;
                        }
                    }
 
                    if (shouldAdd)
                    {
                        this.AddVisibleEdmType(associationType);
                    }
                }
            }
 
            /// <summary>
            /// Add entity sets and association sets
            /// </summary>
            private void PopulateEntityAndAssociationSets()
            {
                foreach (EntityContainer container in this.workspace.GetItems<EntityContainer>(DataSpace.CSpace))
                {
                    this.entityContainers.Add(container);
 
                    string containerName = container == this.defaultEntityContainer ? "" : container.Name;
 
                    List<EntitySet> entitySetInContainer;
                    if (!this.entitySets.TryGetValue(container, out entitySetInContainer))
                    {
                        entitySetInContainer = new List<EntitySet>();
                        this.entitySets.Add(container, entitySetInContainer);
                    }
 
                    List<AssociationSet> associationSetInContainer;
                    if (!this.associationSets.TryGetValue(container, out associationSetInContainer))
                    {
                        associationSetInContainer = new List<AssociationSet>();
                        this.associationSets.Add(container, associationSetInContainer);
                    }
 
                    foreach (EntitySetBase set in container.BaseEntitySets)
                    {
                        if (set.BuiltInTypeKind == BuiltInTypeKind.EntitySet)
                        {
                            // Skip entity sets with no backing metadata.
                            string lookupName = set.Name;
                            if (!String.IsNullOrEmpty(containerName))
                            {
                                lookupName = containerName + "." + lookupName;
                            }
 
                            if (this.provider.TryResolveResourceSet(lookupName) != null)
                            {
                                entitySetInContainer.Add((EntitySet)set);
                            }
                        }
                        else if (set.BuiltInTypeKind == BuiltInTypeKind.AssociationSet)
                        {
                            // Skip association sets where any end is hidden.
                            AssociationSet associationSet = (AssociationSet)set;
                            bool shouldAdd = true;
                            foreach (AssociationSetEnd end in associationSet.AssociationSetEnds)
                            {
                                string lookupName = end.EntitySet.Name;
                                if (!String.IsNullOrEmpty(containerName))
                                {
                                    lookupName = containerName + "." + lookupName;
                                }
 
                                if (this.provider.TryResolveResourceSet(lookupName) == null)
                                {
                                    shouldAdd = false;
                                    break;
                                }
                            }
 
                            if (shouldAdd)
                            {
                                associationSetInContainer.Add((AssociationSet)set);
                            }
                        }
                    }
                }
            }
 
            /// <summary>Add an edm type to the list of visible types</summary>
            /// <param name="edmType">edmType to add</param>
            private void AddVisibleEdmType(EdmType edmType)
            {
                HashSet<EdmType> typesInSameNamespace;
                string typeNamespace = edmType.NamespaceName;
                if (!this.edmTypes.TryGetValue(typeNamespace, out typesInSameNamespace))
                {
                    typesInSameNamespace = new HashSet<EdmType>(EqualityComparer<EdmType>.Default);
                    this.edmTypes.Add(typeNamespace, typesInSameNamespace);
                }
 
                // Add the type to the list of types in the same namespace
                typesInSameNamespace.Add(edmType);
            }
 
            /// <summary>Add a resource type to the list of visible types</summary>
            /// <param name="resourceType">resource type to add</param>
            private void AddVisibleResourceType(ResourceType resourceType)
            {                
                EdmType edmType = this.workspace.GetItem<EdmType>(resourceType.FullName, DataSpace.CSpace);
                Debug.Assert(edmType != null, "edmType != null");
                this.AddVisibleEdmType(edmType);
                if (resourceType.IsMediaLinkEntry)
                {
                    this.hasVisibleMediaLinkEntry = true;
                }
            }
 
            /// <summary>Recursively add complex types reachable through resource properties of the given type</summary>
            /// <param name="resourceType">resource type to inspect</param>
            private void AddComplexPropertTypes(ResourceType resourceType)
            {
                Debug.Assert(resourceType != null, "resourceType != null");
 
                foreach (ResourceProperty property in resourceType.PropertiesDeclaredOnThisType)
                {
                    if (property.ResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
                    {
                        this.AddVisibleResourceType(property.ResourceType);
                        this.AddComplexPropertTypes(property.ResourceType);
                    }
                }
            }
 
            /// <summary>Populate all reachable types from a resource set</summary>
            /// <param name="resourceSet">resource set to inspect</param>
            private void PopulateTypesForSet(ResourceSetWrapper resourceSet)
            {
                // Derived types of a visible type are visible
                foreach (ResourceType derivedType in this.provider.GetDerivedTypes(resourceSet.ResourceType))
                {
                    this.AddVisibleResourceType(derivedType);
                    this.AddComplexPropertTypes(derivedType);
                }
 
                // Base types of a visible type are visible
                ResourceType resourceType = resourceSet.ResourceType;
                while (resourceType != null)
                {
                    this.AddVisibleResourceType(resourceType);
                    this.AddComplexPropertTypes(resourceType);
                    resourceType = resourceType.BaseType;
                }
            }
 
            /// <summary>Populate resource types returned by the given service operation.</summary>
            /// <param name="serviceOperation">Service operation to inspect</param>
            private void PopulateTypeForServiceOperation(ServiceOperationWrapper serviceOperation)
            {
                ResourceType resultType = serviceOperation.ResultType;
                if (resultType != null && resultType.ResourceTypeKind == ResourceTypeKind.ComplexType)
                {
                    this.AddVisibleResourceType(resultType);
                    this.AddComplexPropertTypes(resultType);
                }
 
                Debug.Assert(
                    resultType == null || resultType.ResourceTypeKind != ResourceTypeKind.EntityType ||
                    (this.edmTypes.ContainsKey(resultType.Namespace) && 
                    this.edmTypes[resultType.Namespace].Contains(this.workspace.GetItem<EntityType>(resultType.FullName, DataSpace.CSpace))),
                    "If a result type is an entity type, it must be visible through an entity set.");
            }
 
            #endregion Methods
        }
    }
}