Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / ndp / fx / src / DataWeb / Server / System / Data / Services / Providers / BaseServiceProvider.cs / 1 / BaseServiceProvider.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Abstract Class which contains the common code for ObjectContextServiceProvider
// and ReflectionServiceProvider
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services.Providers
{
#region Namespaces.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Services.Caching;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Web;
using System.Xml;
#endregion Namespaces.
/// Provides a reflection-based implementation.
internal abstract class BaseServiceProvider : IDataServiceProvider, IDisposable, IUpdatable, IExpandProvider
{
/// Bindings Flags to be used for reflection.
protected const BindingFlags ResourceContainerBindingFlags = WebUtil.PublicInstanceBindingFlags;
/// Instance from which data is provided.
private object instance;
/// Metadata to be used by the service provider.
private MetadataCacheItem metadata;
///
/// Initializes a new System.Data.Services.BaseServiceProvider instance.
///
/// Metadata for this provider.
/// instance of the data source provider.
protected BaseServiceProvider(MetadataCacheItem metadata, object dataSourceInstance)
{
WebUtil.CheckArgumentNull(metadata, "metadata");
WebUtil.CheckArgumentNull(dataSourceInstance, "dataSourceInstance");
this.metadata = metadata;
this.instance = dataSourceInstance;
}
/// Returns the instance from which data is provided.
public object CurrentDataSource
{
[DebuggerStepThrough]
get
{
// Many debuggers will try to display this property, and we don't want to trigger an assertion.
Debug.Assert(
System.Diagnostics.Debugger.IsAttached || this.instance != null,
"this.instance != null -- otherwise CurrentDataSource is accessed before initialization or after disposal.");
return this.instance;
}
}
/// Gets a value indicating whether null propagation is required in expression trees.
public abstract bool NullPropagationRequired
{
get;
}
///
/// Provides a name for the context in which all resource containers are.
///
public abstract string ResourceContextName
{
get;
}
/// Returns the list of entity sets.
protected IDictionary EntitySets
{
[DebuggerStepThrough]
get { return this.metadata.EntitySets; }
}
/// Returns all known service operations.
protected IEnumerable ServiceOperations
{
[DebuggerStepThrough]
get { return this.metadata.ServiceOperations.Values; }
}
/// Target type for the data provider
protected Type Type
{
[DebuggerStepThrough]
get { return this.metadata.Type; }
}
/// Returns the list of resource types.
protected IEnumerable Types
{
[DebuggerStepThrough]
get { return this.metadata.TypeCache.Values; }
}
/// Returns true if the metadata is compatible with Edm V1 Schema otherwise false
protected bool CompatibleWithV1Schema
{
get { return this.metadata.EdmVersion1Schema; }
}
/// Cache of resource properties per type.
private Dictionary TypeCache
{
[DebuggerStepThrough]
get { return this.metadata.TypeCache; }
}
/// Applies expansions to the specified .
/// object to expand.
/// A collection of ordered paths.
///
/// An object of the same type as the given ,
/// with the results including the specified .
///
///
/// This method may modify the to indicate which expansions
/// are included.
///
/// The returned may implement the
/// interface to provide enumerable objects for the expansions; otherwise, the expanded
/// information is expected to be found directly in the enumerated objects.
///
public abstract IEnumerable ApplyExpansions(IQueryable queryable, ICollection expandPaths);
///
/// Get the container name corresponding to the given navigation property
/// The declaring type container name refers to the entity set that the declaring type belongs to
///
/// name of the entity container that the declaring type belongs to
/// declaring type
/// resource navigation property
/// name of the container that this property refers to
public abstract ResourceContainer GetContainer(string declaringTypeContainerName, Type declaringResourceType, ResourceProperty resourceProperty);
/// Gets the name of the container that holds this resource type. This method is called for open types only
/// Resource to get container for.
///
/// The name of the container for the specified resource; null if it cannot
/// be determined.
///
public abstract ResourceContainer GetContainerForResourceType(Type resourceType);
/// Gets the metadata document for this provider.
/// Writer to which metadata XML should be written.
public abstract void GetMetadata(XmlWriter writer);
///
/// Get the list of property names that form the ETag for the given resource instance
///
/// name of the container to which the resource belongs t
/// clr type of the resource whose etag properties need to be fetched
/// list of property names that form the ETag for the given resource instance
public abstract ICollection GetETagProperties(string containerName, Type resourceClrType);
#region IUpdatable Members
///
/// Creates the resource of the given type and belonging to the given container
///
/// container name to which the resource needs to be added
/// full type name i.e. Namespace qualified type name of the resource
/// object representing a resource of given type and belonging to the given container
public abstract object CreateResource(string containerName, string fullTypeName);
///
/// Gets the resource of the given type that the query points to
///
/// query pointing to a particular resource
/// full type name i.e. Namespace qualified type name of the resource
/// object representing a resource of given type and as referenced by the query
public abstract object GetResource(IQueryable query, string fullTypeName);
///
/// Checks whether the specified is ordered.
///
/// Type to check.
/// true if the type may be ordered; false otherwise.
///
/// The ordering may still fail at runtime; this method is currently
/// used for cleaner error messages only.
///
public virtual bool GetTypeIsOrdered(Type type)
{
Debug.Assert(type != null, "type != null");
return type == typeof(object) || WebUtil.IsPrimitiveType(type);
}
///
/// Resets the value of the given resource to its default value
///
/// resource whose value needs to be reset
/// same resource with its value reset
public abstract object ResetResource(object resource);
///
/// Sets the value of the given property on the target object
///
/// target object which defines the property
/// name of the property whose value needs to be updated
/// value of the property
public abstract void SetValue(object targetResource, string propertyName, object propertyValue);
///
/// Gets the value of the given property on the target object
///
/// target object which defines the property
/// name of the property whose value needs to be updated
/// the value of the property for the given target resource
public abstract object GetValue(object targetResource, string propertyName);
///
/// Sets the value of the given reference property on the target object
///
/// target object which defines the property
/// name of the property whose value needs to be updated
/// value of the property
public abstract void SetReference(object targetResource, string propertyName, object propertyValue);
///
/// Adds the given value to the collection
///
/// target object which defines the property
/// name of the property whose value needs to be updated
/// value of the property which needs to be added
public abstract void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded);
///
/// Removes the given value from the collection
///
/// target object which defines the property
/// name of the property whose value needs to be updated
/// value of the property which needs to be removed
public abstract void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved);
///
/// Delete the given resource
///
/// resource that needs to be deleted
public abstract void DeleteResource(object targetResource);
///
/// Saves all the pending changes made till now
///
public abstract void SaveChanges();
///
/// Returns the actual instance of the resource represented by the given resource object
///
/// object representing the resource whose instance needs to be fetched
/// The actual instance of the resource represented by the given resource object
public abstract object ResolveResource(object resource);
///
/// Revert all the pending changes.
///
public abstract void ClearChanges();
#endregion
#region IDataServiceProvider Members
///
/// Applies the settings specified in the given .
///
/// Configuration to apply.
/// Instance of the data source for the provider.
void IDataServiceProvider.ApplyConfiguration(DataServiceConfiguration configuration, object dataSourceInstance)
{
Debug.Assert(configuration != null, "configuration != null");
this.PopulateMetadataForUserSpecifiedTypes(configuration.GetKnownTypes(), this.TypeCache, this.EntitySets.Values);
this.ApplyRights(configuration);
this.CheckConfigurationConsistency(dataSourceInstance);
this.RemoveHiddenResourceContainers();
this.RemoveHiddenServiceOperations();
this.RemoveHiddenProperties();
this.RemoveHiddenTypes();
this.CheckServiceOperationContainers();
this.DisallowDerivedEntityTypesWithNavProperties();
}
/// Disposes of the underlying .
void IDataServiceProvider.DisposeDataSource()
{
WebUtil.Dispose(this.instance);
this.instance = null;
}
/// Gets all available containers.
/// An enumerable object with all available containers.
IEnumerable IDataServiceProvider.GetContainers()
{
return this.EntitySets.Values;
}
///
/// Returns the IQueryable that represents the container.
///
/// Name of the resource container.
///
/// An IQueryable that represents the container; null if there is
/// no container for the specified name.
///
IQueryable IDataServiceProvider.ResolveContainerName(string containerName)
{
ResourceContainer resourceContainer = ((IDataServiceProvider)this).TryResolveContainerName(containerName);
if (resourceContainer != null)
{
return this.GetResourceContainerInstance(resourceContainer);
}
else
{
return null;
}
}
/// Gets the root type of the given resource type.
/// ResourceType to get least derived type for.
/// The least derived type for the specified .
ResourceType IDataServiceProvider.GetRootType(ResourceType resourceType)
{
if (resourceType == null)
{
return resourceType;
}
while (resourceType.BaseType != null)
{
resourceType = resourceType.BaseType;
}
return resourceType;
}
/// Gets the for the specified .
/// to map into a .
/// The that maps to for this provider.
ResourceType IDataServiceProvider.GetResourceType(Type type)
{
Debug.Assert(type != null, "type != null");
return this.GetType(type);
}
/// Gets the kind of resource that the type is.
/// to check.
/// The kind of resource that the specified is.
ResourceTypeKind IDataServiceProvider.GetResourceTypeKind(Type type)
{
Debug.Assert(type != null, "type != null");
ResourceType resourceType = this.GetType(type);
Debug.Assert(resourceType != null, "resourceType != null -- otherwise the specified Type '" + type + "' isn't part of the known type system.");
return resourceType.ResourceTypeKind;
}
/// Given the specified name, tries to find a resource container.
/// Name of the resource container to resolve.
/// Resolved resource container, possibly null.
ResourceContainer IDataServiceProvider.TryResolveContainerName(string name)
{
ResourceContainer resourceContainer;
if (this.EntitySets.TryGetValue(name, out resourceContainer))
{
return resourceContainer;
}
else
{
return null;
}
}
/// Given the specified CLR type, finds the named property.
/// Type to get property for.
/// Name of property to resolve.
/// Resolved property, possibly null.
///
/// should be a resource type or a complex type.
///
ResourceProperty IDataServiceProvider.TryResolvePropertyName(Type type, string propertyName)
{
Debug.Assert(type != null, "type != null");
Debug.Assert(propertyName != null, "propertyName != null");
// Look for a known type.
ResourceType resourceType;
if (this.TypeCache.TryGetValue(type, out resourceType))
{
return resourceType.TryResolvePropertyName(propertyName);
}
// The property wasn't found.
return null;
}
/// Given the specified name, tries to find a service operation.
/// Name of the service operation to resolve.
/// Resolved operation, possibly null.
ServiceOperation IDataServiceProvider.TryResolveServiceOperation(string name)
{
Debug.Assert(name != null, "name != null");
ServiceOperation result;
if (this.metadata.ServiceOperations.TryGetValue(name, out result))
{
return result;
}
else
{
return null;
}
}
/// Given the specified name, tries to find a type.
/// Name of the type to resolve.
/// Resolved resource type, possibly null.
ResourceType IDataServiceProvider.TryResolveTypeName(string name)
{
Debug.Assert(name != null, "name != null");
Debug.Assert(this.metadata != null, "this.metadata != null");
Debug.Assert(this.TypeCache != null, "this.TypeCache != null");
foreach (ResourceType t in this.TypeCache.Values)
{
if (t.FullName == name)
{
return t;
}
}
return null;
}
#endregion //IDataServiceProvider Members
/// Releases the current data source object as necessary.
void IDisposable.Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Returns the type of the IEnumerable if the type implements IEnumerable interface; null otherwise.
///
/// type that needs to be checked
/// Element type if the type implements IEnumerable, else returns null
internal static Type GetIEnumerableElement(Type type)
{
return GetGenericInterfaceElementType(type, IEnumerableTypeFilter);
}
///
/// Returns the "T" in the IQueryable of T implementation of type.
///
/// Type to check.
/// filter against which the type is checked
///
/// The element type for the generic IQueryable interface of the type,
/// or null if it has none or if it's ambiguous.
///
internal static Type GetGenericInterfaceElementType(Type type, TypeFilter typeFilter)
{
Debug.Assert(type != null, "type != null");
Debug.Assert(!type.IsGenericTypeDefinition, "!type.IsGenericTypeDefinition");
if (typeFilter(type, null))
{
return type.GetGenericArguments()[0];
}
Type[] queriables = type.FindInterfaces(typeFilter, null);
if (queriables != null && queriables.Length == 1)
{
return queriables[0].GetGenericArguments()[0];
}
else
{
return null;
}
}
/// Adds service operations based on methods of the specified type.
/// Type with methods to add.
internal void AddOperationsFromType(Type type)
{
Debug.Assert(type != null, "type != null");
foreach (MethodInfo methodInfo in type.GetMethods(WebUtil.PublicInstanceBindingFlags | BindingFlags.FlattenHierarchy))
{
if (methodInfo.GetCustomAttributes(typeof(WebGetAttribute), true).Length != 0)
{
this.AddServiceOperation(methodInfo, false);
}
else if (methodInfo.GetCustomAttributes(typeof(WebInvokeAttribute), true).Length != 0)
{
this.AddServiceOperation(methodInfo, true);
}
}
}
/// Populates the metadata for the given provider.
internal void PopulateMetadata()
{
// Add the primitive types to the type cache.
foreach (ResourceType resourceType in WebUtil.GetPrimitiveTypes())
{
this.TypeCache.Add(resourceType.Type, resourceType);
}
this.PopulateMetadata(this.TypeCache, this.EntitySets);
#if ASTORIA_CONTAINMENT
this.ApplyAccessPaths();
#endif
}
#region Protected methods.
///
/// Writes the edmx elements for the metadata document.
///
/// xml writer into which the metadata document needs to be written.
protected static void WriteTopLevelSchemaElements(XmlWriter writer)
{
writer.WriteStartElement(XmlConstants.EdmxNamespacePrefix, XmlConstants.EdmxElement, XmlConstants.EdmxNamespace);
writer.WriteAttributeString(XmlConstants.EdmxVersion, XmlConstants.EdmxVersionValue);
writer.WriteStartElement(XmlConstants.EdmxNamespacePrefix, XmlConstants.EdmxDataServicesElement, XmlConstants.EdmxNamespace);
}
///
/// Writes the lop level schema element
///
/// xml writer into which the schema node definition is written to
/// namespace of the schema
/// true if the metadata is compatible with the edm v1 schema
protected static void WriteSchemaElement(XmlWriter writer, string schemaNamespace, bool compatibleWithV1Schema)
{
if (compatibleWithV1Schema)
{
writer.WriteStartElement(XmlConstants.Schema, XmlConstants.EdmV1Namespace);
}
else
{
writer.WriteStartElement(XmlConstants.Schema, XmlConstants.EdmV2Namespace);
}
writer.WriteAttributeString(XmlConstants.Namespace, schemaNamespace);
writer.WriteAttributeString(XmlConstants.XmlnsNamespacePrefix, XmlConstants.DataWebNamespacePrefix, null, XmlConstants.DataWebNamespace);
writer.WriteAttributeString(XmlConstants.XmlnsNamespacePrefix, XmlConstants.DataWebMetadataNamespacePrefix, null, XmlConstants.DataWebMetadataNamespace);
}
///
/// Returns the type of the IQueryable if the type implements IQueryable interface
///
/// clr type on which IQueryable check needs to be performed.
/// Element type if the property type implements IQueryable, else returns null
protected static Type GetIQueryableElement(Type type)
{
return GetGenericInterfaceElementType(type, IQueryableTypeFilter);
}
/// Writes an attribute in the dataweb metadata namespace.
/// XmlWriter in which the attribute needs to be written
/// Attribute name.
/// Attribute value.
protected static void WriteDataWebMetadata(XmlWriter writer, string name, string value)
{
Debug.Assert(writer != null, "writer != null");
Debug.Assert(name != null, "name != null");
Debug.Assert(value != null, "value != null");
writer.WriteAttributeString(XmlConstants.DataWebMetadataNamespacePrefix, name, XmlConstants.DataWebMetadataNamespace, value);
}
/// Checks that the applied configuration is consistent.
/// Instance of the data source for the provider.
/// At this point in initialization, metadata trimming hasn't taken place.
protected virtual void CheckConfigurationConsistency(object dataSourceInstance)
{
}
/// Releases the current data source object as necessary.
///
/// Whether this method is called from an explicit call to Dispose by
/// the consumer, rather than during finalization.
///
protected virtual void Dispose(bool disposing)
{
((IDataServiceProvider)this).DisposeDataSource();
}
#if ASTORIA_CONTAINMENT
///
/// Returns an object that can enumerate all
/// instances that apply to this model.
///
///
/// An object that can enumerate all
/// instances that apply to this model.
///
protected abstract IEnumerable EnumerateAccessPathAttributes();
#endif
///
/// Writes the service operations as FunctionImports in the specified .
///
/// Writer to which CSDL is being written to.
/// Enumeration of service operations to be written.
protected void WriteServiceOperations(
XmlWriter writer,
IEnumerable serviceOperations)
{
Debug.Assert(writer != null, "writer != null");
foreach (ServiceOperation operation in serviceOperations)
{
string returnTypeString;
string entitySetName = null;
if (operation.ResultKind == ServiceOperationResultKind.Nothing)
{
returnTypeString = null;
}
else
{
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(operation.ResultType);
returnTypeString = resourceType.FullName;
if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
{
ResourceContainer container = operation.EntitySet;
Debug.Assert(
container != null,
"this.TryFindAnyContainerForType(operation.ResultType, out container) -- otherwise, we didn't trim operation '" + operation.Name + "' based on its type having no container.");
entitySetName = container.Name;
}
if (operation.ResultKind != ServiceOperationResultKind.QueryWithSingleResult &&
operation.ResultKind != ServiceOperationResultKind.DirectValue)
{
Debug.Assert(
operation.ResultKind == ServiceOperationResultKind.Enumeration ||
operation.ResultKind == ServiceOperationResultKind.QueryWithMultipleResults,
operation.ResultKind + " == Enumeration or QueryWithMultipleResults");
returnTypeString = String.Format(CultureInfo.InvariantCulture, XmlConstants.EdmCollectionTypeFormat, returnTypeString);
}
}
writer.WriteStartElement(XmlConstants.EdmFunctionImportElementName);
writer.WriteAttributeString(XmlConstants.Name, operation.Name);
if (entitySetName != null)
{
writer.WriteAttributeString(XmlConstants.EdmEntitySetAttributeName, entitySetName);
}
if (returnTypeString != null)
{
writer.WriteAttributeString(XmlConstants.EdmReturnTypeAttributeName, returnTypeString);
}
string verb = operation.Invoke ? XmlConstants.HttpMethodPost : XmlConstants.HttpMethodGet;
BaseServiceProvider.WriteDataWebMetadata(writer, XmlConstants.ServiceOperationHttpMethodName, verb);
if (!String.IsNullOrEmpty(operation.MimeType))
{
BaseServiceProvider.WriteDataWebMetadata(writer, XmlConstants.DataWebMimeTypeAttributeName, operation.MimeType);
}
foreach (ServiceOperationParameter parameter in operation.Parameters)
{
writer.WriteStartElement(XmlConstants.EdmParameterElementName);
writer.WriteAttributeString(XmlConstants.Name, parameter.Name);
writer.WriteAttributeString(XmlConstants.Type, WebUtil.GetEdmTypeName(parameter.Type));
writer.WriteAttributeString(XmlConstants.EdmModeAttributeName, XmlConstants.EdmModeInValue);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
///
/// Creates the object query for the given resource container and returns it
///
/// resource container for which IQueryable instance needs to be created
/// returns the IQueryable instance for the given resource container
protected abstract IQueryable GetResourceContainerInstance(ResourceContainer resourceContainer);
///
/// Populates the metadata for the given provider
///
/// list of known types
/// list of entity sets
protected abstract void PopulateMetadata(
IDictionary knownTypes, IDictionary entitySets);
///
/// Populate types for metadata specified by the provider
///
/// list of types specified by the provider
/// list of already known types
/// list of entity sets as specified in the data source type
protected abstract void PopulateMetadataForUserSpecifiedTypes(IEnumerable userSpecifiedTypes, IDictionary knownTypes, IEnumerable entitySets);
///
/// Populate metadata for the given clr type.
///
/// type whose metadata needs to be loaded.
/// list of already known resource types.
/// list of entity sets as specified in the data source.
/// resource type containing metadata for the given clr type.
protected abstract ResourceType PopulateMetadataForType(Type type, IDictionary knownTypes, IEnumerable entitySets);
#endregion Protected methods.
/// Filter callback for finding IQueryable implementations.
/// Type to inspect.
/// Filter criteria.
/// true if the specified type is an IQueryable of T; false otherwise.
private static bool IQueryableTypeFilter(Type m, object filterCriteria)
{
Debug.Assert(m != null, "m != null");
return m.IsGenericType && m.GetGenericTypeDefinition() == typeof(IQueryable<>);
}
/// Filter callback for finding IEnumerable implementations.
/// Type to inspect.
/// Filter criteria.
/// true if the specified type is an IEnumerable of T; false otherwise.
private static bool IEnumerableTypeFilter(Type m, object filterCriteria)
{
Debug.Assert(m != null, "m != null");
return m.IsGenericType && m.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
///
/// Removes the entries from in the
/// specified .
///
/// Type of dictionary key to remove.
/// Type of dictionary value to remove.
/// Dictionary from which entries should be removed.
/// Keys for entries to remove from .
private static void RemoveKeys(IDictionary dictionary, IEnumerable keys)
{
Debug.Assert(dictionary != null, "dictionary != null");
Debug.Assert(keys != null, "keys != null");
foreach (TKey key in keys)
{
bool removed = dictionary.Remove(key);
Debug.Assert(removed, "removed - key " + key.ToString() + " should be present.");
}
}
///
/// Marks complex types from the specified
/// by adding them to the given ,
/// and adding them to the
/// to be examined later if this is the first time they are seen.
///
/// Set of types that have been marked.
/// Types that need to be examined further.
/// Type with properties to mark.
private static void MarkComplexTypes(HashSet markedTypes, Stack pendingTypes, ResourceType type)
{
foreach (ResourceProperty property in type.Properties)
{
if (property.TypeKind == ResourceTypeKind.ComplexType)
{
Debug.Assert(property.ResourceType != null, "property.ResourceType != null");
Debug.Assert(property.ResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType, "property.ResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType");
bool added = markedTypes.Add(property.ResourceType);
if (added)
{
pendingTypes.Push(property.ResourceType);
}
}
}
}
///
/// Adds a new based on the specified
/// instance.
///
/// Method to expose as a service operation.
/// Whether invocation is required.
private void AddServiceOperation(MethodInfo method, bool invoke)
{
Debug.Assert(method != null, "method != null");
Debug.Assert(!method.IsAbstract, "!method.IsAbstract - if method is abstract, the type is abstract - already checked");
if (this.metadata.ServiceOperations.ContainsKey(method.Name))
{
throw new InvalidOperationException(Strings.BaseServiceProvider_OverloadingNotSupported(this.Type, method));
}
bool hasSingleResult = SingleResultAttribute.MethodHasSingleResult(method);
ServiceOperationResultKind resultKind;
Type resultType;
ResourceType resourceType;
if (method.ReturnType == typeof(void))
{
resultKind = ServiceOperationResultKind.Nothing;
resultType = typeof(void);
resourceType = null;
this.metadata.EdmVersion1Schema = false;
}
else
{
resourceType = this.GetType(method.ReturnType);
if (resourceType != null)
{
resultKind = ServiceOperationResultKind.DirectValue;
resultType = method.ReturnType;
this.metadata.EdmVersion1Schema = false;
}
else
{
Type queryableElement = GetGenericInterfaceElementType(method.ReturnType, IQueryableTypeFilter);
if (queryableElement != null)
{
resultKind = hasSingleResult ?
ServiceOperationResultKind.QueryWithSingleResult :
ServiceOperationResultKind.QueryWithMultipleResults;
resultType = queryableElement;
}
else
{
Type enumerableElement = GetIEnumerableElement(method.ReturnType);
if (enumerableElement != null)
{
if (hasSingleResult)
{
throw new InvalidOperationException(Strings.BaseServiceProvider_IEnumerableAlwaysMultiple(this.Type, method));
}
resultKind = ServiceOperationResultKind.Enumeration;
resultType = enumerableElement;
}
else
{
resultType = method.ReturnType;
resultKind = ServiceOperationResultKind.DirectValue;
this.metadata.EdmVersion1Schema = false;
}
}
resourceType = this.PopulateMetadataForType(resultType, this.TypeCache, this.EntitySets.Values);
if (resourceType == null)
{
throw new InvalidOperationException(Strings.BaseServiceProvider_UnsupportedReturnType(method, method.ReturnType));
}
if (hasSingleResult ||
(!hasSingleResult &&
(resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType ||
resourceType.ResourceTypeKind == ResourceTypeKind.Primitive)))
{
this.metadata.EdmVersion1Schema = false;
}
}
}
string mimeType = MimeTypeAttribute.GetMemberMimeType(method);
ParameterInfo[] parametersInfo = method.GetParameters();
ServiceOperationParameter[] parameters = new ServiceOperationParameter[parametersInfo.Length];
for (int i = 0; i < parameters.Length; i++)
{
if (parametersInfo[i].IsOut || parametersInfo[i].IsRetval)
{
throw new InvalidOperationException(Strings.BaseServiceProvider_ParameterNotIn(method, parametersInfo[i]));
}
if (!WebUtil.IsPrimitiveType(parametersInfo[i].ParameterType))
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_ParameterTypeNotSupported(method, parametersInfo[i], parametersInfo[i].ParameterType));
}
string parameterName = parametersInfo[i].Name ?? "p" + i.ToString(CultureInfo.InvariantCulture);
parameters[i] = new ServiceOperationParameter(parameterName, parametersInfo[i].ParameterType);
}
ServiceOperation operation = new ServiceOperation(method, resultKind, resultType, mimeType, invoke, parameters);
this.metadata.ServiceOperations.Add(method.Name, operation);
if (resourceType != null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
{
operation.EntitySet = this.TryFindSingleContainerForType(resultType);
if (operation.EntitySet == null)
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_ServiceOperationMissingSingleEntitySet(method, resourceType.FullName));
}
}
}
#if ASTORIA_CONTAINMENT
/// Applies access path attributes to the model.
private void ApplyAccessPaths()
{
foreach (AccessPathAttribute path in this.EnumerateAccessPathAttributes())
{
Debug.Assert(path != null, "path != null -- otherwise EnumerateAccessPathAttributes is incorrect.");
Debug.Assert(
path.AnnotatedContainer != null,
"path.AnnotatedContainer -- otherwise EnumerateAccessPathAttributes is incorrect.");
// When an access path is inferred from schema, full validation hasn't taken place. Validation
// errors for inferred values from here on should be ignored, with the result that the proposed
// access path doesn't take effect.
Exception exception = path.Validate(this);
if (exception != null)
{
if (path.InferredFromSchema)
{
continue;
}
else
{
throw exception;
}
}
// Mark the NavigationProperty with the appropriate values.
IDataServiceProvider provider = (IDataServiceProvider)this;
ResourceContainer child = path.AnnotatedContainer;
ResourceContainer parent = provider.TryResolveContainerName(path.Parent);
ResourceProperty parentProperty = parent.ResourceType.TryResolvePropertyName(path.ParentNavigationProperty);
if (parentProperty.ContainmentTarget != null)
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_AccessPathTargetAlreadySpecified(
path.ParentNavigationProperty, path.Parent, parentProperty.ContainmentTarget.Name, child.Name));
}
if (parentProperty.Kind != ResourcePropertyKind.ResourceSetReference)
{
// Because it's a to-one reference doesn't actually add any syntactic sugar, we skip this case.
if (path.InferredFromSchema)
{
continue;
}
else
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_AccessPathNotThroughCollection(path.ParentNavigationProperty));
}
}
parentProperty.SetupContainmentTarget(path.InternalChildKeyMapping, path.InternalParentKeyMapping, child);
// If the path is canonical, mark the resource container.
if (path.Canonical)
{
CanonicalAccessPathAttribute canonical = (CanonicalAccessPathAttribute)path;
if (child.ContainmentCanonicalParent != null)
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_CanonicalPathTargetAlreadySpecified(
path.ParentNavigationProperty,
path.Parent,
child.Name,
child.ContainmentCanonicalProperty.Name,
child.ContainmentCanonicalParent.Name));
}
child.SetupCanonicalAccessPath(parent, parentProperty, canonical.TopLevelAccess);
}
}
}
#endif
///
/// Sets the Rights properties as specified on the given
/// .
///
/// Configuration to apply.
private void ApplyRights(DataServiceConfiguration configuration)
{
Debug.Assert(configuration != null, "configuration != null");
foreach (ResourceContainer container in this.EntitySets.Values)
{
container.Rights = configuration.GetResourceContainerRights(container);
}
foreach (ServiceOperation operation in this.metadata.ServiceOperations.Values)
{
operation.Rights = configuration.GetServiceOperationRights(operation);
}
}
/// Checks that every service operation has a result type with a container.
private void CheckServiceOperationContainers()
{
foreach (ServiceOperation operation in this.metadata.ServiceOperations.Values)
{
if (operation.ResultKind == ServiceOperationResultKind.Nothing)
{
continue;
}
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(operation.ResultType);
if (resourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
{
continue;
}
ResourceContainer container;
bool containerFound = this.TryFindAnyContainerForType(operation.ResultType, out container);
if (!containerFound)
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_ServiceOperationTypeHasNoContainer(operation.Name, operation.ResultType));
}
}
}
///
/// Returns the resource type for the corresponding clr type
///
/// clrType whose corresponding resource type needs to be returned
/// Returns the resource type
private ResourceType GetType(Type type)
{
Debug.Assert(type != null, "type != null");
// Check for the type directly first
ResourceType resourceType;
if (this.TypeCache.TryGetValue(type, out resourceType))
{
return resourceType;
}
// check for nullable types
Type elementType = Nullable.GetUnderlyingType(type);
if (elementType != null && this.TypeCache.TryGetValue(elementType, out resourceType))
{
return resourceType;
}
return null;
}
///
/// Removes from metadata resource containers that are not visible to
/// service consumers.
///
private void RemoveHiddenResourceContainers()
{
List entriesToRemove = new List();
foreach (KeyValuePair container in this.EntitySets)
{
if (container.Value.IsHidden)
{
entriesToRemove.Add(container.Key);
}
}
RemoveKeys(this.EntitySets, entriesToRemove);
}
///
/// Removes from metadata service operations that are not visible to
/// service consumers.
///
private void RemoveHiddenServiceOperations()
{
List entriesToRemove = new List();
foreach (KeyValuePair operation in this.metadata.ServiceOperations)
{
if (operation.Value.IsHidden)
{
entriesToRemove.Add(operation.Key);
}
}
RemoveKeys(this.metadata.ServiceOperations, entriesToRemove);
}
///
/// Removes from metadata propertiers that are not visible to
/// service consumers (those that references hidden resource
/// containers).
///
private void RemoveHiddenProperties()
{
foreach (ResourceType type in this.Types)
{
ResourceType typeEvaluated = type;
while (typeEvaluated != null)
{
List properties = typeEvaluated.PropertiesDeclaredInThisType;
Debug.Assert(
properties == typeEvaluated.PropertiesDeclaredInThisType,
"ResourceType.PropertiesDeclaredInThisType must by a mutable by-reference value for RemoveHiddenProperties to work correctly.");
for (int i = properties.Count - 1; i >= 0; i--)
{
ResourceProperty property = properties[i];
ResourceContainer container = property.ResourceContainer;
if (container != null && container.IsHidden)
{
properties.RemoveAt(i);
}
}
typeEvaluated = typeEvaluated.BaseType;
}
}
}
///
/// Removes types that are no longer visible after applying the service
/// configuration.
///
private void RemoveHiddenTypes()
{
this.RemoveHiddenReferenceTypes();
this.RemoveHiddenComplexTypes();
}
///
/// Removes from metadata reference types that are not accessible
/// through visible resource containers or visible service
/// operations.
///
private void RemoveHiddenReferenceTypes()
{
// Remove reference types that are not ancestors nor descendants
// of any entity sets nor service operations.
List keysToRemove = new List();
foreach (KeyValuePair entry in this.TypeCache)
{
ResourceType candidateType = entry.Value;
if (candidateType.ResourceTypeKind != ResourceTypeKind.EntityType)
{
continue;
}
bool visible = false;
foreach (ResourceContainer container in this.EntitySets.Values)
{
Debug.Assert(!container.IsHidden, "!container.IsHidden - all hidden container should have been removed before invoking RemoveHiddenTypes");
Type containerType = container.ElementType;
if (containerType.IsAssignableFrom(candidateType.Type) ||
candidateType.Type.IsAssignableFrom(containerType))
{
visible = true;
break;
}
}
if (!visible)
{
foreach (ServiceOperation operation in this.ServiceOperations)
{
Debug.Assert(!operation.IsHidden, "!operation.IsHidden - all hidden service operations should have been removed before invoking RemoveHiddenTypes");
Type operationType = operation.ResultType;
if (operationType != null &&
(operationType.IsAssignableFrom(candidateType.Type) ||
candidateType.Type.IsAssignableFrom(operationType)))
{
visible = true;
break;
}
}
if (!visible)
{
keysToRemove.Add(entry.Key);
}
}
}
RemoveKeys(this.TypeCache, keysToRemove);
}
///
/// Removes from metadata reference types that are not accessible
/// through visible types.
///
private void RemoveHiddenComplexTypes()
{
// Make a graph of all available complex types starting from visible
// reference types, then remove those missing.
HashSet visibleComplexTypes = new HashSet(EqualityComparer.Default);
Stack pendingComplexTypes = new Stack();
foreach (ResourceType type in this.Types)
{
// If there is a entity or a complex type which is open, we can't detect
// which complex types to hide. Hence we should just return and assume that
// all complex types are getting used.
#if ASTORIA_OPEN_OBJECT
if (type.IsOpenType)
{
return;
}
#endif
if (type.ResourceTypeKind == ResourceTypeKind.EntityType)
{
MarkComplexTypes(visibleComplexTypes, pendingComplexTypes, type);
}
}
// If the complex type is returned by a service operation, it should be visible.
foreach (ServiceOperation serviceOperation in this.ServiceOperations)
{
if (serviceOperation.ResultKind != ServiceOperationResultKind.Nothing)
{
ResourceType resourceType = this.TypeCache[serviceOperation.ResultType];
Debug.Assert(resourceType != null, "resourceType != null");
if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
{
if (visibleComplexTypes.Add(resourceType))
{
pendingComplexTypes.Push(resourceType);
}
}
}
}
while (pendingComplexTypes.Count > 0)
{
ResourceType type = pendingComplexTypes.Pop();
Debug.Assert(type.ResourceTypeKind == ResourceTypeKind.ComplexType, "type.ResourceTypeKind == ResourceTypeKind.ComplexType");
MarkComplexTypes(visibleComplexTypes, pendingComplexTypes, type);
}
List keysToRemove = new List();
foreach (KeyValuePair entry in this.TypeCache)
{
if (entry.Value.ResourceTypeKind == ResourceTypeKind.ComplexType &&
!visibleComplexTypes.Contains(entry.Value))
{
keysToRemove.Add(entry.Key);
}
}
RemoveKeys(this.TypeCache, keysToRemove);
}
///
/// Looks for the first resource container that the specified
/// could belong to.
///
/// Type to look for.
/// After the method returns, the container to which the type could belong.
/// true if a container was found; false otherwise.
private bool TryFindAnyContainerForType(Type type, out ResourceContainer container)
{
Debug.Assert(type != null, "type != null");
foreach (ResourceContainer c in this.EntitySets.Values)
{
if (c.ElementType.IsAssignableFrom(type))
{
container = c;
return true;
}
}
container = default(ResourceContainer);
return false;
}
///
/// Looks for a single resource container that the specified
/// could belong to.
///
/// Type to look for.
/// The single container that can hold the specified type; null otherwise.
private ResourceContainer TryFindSingleContainerForType(Type type)
{
Debug.Assert(type != null, "type != null");
ResourceContainer result = null;
foreach (ResourceContainer c in this.EntitySets.Values)
{
if (c.ElementType.IsAssignableFrom(type))
{
if (result != null)
{
return null;
}
result = c;
}
}
return result;
}
///
/// Figure out all the disallowed entity types for each entity set in the system.
///
private void DisallowDerivedEntityTypesWithNavProperties()
{
// Perform this check for entity sets whose root types takes part in inheritance
foreach (ResourceContainer entitySet in this.EntitySets.Values.Where(es => es.ResourceType.HasDerivedTypes))
{
foreach (ResourceType resourceType in this.Types)
{
// only takes leaf nodes into account - to make it slightly more performant
// check for sub-class
if (!resourceType.HasDerivedTypes && resourceType.Type.IsSubclassOf(entitySet.ResourceType.Type))
{
// walk up the hierarchy and then down, adding types which are disallowed
Queue hierarchyChain = new Queue();
ResourceType currentType = resourceType;
do
{
hierarchyChain.Enqueue(currentType);
currentType = currentType.BaseType;
}
while (currentType != entitySet.ResourceType);
// If one of the type in the hierarchy chain has navigation property, then
// all the sub-types need to be added to the entity set as disallowed types.
bool hasNavigationProperties = false;
while (hierarchyChain.Count != 0)
{
currentType = hierarchyChain.Dequeue();
if (hasNavigationProperties || currentType.HasNavigationProperties())
{
entitySet.AddDisallowedDerivedType(currentType);
hasNavigationProperties = true;
}
}
}
}
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Abstract Class which contains the common code for ObjectContextServiceProvider
// and ReflectionServiceProvider
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services.Providers
{
#region Namespaces.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Services.Caching;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.ServiceModel.Web;
using System.Xml;
#endregion Namespaces.
/// Provides a reflection-based implementation.
internal abstract class BaseServiceProvider : IDataServiceProvider, IDisposable, IUpdatable, IExpandProvider
{
/// Bindings Flags to be used for reflection.
protected const BindingFlags ResourceContainerBindingFlags = WebUtil.PublicInstanceBindingFlags;
/// Instance from which data is provided.
private object instance;
/// Metadata to be used by the service provider.
private MetadataCacheItem metadata;
///
/// Initializes a new System.Data.Services.BaseServiceProvider instance.
///
/// Metadata for this provider.
/// instance of the data source provider.
protected BaseServiceProvider(MetadataCacheItem metadata, object dataSourceInstance)
{
WebUtil.CheckArgumentNull(metadata, "metadata");
WebUtil.CheckArgumentNull(dataSourceInstance, "dataSourceInstance");
this.metadata = metadata;
this.instance = dataSourceInstance;
}
/// Returns the instance from which data is provided.
public object CurrentDataSource
{
[DebuggerStepThrough]
get
{
// Many debuggers will try to display this property, and we don't want to trigger an assertion.
Debug.Assert(
System.Diagnostics.Debugger.IsAttached || this.instance != null,
"this.instance != null -- otherwise CurrentDataSource is accessed before initialization or after disposal.");
return this.instance;
}
}
/// Gets a value indicating whether null propagation is required in expression trees.
public abstract bool NullPropagationRequired
{
get;
}
///
/// Provides a name for the context in which all resource containers are.
///
public abstract string ResourceContextName
{
get;
}
/// Returns the list of entity sets.
protected IDictionary EntitySets
{
[DebuggerStepThrough]
get { return this.metadata.EntitySets; }
}
/// Returns all known service operations.
protected IEnumerable ServiceOperations
{
[DebuggerStepThrough]
get { return this.metadata.ServiceOperations.Values; }
}
/// Target type for the data provider
protected Type Type
{
[DebuggerStepThrough]
get { return this.metadata.Type; }
}
/// Returns the list of resource types.
protected IEnumerable Types
{
[DebuggerStepThrough]
get { return this.metadata.TypeCache.Values; }
}
/// Returns true if the metadata is compatible with Edm V1 Schema otherwise false
protected bool CompatibleWithV1Schema
{
get { return this.metadata.EdmVersion1Schema; }
}
/// Cache of resource properties per type.
private Dictionary TypeCache
{
[DebuggerStepThrough]
get { return this.metadata.TypeCache; }
}
/// Applies expansions to the specified .
/// object to expand.
/// A collection of ordered paths.
///
/// An object of the same type as the given ,
/// with the results including the specified .
///
///
/// This method may modify the to indicate which expansions
/// are included.
///
/// The returned may implement the
/// interface to provide enumerable objects for the expansions; otherwise, the expanded
/// information is expected to be found directly in the enumerated objects.
///
public abstract IEnumerable ApplyExpansions(IQueryable queryable, ICollection expandPaths);
///
/// Get the container name corresponding to the given navigation property
/// The declaring type container name refers to the entity set that the declaring type belongs to
///
/// name of the entity container that the declaring type belongs to
/// declaring type
/// resource navigation property
/// name of the container that this property refers to
public abstract ResourceContainer GetContainer(string declaringTypeContainerName, Type declaringResourceType, ResourceProperty resourceProperty);
/// Gets the name of the container that holds this resource type. This method is called for open types only
/// Resource to get container for.
///
/// The name of the container for the specified resource; null if it cannot
/// be determined.
///
public abstract ResourceContainer GetContainerForResourceType(Type resourceType);
/// Gets the metadata document for this provider.
/// Writer to which metadata XML should be written.
public abstract void GetMetadata(XmlWriter writer);
///
/// Get the list of property names that form the ETag for the given resource instance
///
/// name of the container to which the resource belongs t
/// clr type of the resource whose etag properties need to be fetched
/// list of property names that form the ETag for the given resource instance
public abstract ICollection GetETagProperties(string containerName, Type resourceClrType);
#region IUpdatable Members
///
/// Creates the resource of the given type and belonging to the given container
///
/// container name to which the resource needs to be added
/// full type name i.e. Namespace qualified type name of the resource
/// object representing a resource of given type and belonging to the given container
public abstract object CreateResource(string containerName, string fullTypeName);
///
/// Gets the resource of the given type that the query points to
///
/// query pointing to a particular resource
/// full type name i.e. Namespace qualified type name of the resource
/// object representing a resource of given type and as referenced by the query
public abstract object GetResource(IQueryable query, string fullTypeName);
///
/// Checks whether the specified is ordered.
///
/// Type to check.
/// true if the type may be ordered; false otherwise.
///
/// The ordering may still fail at runtime; this method is currently
/// used for cleaner error messages only.
///
public virtual bool GetTypeIsOrdered(Type type)
{
Debug.Assert(type != null, "type != null");
return type == typeof(object) || WebUtil.IsPrimitiveType(type);
}
///
/// Resets the value of the given resource to its default value
///
/// resource whose value needs to be reset
/// same resource with its value reset
public abstract object ResetResource(object resource);
///
/// Sets the value of the given property on the target object
///
/// target object which defines the property
/// name of the property whose value needs to be updated
/// value of the property
public abstract void SetValue(object targetResource, string propertyName, object propertyValue);
///
/// Gets the value of the given property on the target object
///
/// target object which defines the property
/// name of the property whose value needs to be updated
/// the value of the property for the given target resource
public abstract object GetValue(object targetResource, string propertyName);
///
/// Sets the value of the given reference property on the target object
///
/// target object which defines the property
/// name of the property whose value needs to be updated
/// value of the property
public abstract void SetReference(object targetResource, string propertyName, object propertyValue);
///
/// Adds the given value to the collection
///
/// target object which defines the property
/// name of the property whose value needs to be updated
/// value of the property which needs to be added
public abstract void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded);
///
/// Removes the given value from the collection
///
/// target object which defines the property
/// name of the property whose value needs to be updated
/// value of the property which needs to be removed
public abstract void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved);
///
/// Delete the given resource
///
/// resource that needs to be deleted
public abstract void DeleteResource(object targetResource);
///
/// Saves all the pending changes made till now
///
public abstract void SaveChanges();
///
/// Returns the actual instance of the resource represented by the given resource object
///
/// object representing the resource whose instance needs to be fetched
/// The actual instance of the resource represented by the given resource object
public abstract object ResolveResource(object resource);
///
/// Revert all the pending changes.
///
public abstract void ClearChanges();
#endregion
#region IDataServiceProvider Members
///
/// Applies the settings specified in the given .
///
/// Configuration to apply.
/// Instance of the data source for the provider.
void IDataServiceProvider.ApplyConfiguration(DataServiceConfiguration configuration, object dataSourceInstance)
{
Debug.Assert(configuration != null, "configuration != null");
this.PopulateMetadataForUserSpecifiedTypes(configuration.GetKnownTypes(), this.TypeCache, this.EntitySets.Values);
this.ApplyRights(configuration);
this.CheckConfigurationConsistency(dataSourceInstance);
this.RemoveHiddenResourceContainers();
this.RemoveHiddenServiceOperations();
this.RemoveHiddenProperties();
this.RemoveHiddenTypes();
this.CheckServiceOperationContainers();
this.DisallowDerivedEntityTypesWithNavProperties();
}
/// Disposes of the underlying .
void IDataServiceProvider.DisposeDataSource()
{
WebUtil.Dispose(this.instance);
this.instance = null;
}
/// Gets all available containers.
/// An enumerable object with all available containers.
IEnumerable IDataServiceProvider.GetContainers()
{
return this.EntitySets.Values;
}
///
/// Returns the IQueryable that represents the container.
///
/// Name of the resource container.
///
/// An IQueryable that represents the container; null if there is
/// no container for the specified name.
///
IQueryable IDataServiceProvider.ResolveContainerName(string containerName)
{
ResourceContainer resourceContainer = ((IDataServiceProvider)this).TryResolveContainerName(containerName);
if (resourceContainer != null)
{
return this.GetResourceContainerInstance(resourceContainer);
}
else
{
return null;
}
}
/// Gets the root type of the given resource type.
/// ResourceType to get least derived type for.
/// The least derived type for the specified .
ResourceType IDataServiceProvider.GetRootType(ResourceType resourceType)
{
if (resourceType == null)
{
return resourceType;
}
while (resourceType.BaseType != null)
{
resourceType = resourceType.BaseType;
}
return resourceType;
}
/// Gets the for the specified .
/// to map into a .
/// The that maps to for this provider.
ResourceType IDataServiceProvider.GetResourceType(Type type)
{
Debug.Assert(type != null, "type != null");
return this.GetType(type);
}
/// Gets the kind of resource that the type is.
/// to check.
/// The kind of resource that the specified is.
ResourceTypeKind IDataServiceProvider.GetResourceTypeKind(Type type)
{
Debug.Assert(type != null, "type != null");
ResourceType resourceType = this.GetType(type);
Debug.Assert(resourceType != null, "resourceType != null -- otherwise the specified Type '" + type + "' isn't part of the known type system.");
return resourceType.ResourceTypeKind;
}
/// Given the specified name, tries to find a resource container.
/// Name of the resource container to resolve.
/// Resolved resource container, possibly null.
ResourceContainer IDataServiceProvider.TryResolveContainerName(string name)
{
ResourceContainer resourceContainer;
if (this.EntitySets.TryGetValue(name, out resourceContainer))
{
return resourceContainer;
}
else
{
return null;
}
}
/// Given the specified CLR type, finds the named property.
/// Type to get property for.
/// Name of property to resolve.
/// Resolved property, possibly null.
///
/// should be a resource type or a complex type.
///
ResourceProperty IDataServiceProvider.TryResolvePropertyName(Type type, string propertyName)
{
Debug.Assert(type != null, "type != null");
Debug.Assert(propertyName != null, "propertyName != null");
// Look for a known type.
ResourceType resourceType;
if (this.TypeCache.TryGetValue(type, out resourceType))
{
return resourceType.TryResolvePropertyName(propertyName);
}
// The property wasn't found.
return null;
}
/// Given the specified name, tries to find a service operation.
/// Name of the service operation to resolve.
/// Resolved operation, possibly null.
ServiceOperation IDataServiceProvider.TryResolveServiceOperation(string name)
{
Debug.Assert(name != null, "name != null");
ServiceOperation result;
if (this.metadata.ServiceOperations.TryGetValue(name, out result))
{
return result;
}
else
{
return null;
}
}
/// Given the specified name, tries to find a type.
/// Name of the type to resolve.
/// Resolved resource type, possibly null.
ResourceType IDataServiceProvider.TryResolveTypeName(string name)
{
Debug.Assert(name != null, "name != null");
Debug.Assert(this.metadata != null, "this.metadata != null");
Debug.Assert(this.TypeCache != null, "this.TypeCache != null");
foreach (ResourceType t in this.TypeCache.Values)
{
if (t.FullName == name)
{
return t;
}
}
return null;
}
#endregion //IDataServiceProvider Members
/// Releases the current data source object as necessary.
void IDisposable.Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Returns the type of the IEnumerable if the type implements IEnumerable interface; null otherwise.
///
/// type that needs to be checked
/// Element type if the type implements IEnumerable, else returns null
internal static Type GetIEnumerableElement(Type type)
{
return GetGenericInterfaceElementType(type, IEnumerableTypeFilter);
}
///
/// Returns the "T" in the IQueryable of T implementation of type.
///
/// Type to check.
/// filter against which the type is checked
///
/// The element type for the generic IQueryable interface of the type,
/// or null if it has none or if it's ambiguous.
///
internal static Type GetGenericInterfaceElementType(Type type, TypeFilter typeFilter)
{
Debug.Assert(type != null, "type != null");
Debug.Assert(!type.IsGenericTypeDefinition, "!type.IsGenericTypeDefinition");
if (typeFilter(type, null))
{
return type.GetGenericArguments()[0];
}
Type[] queriables = type.FindInterfaces(typeFilter, null);
if (queriables != null && queriables.Length == 1)
{
return queriables[0].GetGenericArguments()[0];
}
else
{
return null;
}
}
/// Adds service operations based on methods of the specified type.
/// Type with methods to add.
internal void AddOperationsFromType(Type type)
{
Debug.Assert(type != null, "type != null");
foreach (MethodInfo methodInfo in type.GetMethods(WebUtil.PublicInstanceBindingFlags | BindingFlags.FlattenHierarchy))
{
if (methodInfo.GetCustomAttributes(typeof(WebGetAttribute), true).Length != 0)
{
this.AddServiceOperation(methodInfo, false);
}
else if (methodInfo.GetCustomAttributes(typeof(WebInvokeAttribute), true).Length != 0)
{
this.AddServiceOperation(methodInfo, true);
}
}
}
/// Populates the metadata for the given provider.
internal void PopulateMetadata()
{
// Add the primitive types to the type cache.
foreach (ResourceType resourceType in WebUtil.GetPrimitiveTypes())
{
this.TypeCache.Add(resourceType.Type, resourceType);
}
this.PopulateMetadata(this.TypeCache, this.EntitySets);
#if ASTORIA_CONTAINMENT
this.ApplyAccessPaths();
#endif
}
#region Protected methods.
///
/// Writes the edmx elements for the metadata document.
///
/// xml writer into which the metadata document needs to be written.
protected static void WriteTopLevelSchemaElements(XmlWriter writer)
{
writer.WriteStartElement(XmlConstants.EdmxNamespacePrefix, XmlConstants.EdmxElement, XmlConstants.EdmxNamespace);
writer.WriteAttributeString(XmlConstants.EdmxVersion, XmlConstants.EdmxVersionValue);
writer.WriteStartElement(XmlConstants.EdmxNamespacePrefix, XmlConstants.EdmxDataServicesElement, XmlConstants.EdmxNamespace);
}
///
/// Writes the lop level schema element
///
/// xml writer into which the schema node definition is written to
/// namespace of the schema
/// true if the metadata is compatible with the edm v1 schema
protected static void WriteSchemaElement(XmlWriter writer, string schemaNamespace, bool compatibleWithV1Schema)
{
if (compatibleWithV1Schema)
{
writer.WriteStartElement(XmlConstants.Schema, XmlConstants.EdmV1Namespace);
}
else
{
writer.WriteStartElement(XmlConstants.Schema, XmlConstants.EdmV2Namespace);
}
writer.WriteAttributeString(XmlConstants.Namespace, schemaNamespace);
writer.WriteAttributeString(XmlConstants.XmlnsNamespacePrefix, XmlConstants.DataWebNamespacePrefix, null, XmlConstants.DataWebNamespace);
writer.WriteAttributeString(XmlConstants.XmlnsNamespacePrefix, XmlConstants.DataWebMetadataNamespacePrefix, null, XmlConstants.DataWebMetadataNamespace);
}
///
/// Returns the type of the IQueryable if the type implements IQueryable interface
///
/// clr type on which IQueryable check needs to be performed.
/// Element type if the property type implements IQueryable, else returns null
protected static Type GetIQueryableElement(Type type)
{
return GetGenericInterfaceElementType(type, IQueryableTypeFilter);
}
/// Writes an attribute in the dataweb metadata namespace.
/// XmlWriter in which the attribute needs to be written
/// Attribute name.
/// Attribute value.
protected static void WriteDataWebMetadata(XmlWriter writer, string name, string value)
{
Debug.Assert(writer != null, "writer != null");
Debug.Assert(name != null, "name != null");
Debug.Assert(value != null, "value != null");
writer.WriteAttributeString(XmlConstants.DataWebMetadataNamespacePrefix, name, XmlConstants.DataWebMetadataNamespace, value);
}
/// Checks that the applied configuration is consistent.
/// Instance of the data source for the provider.
/// At this point in initialization, metadata trimming hasn't taken place.
protected virtual void CheckConfigurationConsistency(object dataSourceInstance)
{
}
/// Releases the current data source object as necessary.
///
/// Whether this method is called from an explicit call to Dispose by
/// the consumer, rather than during finalization.
///
protected virtual void Dispose(bool disposing)
{
((IDataServiceProvider)this).DisposeDataSource();
}
#if ASTORIA_CONTAINMENT
///
/// Returns an object that can enumerate all
/// instances that apply to this model.
///
///
/// An object that can enumerate all
/// instances that apply to this model.
///
protected abstract IEnumerable EnumerateAccessPathAttributes();
#endif
///
/// Writes the service operations as FunctionImports in the specified .
///
/// Writer to which CSDL is being written to.
/// Enumeration of service operations to be written.
protected void WriteServiceOperations(
XmlWriter writer,
IEnumerable serviceOperations)
{
Debug.Assert(writer != null, "writer != null");
foreach (ServiceOperation operation in serviceOperations)
{
string returnTypeString;
string entitySetName = null;
if (operation.ResultKind == ServiceOperationResultKind.Nothing)
{
returnTypeString = null;
}
else
{
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(operation.ResultType);
returnTypeString = resourceType.FullName;
if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
{
ResourceContainer container = operation.EntitySet;
Debug.Assert(
container != null,
"this.TryFindAnyContainerForType(operation.ResultType, out container) -- otherwise, we didn't trim operation '" + operation.Name + "' based on its type having no container.");
entitySetName = container.Name;
}
if (operation.ResultKind != ServiceOperationResultKind.QueryWithSingleResult &&
operation.ResultKind != ServiceOperationResultKind.DirectValue)
{
Debug.Assert(
operation.ResultKind == ServiceOperationResultKind.Enumeration ||
operation.ResultKind == ServiceOperationResultKind.QueryWithMultipleResults,
operation.ResultKind + " == Enumeration or QueryWithMultipleResults");
returnTypeString = String.Format(CultureInfo.InvariantCulture, XmlConstants.EdmCollectionTypeFormat, returnTypeString);
}
}
writer.WriteStartElement(XmlConstants.EdmFunctionImportElementName);
writer.WriteAttributeString(XmlConstants.Name, operation.Name);
if (entitySetName != null)
{
writer.WriteAttributeString(XmlConstants.EdmEntitySetAttributeName, entitySetName);
}
if (returnTypeString != null)
{
writer.WriteAttributeString(XmlConstants.EdmReturnTypeAttributeName, returnTypeString);
}
string verb = operation.Invoke ? XmlConstants.HttpMethodPost : XmlConstants.HttpMethodGet;
BaseServiceProvider.WriteDataWebMetadata(writer, XmlConstants.ServiceOperationHttpMethodName, verb);
if (!String.IsNullOrEmpty(operation.MimeType))
{
BaseServiceProvider.WriteDataWebMetadata(writer, XmlConstants.DataWebMimeTypeAttributeName, operation.MimeType);
}
foreach (ServiceOperationParameter parameter in operation.Parameters)
{
writer.WriteStartElement(XmlConstants.EdmParameterElementName);
writer.WriteAttributeString(XmlConstants.Name, parameter.Name);
writer.WriteAttributeString(XmlConstants.Type, WebUtil.GetEdmTypeName(parameter.Type));
writer.WriteAttributeString(XmlConstants.EdmModeAttributeName, XmlConstants.EdmModeInValue);
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
///
/// Creates the object query for the given resource container and returns it
///
/// resource container for which IQueryable instance needs to be created
/// returns the IQueryable instance for the given resource container
protected abstract IQueryable GetResourceContainerInstance(ResourceContainer resourceContainer);
///
/// Populates the metadata for the given provider
///
/// list of known types
/// list of entity sets
protected abstract void PopulateMetadata(
IDictionary knownTypes, IDictionary entitySets);
///
/// Populate types for metadata specified by the provider
///
/// list of types specified by the provider
/// list of already known types
/// list of entity sets as specified in the data source type
protected abstract void PopulateMetadataForUserSpecifiedTypes(IEnumerable userSpecifiedTypes, IDictionary knownTypes, IEnumerable entitySets);
///
/// Populate metadata for the given clr type.
///
/// type whose metadata needs to be loaded.
/// list of already known resource types.
/// list of entity sets as specified in the data source.
/// resource type containing metadata for the given clr type.
protected abstract ResourceType PopulateMetadataForType(Type type, IDictionary knownTypes, IEnumerable entitySets);
#endregion Protected methods.
/// Filter callback for finding IQueryable implementations.
/// Type to inspect.
/// Filter criteria.
/// true if the specified type is an IQueryable of T; false otherwise.
private static bool IQueryableTypeFilter(Type m, object filterCriteria)
{
Debug.Assert(m != null, "m != null");
return m.IsGenericType && m.GetGenericTypeDefinition() == typeof(IQueryable<>);
}
/// Filter callback for finding IEnumerable implementations.
/// Type to inspect.
/// Filter criteria.
/// true if the specified type is an IEnumerable of T; false otherwise.
private static bool IEnumerableTypeFilter(Type m, object filterCriteria)
{
Debug.Assert(m != null, "m != null");
return m.IsGenericType && m.GetGenericTypeDefinition() == typeof(IEnumerable<>);
}
///
/// Removes the entries from in the
/// specified .
///
/// Type of dictionary key to remove.
/// Type of dictionary value to remove.
/// Dictionary from which entries should be removed.
/// Keys for entries to remove from .
private static void RemoveKeys(IDictionary dictionary, IEnumerable keys)
{
Debug.Assert(dictionary != null, "dictionary != null");
Debug.Assert(keys != null, "keys != null");
foreach (TKey key in keys)
{
bool removed = dictionary.Remove(key);
Debug.Assert(removed, "removed - key " + key.ToString() + " should be present.");
}
}
///
/// Marks complex types from the specified
/// by adding them to the given ,
/// and adding them to the
/// to be examined later if this is the first time they are seen.
///
/// Set of types that have been marked.
/// Types that need to be examined further.
/// Type with properties to mark.
private static void MarkComplexTypes(HashSet markedTypes, Stack pendingTypes, ResourceType type)
{
foreach (ResourceProperty property in type.Properties)
{
if (property.TypeKind == ResourceTypeKind.ComplexType)
{
Debug.Assert(property.ResourceType != null, "property.ResourceType != null");
Debug.Assert(property.ResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType, "property.ResourceType.ResourceTypeKind == ResourceTypeKind.ComplexType");
bool added = markedTypes.Add(property.ResourceType);
if (added)
{
pendingTypes.Push(property.ResourceType);
}
}
}
}
///
/// Adds a new based on the specified
/// instance.
///
/// Method to expose as a service operation.
/// Whether invocation is required.
private void AddServiceOperation(MethodInfo method, bool invoke)
{
Debug.Assert(method != null, "method != null");
Debug.Assert(!method.IsAbstract, "!method.IsAbstract - if method is abstract, the type is abstract - already checked");
if (this.metadata.ServiceOperations.ContainsKey(method.Name))
{
throw new InvalidOperationException(Strings.BaseServiceProvider_OverloadingNotSupported(this.Type, method));
}
bool hasSingleResult = SingleResultAttribute.MethodHasSingleResult(method);
ServiceOperationResultKind resultKind;
Type resultType;
ResourceType resourceType;
if (method.ReturnType == typeof(void))
{
resultKind = ServiceOperationResultKind.Nothing;
resultType = typeof(void);
resourceType = null;
this.metadata.EdmVersion1Schema = false;
}
else
{
resourceType = this.GetType(method.ReturnType);
if (resourceType != null)
{
resultKind = ServiceOperationResultKind.DirectValue;
resultType = method.ReturnType;
this.metadata.EdmVersion1Schema = false;
}
else
{
Type queryableElement = GetGenericInterfaceElementType(method.ReturnType, IQueryableTypeFilter);
if (queryableElement != null)
{
resultKind = hasSingleResult ?
ServiceOperationResultKind.QueryWithSingleResult :
ServiceOperationResultKind.QueryWithMultipleResults;
resultType = queryableElement;
}
else
{
Type enumerableElement = GetIEnumerableElement(method.ReturnType);
if (enumerableElement != null)
{
if (hasSingleResult)
{
throw new InvalidOperationException(Strings.BaseServiceProvider_IEnumerableAlwaysMultiple(this.Type, method));
}
resultKind = ServiceOperationResultKind.Enumeration;
resultType = enumerableElement;
}
else
{
resultType = method.ReturnType;
resultKind = ServiceOperationResultKind.DirectValue;
this.metadata.EdmVersion1Schema = false;
}
}
resourceType = this.PopulateMetadataForType(resultType, this.TypeCache, this.EntitySets.Values);
if (resourceType == null)
{
throw new InvalidOperationException(Strings.BaseServiceProvider_UnsupportedReturnType(method, method.ReturnType));
}
if (hasSingleResult ||
(!hasSingleResult &&
(resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType ||
resourceType.ResourceTypeKind == ResourceTypeKind.Primitive)))
{
this.metadata.EdmVersion1Schema = false;
}
}
}
string mimeType = MimeTypeAttribute.GetMemberMimeType(method);
ParameterInfo[] parametersInfo = method.GetParameters();
ServiceOperationParameter[] parameters = new ServiceOperationParameter[parametersInfo.Length];
for (int i = 0; i < parameters.Length; i++)
{
if (parametersInfo[i].IsOut || parametersInfo[i].IsRetval)
{
throw new InvalidOperationException(Strings.BaseServiceProvider_ParameterNotIn(method, parametersInfo[i]));
}
if (!WebUtil.IsPrimitiveType(parametersInfo[i].ParameterType))
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_ParameterTypeNotSupported(method, parametersInfo[i], parametersInfo[i].ParameterType));
}
string parameterName = parametersInfo[i].Name ?? "p" + i.ToString(CultureInfo.InvariantCulture);
parameters[i] = new ServiceOperationParameter(parameterName, parametersInfo[i].ParameterType);
}
ServiceOperation operation = new ServiceOperation(method, resultKind, resultType, mimeType, invoke, parameters);
this.metadata.ServiceOperations.Add(method.Name, operation);
if (resourceType != null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
{
operation.EntitySet = this.TryFindSingleContainerForType(resultType);
if (operation.EntitySet == null)
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_ServiceOperationMissingSingleEntitySet(method, resourceType.FullName));
}
}
}
#if ASTORIA_CONTAINMENT
/// Applies access path attributes to the model.
private void ApplyAccessPaths()
{
foreach (AccessPathAttribute path in this.EnumerateAccessPathAttributes())
{
Debug.Assert(path != null, "path != null -- otherwise EnumerateAccessPathAttributes is incorrect.");
Debug.Assert(
path.AnnotatedContainer != null,
"path.AnnotatedContainer -- otherwise EnumerateAccessPathAttributes is incorrect.");
// When an access path is inferred from schema, full validation hasn't taken place. Validation
// errors for inferred values from here on should be ignored, with the result that the proposed
// access path doesn't take effect.
Exception exception = path.Validate(this);
if (exception != null)
{
if (path.InferredFromSchema)
{
continue;
}
else
{
throw exception;
}
}
// Mark the NavigationProperty with the appropriate values.
IDataServiceProvider provider = (IDataServiceProvider)this;
ResourceContainer child = path.AnnotatedContainer;
ResourceContainer parent = provider.TryResolveContainerName(path.Parent);
ResourceProperty parentProperty = parent.ResourceType.TryResolvePropertyName(path.ParentNavigationProperty);
if (parentProperty.ContainmentTarget != null)
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_AccessPathTargetAlreadySpecified(
path.ParentNavigationProperty, path.Parent, parentProperty.ContainmentTarget.Name, child.Name));
}
if (parentProperty.Kind != ResourcePropertyKind.ResourceSetReference)
{
// Because it's a to-one reference doesn't actually add any syntactic sugar, we skip this case.
if (path.InferredFromSchema)
{
continue;
}
else
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_AccessPathNotThroughCollection(path.ParentNavigationProperty));
}
}
parentProperty.SetupContainmentTarget(path.InternalChildKeyMapping, path.InternalParentKeyMapping, child);
// If the path is canonical, mark the resource container.
if (path.Canonical)
{
CanonicalAccessPathAttribute canonical = (CanonicalAccessPathAttribute)path;
if (child.ContainmentCanonicalParent != null)
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_CanonicalPathTargetAlreadySpecified(
path.ParentNavigationProperty,
path.Parent,
child.Name,
child.ContainmentCanonicalProperty.Name,
child.ContainmentCanonicalParent.Name));
}
child.SetupCanonicalAccessPath(parent, parentProperty, canonical.TopLevelAccess);
}
}
}
#endif
///
/// Sets the Rights properties as specified on the given
/// .
///
/// Configuration to apply.
private void ApplyRights(DataServiceConfiguration configuration)
{
Debug.Assert(configuration != null, "configuration != null");
foreach (ResourceContainer container in this.EntitySets.Values)
{
container.Rights = configuration.GetResourceContainerRights(container);
}
foreach (ServiceOperation operation in this.metadata.ServiceOperations.Values)
{
operation.Rights = configuration.GetServiceOperationRights(operation);
}
}
/// Checks that every service operation has a result type with a container.
private void CheckServiceOperationContainers()
{
foreach (ServiceOperation operation in this.metadata.ServiceOperations.Values)
{
if (operation.ResultKind == ServiceOperationResultKind.Nothing)
{
continue;
}
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(operation.ResultType);
if (resourceType.ResourceTypeKind != ResourceTypeKind.EntityType)
{
continue;
}
ResourceContainer container;
bool containerFound = this.TryFindAnyContainerForType(operation.ResultType, out container);
if (!containerFound)
{
throw new InvalidOperationException(
Strings.BaseServiceProvider_ServiceOperationTypeHasNoContainer(operation.Name, operation.ResultType));
}
}
}
///
/// Returns the resource type for the corresponding clr type
///
/// clrType whose corresponding resource type needs to be returned
/// Returns the resource type
private ResourceType GetType(Type type)
{
Debug.Assert(type != null, "type != null");
// Check for the type directly first
ResourceType resourceType;
if (this.TypeCache.TryGetValue(type, out resourceType))
{
return resourceType;
}
// check for nullable types
Type elementType = Nullable.GetUnderlyingType(type);
if (elementType != null && this.TypeCache.TryGetValue(elementType, out resourceType))
{
return resourceType;
}
return null;
}
///
/// Removes from metadata resource containers that are not visible to
/// service consumers.
///
private void RemoveHiddenResourceContainers()
{
List entriesToRemove = new List();
foreach (KeyValuePair container in this.EntitySets)
{
if (container.Value.IsHidden)
{
entriesToRemove.Add(container.Key);
}
}
RemoveKeys(this.EntitySets, entriesToRemove);
}
///
/// Removes from metadata service operations that are not visible to
/// service consumers.
///
private void RemoveHiddenServiceOperations()
{
List entriesToRemove = new List();
foreach (KeyValuePair operation in this.metadata.ServiceOperations)
{
if (operation.Value.IsHidden)
{
entriesToRemove.Add(operation.Key);
}
}
RemoveKeys(this.metadata.ServiceOperations, entriesToRemove);
}
///
/// Removes from metadata propertiers that are not visible to
/// service consumers (those that references hidden resource
/// containers).
///
private void RemoveHiddenProperties()
{
foreach (ResourceType type in this.Types)
{
ResourceType typeEvaluated = type;
while (typeEvaluated != null)
{
List properties = typeEvaluated.PropertiesDeclaredInThisType;
Debug.Assert(
properties == typeEvaluated.PropertiesDeclaredInThisType,
"ResourceType.PropertiesDeclaredInThisType must by a mutable by-reference value for RemoveHiddenProperties to work correctly.");
for (int i = properties.Count - 1; i >= 0; i--)
{
ResourceProperty property = properties[i];
ResourceContainer container = property.ResourceContainer;
if (container != null && container.IsHidden)
{
properties.RemoveAt(i);
}
}
typeEvaluated = typeEvaluated.BaseType;
}
}
}
///
/// Removes types that are no longer visible after applying the service
/// configuration.
///
private void RemoveHiddenTypes()
{
this.RemoveHiddenReferenceTypes();
this.RemoveHiddenComplexTypes();
}
///
/// Removes from metadata reference types that are not accessible
/// through visible resource containers or visible service
/// operations.
///
private void RemoveHiddenReferenceTypes()
{
// Remove reference types that are not ancestors nor descendants
// of any entity sets nor service operations.
List keysToRemove = new List();
foreach (KeyValuePair entry in this.TypeCache)
{
ResourceType candidateType = entry.Value;
if (candidateType.ResourceTypeKind != ResourceTypeKind.EntityType)
{
continue;
}
bool visible = false;
foreach (ResourceContainer container in this.EntitySets.Values)
{
Debug.Assert(!container.IsHidden, "!container.IsHidden - all hidden container should have been removed before invoking RemoveHiddenTypes");
Type containerType = container.ElementType;
if (containerType.IsAssignableFrom(candidateType.Type) ||
candidateType.Type.IsAssignableFrom(containerType))
{
visible = true;
break;
}
}
if (!visible)
{
foreach (ServiceOperation operation in this.ServiceOperations)
{
Debug.Assert(!operation.IsHidden, "!operation.IsHidden - all hidden service operations should have been removed before invoking RemoveHiddenTypes");
Type operationType = operation.ResultType;
if (operationType != null &&
(operationType.IsAssignableFrom(candidateType.Type) ||
candidateType.Type.IsAssignableFrom(operationType)))
{
visible = true;
break;
}
}
if (!visible)
{
keysToRemove.Add(entry.Key);
}
}
}
RemoveKeys(this.TypeCache, keysToRemove);
}
///
/// Removes from metadata reference types that are not accessible
/// through visible types.
///
private void RemoveHiddenComplexTypes()
{
// Make a graph of all available complex types starting from visible
// reference types, then remove those missing.
HashSet visibleComplexTypes = new HashSet(EqualityComparer.Default);
Stack pendingComplexTypes = new Stack();
foreach (ResourceType type in this.Types)
{
// If there is a entity or a complex type which is open, we can't detect
// which complex types to hide. Hence we should just return and assume that
// all complex types are getting used.
#if ASTORIA_OPEN_OBJECT
if (type.IsOpenType)
{
return;
}
#endif
if (type.ResourceTypeKind == ResourceTypeKind.EntityType)
{
MarkComplexTypes(visibleComplexTypes, pendingComplexTypes, type);
}
}
// If the complex type is returned by a service operation, it should be visible.
foreach (ServiceOperation serviceOperation in this.ServiceOperations)
{
if (serviceOperation.ResultKind != ServiceOperationResultKind.Nothing)
{
ResourceType resourceType = this.TypeCache[serviceOperation.ResultType];
Debug.Assert(resourceType != null, "resourceType != null");
if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
{
if (visibleComplexTypes.Add(resourceType))
{
pendingComplexTypes.Push(resourceType);
}
}
}
}
while (pendingComplexTypes.Count > 0)
{
ResourceType type = pendingComplexTypes.Pop();
Debug.Assert(type.ResourceTypeKind == ResourceTypeKind.ComplexType, "type.ResourceTypeKind == ResourceTypeKind.ComplexType");
MarkComplexTypes(visibleComplexTypes, pendingComplexTypes, type);
}
List keysToRemove = new List();
foreach (KeyValuePair entry in this.TypeCache)
{
if (entry.Value.ResourceTypeKind == ResourceTypeKind.ComplexType &&
!visibleComplexTypes.Contains(entry.Value))
{
keysToRemove.Add(entry.Key);
}
}
RemoveKeys(this.TypeCache, keysToRemove);
}
///
/// Looks for the first resource container that the specified
/// could belong to.
///
/// Type to look for.
/// After the method returns, the container to which the type could belong.
/// true if a container was found; false otherwise.
private bool TryFindAnyContainerForType(Type type, out ResourceContainer container)
{
Debug.Assert(type != null, "type != null");
foreach (ResourceContainer c in this.EntitySets.Values)
{
if (c.ElementType.IsAssignableFrom(type))
{
container = c;
return true;
}
}
container = default(ResourceContainer);
return false;
}
///
/// Looks for a single resource container that the specified
/// could belong to.
///
/// Type to look for.
/// The single container that can hold the specified type; null otherwise.
private ResourceContainer TryFindSingleContainerForType(Type type)
{
Debug.Assert(type != null, "type != null");
ResourceContainer result = null;
foreach (ResourceContainer c in this.EntitySets.Values)
{
if (c.ElementType.IsAssignableFrom(type))
{
if (result != null)
{
return null;
}
result = c;
}
}
return result;
}
///
/// Figure out all the disallowed entity types for each entity set in the system.
///
private void DisallowDerivedEntityTypesWithNavProperties()
{
// Perform this check for entity sets whose root types takes part in inheritance
foreach (ResourceContainer entitySet in this.EntitySets.Values.Where(es => es.ResourceType.HasDerivedTypes))
{
foreach (ResourceType resourceType in this.Types)
{
// only takes leaf nodes into account - to make it slightly more performant
// check for sub-class
if (!resourceType.HasDerivedTypes && resourceType.Type.IsSubclassOf(entitySet.ResourceType.Type))
{
// walk up the hierarchy and then down, adding types which are disallowed
Queue hierarchyChain = new Queue();
ResourceType currentType = resourceType;
do
{
hierarchyChain.Enqueue(currentType);
currentType = currentType.BaseType;
}
while (currentType != entitySet.ResourceType);
// If one of the type in the hierarchy chain has navigation property, then
// all the sub-types need to be added to the entity set as disallowed types.
bool hasNavigationProperties = false;
while (hierarchyChain.Count != 0)
{
currentType = hierarchyChain.Dequeue();
if (hasNavigationProperties || currentType.HasNavigationProperties())
{
entitySet.AddDisallowedDerivedType(currentType);
hasNavigationProperties = true;
}
}
}
}
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- SqlAliasesReferenced.cs
- AdapterUtil.cs
- ReferenceConverter.cs
- CommunicationObjectManager.cs
- RequestCachePolicyConverter.cs
- FactoryGenerator.cs
- MetabaseServerConfig.cs
- OleStrCAMarshaler.cs
- TlsnegoTokenAuthenticator.cs
- PDBReader.cs
- DecimalAnimationUsingKeyFrames.cs
- FilterQuery.cs
- DbConnectionPoolGroupProviderInfo.cs
- WebSysDescriptionAttribute.cs
- TextBlockAutomationPeer.cs
- TraceContextEventArgs.cs
- DebugView.cs
- TextRangeSerialization.cs
- IntegerValidator.cs
- SessionStateContainer.cs
- GenericWebPart.cs
- SQLRoleProvider.cs
- SequenceRange.cs
- ToolStripSystemRenderer.cs
- InfoCardKeyedHashAlgorithm.cs
- DBParameter.cs
- SimpleWorkerRequest.cs
- PolyBezierSegmentFigureLogic.cs
- CompressStream.cs
- CacheRequest.cs
- UserPersonalizationStateInfo.cs
- ObjectIDGenerator.cs
- METAHEADER.cs
- HandlerBase.cs
- DataSysAttribute.cs
- TextServicesLoader.cs
- TreeBuilderXamlTranslator.cs
- DataConnectionHelper.cs
- mongolianshape.cs
- AvTraceFormat.cs
- Span.cs
- CircleHotSpot.cs
- ExtensionFile.cs
- NativeWindow.cs
- RootBuilder.cs
- PropertyTabAttribute.cs
- PasswordRecoveryAutoFormat.cs
- ApplyTemplatesAction.cs
- SchemaImporter.cs
- RedistVersionInfo.cs
- ListContractAdapter.cs
- LocatorPartList.cs
- AmbientProperties.cs
- ColumnHeaderConverter.cs
- UnknownBitmapDecoder.cs
- XmlResolver.cs
- FontFamily.cs
- XMLSchema.cs
- UnsupportedPolicyOptionsException.cs
- ContentPropertyAttribute.cs
- WorkflowOperationBehavior.cs
- EventRecord.cs
- StorageComplexTypeMapping.cs
- TypeTypeConverter.cs
- HostVisual.cs
- oledbconnectionstring.cs
- PropertyGridEditorPart.cs
- XPathException.cs
- BindableAttribute.cs
- DataBindingCollection.cs
- AlgoModule.cs
- NonValidatingSecurityTokenAuthenticator.cs
- Variant.cs
- RemoteWebConfigurationHostServer.cs
- WeakReferenceList.cs
- NextPreviousPagerField.cs
- MessageSmuggler.cs
- DbProviderConfigurationHandler.cs
- DataGridViewHitTestInfo.cs
- MSAAEventDispatcher.cs
- MultiTouchSystemGestureLogic.cs
- JpegBitmapDecoder.cs
- Bidi.cs
- FixUpCollection.cs
- WebEvents.cs
- WinFormsSpinner.cs
- ResourceDisplayNameAttribute.cs
- DataGridTextBox.cs
- MdImport.cs
- WebResourceAttribute.cs
- ConnectorSelectionGlyph.cs
- IgnorePropertiesAttribute.cs
- HWStack.cs
- LocalIdKeyIdentifierClause.cs
- DoubleUtil.cs
- SerializeAbsoluteContext.cs
- XmlRawWriter.cs
- VerbConverter.cs
- ConnectionStringSettings.cs
- ComponentEditorForm.cs