ReflectionServiceProvider.cs source code in C# .NET
Source code for the .NET framework in C#
Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / DataWeb / Server / System / Data / Services / Providers / ReflectionServiceProvider.cs / 1305376 / ReflectionServiceProvider.cs
//----------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Provides the interface definition for web data service
// data sources.
//
//
// @owner [....]
//---------------------------------------------------------------------
namespace System.Data.Services.Providers
{
#region Namespaces.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.Services.Caching;
using System.Data.Services.Common;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
#endregion Namespaces.
///
/// Provides a reflection-based provider implementation.
///
[DebuggerDisplay("ReflectionServiceProvider: {Type}")]
internal class ReflectionServiceProvider : BaseServiceProvider
{
///
/// Initializes a new System.Data.Services.ReflectionServiceProvider instance.
///
/// Metadata for this provider.
/// data service instance.
internal ReflectionServiceProvider(MetadataCacheItem metadata, object dataServiceInstance)
: base(metadata, dataServiceInstance)
{
}
/// Gets a value indicating whether null propagation is required in expression trees.
public override bool IsNullPropagationRequired
{
get { return true; }
}
/// Namespace name for the EDM container.
public override string ContainerNamespace
{
get { return this.Type.Namespace; }
}
/// Name of the EDM container
public override string ContainerName
{
get { return this.Type.Name; }
}
///
/// 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 override ResourceAssociationSet GetResourceAssociationSet(ResourceSet resourceSet, ResourceType resourceType, ResourceProperty resourceProperty)
{
Debug.Assert(resourceSet != null, "resourceSet != null");
Debug.Assert(resourceType != null, "resourceType != null");
Debug.Assert(resourceProperty != null, "resourceProperty != null");
Debug.Assert(resourceType == DataServiceProviderWrapper.GetDeclaringTypeForProperty(resourceType, resourceProperty), "resourceType should be the declaring type for resourceProperty");
ResourceType targetType = resourceProperty.ResourceType;
Debug.Assert(targetType != null && targetType.ResourceTypeKind == ResourceTypeKind.EntityType, "targetType != null && targetType.ResourceTypeKind == ResourceTypeKind.EntityType");
ResourceSet targetSet = InternalGetContainerForResourceType(targetType.InstanceType, this.EntitySets.Values);
Debug.Assert(targetSet != null, "targetSet != null");
string associationName = resourceType.Name + '_' + resourceProperty.Name;
// EF associations are first-class, navigation properties come second. So you actually
// define the two-way association first, then say that the nav props "go through" them.
// For CLR, however, there is no such constraint - in fact, we're very happy with one-way (link) associations.
// For one-way associations, the target property is always null.
ResourceAssociationSetEnd sourceEnd = new ResourceAssociationSetEnd(resourceSet, resourceType, resourceProperty);
ResourceAssociationSetEnd targetEnd = new ResourceAssociationSetEnd(targetSet, targetType, null);
return new ResourceAssociationSet(associationName, sourceEnd, targetEnd);
}
///
/// 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 override bool GetTypeIsOrdered(Type type)
{
Debug.Assert(type != null, "type != null");
if (typeof(IComparable).IsAssignableFrom(type))
{
return true;
}
else
{
return base.GetTypeIsOrdered(type);
}
}
/// Applies expansions and projections to the specified .
/// object to expand and apply projections to.
/// The root node of the tree which describes
/// the projections and expansions to be applied to the .
///
/// An object, with the results including
/// the expansions and projections specified in .
///
///
/// 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. If paging is
/// requested by providing a non-empty list in .OrderingInfo then
/// it is expected that the topmost would have a $skiptoken property
/// which will be an in itself and each of it's sub-properties will
/// be named SkipTokenPropertyXX where XX represents numbers in increasing order starting from 0. Each of
/// SkipTokenPropertyXX properties will be used to generated the $skiptoken to support paging.
/// If projections are required, the provider may choose to return
/// which returns instances of . In that case property values are determined
/// by calling the method instead of
/// accessing properties of the returned object directly.
/// If both expansion and projections are required, the provider may choose to return
/// of which in turn returns from its
/// property.
///
public override IQueryable ApplyProjections(
IQueryable source,
ProjectionNode projection)
{
Debug.Assert(projection is RootProjectionNode, "We always get the special root node.");
RootProjectionNode rootNode = (RootProjectionNode)projection;
Debug.Assert(rootNode.OrderingInfo != null, "We always get non-null OrderingInfo");
bool useBasicExpandProvider = ShouldUseBasicExpandProvider(rootNode);
// We need the $skiptoken for top level result if it is paged, hence we need to use ApplyExpansions in that case
if (useBasicExpandProvider || rootNode.OrderingInfo.IsPaged || rootNode.ProjectionsSpecified)
{
return new BasicExpandProvider(this.ProviderWrapper, true, true).ApplyProjections(source, projection);
}
// This pass-through implementation is appropriate for providers that fault-in on demand.
return BasicExpandProvider.ApplyOrderSkipTakeOnTopLevelResultBeforeProjections(
source,
rootNode.OrderingInfo,
rootNode.SkipCount,
rootNode.TakeCount);
}
#region IDataServiceQueryProvider Methods
///
/// Returns the collection of open properties name and value for the given resource instance.
///
/// instance of the resource.
/// Returns the collection of open properties name and value for the given resource instance. Currently not supported for Reflection provider.
public override IEnumerable> GetOpenPropertyValues(object target)
{
throw new NotImplementedException();
}
///
/// Gets the value of the open property.
///
/// instance of the resource type.
/// name of the property.
/// the value of the open property. Currently this is not supported for Reflection provider.
public override object GetOpenPropertyValue(object target, string propertyName)
{
throw new NotImplementedException();
}
#endregion IDataServiceQueryProvider Methods
/// Checks whether the given property is a key property.
/// property to check
/// returns the key kind of the property, based on the heuristic it matches
/// true if this is a key property, else returns false
internal static bool IsPropertyKeyProperty(PropertyInfo property, out ResourceKeyKind keyKind)
{
keyKind = (ResourceKeyKind)(-1);
// Only primitive types are allowed to be keys.
// Checks for generic to exclude Nullable<> value-type primitives, since we don't allows keys to be null.
if (WebUtil.IsPrimitiveType(property.PropertyType) &&
!property.PropertyType.IsGenericType)
{
DataServiceKeyAttribute keyAttribute = property.ReflectedType.GetCustomAttributes(true).OfType().FirstOrDefault();
if (keyAttribute != null && keyAttribute.KeyNames.Contains(property.Name))
{
keyKind = ResourceKeyKind.AttributedKey;
return true;
}
// For now, the key property must be {TypeName}Id or Id and the property
// type must be primitive, since we do not support non-primitive types
// as keys
if (property.Name == property.DeclaringType.Name + "ID")
{
keyKind = ResourceKeyKind.TypeNameId;
return true;
}
else if (property.Name == "ID")
{
keyKind = ResourceKeyKind.Id;
return true;
}
}
return false;
}
///
/// Checks whether the provider implements IUpdatable.
///
/// returns true if the provider implements IUpdatable. otherwise returns false.
internal override bool ImplementsIUpdatable()
{
return typeof(IUpdatable).IsAssignableFrom(this.Type);
}
/// Populates the metadata for this provider.
/// Dictionary of known CLR to ResourceType entries, which is populated as metadata is built.
/// list of already known types and their immediate children
/// Dictionary of name to ResourceSet for entity sets, populated as metadata is built.
protected override void PopulateMetadata(
IDictionary knownTypes,
IDictionary> childTypes,
IDictionary entitySets)
{
Queue unvisitedTypes = new Queue();
// Get the list of properties to be ignored.
List propertiesToBeIgnored = new List(
IgnorePropertiesAttribute.GetProperties(this.Type, true /*inherit*/, WebUtil.PublicInstanceBindingFlags));
PropertyInfo[] properties = this.Type.GetProperties(WebUtil.PublicInstanceBindingFlags);
foreach (PropertyInfo property in properties)
{
if (!propertiesToBeIgnored.Contains(property.Name) && property.CanRead && property.GetIndexParameters().Length == 0)
{
Type elementType = BaseServiceProvider.GetIQueryableElement(property.PropertyType);
if (elementType != null)
{
// If the element type has key defined (in itself or one of its ancestors)
ResourceType resourceType = BuildHierarchyForEntityType(elementType, knownTypes, childTypes, unvisitedTypes, true /* entity type candidate */);
if (resourceType != null)
{
// We do not allow MEST scenario for reflection provider
foreach (KeyValuePair entitySetInfo in entitySets)
{
Type entitySetType = entitySetInfo.Value.ResourceType.InstanceType;
if (entitySetType.IsAssignableFrom(elementType))
{
throw new InvalidOperationException(Strings.ReflectionProvider_MultipleEntitySetsForSameType(entitySetInfo.Value.Name, property.Name, entitySetType.FullName, resourceType.FullName));
}
else if (elementType.IsAssignableFrom(entitySetType))
{
throw new InvalidOperationException(Strings.ReflectionProvider_MultipleEntitySetsForSameType(property.Name, entitySetInfo.Value.Name, resourceType.FullName, entitySetType.FullName));
}
}
// Add the entity set to the list of entity sets.
ResourceSet resourceContainer = new ResourceSet(property.Name, resourceType);
entitySets.Add(property.Name, resourceContainer);
}
else
{
throw new InvalidOperationException(Strings.ReflectionProvider_InvalidEntitySetProperty(property.Name, XmlConvert.EncodeName(((IDataServiceMetadataProvider)this).ContainerName)));
}
}
}
}
// Populate the metadata for all the types in unvisited types
// and also their properties and populates metadata about property types
PopulateMetadataForTypes(knownTypes, childTypes, unvisitedTypes, entitySets.Values);
// At this point, we should have all the top level entity types and the complex types
PopulateMetadataForDerivedTypes(knownTypes, childTypes, unvisitedTypes, entitySets.Values);
// Populate and initialize the EntityPropertyMappingInfos for the data context
foreach (ResourceType resourceType in knownTypes.Values)
{
resourceType.BuildReflectionEpmInfo(resourceType);
resourceType.EpmInfoInitialized = true;
}
}
///
/// Creates the IQueryable instance for the given resource set and returns it
///
/// resource set for which IQueryable instance needs to be created
/// returns the IQueryable instance for the given resource set
protected override IQueryable GetResourceContainerInstance(ResourceSet resourceContainer)
{
Debug.Assert(resourceContainer != null, "resourceContainer != null");
if (resourceContainer.ReadFromContextDelegate == null)
{
PropertyInfo propertyInfo = this.Type.GetProperty(resourceContainer.Name, WebUtil.PublicInstanceBindingFlags);
MethodInfo getValueMethod = propertyInfo.GetGetMethod();
// return ((TheContext)arg0).get_Property();
Type[] parameterTypes = new Type[] { typeof(object) };
System.Reflection.Emit.DynamicMethod readerMethod = new System.Reflection.Emit.DynamicMethod("queryable_reader", typeof(IQueryable), parameterTypes, false);
var generator = readerMethod.GetILGenerator();
generator.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
generator.Emit(System.Reflection.Emit.OpCodes.Castclass, this.Type);
generator.Emit(System.Reflection.Emit.OpCodes.Call, getValueMethod);
generator.Emit(System.Reflection.Emit.OpCodes.Ret);
resourceContainer.ReadFromContextDelegate = (Func