Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / Providers / DataServiceProviderWrapper.cs / 1305376 / DataServiceProviderWrapper.cs
//---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // //// Provides the wrapper over IDataServiceMetadataProvider and IDataServiceQueryProvider calls // so that we can bunch of validation in one place. // // // @owner [....] //--------------------------------------------------------------------- namespace System.Data.Services.Providers { #region Namespaces. using System; using System.Collections.Generic; using System.Data.Services.Serializers; using System.Diagnostics; using System.Linq; using System.Xml; using System.Data.Services.Caching; using System.Reflection; #endregion Namespaces. ///Schema version compliance of the metadata. internal enum MetadataEdmSchemaVersion { ///EDM v1.0 compliant. Version1Dot0, ///EDM v1.1 compliant. Version1Dot1, ///EDM v1.2 compliant. Version1Dot2, ///EDM v2.0 compliant. Version2Dot0, } ////// Class to abstract IDataServiceMetadataProvider and IDataServiceQueryProvider, /// hence making sure all the metadata and query provider calls are made via this class. /// /// Each request must create a new instance of this class because a /// request is the defined scope of metadata consistency. /// internal class DataServiceProviderWrapper { #region Private Fields ////// Object reference that represents true. This is a workaround to the issue that 'Dictionary<string, bool>' will require /// JIT compilation at runtime for precompiled assemblies. So we use 'Dictionary<string, object>' instead. /// private static readonly object RefTrue = new object(); ////// Object reference that represents false. This is a workaround to the issue that 'Dictionary<string, bool>' will require /// JIT compilation at runtime for precompiled assemblies. So we use 'Dictionary<string, object>' instead. /// private static readonly object RefFalse = new object(); ////// Empty open property values /// private static readonly IEnumerable> EmptyOpenPropertyValues = new KeyValuePair [0]; /// /// Metadata to be used by the service provider wrapper. /// private readonly MetadataCacheItem metadata; ////// metadata provider instance. /// private IDataServiceMetadataProvider metadataProvider; ////// metadata provider instance. /// private IDataServiceQueryProvider queryProvider; #endregion Private Fields #region Constructors ////// Creates a new instance of DataServiceProviderWrapper instance. /// /// Metadata to be used by the service provider wrapper. /// instance of the metadata provider. /// instance of the query provider. internal DataServiceProviderWrapper(MetadataCacheItem metadata, IDataServiceMetadataProvider metadataProvider, IDataServiceQueryProvider queryProvider) { Debug.Assert(metadata != null, "metadata != null"); Debug.Assert(metadataProvider != null, "metadataProvider != null"); Debug.Assert(queryProvider != null, "queryProvider != null"); Debug.Assert( metadataProvider is BaseServiceProvider && queryProvider is BaseServiceProvider || (metadata.ResourceSetWrapperCache.Count == 0 && metadata.VisibleTypeCache.Count == 0), "For V1 providers, both metadata and query providers must be BaseServiceProvider, otherwise the metadata cache must be empty for IDSP providers"); this.metadata = metadata; this.metadataProvider = metadataProvider; this.queryProvider = queryProvider; } #endregion Constructors #region IDataServiceQueryProvider Properties ///The data source from which data is provided. public object CurrentDataSource { get { return this.queryProvider.CurrentDataSource; } } ///Gets a value indicating whether null propagation is required in expression trees. public bool NullPropagationRequired { get { return this.queryProvider.IsNullPropagationRequired; } } #endregion IDataServiceQueryProvider Properties #region IDataServiceMetadataProvider Properties ///Namespace name for the container. public string ContainerNamespace { get { string containerNamespace = this.metadataProvider.ContainerNamespace; // 752636 [Breaking Change]: Reflection Provider should not allow null ContainerNamespace // In V1 the reflection provider allows the namespace to be null. Fixing this would be a breaking change. // We will skip this check for V1 providers for now. if (string.IsNullOrEmpty(containerNamespace) && !this.IsV1Provider) { throw new InvalidOperationException(Strings.DataServiceProviderWrapper_ContainerNamespaceMustNotBeNullOrEmpty); } return containerNamespace; } } ///Name of the container public string ContainerName { get { string containerName = this.metadataProvider.ContainerName; if (string.IsNullOrEmpty(containerName)) { throw new InvalidOperationException(Strings.DataServiceProviderWrapper_ContainerNameMustNotBeNullOrEmpty); } return containerName; } } ////// Gets all visible containers. /// WARNING!!! This property can only be called for the $metadata path because it enumerates through all resource sets. /// Calling it from outside of the $metadata path would break our IDSP contract. /// public IEnumerableResourceSets { get { var resourceSets = this.metadataProvider.ResourceSets; if (resourceSets != null) { HashSet resourceSetNames = new HashSet (EqualityComparer .Default); foreach (ResourceSet resourceSet in resourceSets) { // verify that the name of the resource set is unique AddUniqueNameToSet( resourceSet != null ? resourceSet.Name : null, resourceSetNames, Strings.DataServiceProviderWrapper_MultipleEntitySetsWithSameName(resourceSet.Name)); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. ResourceSetWrapper resourceSetWrapper = this.ValidateResourceSet(resourceSet); if (resourceSetWrapper != null) { yield return resourceSetWrapper; } } } } } /// /// Returns all types in this data source /// WARNING!!! This property can only be called for the $metadata path because it enumerates through all resource types. /// Calling it from outside of the $metadata path would break our IDSP contract. /// public IEnumerableTypes { get { var types = this.metadataProvider.Types; if (types != null) { HashSet resourceTypeNames = new HashSet (EqualityComparer .Default); foreach (ResourceType resourceType in types) { // verify that the name of the resource type is unique AddUniqueNameToSet( resourceType != null ? resourceType.Name : null, resourceTypeNames, Strings.DataServiceProviderWrapper_MultipleResourceTypesWithSameName(resourceType.Name)); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. ResourceType type = this.ValidateResourceType(resourceType); if (type != null) { yield return type; } } } } } /// /// Returns all the visible service operations in this data source. /// WARNING!!! This property can only be called for the $metadata path because it enumerates through all service operations. /// Calling it from outside of the $metadata path would break our IDSP contract. /// public IEnumerableServiceOperations { get { var serviceOperations = this.metadataProvider.ServiceOperations; if (serviceOperations != null) { HashSet serviceOperationNames = new HashSet (EqualityComparer .Default); foreach (ServiceOperation serviceOperation in serviceOperations) { // verify that the name of the service operation is unique AddUniqueNameToSet( serviceOperation != null ? serviceOperation.Name : null, serviceOperationNames, Strings.DataServiceProviderWrapper_MultipleServiceOperationsWithSameName(serviceOperation.Name)); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. ServiceOperationWrapper serviceOperationWrapper = this.ValidateServiceOperation(serviceOperation); if (serviceOperationWrapper != null) { yield return serviceOperationWrapper; } } } } } #endregion IDataServiceMetadataProvider Properties #region Properties /// /// Cached configuration with access rights info. /// internal DataServiceConfiguration Configuration { [DebuggerStepThrough] get { return this.metadata.Configuration; } } #if DEBUG ////// Used for verifying expression generated for queries, checks if all the /// internal bool AreAllResourceTypesNonOpen { get { return !this.VisibleTypeCache.Values.Any(rt => rt.IsOpenType); } } #endif ////// Returns true if the data provider is a V1 provider i.e. ReflectionServiceProvider or ObjectContextServiceProvider. /// Otherwise returns false. /// internal bool IsV1Provider { get { return this.metadataProvider is BaseServiceProvider; } } ////// Returns the ///for this provider /// The ///for this provider Note that this will only return non-null on V1 providers /// in which case it returns our V1 provider's implementation of this interface. /// In all other cases this returns null as we don't allow custom implementation of this interface yet. internal IProjectionProvider ProjectionProvider { get { if (this.IsV1Provider) { return (IProjectionProvider)this.metadataProvider; } else { return null; } } } ////// Keep track of the calculated visibility of resource types. /// private DictionaryVisibleTypeCache { [DebuggerStepThrough] get { return this.metadata.VisibleTypeCache; } } /// /// Maps resource set names to ResourceSetWrappers. /// private DictionaryResourceSetWrapperCache { [DebuggerStepThrough] get { return this.metadata.ResourceSetWrapperCache; } } /// /// Maps service operation names to ServiceOperationWrappers. /// private DictionaryServiceOperationWrapperCache { [DebuggerStepThrough] get { return this.metadata.ServiceOperationWrapperCache; } } /// /// Maps names to ResourceAssociationSets. /// private DictionaryResourceAssociationSetCache { [DebuggerStepThrough] get { return this.metadata.ResourceAssociationSetCache; } } /// /// Mapes "resourceSetName_resourceTypeName" to the list of visible properties from the set. /// private Dictionary> ResourcePropertyCache { [DebuggerStepThrough] get { return this.metadata.ResourcePropertyCache; } } /// /// Mapes "resourceSetName_resourceTypeName" to boolean of whether resourceType is allowed for resourceSet /// private DictionaryEntityTypeDisallowedForSet { [DebuggerStepThrough] get { return this.metadata.EntityTypeDisallowedForSet; } } #endregion Properties #region IDataServiceQueryProvider Methods #if DEBUG /// /// Returns the IQueryable that represents the container. /// /// resource set representing the entity set. ////// An IQueryable that represents the container; null if there is /// no container for the specified name. /// public IQueryable GetQueryRootForResourceSet(ResourceSetWrapper resourceSet, IDataService dataService) #else ////// Returns the IQueryable that represents the container. /// /// resource set representing the entity set. ////// An IQueryable that represents the container; null if there is /// no container for the specified name. /// public IQueryable GetQueryRootForResourceSet(ResourceSetWrapper resourceSet) #endif { Debug.Assert(resourceSet != null, "resourceSet != null"); #if DEBUG dataService.ProcessingPipeline.AssertDebugStateDuringRequestProcessing(dataService); #endif return this.queryProvider.GetQueryRootForResourceSet(resourceSet.ResourceSet); } ///Gets the /// Instance to extract afor the specified . from. /// The public ResourceType GetResourceType(object instance) { Debug.Assert(instance != null, "instance != null"); return this.ValidateResourceType(this.queryProvider.GetResourceType(instance)); } ///that describes this in this provider. /// Get the value of the strongly typed property. /// /// instance of the type declaring the property. /// resource property describing the property. /// Resource type to which the property belongs. ///value for the property. public object GetPropertyValue(object target, ResourceProperty resourceProperty, ResourceType resourceType) { Debug.Assert(target != null, "target != null"); Debug.Assert(resourceProperty != null, "resourceProperty != null"); Debug.Assert(resourceProperty.IsReadOnly, "resourceProperty.IsReadOnly"); if (resourceProperty.CanReflectOnInstanceTypeProperty) { try { if (resourceType == null) { resourceType = this.GetResourceType(target); } Debug.Assert(resourceType != null, "resourceType != null"); PropertyInfo propertyInfo = resourceType.GetPropertyInfo(resourceProperty); Debug.Assert(propertyInfo != null, "propertyInfo != null"); return propertyInfo.GetGetMethod().Invoke(target, null); } catch (TargetInvocationException exception) { ErrorHandler.HandleTargetInvocationException(exception); throw; } } else { return this.queryProvider.GetPropertyValue(target, resourceProperty); } } ////// Get the value of the open property. /// /// instance of the type declaring the open property. /// name of the open property. ///value for the open property. public object GetOpenPropertyValue(object target, string propertyName) { Debug.Assert(target != null, "target != null"); Debug.Assert(!string.IsNullOrEmpty(propertyName), "!string.IsNullOrEmpty(propertyName)"); return this.queryProvider.GetOpenPropertyValue(target, propertyName); } ////// Get the name and values of all the properties defined in the given instance of an open type. /// /// instance of a open type. ///collection of name and values of all the open properties. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "need to return a collection of key value pair")] public IEnumerable> GetOpenPropertyValues(object target) { Debug.Assert(target != null, "target != null"); IEnumerable > result = this.queryProvider.GetOpenPropertyValues(target); if (result == null) { return EmptyOpenPropertyValues; } return result; } /// /// Invoke the given service operation and returns the results. /// /// service operation to invoke. /// value of parameters to pass to the service operation. ///returns the result of the service operation. If the service operation returns void, then this should return null. public object InvokeServiceOperation(ServiceOperationWrapper serviceOperation, object[] parameters) { Debug.Assert(serviceOperation != null, "serviceOperation != null"); return this.queryProvider.InvokeServiceOperation(serviceOperation.ServiceOperation, parameters); } #endregion IDataServiceQueryProvider Methods #region IDataServiceMetadataProvider Methods ///Given the specified name, tries to find a resource set. /// Name of the resource set to resolve. ///Resolved resource set, possibly null. public ResourceSetWrapper TryResolveResourceSet(string name) { Debug.Assert(!string.IsNullOrEmpty(name), "!string.IsNullOrEmpty(name)"); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. ResourceSetWrapper resourceSetWrapper; if (this.ResourceSetWrapperCache.TryGetValue(name, out resourceSetWrapper)) { return resourceSetWrapper; } ResourceSet resourceSet; if (this.metadataProvider.TryResolveResourceSet(name, out resourceSet)) { return this.ValidateResourceSet(resourceSet); } return null; } ////// Gets the ResourceAssociationSet instance when given the source association end. /// /// Resource set of the source association end. /// Resource type of the source association end. /// Resource property of the source association end. ///ResourceAssociationSet instance. public ResourceAssociationSet GetResourceAssociationSet(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty resourceProperty) { Debug.Assert(resourceSet != null, "resourceSet != null"); Debug.Assert(resourceType != null, "resourceType != null"); Debug.Assert(resourceProperty != null, "resourceProperty != null"); // If the association set has already been cached, use the cached copy resourceType = GetDeclaringTypeForProperty(resourceType, resourceProperty); string associationSetKey = resourceSet.Name + '_' + resourceType.FullName + '_' + resourceProperty.Name; ResourceAssociationSet associationSet; if (this.ResourceAssociationSetCache.TryGetValue(associationSetKey, out associationSet)) { return associationSet; } // Get the association set from the underlying provider. associationSet = this.metadataProvider.GetResourceAssociationSet(resourceSet.ResourceSet, resourceType, resourceProperty); if (associationSet != null) { ResourceAssociationSetEnd thisEnd = associationSet.GetResourceAssociationSetEnd(resourceSet, resourceType, resourceProperty); ResourceAssociationSetEnd relatedEnd = associationSet.GetRelatedResourceAssociationSetEnd(resourceSet, resourceType, resourceProperty); ResourceSetWrapper relatedSet = this.ValidateResourceSet(relatedEnd.ResourceSet); if (relatedSet == null) { // If the related set is not visible, the association set is also not visible. associationSet = null; } else { ResourceType relatedType = this.ValidateResourceType(relatedEnd.ResourceType); ResourceProperty relatedProperty = null; if (relatedEnd.ResourceProperty != null) { relatedProperty = relatedType.TryResolvePropertyName(relatedEnd.ResourceProperty.Name); } // For IDSP, we want to make sure the metadata object instance stay the same within a request because we // do reference comparisons. Note if the provider returns a ResourceAssociationSet with different instances // of ResourceSet, ResourceType and ResourceProperty than what we've seen earlier in the same request, we // create a new instance of the association set using the metadata objects we've already cached. // If the metadata change should cause a failure, we want the IDSP to detect it and fail. At the Astoria runtime // layer we assume the metadata objects to remain constant throughout the request. resourceType = this.ValidateResourceType(thisEnd.ResourceType); if (thisEnd.ResourceSet != resourceSet.ResourceSet || thisEnd.ResourceType != resourceType || thisEnd.ResourceProperty != resourceProperty || relatedEnd.ResourceSet != relatedSet.ResourceSet || relatedEnd.ResourceType != relatedType || relatedEnd.ResourceProperty != relatedProperty) { associationSet = new ResourceAssociationSet( associationSet.Name, new ResourceAssociationSetEnd(resourceSet.ResourceSet, resourceType, resourceProperty), new ResourceAssociationSetEnd(relatedSet.ResourceSet, relatedType, relatedProperty)); } } } this.ResourceAssociationSetCache.Add(associationSetKey, associationSet); return associationSet; } ///Given the specified name, tries to find a type. /// Name of the type to resolve. ///Resolved resource type, possibly null. public ResourceType TryResolveResourceType(string name) { Debug.Assert(!string.IsNullOrEmpty(name), "!string.IsNullOrEmpty(name)"); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. ResourceType resourceType; if (this.VisibleTypeCache.TryGetValue(name, out resourceType)) { return resourceType; } if (this.metadataProvider.TryResolveResourceType(name, out resourceType)) { return this.ValidateResourceType(resourceType); } return null; } ////// The method must return a collection of all the types derived from /// Resource to get derived resource types from. ///. /// The collection returned should NOT include the type passed in as a parameter. /// An implementer of the interface should return null if the type does not have any derived types (ie. null == no derived types). /// /// A collection of resource types ( public IEnumerable) derived from the specified /// or null if there no types derived from the specified exist. /// GetDerivedTypes(ResourceType resourceType) { Debug.Assert(resourceType != null, "resourceType != null"); var derivedTypes = this.metadataProvider.GetDerivedTypes(resourceType); if (derivedTypes != null) { foreach (ResourceType derivedType in derivedTypes) { ResourceType type = this.ValidateResourceType(derivedType); if (type != null) { yield return type; } } } } /// /// Returns true if /// instance of the resource type in question. ///represents an Entity Type which has derived Entity Types, else false. /// True if public bool HasDerivedTypes(ResourceType resourceType) { Debug.Assert(this.ValidateResourceType(resourceType) != null, "resourceType must be read-only and visible."); return this.metadataProvider.HasDerivedTypes(resourceType); } ///represents an Entity Type which has derived Entity Types, else false. Given the specified name, tries to find a service operation. /// Name of the service operation to resolve. ///Resolved service operation, possibly null. public ServiceOperationWrapper TryResolveServiceOperation(string name) { Debug.Assert(!string.IsNullOrEmpty(name), "!string.IsNullOrEmpty(name)"); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. ServiceOperationWrapper serviceOperationWrapper; if (this.ServiceOperationWrapperCache.TryGetValue(name, out serviceOperationWrapper)) { return serviceOperationWrapper; } ServiceOperation serviceOperation; if (this.metadataProvider.TryResolveServiceOperation(name, out serviceOperation)) { return this.ValidateServiceOperation(serviceOperation); } return null; } #endregion IDataServiceMetadataProvider Methods #region Internal Methods ////// Gets the resource type which the resource property is declared on. /// /// resource type to start looking /// resource property in question ///actual resource type that declares the property internal static ResourceType GetDeclaringTypeForProperty(ResourceType resourceType, ResourceProperty resourceProperty) { Debug.Assert(resourceType != null, "resourceType != null"); Debug.Assert(resourceProperty != null, "resourceProperty != null"); while (resourceType != null) { if (resourceType.TryResolvePropertiesDeclaredOnThisTypeByName(resourceProperty.Name) != null) { break; } resourceType = resourceType.BaseType; } Debug.Assert(resourceType != null, "resourceType != null"); return resourceType; } ///Disposes of the metadata and query providers. internal void DisposeDataSource() { Debug.Assert(this.queryProvider != null, "this.queryProvider != null"); Debug.Assert(this.metadataProvider != null, "this.metadataProvider != null"); WebUtil.Dispose(this.metadataProvider); // If the same instance implements IDataServiceMetadataProvider and IDataServiceQueryProvider interface, // we call dispose only once. if (this.metadataProvider != this.queryProvider) { WebUtil.Dispose(this.queryProvider); } this.metadataProvider = null; this.queryProvider = null; } ////// Iterates through the resource sets, service operations and resource types to pre-populate the metadata cache item. /// internal void PopulateMetadataCacheItemForV1Provider() { Debug.Assert(this.IsV1Provider, "this.IsV1Provider"); // This is only called when we initialize the service for the first time. // The Count extention method will cause the iterator to instantiate the wrapper classes. this.ServiceOperations.Count(); this.Types.Count(); foreach (ResourceSetWrapper resourceSet in this.ResourceSets) { // Derived types of a visible type are visible foreach (ResourceType derivedType in this.GetDerivedTypes(resourceSet.ResourceType)) { this.GetResourceProperties(resourceSet, derivedType); this.IsEntityTypeDisallowedForSet(resourceSet, derivedType); } // Base types of a visible type are visible ResourceType resourceType = resourceSet.ResourceType; while (resourceType != null) { this.GetResourceProperties(resourceSet, resourceType); this.IsEntityTypeDisallowedForSet(resourceSet, resourceType); resourceType = resourceType.BaseType; } } } ////// Gets the target container for the given navigation property, source container and the source resource type /// /// source entity set. /// source resource type. /// navigation property. ///target container that the navigation property refers to. internal ResourceSetWrapper GetContainer(ResourceSetWrapper sourceContainer, ResourceType sourceResourceType, ResourceProperty navigationProperty) { ResourceAssociationSet associationSet = this.GetResourceAssociationSet(sourceContainer, sourceResourceType, navigationProperty); if (associationSet != null) { ResourceAssociationSetEnd relatedEnd = associationSet.GetRelatedResourceAssociationSetEnd(sourceContainer, sourceResourceType, navigationProperty); return this.ValidateResourceSet(relatedEnd.ResourceSet); } return null; } ////// For a V1 provider checks the Epm compatiblity in order to write the appropriate /// versioning header for metadata requests /// ///true if the provider is V1 compatible, false otherwise internal bool GetEpmCompatiblityForV1Provider() { Debug.Assert(this.IsV1Provider, "Must be a V1 provider to call this function"); return (this.metadataProvider as BaseServiceProvider).EpmIsV1Compatible; } ////// Return the list of ETag properties for a given type in the context of a given container /// /// Name of the container to use for context (for MEST-enabled providers) /// Type to get the ETag properties for ///A collection of the properties that form the ETag for the given type in the given container internal IListGetETagProperties(string containerName, ResourceType resourceType) { Debug.Assert(containerName != null || !(this.metadataProvider is ObjectContextServiceProvider), "ContainerName is required for MEST-enabled provider (EFx provider)"); Debug.Assert(resourceType != null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "Resource should be non-null and of an entity type"); // Note only primitive properties can be part of an etag, they are always visible. ObjectContextServiceProvider efxProvider = this.metadataProvider as ObjectContextServiceProvider; return efxProvider == null ? resourceType.ETagProperties : efxProvider.GetETagProperties(containerName, resourceType); } /// /// Gets the visible resource properties for /// Resource set in question. /// Resource type in question. ///from . /// We cache the list of visible resource properties so we don't have to calculate it repeatedly when serializing feeds. /// List of visible resource properties from the given resource set and resource type. internal IEnumerableGetResourceProperties(ResourceSetWrapper resourceSet, ResourceType resourceType) { Debug.Assert(resourceType != null, "resourceType != null"); if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) { Debug.Assert(resourceSet != null, "resourceSet != null"); string key = resourceSet.Name + '_' + resourceType.FullName; List properties; if (!this.ResourcePropertyCache.TryGetValue(key, out properties)) { properties = new List (); foreach (ResourceProperty property in resourceType.Properties) { if (property.TypeKind == ResourceTypeKind.EntityType && this.GetContainer(resourceSet, resourceType, property) == null) { continue; } properties.Add(property); } this.ResourcePropertyCache.Add(key, properties); } return properties; } else { return resourceType.Properties; } } /// /// Write the metadata document. /// /// instance of the metadata serializer. /// xml writer in which we need to write the metadata document. /// Data service instance. internal void WriteMetadataDocument(MetadataSerializer serializer, XmlWriter writer, IDataService service) { Debug.Assert(serializer != null, "serializer != null"); Debug.Assert(writer != null, "writer != null"); BaseServiceProvider internalProvider = this.metadataProvider as BaseServiceProvider; ObjectContextServiceProvider efxProvider = this.metadataProvider as ObjectContextServiceProvider; // always v1.1+ schemas for custom providers MetadataEdmSchemaVersion metadataEdmSchemaVersion = internalProvider == null ? MetadataEdmSchemaVersion.Version1Dot1 : internalProvider.EdmSchemaVersion; if (efxProvider == null) { serializer.GenerateMetadata(metadataEdmSchemaVersion, service); } else { efxProvider.GetMetadata(writer, this, service); } } ////// Check if the given type can be ordered. If not, throw an exception. /// /// clr type which needs to checked for ordering. internal void CheckIfOrderedType(Type clrType) { // For known providers check for sort-ability for better error reporting BaseServiceProvider baseProvider = this.metadataProvider as BaseServiceProvider; if (baseProvider != null && !baseProvider.GetTypeIsOrdered(clrType)) { string resourceTypeName = WebUtil.GetTypeName(clrType); throw DataServiceException.CreateBadRequestError(Strings.RequestQueryParser_OrderByDoesNotSupportType(resourceTypeName)); } } ////// Checks whether the current data provider is a V1 provider or not. /// ///Returns true if the current data source is a V1 provider. Otherwise, returns false. internal bool IsV1ProviderAndImplementsUpdatable() { BaseServiceProvider baseServiceProvider = this.metadataProvider as BaseServiceProvider; if (baseServiceProvider != null && baseServiceProvider.ImplementsIUpdatable()) { return true; } return false; } ////// Retrieve an implementation of a data service interface (ie. IUpdatable, IExpandProvider,etc) /// ///The type representing the requested interface /// Data service instance ///An object implementing the requested interface, or null if not available internal T GetService(IDataService dataService) where T : class { Debug.Assert(dataService != null, "dataService != null"); Debug.Assert(dataService.Provider == this, "dataService.Provider == this"); Debug.Assert(typeof(T) != typeof(IDataServiceMetadataProvider), "typeof(T) != typeof(IDataServiceMetadataProvider)"); Debug.Assert(typeof(T) != typeof(IDataServiceQueryProvider), "typeof(T) != typeof(IDataServiceQueryProvider)"); Debug.Assert(typeof(T).IsVisible, "Trying to ask the service for non-public interface."); #if DEBUG dataService.ProcessingPipeline.AssertDebugStateDuringRequestProcessing(dataService); dataService.ProcessingPipeline.HasInstantiatedProviderInterfaces = true; #endif // *NOTE* to limit test surface area, IDataServiceStreamProvider && IExpandProvider are the only custom implementation // we support for ObjectContextServiceProvider. // Should remove this in the future. if (this.metadataProvider is ObjectContextServiceProvider && typeof(T) != typeof(IDataServiceStreamProvider) && typeof(T) != typeof(IExpandProvider)) { // Return internal implementation of the interface if there is one. return WebUtil.GetService (this.metadataProvider); } // 1. Check if subclass of DataService implements IServiceProvider. If it does, then call IServiceProvider.GetService() // with the appropriate type. If it doesn’t proceed to Step 2 // a. If IServiceProvider.GetService() returns something, then go ahead and use that instance // b. If IServiceProvider.GetService() doesn't return anything, proceed to Step 2. T result = WebUtil.GetService (dataService.Instance); if (result != null) { return result; } // 2. Check if the T (where T is the type from DataService ) implements the interface. // a. If yes, use that. // b. If no, proceed to Step 3 result = this.CurrentDataSource as T; if (result != null) { // Since IDataServiceUpdateProvider derives from IUpdatable, we need to make sure that // when asked for IUpdatable, this method checks only for IUpdatable, and returns null // if the type returns IDataServiceUpdateProvider. if (typeof(T) != typeof(IUpdatable) || ((result as IDataServiceUpdateProvider) == null)) { return result; } } // 3. Check if the data service provider is a V1 provider // a. If yes, return an internal implementation if we can find one. // b. If no, then the provider doesn’t support the current interface functionality. if (this.IsV1Provider) { // Look for internal implementation for the interface return WebUtil.GetService (this.metadataProvider); } return null; } /// /// We do not allow entity sets to contain any derived type with navigation properties in the GET path /// /// entity set containing the resource type. /// entity type in the hierarchy returned by. /// True if the derived type has any navigation properties. internal bool IsEntityTypeDisallowedForSet(ResourceSetWrapper resourceSet, ResourceType resourceType) { Debug.Assert(resourceSet != null, "resourceSet != null"); Debug.Assert(resourceType != null, "resourceType != null"); object result; string key = resourceSet.Name + '_' + resourceType.FullName; if (this.EntityTypeDisallowedForSet.TryGetValue(key, out result)) { Debug.Assert(result == DataServiceProviderWrapper.RefTrue || result == DataServiceProviderWrapper.RefFalse, "result must be either RefTrue or RefFalse."); return result == DataServiceProviderWrapper.RefTrue ? true : false; } ResourceType baseType = resourceSet.ResourceType; if (baseType != resourceType && baseType.IsAssignableFrom(resourceType) && this.HasNavigationProperties(resourceSet, resourceType)) { this.EntityTypeDisallowedForSet.Add(key, DataServiceProviderWrapper.RefTrue); return true; } else { this.EntityTypeDisallowedForSet.Add(key, DataServiceProviderWrapper.RefFalse); return false; } } ////// Validates if the container should be visible and is not read only. If the container rights /// are set to None the container should not be visible. /// /// Resource set to be validated. ///Validated container, null if the container is not supposed to be visible. internal ResourceSetWrapper ValidateResourceSet(ResourceSet resourceSet) { ResourceSetWrapper resourceSetWrapper = null; if (resourceSet != null) { // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. if (this.ResourceSetWrapperCache.TryGetValue(resourceSet.Name, out resourceSetWrapper)) { return resourceSetWrapper; } resourceSetWrapper = new ResourceSetWrapper(resourceSet, this.ValidateResourceType(resourceSet.ResourceType)); resourceSetWrapper.ApplyConfiguration(this.Configuration); if (!resourceSetWrapper.IsVisible) { resourceSetWrapper = null; } this.ResourceSetWrapperCache[resourceSet.Name] = resourceSetWrapper; } return resourceSetWrapper; } #endregion Internal Methods #region Private Methods ////// Throws if resource type is not sealed. /// /// resource type to inspect. private static void ValidateResourceTypeReadOnly(ResourceType resourceType) { Debug.Assert(resourceType != null, "resourceType != null"); if (!resourceType.IsReadOnly) { throw new DataServiceException(500, Strings.DataServiceProviderWrapper_ResourceTypeNotReadonly(resourceType.FullName)); } } ////// This is a common method for checking uniqe names across entity sets, resource types and service operations. /// /// Name to be added to set. /// Set containing already verified names. /// String for exception to be thrown if the name is not unique. private static void AddUniqueNameToSet(string name, HashSetnames, string exceptionString) { if (name != null) { if (names.Contains(name)) { throw new DataServiceException(500, exceptionString); } names.Add(name); } } /// Validates that /// Resource type to be validated. ///is cached and read only. Validated resource type, null if the resource type is not supposed to be visible. private ResourceType ValidateResourceType(ResourceType resourceType) { if (resourceType != null) { // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. ResourceType cachedType; if (this.VisibleTypeCache.TryGetValue(resourceType.FullName, out cachedType)) { return cachedType; } ValidateResourceTypeReadOnly(resourceType); this.VisibleTypeCache[resourceType.FullName] = resourceType; return resourceType; } return null; } ////// Validates if the service operation should be visible and is read only. If the service operation /// rights are set to None the service operation should not be visible. /// /// Service operation to be validated. ///Validated service operation, null if the service operation is not supposed to be visible. private ServiceOperationWrapper ValidateServiceOperation(ServiceOperation serviceOperation) { ServiceOperationWrapper serviceOperationWrapper = null; if (serviceOperation != null) { // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. if (this.ServiceOperationWrapperCache.TryGetValue(serviceOperation.Name, out serviceOperationWrapper)) { return serviceOperationWrapper; } serviceOperationWrapper = new ServiceOperationWrapper(serviceOperation); serviceOperationWrapper.ApplyConfiguration(this.Configuration, this); if (!serviceOperationWrapper.IsVisible) { serviceOperationWrapper = null; } this.ServiceOperationWrapperCache[serviceOperation.Name] = serviceOperationWrapper; } return serviceOperationWrapper; } ////// Checks whether the resource type from the resource set has visible navigation properties or not. /// /// resource set instance to inspect. /// resource type to inspect. ///Returns true if the resource type has one or more visible navigation properties from the resource set. Otherwise returns false. private bool HasNavigationProperties(ResourceSetWrapper resourceSet, ResourceType resourceType) { foreach (ResourceProperty property in resourceType.PropertiesDeclaredOnThisType) { if (property.TypeKind != ResourceTypeKind.EntityType) { continue; } if (this.GetResourceAssociationSet(resourceSet, resourceType, property) != null) { return true; } } return false; } #endregion Private Methods } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //---------------------------------------------------------------------- //// Copyright (c) Microsoft Corporation. All rights reserved. // //// Provides the wrapper over IDataServiceMetadataProvider and IDataServiceQueryProvider calls // so that we can bunch of validation in one place. // // // @owner [....] //--------------------------------------------------------------------- namespace System.Data.Services.Providers { #region Namespaces. using System; using System.Collections.Generic; using System.Data.Services.Serializers; using System.Diagnostics; using System.Linq; using System.Xml; using System.Data.Services.Caching; using System.Reflection; #endregion Namespaces. ///Schema version compliance of the metadata. internal enum MetadataEdmSchemaVersion { ///EDM v1.0 compliant. Version1Dot0, ///EDM v1.1 compliant. Version1Dot1, ///EDM v1.2 compliant. Version1Dot2, ///EDM v2.0 compliant. Version2Dot0, } ////// Class to abstract IDataServiceMetadataProvider and IDataServiceQueryProvider, /// hence making sure all the metadata and query provider calls are made via this class. /// /// Each request must create a new instance of this class because a /// request is the defined scope of metadata consistency. /// internal class DataServiceProviderWrapper { #region Private Fields ////// Object reference that represents true. This is a workaround to the issue that 'Dictionary<string, bool>' will require /// JIT compilation at runtime for precompiled assemblies. So we use 'Dictionary<string, object>' instead. /// private static readonly object RefTrue = new object(); ////// Object reference that represents false. This is a workaround to the issue that 'Dictionary<string, bool>' will require /// JIT compilation at runtime for precompiled assemblies. So we use 'Dictionary<string, object>' instead. /// private static readonly object RefFalse = new object(); ////// Empty open property values /// private static readonly IEnumerable> EmptyOpenPropertyValues = new KeyValuePair [0]; /// /// Metadata to be used by the service provider wrapper. /// private readonly MetadataCacheItem metadata; ////// metadata provider instance. /// private IDataServiceMetadataProvider metadataProvider; ////// metadata provider instance. /// private IDataServiceQueryProvider queryProvider; #endregion Private Fields #region Constructors ////// Creates a new instance of DataServiceProviderWrapper instance. /// /// Metadata to be used by the service provider wrapper. /// instance of the metadata provider. /// instance of the query provider. internal DataServiceProviderWrapper(MetadataCacheItem metadata, IDataServiceMetadataProvider metadataProvider, IDataServiceQueryProvider queryProvider) { Debug.Assert(metadata != null, "metadata != null"); Debug.Assert(metadataProvider != null, "metadataProvider != null"); Debug.Assert(queryProvider != null, "queryProvider != null"); Debug.Assert( metadataProvider is BaseServiceProvider && queryProvider is BaseServiceProvider || (metadata.ResourceSetWrapperCache.Count == 0 && metadata.VisibleTypeCache.Count == 0), "For V1 providers, both metadata and query providers must be BaseServiceProvider, otherwise the metadata cache must be empty for IDSP providers"); this.metadata = metadata; this.metadataProvider = metadataProvider; this.queryProvider = queryProvider; } #endregion Constructors #region IDataServiceQueryProvider Properties ///The data source from which data is provided. public object CurrentDataSource { get { return this.queryProvider.CurrentDataSource; } } ///Gets a value indicating whether null propagation is required in expression trees. public bool NullPropagationRequired { get { return this.queryProvider.IsNullPropagationRequired; } } #endregion IDataServiceQueryProvider Properties #region IDataServiceMetadataProvider Properties ///Namespace name for the container. public string ContainerNamespace { get { string containerNamespace = this.metadataProvider.ContainerNamespace; // 752636 [Breaking Change]: Reflection Provider should not allow null ContainerNamespace // In V1 the reflection provider allows the namespace to be null. Fixing this would be a breaking change. // We will skip this check for V1 providers for now. if (string.IsNullOrEmpty(containerNamespace) && !this.IsV1Provider) { throw new InvalidOperationException(Strings.DataServiceProviderWrapper_ContainerNamespaceMustNotBeNullOrEmpty); } return containerNamespace; } } ///Name of the container public string ContainerName { get { string containerName = this.metadataProvider.ContainerName; if (string.IsNullOrEmpty(containerName)) { throw new InvalidOperationException(Strings.DataServiceProviderWrapper_ContainerNameMustNotBeNullOrEmpty); } return containerName; } } ////// Gets all visible containers. /// WARNING!!! This property can only be called for the $metadata path because it enumerates through all resource sets. /// Calling it from outside of the $metadata path would break our IDSP contract. /// public IEnumerableResourceSets { get { var resourceSets = this.metadataProvider.ResourceSets; if (resourceSets != null) { HashSet resourceSetNames = new HashSet (EqualityComparer .Default); foreach (ResourceSet resourceSet in resourceSets) { // verify that the name of the resource set is unique AddUniqueNameToSet( resourceSet != null ? resourceSet.Name : null, resourceSetNames, Strings.DataServiceProviderWrapper_MultipleEntitySetsWithSameName(resourceSet.Name)); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. ResourceSetWrapper resourceSetWrapper = this.ValidateResourceSet(resourceSet); if (resourceSetWrapper != null) { yield return resourceSetWrapper; } } } } } /// /// Returns all types in this data source /// WARNING!!! This property can only be called for the $metadata path because it enumerates through all resource types. /// Calling it from outside of the $metadata path would break our IDSP contract. /// public IEnumerableTypes { get { var types = this.metadataProvider.Types; if (types != null) { HashSet resourceTypeNames = new HashSet (EqualityComparer .Default); foreach (ResourceType resourceType in types) { // verify that the name of the resource type is unique AddUniqueNameToSet( resourceType != null ? resourceType.Name : null, resourceTypeNames, Strings.DataServiceProviderWrapper_MultipleResourceTypesWithSameName(resourceType.Name)); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. ResourceType type = this.ValidateResourceType(resourceType); if (type != null) { yield return type; } } } } } /// /// Returns all the visible service operations in this data source. /// WARNING!!! This property can only be called for the $metadata path because it enumerates through all service operations. /// Calling it from outside of the $metadata path would break our IDSP contract. /// public IEnumerableServiceOperations { get { var serviceOperations = this.metadataProvider.ServiceOperations; if (serviceOperations != null) { HashSet serviceOperationNames = new HashSet (EqualityComparer .Default); foreach (ServiceOperation serviceOperation in serviceOperations) { // verify that the name of the service operation is unique AddUniqueNameToSet( serviceOperation != null ? serviceOperation.Name : null, serviceOperationNames, Strings.DataServiceProviderWrapper_MultipleServiceOperationsWithSameName(serviceOperation.Name)); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. ServiceOperationWrapper serviceOperationWrapper = this.ValidateServiceOperation(serviceOperation); if (serviceOperationWrapper != null) { yield return serviceOperationWrapper; } } } } } #endregion IDataServiceMetadataProvider Properties #region Properties /// /// Cached configuration with access rights info. /// internal DataServiceConfiguration Configuration { [DebuggerStepThrough] get { return this.metadata.Configuration; } } #if DEBUG ////// Used for verifying expression generated for queries, checks if all the /// internal bool AreAllResourceTypesNonOpen { get { return !this.VisibleTypeCache.Values.Any(rt => rt.IsOpenType); } } #endif ////// Returns true if the data provider is a V1 provider i.e. ReflectionServiceProvider or ObjectContextServiceProvider. /// Otherwise returns false. /// internal bool IsV1Provider { get { return this.metadataProvider is BaseServiceProvider; } } ////// Returns the ///for this provider /// The ///for this provider Note that this will only return non-null on V1 providers /// in which case it returns our V1 provider's implementation of this interface. /// In all other cases this returns null as we don't allow custom implementation of this interface yet. internal IProjectionProvider ProjectionProvider { get { if (this.IsV1Provider) { return (IProjectionProvider)this.metadataProvider; } else { return null; } } } ////// Keep track of the calculated visibility of resource types. /// private DictionaryVisibleTypeCache { [DebuggerStepThrough] get { return this.metadata.VisibleTypeCache; } } /// /// Maps resource set names to ResourceSetWrappers. /// private DictionaryResourceSetWrapperCache { [DebuggerStepThrough] get { return this.metadata.ResourceSetWrapperCache; } } /// /// Maps service operation names to ServiceOperationWrappers. /// private DictionaryServiceOperationWrapperCache { [DebuggerStepThrough] get { return this.metadata.ServiceOperationWrapperCache; } } /// /// Maps names to ResourceAssociationSets. /// private DictionaryResourceAssociationSetCache { [DebuggerStepThrough] get { return this.metadata.ResourceAssociationSetCache; } } /// /// Mapes "resourceSetName_resourceTypeName" to the list of visible properties from the set. /// private Dictionary> ResourcePropertyCache { [DebuggerStepThrough] get { return this.metadata.ResourcePropertyCache; } } /// /// Mapes "resourceSetName_resourceTypeName" to boolean of whether resourceType is allowed for resourceSet /// private DictionaryEntityTypeDisallowedForSet { [DebuggerStepThrough] get { return this.metadata.EntityTypeDisallowedForSet; } } #endregion Properties #region IDataServiceQueryProvider Methods #if DEBUG /// /// Returns the IQueryable that represents the container. /// /// resource set representing the entity set. ////// An IQueryable that represents the container; null if there is /// no container for the specified name. /// public IQueryable GetQueryRootForResourceSet(ResourceSetWrapper resourceSet, IDataService dataService) #else ////// Returns the IQueryable that represents the container. /// /// resource set representing the entity set. ////// An IQueryable that represents the container; null if there is /// no container for the specified name. /// public IQueryable GetQueryRootForResourceSet(ResourceSetWrapper resourceSet) #endif { Debug.Assert(resourceSet != null, "resourceSet != null"); #if DEBUG dataService.ProcessingPipeline.AssertDebugStateDuringRequestProcessing(dataService); #endif return this.queryProvider.GetQueryRootForResourceSet(resourceSet.ResourceSet); } ///Gets the /// Instance to extract afor the specified . from. /// The public ResourceType GetResourceType(object instance) { Debug.Assert(instance != null, "instance != null"); return this.ValidateResourceType(this.queryProvider.GetResourceType(instance)); } ///that describes this in this provider. /// Get the value of the strongly typed property. /// /// instance of the type declaring the property. /// resource property describing the property. /// Resource type to which the property belongs. ///value for the property. public object GetPropertyValue(object target, ResourceProperty resourceProperty, ResourceType resourceType) { Debug.Assert(target != null, "target != null"); Debug.Assert(resourceProperty != null, "resourceProperty != null"); Debug.Assert(resourceProperty.IsReadOnly, "resourceProperty.IsReadOnly"); if (resourceProperty.CanReflectOnInstanceTypeProperty) { try { if (resourceType == null) { resourceType = this.GetResourceType(target); } Debug.Assert(resourceType != null, "resourceType != null"); PropertyInfo propertyInfo = resourceType.GetPropertyInfo(resourceProperty); Debug.Assert(propertyInfo != null, "propertyInfo != null"); return propertyInfo.GetGetMethod().Invoke(target, null); } catch (TargetInvocationException exception) { ErrorHandler.HandleTargetInvocationException(exception); throw; } } else { return this.queryProvider.GetPropertyValue(target, resourceProperty); } } ////// Get the value of the open property. /// /// instance of the type declaring the open property. /// name of the open property. ///value for the open property. public object GetOpenPropertyValue(object target, string propertyName) { Debug.Assert(target != null, "target != null"); Debug.Assert(!string.IsNullOrEmpty(propertyName), "!string.IsNullOrEmpty(propertyName)"); return this.queryProvider.GetOpenPropertyValue(target, propertyName); } ////// Get the name and values of all the properties defined in the given instance of an open type. /// /// instance of a open type. ///collection of name and values of all the open properties. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "need to return a collection of key value pair")] public IEnumerable> GetOpenPropertyValues(object target) { Debug.Assert(target != null, "target != null"); IEnumerable > result = this.queryProvider.GetOpenPropertyValues(target); if (result == null) { return EmptyOpenPropertyValues; } return result; } /// /// Invoke the given service operation and returns the results. /// /// service operation to invoke. /// value of parameters to pass to the service operation. ///returns the result of the service operation. If the service operation returns void, then this should return null. public object InvokeServiceOperation(ServiceOperationWrapper serviceOperation, object[] parameters) { Debug.Assert(serviceOperation != null, "serviceOperation != null"); return this.queryProvider.InvokeServiceOperation(serviceOperation.ServiceOperation, parameters); } #endregion IDataServiceQueryProvider Methods #region IDataServiceMetadataProvider Methods ///Given the specified name, tries to find a resource set. /// Name of the resource set to resolve. ///Resolved resource set, possibly null. public ResourceSetWrapper TryResolveResourceSet(string name) { Debug.Assert(!string.IsNullOrEmpty(name), "!string.IsNullOrEmpty(name)"); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. ResourceSetWrapper resourceSetWrapper; if (this.ResourceSetWrapperCache.TryGetValue(name, out resourceSetWrapper)) { return resourceSetWrapper; } ResourceSet resourceSet; if (this.metadataProvider.TryResolveResourceSet(name, out resourceSet)) { return this.ValidateResourceSet(resourceSet); } return null; } ////// Gets the ResourceAssociationSet instance when given the source association end. /// /// Resource set of the source association end. /// Resource type of the source association end. /// Resource property of the source association end. ///ResourceAssociationSet instance. public ResourceAssociationSet GetResourceAssociationSet(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty resourceProperty) { Debug.Assert(resourceSet != null, "resourceSet != null"); Debug.Assert(resourceType != null, "resourceType != null"); Debug.Assert(resourceProperty != null, "resourceProperty != null"); // If the association set has already been cached, use the cached copy resourceType = GetDeclaringTypeForProperty(resourceType, resourceProperty); string associationSetKey = resourceSet.Name + '_' + resourceType.FullName + '_' + resourceProperty.Name; ResourceAssociationSet associationSet; if (this.ResourceAssociationSetCache.TryGetValue(associationSetKey, out associationSet)) { return associationSet; } // Get the association set from the underlying provider. associationSet = this.metadataProvider.GetResourceAssociationSet(resourceSet.ResourceSet, resourceType, resourceProperty); if (associationSet != null) { ResourceAssociationSetEnd thisEnd = associationSet.GetResourceAssociationSetEnd(resourceSet, resourceType, resourceProperty); ResourceAssociationSetEnd relatedEnd = associationSet.GetRelatedResourceAssociationSetEnd(resourceSet, resourceType, resourceProperty); ResourceSetWrapper relatedSet = this.ValidateResourceSet(relatedEnd.ResourceSet); if (relatedSet == null) { // If the related set is not visible, the association set is also not visible. associationSet = null; } else { ResourceType relatedType = this.ValidateResourceType(relatedEnd.ResourceType); ResourceProperty relatedProperty = null; if (relatedEnd.ResourceProperty != null) { relatedProperty = relatedType.TryResolvePropertyName(relatedEnd.ResourceProperty.Name); } // For IDSP, we want to make sure the metadata object instance stay the same within a request because we // do reference comparisons. Note if the provider returns a ResourceAssociationSet with different instances // of ResourceSet, ResourceType and ResourceProperty than what we've seen earlier in the same request, we // create a new instance of the association set using the metadata objects we've already cached. // If the metadata change should cause a failure, we want the IDSP to detect it and fail. At the Astoria runtime // layer we assume the metadata objects to remain constant throughout the request. resourceType = this.ValidateResourceType(thisEnd.ResourceType); if (thisEnd.ResourceSet != resourceSet.ResourceSet || thisEnd.ResourceType != resourceType || thisEnd.ResourceProperty != resourceProperty || relatedEnd.ResourceSet != relatedSet.ResourceSet || relatedEnd.ResourceType != relatedType || relatedEnd.ResourceProperty != relatedProperty) { associationSet = new ResourceAssociationSet( associationSet.Name, new ResourceAssociationSetEnd(resourceSet.ResourceSet, resourceType, resourceProperty), new ResourceAssociationSetEnd(relatedSet.ResourceSet, relatedType, relatedProperty)); } } } this.ResourceAssociationSetCache.Add(associationSetKey, associationSet); return associationSet; } ///Given the specified name, tries to find a type. /// Name of the type to resolve. ///Resolved resource type, possibly null. public ResourceType TryResolveResourceType(string name) { Debug.Assert(!string.IsNullOrEmpty(name), "!string.IsNullOrEmpty(name)"); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. ResourceType resourceType; if (this.VisibleTypeCache.TryGetValue(name, out resourceType)) { return resourceType; } if (this.metadataProvider.TryResolveResourceType(name, out resourceType)) { return this.ValidateResourceType(resourceType); } return null; } ////// The method must return a collection of all the types derived from /// Resource to get derived resource types from. ///. /// The collection returned should NOT include the type passed in as a parameter. /// An implementer of the interface should return null if the type does not have any derived types (ie. null == no derived types). /// /// A collection of resource types ( public IEnumerable) derived from the specified /// or null if there no types derived from the specified exist. /// GetDerivedTypes(ResourceType resourceType) { Debug.Assert(resourceType != null, "resourceType != null"); var derivedTypes = this.metadataProvider.GetDerivedTypes(resourceType); if (derivedTypes != null) { foreach (ResourceType derivedType in derivedTypes) { ResourceType type = this.ValidateResourceType(derivedType); if (type != null) { yield return type; } } } } /// /// Returns true if /// instance of the resource type in question. ///represents an Entity Type which has derived Entity Types, else false. /// True if public bool HasDerivedTypes(ResourceType resourceType) { Debug.Assert(this.ValidateResourceType(resourceType) != null, "resourceType must be read-only and visible."); return this.metadataProvider.HasDerivedTypes(resourceType); } ///represents an Entity Type which has derived Entity Types, else false. Given the specified name, tries to find a service operation. /// Name of the service operation to resolve. ///Resolved service operation, possibly null. public ServiceOperationWrapper TryResolveServiceOperation(string name) { Debug.Assert(!string.IsNullOrEmpty(name), "!string.IsNullOrEmpty(name)"); // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. ServiceOperationWrapper serviceOperationWrapper; if (this.ServiceOperationWrapperCache.TryGetValue(name, out serviceOperationWrapper)) { return serviceOperationWrapper; } ServiceOperation serviceOperation; if (this.metadataProvider.TryResolveServiceOperation(name, out serviceOperation)) { return this.ValidateServiceOperation(serviceOperation); } return null; } #endregion IDataServiceMetadataProvider Methods #region Internal Methods ////// Gets the resource type which the resource property is declared on. /// /// resource type to start looking /// resource property in question ///actual resource type that declares the property internal static ResourceType GetDeclaringTypeForProperty(ResourceType resourceType, ResourceProperty resourceProperty) { Debug.Assert(resourceType != null, "resourceType != null"); Debug.Assert(resourceProperty != null, "resourceProperty != null"); while (resourceType != null) { if (resourceType.TryResolvePropertiesDeclaredOnThisTypeByName(resourceProperty.Name) != null) { break; } resourceType = resourceType.BaseType; } Debug.Assert(resourceType != null, "resourceType != null"); return resourceType; } ///Disposes of the metadata and query providers. internal void DisposeDataSource() { Debug.Assert(this.queryProvider != null, "this.queryProvider != null"); Debug.Assert(this.metadataProvider != null, "this.metadataProvider != null"); WebUtil.Dispose(this.metadataProvider); // If the same instance implements IDataServiceMetadataProvider and IDataServiceQueryProvider interface, // we call dispose only once. if (this.metadataProvider != this.queryProvider) { WebUtil.Dispose(this.queryProvider); } this.metadataProvider = null; this.queryProvider = null; } ////// Iterates through the resource sets, service operations and resource types to pre-populate the metadata cache item. /// internal void PopulateMetadataCacheItemForV1Provider() { Debug.Assert(this.IsV1Provider, "this.IsV1Provider"); // This is only called when we initialize the service for the first time. // The Count extention method will cause the iterator to instantiate the wrapper classes. this.ServiceOperations.Count(); this.Types.Count(); foreach (ResourceSetWrapper resourceSet in this.ResourceSets) { // Derived types of a visible type are visible foreach (ResourceType derivedType in this.GetDerivedTypes(resourceSet.ResourceType)) { this.GetResourceProperties(resourceSet, derivedType); this.IsEntityTypeDisallowedForSet(resourceSet, derivedType); } // Base types of a visible type are visible ResourceType resourceType = resourceSet.ResourceType; while (resourceType != null) { this.GetResourceProperties(resourceSet, resourceType); this.IsEntityTypeDisallowedForSet(resourceSet, resourceType); resourceType = resourceType.BaseType; } } } ////// Gets the target container for the given navigation property, source container and the source resource type /// /// source entity set. /// source resource type. /// navigation property. ///target container that the navigation property refers to. internal ResourceSetWrapper GetContainer(ResourceSetWrapper sourceContainer, ResourceType sourceResourceType, ResourceProperty navigationProperty) { ResourceAssociationSet associationSet = this.GetResourceAssociationSet(sourceContainer, sourceResourceType, navigationProperty); if (associationSet != null) { ResourceAssociationSetEnd relatedEnd = associationSet.GetRelatedResourceAssociationSetEnd(sourceContainer, sourceResourceType, navigationProperty); return this.ValidateResourceSet(relatedEnd.ResourceSet); } return null; } ////// For a V1 provider checks the Epm compatiblity in order to write the appropriate /// versioning header for metadata requests /// ///true if the provider is V1 compatible, false otherwise internal bool GetEpmCompatiblityForV1Provider() { Debug.Assert(this.IsV1Provider, "Must be a V1 provider to call this function"); return (this.metadataProvider as BaseServiceProvider).EpmIsV1Compatible; } ////// Return the list of ETag properties for a given type in the context of a given container /// /// Name of the container to use for context (for MEST-enabled providers) /// Type to get the ETag properties for ///A collection of the properties that form the ETag for the given type in the given container internal IListGetETagProperties(string containerName, ResourceType resourceType) { Debug.Assert(containerName != null || !(this.metadataProvider is ObjectContextServiceProvider), "ContainerName is required for MEST-enabled provider (EFx provider)"); Debug.Assert(resourceType != null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "Resource should be non-null and of an entity type"); // Note only primitive properties can be part of an etag, they are always visible. ObjectContextServiceProvider efxProvider = this.metadataProvider as ObjectContextServiceProvider; return efxProvider == null ? resourceType.ETagProperties : efxProvider.GetETagProperties(containerName, resourceType); } /// /// Gets the visible resource properties for /// Resource set in question. /// Resource type in question. ///from . /// We cache the list of visible resource properties so we don't have to calculate it repeatedly when serializing feeds. /// List of visible resource properties from the given resource set and resource type. internal IEnumerableGetResourceProperties(ResourceSetWrapper resourceSet, ResourceType resourceType) { Debug.Assert(resourceType != null, "resourceType != null"); if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType) { Debug.Assert(resourceSet != null, "resourceSet != null"); string key = resourceSet.Name + '_' + resourceType.FullName; List properties; if (!this.ResourcePropertyCache.TryGetValue(key, out properties)) { properties = new List (); foreach (ResourceProperty property in resourceType.Properties) { if (property.TypeKind == ResourceTypeKind.EntityType && this.GetContainer(resourceSet, resourceType, property) == null) { continue; } properties.Add(property); } this.ResourcePropertyCache.Add(key, properties); } return properties; } else { return resourceType.Properties; } } /// /// Write the metadata document. /// /// instance of the metadata serializer. /// xml writer in which we need to write the metadata document. /// Data service instance. internal void WriteMetadataDocument(MetadataSerializer serializer, XmlWriter writer, IDataService service) { Debug.Assert(serializer != null, "serializer != null"); Debug.Assert(writer != null, "writer != null"); BaseServiceProvider internalProvider = this.metadataProvider as BaseServiceProvider; ObjectContextServiceProvider efxProvider = this.metadataProvider as ObjectContextServiceProvider; // always v1.1+ schemas for custom providers MetadataEdmSchemaVersion metadataEdmSchemaVersion = internalProvider == null ? MetadataEdmSchemaVersion.Version1Dot1 : internalProvider.EdmSchemaVersion; if (efxProvider == null) { serializer.GenerateMetadata(metadataEdmSchemaVersion, service); } else { efxProvider.GetMetadata(writer, this, service); } } ////// Check if the given type can be ordered. If not, throw an exception. /// /// clr type which needs to checked for ordering. internal void CheckIfOrderedType(Type clrType) { // For known providers check for sort-ability for better error reporting BaseServiceProvider baseProvider = this.metadataProvider as BaseServiceProvider; if (baseProvider != null && !baseProvider.GetTypeIsOrdered(clrType)) { string resourceTypeName = WebUtil.GetTypeName(clrType); throw DataServiceException.CreateBadRequestError(Strings.RequestQueryParser_OrderByDoesNotSupportType(resourceTypeName)); } } ////// Checks whether the current data provider is a V1 provider or not. /// ///Returns true if the current data source is a V1 provider. Otherwise, returns false. internal bool IsV1ProviderAndImplementsUpdatable() { BaseServiceProvider baseServiceProvider = this.metadataProvider as BaseServiceProvider; if (baseServiceProvider != null && baseServiceProvider.ImplementsIUpdatable()) { return true; } return false; } ////// Retrieve an implementation of a data service interface (ie. IUpdatable, IExpandProvider,etc) /// ///The type representing the requested interface /// Data service instance ///An object implementing the requested interface, or null if not available internal T GetService(IDataService dataService) where T : class { Debug.Assert(dataService != null, "dataService != null"); Debug.Assert(dataService.Provider == this, "dataService.Provider == this"); Debug.Assert(typeof(T) != typeof(IDataServiceMetadataProvider), "typeof(T) != typeof(IDataServiceMetadataProvider)"); Debug.Assert(typeof(T) != typeof(IDataServiceQueryProvider), "typeof(T) != typeof(IDataServiceQueryProvider)"); Debug.Assert(typeof(T).IsVisible, "Trying to ask the service for non-public interface."); #if DEBUG dataService.ProcessingPipeline.AssertDebugStateDuringRequestProcessing(dataService); dataService.ProcessingPipeline.HasInstantiatedProviderInterfaces = true; #endif // *NOTE* to limit test surface area, IDataServiceStreamProvider && IExpandProvider are the only custom implementation // we support for ObjectContextServiceProvider. // Should remove this in the future. if (this.metadataProvider is ObjectContextServiceProvider && typeof(T) != typeof(IDataServiceStreamProvider) && typeof(T) != typeof(IExpandProvider)) { // Return internal implementation of the interface if there is one. return WebUtil.GetService (this.metadataProvider); } // 1. Check if subclass of DataService implements IServiceProvider. If it does, then call IServiceProvider.GetService() // with the appropriate type. If it doesn’t proceed to Step 2 // a. If IServiceProvider.GetService() returns something, then go ahead and use that instance // b. If IServiceProvider.GetService() doesn't return anything, proceed to Step 2. T result = WebUtil.GetService (dataService.Instance); if (result != null) { return result; } // 2. Check if the T (where T is the type from DataService ) implements the interface. // a. If yes, use that. // b. If no, proceed to Step 3 result = this.CurrentDataSource as T; if (result != null) { // Since IDataServiceUpdateProvider derives from IUpdatable, we need to make sure that // when asked for IUpdatable, this method checks only for IUpdatable, and returns null // if the type returns IDataServiceUpdateProvider. if (typeof(T) != typeof(IUpdatable) || ((result as IDataServiceUpdateProvider) == null)) { return result; } } // 3. Check if the data service provider is a V1 provider // a. If yes, return an internal implementation if we can find one. // b. If no, then the provider doesn’t support the current interface functionality. if (this.IsV1Provider) { // Look for internal implementation for the interface return WebUtil.GetService (this.metadataProvider); } return null; } /// /// We do not allow entity sets to contain any derived type with navigation properties in the GET path /// /// entity set containing the resource type. /// entity type in the hierarchy returned by. /// True if the derived type has any navigation properties. internal bool IsEntityTypeDisallowedForSet(ResourceSetWrapper resourceSet, ResourceType resourceType) { Debug.Assert(resourceSet != null, "resourceSet != null"); Debug.Assert(resourceType != null, "resourceType != null"); object result; string key = resourceSet.Name + '_' + resourceType.FullName; if (this.EntityTypeDisallowedForSet.TryGetValue(key, out result)) { Debug.Assert(result == DataServiceProviderWrapper.RefTrue || result == DataServiceProviderWrapper.RefFalse, "result must be either RefTrue or RefFalse."); return result == DataServiceProviderWrapper.RefTrue ? true : false; } ResourceType baseType = resourceSet.ResourceType; if (baseType != resourceType && baseType.IsAssignableFrom(resourceType) && this.HasNavigationProperties(resourceSet, resourceType)) { this.EntityTypeDisallowedForSet.Add(key, DataServiceProviderWrapper.RefTrue); return true; } else { this.EntityTypeDisallowedForSet.Add(key, DataServiceProviderWrapper.RefFalse); return false; } } ////// Validates if the container should be visible and is not read only. If the container rights /// are set to None the container should not be visible. /// /// Resource set to be validated. ///Validated container, null if the container is not supposed to be visible. internal ResourceSetWrapper ValidateResourceSet(ResourceSet resourceSet) { ResourceSetWrapper resourceSetWrapper = null; if (resourceSet != null) { // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. if (this.ResourceSetWrapperCache.TryGetValue(resourceSet.Name, out resourceSetWrapper)) { return resourceSetWrapper; } resourceSetWrapper = new ResourceSetWrapper(resourceSet, this.ValidateResourceType(resourceSet.ResourceType)); resourceSetWrapper.ApplyConfiguration(this.Configuration); if (!resourceSetWrapper.IsVisible) { resourceSetWrapper = null; } this.ResourceSetWrapperCache[resourceSet.Name] = resourceSetWrapper; } return resourceSetWrapper; } #endregion Internal Methods #region Private Methods ////// Throws if resource type is not sealed. /// /// resource type to inspect. private static void ValidateResourceTypeReadOnly(ResourceType resourceType) { Debug.Assert(resourceType != null, "resourceType != null"); if (!resourceType.IsReadOnly) { throw new DataServiceException(500, Strings.DataServiceProviderWrapper_ResourceTypeNotReadonly(resourceType.FullName)); } } ////// This is a common method for checking uniqe names across entity sets, resource types and service operations. /// /// Name to be added to set. /// Set containing already verified names. /// String for exception to be thrown if the name is not unique. private static void AddUniqueNameToSet(string name, HashSetnames, string exceptionString) { if (name != null) { if (names.Contains(name)) { throw new DataServiceException(500, exceptionString); } names.Add(name); } } /// Validates that /// Resource type to be validated. ///is cached and read only. Validated resource type, null if the resource type is not supposed to be visible. private ResourceType ValidateResourceType(ResourceType resourceType) { if (resourceType != null) { // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. ResourceType cachedType; if (this.VisibleTypeCache.TryGetValue(resourceType.FullName, out cachedType)) { return cachedType; } ValidateResourceTypeReadOnly(resourceType); this.VisibleTypeCache[resourceType.FullName] = resourceType; return resourceType; } return null; } ////// Validates if the service operation should be visible and is read only. If the service operation /// rights are set to None the service operation should not be visible. /// /// Service operation to be validated. ///Validated service operation, null if the service operation is not supposed to be visible. private ServiceOperationWrapper ValidateServiceOperation(ServiceOperation serviceOperation) { ServiceOperationWrapper serviceOperationWrapper = null; if (serviceOperation != null) { // For IDSP, we want to make sure the metadata object instance stay the same within // a request because we do reference comparisons. Note the provider can return // different metadata instances within the same request. The the Validate*() methods // will make sure to return the first cached instance. if (this.ServiceOperationWrapperCache.TryGetValue(serviceOperation.Name, out serviceOperationWrapper)) { return serviceOperationWrapper; } serviceOperationWrapper = new ServiceOperationWrapper(serviceOperation); serviceOperationWrapper.ApplyConfiguration(this.Configuration, this); if (!serviceOperationWrapper.IsVisible) { serviceOperationWrapper = null; } this.ServiceOperationWrapperCache[serviceOperation.Name] = serviceOperationWrapper; } return serviceOperationWrapper; } ////// Checks whether the resource type from the resource set has visible navigation properties or not. /// /// resource set instance to inspect. /// resource type to inspect. ///Returns true if the resource type has one or more visible navigation properties from the resource set. Otherwise returns false. private bool HasNavigationProperties(ResourceSetWrapper resourceSet, ResourceType resourceType) { foreach (ResourceProperty property in resourceType.PropertiesDeclaredOnThisType) { if (property.TypeKind != ResourceTypeKind.EntityType) { continue; } if (this.GetResourceAssociationSet(resourceSet, resourceType, property) != null) { return true; } } return false; } #endregion Private Methods } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XmlEntity.cs
- AffineTransform3D.cs
- CompilerCollection.cs
- FormsAuthenticationCredentials.cs
- QilPatternVisitor.cs
- _NetworkingPerfCounters.cs
- MemberDomainMap.cs
- ToolStripOverflowButton.cs
- LinkedDataMemberFieldEditor.cs
- PeerObject.cs
- RegexWriter.cs
- WebPartVerbCollection.cs
- ISAPIApplicationHost.cs
- DocumentXPathNavigator.cs
- FlagsAttribute.cs
- ZoneIdentityPermission.cs
- CompareValidator.cs
- Debug.cs
- Message.cs
- XmlBindingWorker.cs
- ReadOnlyObservableCollection.cs
- Pens.cs
- ExceptionWrapper.cs
- DataServiceContext.cs
- ConsumerConnectionPointCollection.cs
- XmlUtilWriter.cs
- DataRowComparer.cs
- metadatamappinghashervisitor.cs
- EncoderParameter.cs
- ConversionContext.cs
- UriScheme.cs
- InterleavedZipPartStream.cs
- KeyedCollection.cs
- WebPartsPersonalizationAuthorization.cs
- DataExpression.cs
- ValidatorUtils.cs
- ActivityInterfaces.cs
- EntityDataSourceSelectingEventArgs.cs
- ModelPerspective.cs
- MembershipPasswordException.cs
- RoleServiceManager.cs
- CheckBoxRenderer.cs
- TreeViewAutomationPeer.cs
- XmlAutoDetectWriter.cs
- HtmlHead.cs
- VersionedStreamOwner.cs
- ContextMarshalException.cs
- DeobfuscatingStream.cs
- IndexedEnumerable.cs
- ObjectDataSourceMethodEditor.cs
- ResizeBehavior.cs
- Soap.cs
- Hex.cs
- FontFamilyIdentifier.cs
- RSACryptoServiceProvider.cs
- GeneralTransform3D.cs
- RegularExpressionValidator.cs
- StorageFunctionMapping.cs
- SpecialNameAttribute.cs
- BooleanAnimationUsingKeyFrames.cs
- BitmapPalette.cs
- HashCodeCombiner.cs
- PageVisual.cs
- ISAPIRuntime.cs
- MessageQueuePermission.cs
- SafeLibraryHandle.cs
- CipherData.cs
- XmlObjectSerializerReadContextComplexJson.cs
- ImageList.cs
- CancellableEnumerable.cs
- CaseInsensitiveOrdinalStringComparer.cs
- Point3DCollectionConverter.cs
- ValueTable.cs
- ToolTip.cs
- Bezier.cs
- WizardPanel.cs
- OptimisticConcurrencyException.cs
- AttributeUsageAttribute.cs
- PEFileEvidenceFactory.cs
- BooleanFunctions.cs
- AspNetSynchronizationContext.cs
- DeviceSpecificChoice.cs
- TypeLoadException.cs
- CommandManager.cs
- COM2Properties.cs
- TextHintingModeValidation.cs
- ProfileSection.cs
- cookie.cs
- FontSizeConverter.cs
- TileModeValidation.cs
- SiteMapNodeCollection.cs
- BaseDataListDesigner.cs
- ComponentSerializationService.cs
- InvalidOleVariantTypeException.cs
- WebDescriptionAttribute.cs
- LinqDataSourceInsertEventArgs.cs
- SoapExtensionReflector.cs
- WorkflowNamespace.cs
- DataGridViewRowEventArgs.cs
- EditorZone.cs