Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataWeb / Server / System / Data / Services / Providers / ObjectContextServiceProvider.cs / 1 / ObjectContextServiceProvider.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;
using System.Data.EntityClient;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Data.Services.Caching;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
#endregion Namespaces.
///
/// Provides a reflection-based IDataServiceProvider implementation.
///
[DebuggerDisplay("ObjectContextServiceProvider: {Type}")]
internal class ObjectContextServiceProvider : BaseServiceProvider
{
#region Private fields.
///
/// List of objects that we need to be replaced. The key value
/// indicates the new instance of the type with whatever values
/// are specified in the payload. The value is the actual object
/// which is attached to the object context
///
private Dictionary objectsToBeReplaced = new Dictionary();
///
/// List of cspace types for which ospace metadata couldn't be found.
///
private List typesWithoutOSpaceMetadata;
#endregion Private fields.
///
/// Initializes a new System.Data.Services.ReflectionServiceProvider instance.
///
/// Metadata for this provider.
/// instance of the data source provider.
internal ObjectContextServiceProvider(MetadataCacheItem metadata, object dataSourceInstance)
: base(metadata, dataSourceInstance)
{
this.typesWithoutOSpaceMetadata = new List();
}
/// Gets a value indicating whether null propagation is required in expression trees.
public override bool NullPropagationRequired
{
get { return false; }
}
///
/// Provides a name for the context in which all resource containers are.
///
public override string ResourceContextName
{
get
{
return this.ObjectContext.DefaultContainerName;
}
}
/// Strongly-types instance being reflected upon.
private ObjectContext ObjectContext
{
get
{
return (ObjectContext)this.CurrentDataSource;
}
}
///
/// Gets the container 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 override ResourceContainer GetContainer(string declaringTypeContainerName, Type declaringResourceType, ResourceProperty resourceProperty)
{
Debug.Assert(!String.IsNullOrEmpty(declaringTypeContainerName), "!String.IsNullOrEmpty(declaringTypeContainerName)");
Debug.Assert(declaringResourceType != null && resourceProperty != null, "declaringResourceType != null && resourceProperty != null");
EntitySet entitySet = this.GetEntitySet(declaringTypeContainerName);
Debug.Assert(entitySet != null, "entitySet != null -- GetEntitySet should never return null");
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(declaringResourceType);
EntityType entityType = this.ObjectContext.MetadataWorkspace.GetItem(resourceType.FullName, DataSpace.CSpace);
Debug.Assert(entityType != null, "entityType != null");
NavigationProperty navigationProperty;
entityType.NavigationProperties.TryGetValue(resourceProperty.Name, false /*ignoreCase*/, out navigationProperty);
Debug.Assert(navigationProperty != null, "navigationProperty != null");
ResourceContainer result = null;
foreach (EntitySetBase set in entitySet.EntityContainer.BaseEntitySets)
{
if (set.BuiltInTypeKind != BuiltInTypeKind.AssociationSet)
{
continue;
}
AssociationSet associationSet = (AssociationSet)set;
if (associationSet.ElementType == navigationProperty.RelationshipType)
{
AssociationSetEnd setEnd = associationSet.AssociationSetEnds[navigationProperty.FromEndMember.Name];
if (setEnd.EntitySet == entitySet)
{
entitySet = associationSet.AssociationSetEnds[navigationProperty.ToEndMember.Name].EntitySet;
string entitySetName = GetEntitySetName(entitySet.Name, entitySet.EntityContainer.Name, this.ObjectContext.DefaultContainerName == entitySet.EntityContainer.Name);
result = ((IDataServiceProvider)this).TryResolveContainerName(entitySetName);
break;
}
}
}
Debug.Assert(result != null, "result != null -- we should be able to find the entity set for the given navigation property");
return result;
}
/// 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 override ResourceContainer GetContainerForResourceType(Type resourceType)
{
throw Error.NotSupported();
}
///
/// Returns the metadata in edm format
///
/// Writer to which metadata XML should be written.
public override void GetMetadata(XmlWriter xmlWriter)
{
Debug.Assert(xmlWriter != null, "xmlWriter != null");
InitializeObjectItemCollection(this.ObjectContext, this.Type.Assembly);
var typeManager = new TypeManager(this.ObjectContext.MetadataWorkspace);
BaseServiceProvider.WriteTopLevelSchemaElements(xmlWriter);
bool entityContainerDefinitionWritten = false;
// Write all the types in their respective namespaces
foreach (KeyValuePair> typesInNamespace in typeManager.NamespaceAlongWithTypes)
{
BaseServiceProvider.WriteSchemaElement(xmlWriter, typesInNamespace.Key, this.CompatibleWithV1Schema);
// Write the entity container in the same namespace as that of the type
if (!entityContainerDefinitionWritten && typesInNamespace.Key == this.Type.Namespace)
{
this.WriteEntityContainers(xmlWriter, typeManager.GetEntityContainers(), this.GetDefaultEntityContainer());
entityContainerDefinitionWritten = true;
}
this.WriteEdmTypes(xmlWriter, typesInNamespace.Value);
xmlWriter.WriteEndElement();
}
// If the entity container is in a different namespace than the types, then write the entity container definition
// in a different namespace
if (!entityContainerDefinitionWritten)
{
BaseServiceProvider.WriteSchemaElement(xmlWriter, this.Type.Namespace, this.CompatibleWithV1Schema);
this.WriteEntityContainers(xmlWriter, typeManager.GetEntityContainers(), this.GetDefaultEntityContainer());
xmlWriter.WriteEndElement();
}
// These end elements balance the elements written out in WriteTopLevelSchemaElements
xmlWriter.WriteEndElement();
xmlWriter.WriteEndElement();
xmlWriter.Flush();
}
///
/// Get the list of etag property names given the entity set name and the instance of the resource
///
/// name of the entity set
/// clr type of the resource whose etag properties need to be fetched
/// list of etag property names
public override ICollection GetETagProperties(string containerName, Type resourceClrType)
{
Debug.Assert(!String.IsNullOrEmpty(containerName), "container name must not be empty");
Debug.Assert(resourceClrType != null, "clrType cannot be null");
EntitySetBase entitySet = this.GetEntitySet(containerName);
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(resourceClrType);
EntityType entityType = this.ObjectContext.MetadataWorkspace.GetItem(resourceType.FullName, DataSpace.CSpace);
Debug.Assert(entityType != null, "entityType != null");
List etagProperties = new List();
// Workspace associated directly with the ObjectContext has metadata only about OSpace, CSpace and OCSpace.
// Since GetRequiredOriginalValueMembers depends on mapping information (CSSpace metadata),
// we need to make sure we call this API on a workspace which has information about the CS Mapping.
// Hence getting workspace from the underlying Entity connection.
MetadataWorkspace workspace = ((EntityConnection)this.ObjectContext.Connection).GetMetadataWorkspace();
foreach (EdmMember member in workspace.GetRequiredOriginalValueMembers(entitySet, entityType))
{
ResourceProperty property = resourceType.TryResolvePropertyName(member.Name);
Debug.Assert(property != null, "property != null");
Debug.Assert(property.TypeKind == ResourceTypeKind.Primitive, "property.TypeKind == ResourceTypeKind.Primitive");
// Ignore key properties if they are part of etag, since the uri already has the key information
// and it makes no sense to duplicate them in etag
if (!property.IsOfKind(ResourcePropertyKind.Key))
{
etagProperties.Add(property);
}
}
return etagProperties;
}
#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 override object CreateResource(string containerName, string fullTypeName)
{
ResourceType resourceType = ((IDataServiceProvider)this).TryResolveTypeName(fullTypeName);
Debug.Assert(resourceType != null, "resourceType != null");
if (resourceType.Type.IsAbstract)
{
throw DataServiceException.CreateBadRequestError(Strings.CannotCreateInstancesOfAbstractType(resourceType.FullName));
}
object resource = resourceType.ConstructorDelegate();
if (containerName != null)
{
this.ObjectContext.AddObject(containerName, resource);
}
return resource;
}
///
/// 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 override object GetResource(IQueryable query, string fullTypeName)
{
Debug.Assert(query != null, "query != null");
ObjectQuery objectQuery = query as ObjectQuery;
Debug.Assert(objectQuery != null, "objectQuery != null - otherwise we're passed an IQueryable we didn't produce");
objectQuery.MergeOption = MergeOption.AppendOnly;
object result = null;
foreach (object resource in objectQuery)
{
if (result != null)
{
throw new InvalidOperationException(Strings.SingleResourceExpected);
}
result = resource;
}
if (result != null && fullTypeName != null)
{
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(result.GetType());
Debug.Assert(resourceType != null, "the result must return a known type");
if (resourceType.FullName != fullTypeName)
{
throw DataServiceException.CreateBadRequestError(Strings.TargetElementTypeOfTheUriSpecifiedDoesNotMatchWithTheExpectedType(resourceType.FullName, fullTypeName));
}
}
return result;
}
///
/// 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 override object ResetResource(object resource)
{
Debug.Assert(resource != null, "resource != null");
Type resourceClrType = resource.GetType();
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(resourceClrType);
if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
{
// For entity types, do the following:
// create a new instance of the same type and set the key values on it
// detach the given instance and attach the new instance
// mark all the properties as modified on the new instance so that the default values get persisted
object newInstance = resourceType.ConstructorDelegate();
// set the key value on the new instance
foreach (ResourceProperty property in resourceType.KeyProperties)
{
object propertyValue = property.GetValue(resource);
property.SetValue(newInstance, propertyValue);
}
this.objectsToBeReplaced.Add(newInstance, resource);
return newInstance;
}
else if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
{
// For complex types, just return a brand new instance.
return resourceType.ConstructorDelegate();
}
return 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 override void SetValue(object targetResource, string propertyName, object propertyValue)
{
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(targetResource.GetType());
Debug.Assert(resourceType != null, "resourceType != null");
ResourceProperty resourceProperty = resourceType.TryResolvePropertyName(propertyName);
Debug.Assert(resourceProperty != null, "resourceProperty != null");
resourceProperty.SetValue(targetResource, 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 override object GetValue(object targetResource, string propertyName)
{
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(targetResource.GetType());
Debug.Assert(resourceType != null, "resourceType != null");
ResourceProperty resourceProperty = resourceType.TryResolvePropertyName(propertyName);
Debug.Assert(resourceProperty != null, "resourceProperty != null");
object resource = resourceProperty.GetValue(targetResource);
return resource;
}
///
/// 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 override void SetReference(object targetResource, string propertyName, object propertyValue)
{
// Set the value of the reference property
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(targetResource.GetType());
Debug.Assert(resourceType != null, "resourceType != null");
ResourceProperty resourceProperty = resourceType.TryResolvePropertyName(propertyName);
Debug.Assert(resourceProperty != null, "resourceProperty != null");
resourceProperty.SetValue(targetResource, 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 override void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
{
Debug.Assert(targetResource != null, "propertyValue != null");
Debug.Assert(!String.IsNullOrEmpty(propertyName), "!String.IsNullOrEmpty(propertyName)");
Debug.Assert(resourceToBeAdded != null, "propertyValue != null");
var entityToBeAdded = (System.Data.Objects.DataClasses.IEntityWithRelationships)resourceToBeAdded;
ObjectStateEntry stateEntry = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(targetResource);
try
{
// Get the EntityCollection value
object collection = targetResource.GetType().InvokeMember(
propertyName,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty,
null,
targetResource,
new object[0],
CultureInfo.InvariantCulture);
Debug.Assert(collection != null, "collection != null");
var end = (System.Data.Objects.DataClasses.IRelatedEnd)collection;
if (stateEntry.State == EntityState.Added || stateEntry.State == EntityState.Unchanged)
{
end.Add(entityToBeAdded);
}
else
{
end.Attach(entityToBeAdded);
}
}
catch (TargetInvocationException e)
{
ErrorHandler.HandleTargetInvocationException(e);
throw;
}
}
/// 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 override IEnumerable ApplyExpansions(IQueryable queryable, ICollection expandPaths)
{
IExpandProvider provider = this.CurrentDataSource as IExpandProvider;
if (provider != null)
{
return provider.ApplyExpansions(queryable, expandPaths);
}
else
{
// Start by adding all the containers for the root type, in case the query originates from
// a service operation and we don't know what the origin is.
HashSet containers = new HashSet(EqualityComparer.Default);
foreach (var set in this.EntitySets)
{
if (set.Value.ElementType.IsAssignableFrom(queryable.ElementType))
{
containers.Add(set.Value);
}
}
bool useBasicExpandProvider = false;
foreach (ExpandSegmentCollection path in expandPaths)
{
if (!useBasicExpandProvider)
{
if (path.Any(segment => segment.HasFilterOrMaxResults))
{
useBasicExpandProvider = true;
}
else
{
// If we expand properties that come from a container we've already encountered,
// we should use the basic provider; otherwise the EF materializer will throw because
// tracking is turned off for querying.
foreach (ExpandSegment segment in path)
{
Debug.Assert(
segment.Container != null,
"segment.Container != null -- otherwise we have an open property in EF or forgot to bind a segment");
if (!containers.Add(segment.Container))
{
useBasicExpandProvider = true;
break;
}
}
}
}
if (path.Count > 8)
{
throw DataServiceException.CreateBadRequestError(Strings.ObjectContext_ExpandTooDeep);
}
}
if (useBasicExpandProvider)
{
return new BasicExpandProvider(this, false).ApplyExpansions(queryable, expandPaths);
}
MethodInfo includeMethod = typeof(ObjectContextServiceProvider).GetMethod("Include", BindingFlags.Static | BindingFlags.NonPublic);
includeMethod = includeMethod.MakeGenericMethod(queryable.ElementType);
IQueryable result = queryable;
foreach (ExpandSegmentCollection path in expandPaths)
{
string dottedPath = JoinIdentifiers(path);
result = (IQueryable)includeMethod.Invoke(null, new object[] { result, dottedPath });
}
return result;
}
}
///
/// 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 override void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
{
Debug.Assert(targetResource != null, "targetResource != null");
Debug.Assert(!String.IsNullOrEmpty(propertyName), "!String.IsNullOrEmpty(propertyName)");
Debug.Assert(resourceToBeRemoved != null, "resourceToBeRemoved != null");
try
{
// Get the EntityCollection value
object collection = targetResource.GetType().InvokeMember(
propertyName,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty,
null,
targetResource,
new object[0],
CultureInfo.InvariantCulture);
Debug.Assert(collection != null, "collection != null");
// For many to many relationships, we need to attach the other end first, otherwise
// the collection is null and hence removing it won't have any effect.
collection.GetType().InvokeMember(
"Attach",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod,
null,
collection,
new object[] { resourceToBeRemoved },
CultureInfo.InvariantCulture);
collection.GetType().InvokeMember(
"Remove",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod,
null,
collection,
new object[] { resourceToBeRemoved },
CultureInfo.InvariantCulture);
}
catch (TargetInvocationException exception)
{
ErrorHandler.HandleTargetInvocationException(exception);
throw;
}
}
///
/// Delete the given resource
///
/// resource that needs to be deleted
public override void DeleteResource(object resource)
{
this.ObjectContext.DeleteObject(resource);
}
///
/// Saves all the pending changes made till now
///
public override void SaveChanges()
{
// handle the resource which need to be replaced.
foreach (KeyValuePair objectToBeReplaced in this.objectsToBeReplaced)
{
// Get the key from the actual object
EntityKey entityKey = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(objectToBeReplaced.Value).EntityKey;
// Apply property changes as specified in the new object.
this.ObjectContext.ApplyPropertyChanges(entityKey.EntitySetName, objectToBeReplaced.Key);
}
// clear all these once we have processed all the entities that need to be replaced.
this.objectsToBeReplaced.Clear();
// Save Changes
this.ObjectContext.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 override object ResolveResource(object resource)
{
Debug.Assert(resource != null, "resource != null");
return resource;
}
///
/// Revert all the pending changes.
///
public override void ClearChanges()
{
// Detach all the existing entries in the object context
foreach (ObjectStateEntry entry in this.ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged))
{
// entry.State != Entry.Detached: Also, if they are stub entries (no entity object
// associated with the entry yet - these get automatically populated when we query the related entity),
// they also get automatically detached once we detach the associated entity).
// Both Entity and IsRelationship property throws if the entry is detached.
// !entry.IsRelationship: We just need to remove the key entries.
// While detaching the key entries, the relationship entries associated
// with the key entries also get detached.
// entry.Entity != null: Since they are no ordering gaurantees, the stub key entries
// can come first, we need to skip these.
if (entry.State != EntityState.Detached && !entry.IsRelationship && entry.Entity != null)
{
this.ObjectContext.Detach(entry.Entity);
}
}
// clear the list of objects that need to be special handled during save changes for replace semantics
this.objectsToBeReplaced.Clear();
}
#endregion
#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 override IEnumerable EnumerateAccessPathAttributes()
{
// To synthesize AccessPathAttribute instances, we look for Association types with the
// ReferentialConstraint property set. Additionally we have attributes on EntitySet
// to determine whether top-level visibility is enabled, and on NavigationProperty to
// determine whether an access path is canonical or non-canonical.
Debug.Assert(this.ObjectContext != null, "this.ObjectContext != null");
MetadataWorkspace workspace = this.ObjectContext.MetadataWorkspace;
foreach (EntityContainer container in workspace.GetItems(DataSpace.CSpace))
{
foreach (AssociationSet association in container.BaseEntitySets.OfType())
{
AssociationType type = association.ElementType;
foreach (ReferentialConstraint constraint in type.ReferentialConstraints)
{
EntitySet fromSet = association.AssociationSetEnds[constraint.ToRole.Name].EntitySet;
NavigationProperty fromNavigationProperty = PropertyForEnd(fromSet, constraint.ToRole);
if (fromNavigationProperty == null)
{
// If there is no navigation property, no traversal is possible - simply ignore.
continue;
}
bool canonical = GetNavigationPropertyCanonical(fromNavigationProperty);
EntitySet targetSet = association.AssociationSetEnds[constraint.FromRole.Name].EntitySet;
bool topLevelAccess = GetEntitySetTopLevelAccess(targetSet);
AccessPathAttribute attribute;
if (canonical)
{
CanonicalAccessPathAttribute ca = new CanonicalAccessPathAttribute();
ca.TopLevelAccess = topLevelAccess;
attribute = ca;
}
else
{
attribute = new AccessPathAttribute();
}
attribute.AnnotatedContainer = this.EntitySets[targetSet.Name];
attribute.InternalChildKeyMapping = PropertyNamesToStrings(constraint.FromProperties);
attribute.InferredFromSchema = true;
attribute.Parent = fromSet.Name;
attribute.InternalParentKeyMapping = PropertyNamesToStrings(constraint.ToProperties);
attribute.ParentNavigationProperty = fromNavigationProperty.Name;
yield return attribute;
}
}
}
}
#endif
/// 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 override void CheckConfigurationConsistency(object dataSourceInstance)
{
base.CheckConfigurationConsistency(dataSourceInstance);
// Check that rights are consistent in MEST scenarios.
//
// Strictly we should only check for consistent visibility
// for all entity sets of a given type, however the current
// metadata design doesn't differentiate between resource
// container types and resource container instances on
// associations, and therefore all rights are checked at
// the resource type level, which forces this check to have
// consistent rights.
//
// The only exception could be references which are not connected
// (technically those that are not targets, but in EDM all
// associations are two-way). These can have entity sets
// with different rights, enforced at the container level.
// Discover connected types.
HashSet connectedTypes = new HashSet(EqualityComparer.Default);
foreach (ResourceType type in this.Types)
{
foreach (ResourceProperty property in type.PropertiesDeclaredInThisType)
{
if (property.TypeKind == ResourceTypeKind.EntityType)
{
connectedTypes.Add(property.ResourceType);
}
}
}
// Discover containers of same type with conflicting rights.
Dictionary typeRights = new Dictionary();
foreach (KeyValuePair containerEntry in this.EntitySets)
{
Debug.Assert(containerEntry.Key != null, "containerEntry.Key != null");
Debug.Assert(containerEntry.Value != null, "containerEntry.Value != null");
ResourceType resourceType = containerEntry.Value.ResourceType;
// Disregard types that are not connected to any other types.
if (!connectedTypes.Contains(resourceType))
{
continue;
}
ResourceContainer previouslyFoundContainer;
if (typeRights.TryGetValue(resourceType, out previouslyFoundContainer))
{
if (containerEntry.Value.Rights != previouslyFoundContainer.Rights)
{
throw new InvalidOperationException(Strings.ObjectContext_DifferentContainerRights(
previouslyFoundContainer.Name,
previouslyFoundContainer.Rights,
containerEntry.Value.Name,
containerEntry.Value.Rights));
}
}
else
{
typeRights.Add(resourceType, containerEntry.Value);
}
}
CheckNavigationPropertiesBound(dataSourceInstance);
}
///
/// Populates metadata from the given object context
///
/// dictionary of already known types
/// list of already known entity sets
protected override void PopulateMetadata(
IDictionary knownTypes, IDictionary entitySets)
{
Debug.Assert(knownTypes != null, "knownTypes != null");
Debug.Assert(entitySets != null, "entitySets != null");
Debug.Assert(this.ObjectContext != null, "this.ObjectContext != null");
InitializeObjectItemCollection(this.ObjectContext, this.Type.Assembly);
MetadataWorkspace metadataWorkspace = this.ObjectContext.MetadataWorkspace;
// Create Resource types for all the top level entity types and complexTypes
foreach (StructuralType edmType in metadataWorkspace.GetItems(DataSpace.CSpace))
{
if (edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType ||
edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType)
{
// Populates metadata for the given types and all its base types
if (PopulateTypeMetadata(metadataWorkspace, edmType, knownTypes) == null)
{
this.typesWithoutOSpaceMetadata.Add(edmType);
}
}
}
foreach (EntityContainer entityContainer in metadataWorkspace.GetItems(DataSpace.CSpace))
{
bool defaultEntityContainer = entityContainer.Name == this.ObjectContext.DefaultContainerName;
// Get the list of entity sets (Ignore the relationship sets, since we won't allow that to be queried directly
foreach (EntitySetBase entitySetBase in entityContainer.BaseEntitySets)
{
// Ignore all the association sets for the type being, since we are caching only entity sets
if (entitySetBase.BuiltInTypeKind != BuiltInTypeKind.EntitySet)
{
continue;
}
EntitySet entitySet = (EntitySet)entitySetBase;
Type elementType = GetClrTypeForCSpaceType(metadataWorkspace, entitySet.ElementType);
ResourceType resourceType = knownTypes[elementType];
string entitySetName = GetEntitySetName(entitySet.Name, entitySet.EntityContainer.Name, defaultEntityContainer);
ResourceContainer resourceContainer = new ResourceContainer(entitySetName, resourceType);
entitySets.Add(entitySetName, resourceContainer);
}
}
// Now go and populate the member information for each resource type
foreach (ResourceType resourceType in knownTypes.Values)
{
if (resourceType.ResourceTypeKind == ResourceTypeKind.Primitive)
{
continue;
}
PopulateMemberMetadata(resourceType, metadataWorkspace, entitySets, knownTypes);
}
}
///
/// 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 override IQueryable GetResourceContainerInstance(ResourceContainer resourceContainer)
{
Debug.Assert(resourceContainer != null, "resourceContainer != null");
ObjectQuery result = this.InternalGetResourceContainerInstance(resourceContainer);
result.MergeOption = MergeOption.NoTracking;
return result;
}
///
/// 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 override void PopulateMetadataForUserSpecifiedTypes(
IEnumerable userSpecifiedTypes, IDictionary knownTypes, IEnumerable entitySets)
{
foreach (Type type in userSpecifiedTypes)
{
if (this.PopulateMetadataForType(type, knownTypes, entitySets) == null)
{
throw new InvalidOperationException(Strings.BadProvider_InvalidTypeSpecified(type.FullName));
}
}
// If there is a type in the model, for which we couldn't load the metadata, we should throw.
if (this.typesWithoutOSpaceMetadata.Count != 0)
{
throw new InvalidOperationException(Strings.ObjectContext_UnableToLoadMetadataForType(this.typesWithoutOSpaceMetadata[0].FullName));
}
this.typesWithoutOSpaceMetadata = null;
}
///
/// 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 override ResourceType PopulateMetadataForType(
Type type,
IDictionary knownTypes,
IEnumerable entitySets)
{
ResourceType resourceType;
if (!knownTypes.TryGetValue(type, out resourceType))
{
InitializeObjectItemCollection(this.ObjectContext, type.Assembly);
ObjectItemCollection objectItemCollection = (ObjectItemCollection)this.ObjectContext.MetadataWorkspace.GetItemCollection(DataSpace.OSpace);
StructuralType ospaceType, cspaceType;
if (objectItemCollection.TryGetItem(type.FullName, out ospaceType))
{
if (this.ObjectContext.MetadataWorkspace.TryGetEdmSpaceType(ospaceType, out cspaceType))
{
ResourceType baseType = null;
if (cspaceType.BaseType != null)
{
baseType = this.PopulateMetadataForType(type.BaseType, knownTypes, entitySets);
}
resourceType = CreateResourceType(cspaceType, type, baseType, knownTypes);
this.typesWithoutOSpaceMetadata.Remove(cspaceType);
}
}
}
return resourceType;
}
///
/// Checks that all navigation properties are bound to some association set for every entity set.
///
/// Instance of the data source for the provider.
private static void CheckNavigationPropertiesBound(object dataSourceInstance)
{
// For every navigation property, ensure that all of the EntitySets that can
// take their EntityType have an AssociationSet of the appropriate Association type.
Debug.Assert(dataSourceInstance != null, "dataSourceInstance != null");
MetadataWorkspace workspace = ((ObjectContext)dataSourceInstance).MetadataWorkspace;
foreach (EntityType type in workspace.GetItems(DataSpace.CSpace))
{
foreach (NavigationProperty navigationProperty in type.NavigationProperties)
{
foreach (EntitySet entitySet in GetEntitySetsForType(workspace, type))
{
IEnumerable entitySetsWithAssocation = GetEntitySetsWithAssociationSets(
workspace,
navigationProperty.RelationshipType,
navigationProperty.FromEndMember);
if (!entitySetsWithAssocation.Contains(entitySet))
{
throw new InvalidOperationException(Strings.ObjectContext_NavigationPropertyUnbound(
navigationProperty.Name,
type.FullName,
entitySet.Name));
}
}
}
}
}
/// Finds a target container for the specified .
/// CLR type to find.
/// Containers to examine.
/// The resolved ; never null.
private static ResourceContainer FindSampleTargetContainer(Type clrType, IEnumerable containers)
{
Debug.Assert(clrType != null, "clrType != null");
Debug.Assert(containers != null, "clrType != containers");
ResourceContainer result = null;
foreach (ResourceContainer container in containers)
{
if (container.ElementType.IsAssignableFrom(clrType))
{
result = container;
break;
}
}
Debug.Assert(result != null, "result != null - otherwise unable to find container for " + clrType);
return result;
}
/// Gets the CLR type mapped to the specified C-Space type.
/// Workspace in which the type is defined.
/// C-Space type whose matching clr type needs to be looked up.
/// The resolved for the given .
private static Type GetClrTypeForCSpaceType(MetadataWorkspace workspace, StructuralType edmType)
{
Debug.Assert(workspace != null, "workspace != null");
Debug.Assert(edmType != null, "edmType != null");
Debug.Assert(
edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType || edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType,
"Must be entityType or complexType");
StructuralType ospaceType;
if (workspace.TryGetObjectSpaceType(edmType, out ospaceType))
{
ObjectItemCollection objectItemCollection = (ObjectItemCollection)workspace.GetItemCollection(DataSpace.OSpace);
return objectItemCollection.GetClrType(ospaceType);
}
return null;
}
///
/// Gets all instance that may hold an entity of type .
///
/// Workspace with metadata.
/// Entity type to get entity sets for.
/// An enumeration of instances that can hold .
private static IEnumerable GetEntitySetsForType(MetadataWorkspace workspace, EntityType type)
{
Debug.Assert(type != null, "type != null");
Debug.Assert(workspace != null, "workspace != null");
foreach (EntityContainer container in workspace.GetItems(DataSpace.CSpace))
{
foreach (EntitySet entitySet in container.BaseEntitySets.OfType())
{
if (IsAssignableFrom(entitySet.ElementType, type))
{
yield return entitySet;
}
}
}
}
///
/// Gets all entity sets that participate as members for the specified .
///
/// Workspace with metadata.
/// Type of assocation to check.
/// Member of association to check.
///
/// All instances that are are on the role for
/// some association of .
///
private static IEnumerable GetEntitySetsWithAssociationSets(
MetadataWorkspace workspace,
RelationshipType associationType,
RelationshipEndMember member)
{
Debug.Assert(workspace != null, "workspace != null");
Debug.Assert(associationType != null, "associationType != null");
Debug.Assert(member != null, "member != null");
foreach (EntityContainer container in workspace.GetItems(DataSpace.CSpace))
{
foreach (AssociationSet associationSet in container.BaseEntitySets.OfType())
{
if (associationSet.ElementType == associationType)
{
foreach (AssociationSetEnd end in associationSet.AssociationSetEnds)
{
if (end.CorrespondingAssociationEndMember == member)
{
yield return end.EntitySet;
}
}
}
}
}
}
/// Reads the TopLevelAccess property from the specified .
/// Set to read attribute from.
/// true or false depending on the value; true if the value is missing.
private static bool GetEntitySetTopLevelAccess(EntitySet set)
{
Debug.Assert(set != null, "set != null");
const string TopLevelAttributeName = XmlConstants.DataWebMetadataNamespace + ":" + XmlConstants.DataWebAccessTopLevelAccessAttribute;
bool result = true;
MetadataProperty property;
if (set.MetadataProperties.TryGetValue(TopLevelAttributeName, false /* ignoreCase */, out property))
{
string text = (string)property.Value;
if (String.IsNullOrEmpty(text))
{
throw new InvalidOperationException(Strings.ObjectContext_TopLevelEmpty(set.Name));
}
if (!bool.TryParse(text, out result))
{
throw new InvalidOperationException(Strings.ObjectContext_TopLevelIncorrect(property.Name, text));
}
}
return result;
}
/// Gets the MIME type specified for the specified member.
/// C-Space member for which we need to find the C-Space mime type attribute.
/// The MIME type for the specified member, null if none deifned.
private static string GetMimeTypeForMappedMember(EdmMember csdlMember)
{
const string MimePropertyName = XmlConstants.DataWebMetadataNamespace + ":" + XmlConstants.DataWebMimeTypeAttributeName;
string mimeType = null;
MetadataProperty property;
if (csdlMember.MetadataProperties.TryGetValue(MimePropertyName, false /* ignoreCase */, out property))
{
mimeType = (string)property.Value;
if (mimeType != null && mimeType.Length == 0)
{
throw new InvalidOperationException(Strings.ObjectContext_MimeTypeAttributeEmpty(csdlMember.Name, csdlMember.DeclaringType.FullName));
}
if (!IsPrimitiveType(csdlMember.TypeUsage.EdmType))
{
throw new InvalidOperationException(Strings.ObjectContext_MimeTypeAttributeOnNonPrimitive(csdlMember.DeclaringType.FullName, csdlMember.Name, csdlMember.TypeUsage.EdmType.FullName));
}
}
return mimeType;
}
#if ASTORIA_CONTAINMENT
///
/// Reads the AccessPath canonical-ness property from the specified .
///
/// Property to read attribute from.
/// true or false depending on the value; false if the value is missing.
private static bool GetNavigationPropertyCanonical(NavigationProperty property)
{
Debug.Assert(property != null, "property != null");
const string AccessPathAttributeName = XmlConstants.DataWebMetadataNamespace + ":" + XmlConstants.DataWebAccessPathAttribute;
bool result = false;
MetadataProperty metadata;
if (property.MetadataProperties.TryGetValue(AccessPathAttributeName, false /* ignoreCase */, out metadata))
{
string text = (string)metadata.Value;
if (String.IsNullOrEmpty(text))
{
throw new InvalidOperationException(Strings.ObjectContext_AccessPathEmpty(property.Name));
}
if (text == XmlConstants.DataWebAccessPathCanonicalValue)
{
result = true;
}
else if (text == XmlConstants.DataWebAccessPathNonCanonicalValue)
{
result = false;
}
else
{
throw new InvalidOperationException(Strings.ObjectContext_AccessPathIncorrect(property.Name, text));
}
}
return result;
}
#endif
/// Generic method to invoke an Include method on an ObjectQuery source.
/// Element type of the source.
/// Source query.
/// Path to include.
/// A new query that includes in .
private static ObjectQuery Include(IQueryable query, string dottedPath)
{
Debug.Assert(query != null, "query != null");
Debug.Assert(dottedPath != null, "dottedPath != null");
ObjectQuery typedQuery = (ObjectQuery)query;
return typedQuery.Include(dottedPath);
}
/// Checks whether may be assigned to .
/// Type to check assignment to.
/// Type to check assignment from.
///
/// true if an instance of can be assigned to a variable of
/// ; false otherwise.
///
private static bool IsAssignableFrom(EntityType baseType, EntityType derivedType)
{
while (derivedType != null)
{
if (derivedType == baseType)
{
return true;
}
derivedType = (EntityType)derivedType.BaseType;
}
return false;
}
/// Checks whether the specified type is a known primitive type.
/// Type to check.
/// true if the specified type is known to be a primitive type; false otherwise.
private static bool IsPrimitiveType(EdmType type)
{
Debug.Assert(type != null, "type != null");
if (type.BuiltInTypeKind != BuiltInTypeKind.PrimitiveType)
{
return false;
}
else
{
Debug.Assert(
WebUtil.IsPrimitiveType(((PrimitiveType)type).ClrEquivalentType),
"WebUtil.IsPrimitiveType(((PrimitiveType)type).ClrEquivalentType) - all EDM primitive types are Astoria primitive types");
return true;
}
}
/// Joins the list of segment identifiers by dots.
/// List of segments to join.
/// A string with the identifiers joined by dots.
private static string JoinIdentifiers(List segments)
{
Debug.Assert(segments != null, "segments != null");
int capacity = 0;
foreach (ExpandSegment segment in segments)
{
capacity += segment.Name.Length;
}
capacity += segments.Count - 1;
StringBuilder builder = new StringBuilder(capacity);
foreach (ExpandSegment segment in segments)
{
if (builder.Length > 0)
{
builder.Append('.');
}
builder.Append(segment.Name);
}
return builder.ToString();
}
///
/// Populates the metadata for the given type and its base type
///
/// metadata workspace containing all the metadata information
/// type whose metadata needs to be populated
/// list of known types
/// returns the resource type corresponding to the given edmType
private static ResourceType PopulateTypeMetadata(
MetadataWorkspace workspace,
StructuralType edmType,
IDictionary knownTypes)
{
Debug.Assert(
edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType ||
edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType,
"type must be entity or complex type");
ResourceType resourceType = null;
Type clrType = GetClrTypeForCSpaceType(workspace, edmType);
if (clrType != null && !knownTypes.TryGetValue(clrType, out resourceType))
{
ResourceType baseType = null;
if (edmType.BaseType != null)
{
baseType = PopulateTypeMetadata(workspace, (StructuralType)edmType.BaseType, knownTypes);
}
resourceType = CreateResourceType(edmType, clrType, baseType, knownTypes);
}
return resourceType;
}
///
/// Creates a new instance of resource type given the cspace structural type and mapping clr type.
///
/// cspace structural type.
/// mapping clr type for the given structural type.
/// the base resource type for the given resource type.
/// list of already known resource types.
/// the new resource type instance created for the given cspace type.
private static ResourceType CreateResourceType(
StructuralType cspaceType,
Type clrType,
ResourceType baseResourceType,
IDictionary knownTypes)
{
ResourceTypeKind resourceTypeKind = cspaceType.BuiltInTypeKind == BuiltInTypeKind.EntityType ? ResourceTypeKind.EntityType : ResourceTypeKind.ComplexType;
ResourceType resourceType = new ResourceType(clrType, resourceTypeKind, baseResourceType, cspaceType.FullName, cspaceType.Name);
knownTypes.Add(clrType, resourceType);
return resourceType;
}
///
/// Populates the member metadata for the given type
///
/// resource type whose member metadata needs to be filled
/// workspace containing the metadata information
/// Available entity sets.
/// list of already known types
private static void PopulateMemberMetadata(
ResourceType resourceType,
MetadataWorkspace workspace,
IDictionary entitySets,
IDictionary knownTypes)
{
Debug.Assert(resourceType != null, "resourceType != null");
Debug.Assert(workspace != null, "workspace != null");
Debug.Assert(entitySets != null, "entitySets != null");
// Find the type from the OSpace
StructuralType edmType = workspace.GetItem(resourceType.FullName, DataSpace.CSpace);
foreach (EdmMember member in edmType.Members)
{
// only look at the instance members
if (member.DeclaringType != edmType)
{
continue;
}
ResourceContainer resourceContainer = null;
ResourcePropertyKind kind = (ResourcePropertyKind)(-1);
PropertyInfo propertyInfo = resourceType.Type.GetProperty(member.Name);
ResourceType propertyType = null;
switch (member.TypeUsage.EdmType.BuiltInTypeKind)
{
case BuiltInTypeKind.PrimitiveType:
propertyType = knownTypes[Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType];
if (edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType &&
((EntityType)edmType).KeyMembers.Contains(member))
{
kind = ResourcePropertyKind.Key | ResourcePropertyKind.Primitive;
}
else
{
kind = ResourcePropertyKind.Primitive;
}
break;
case BuiltInTypeKind.ComplexType:
kind = ResourcePropertyKind.ComplexType;
propertyType = knownTypes[propertyInfo.PropertyType];
break;
case BuiltInTypeKind.EntityType:
kind = ResourcePropertyKind.ResourceReference;
propertyType = knownTypes[propertyInfo.PropertyType];
resourceContainer = FindSampleTargetContainer(propertyType.Type, entitySets.Values);
break;
case BuiltInTypeKind.CollectionType:
kind = ResourcePropertyKind.ResourceSetReference;
Type propertyClrType = GetClrTypeForCSpaceType(workspace, (EntityType)((CollectionType)member.TypeUsage.EdmType).TypeUsage.EdmType);
propertyType = knownTypes[propertyClrType];
resourceContainer = FindSampleTargetContainer(propertyType.Type, entitySets.Values);
break;
default:
Debug.Assert(false, "Invalid member type encountered on " + member.Name + " - " + member.TypeUsage.EdmType.BuiltInTypeKind);
break;
}
Debug.Assert(propertyType != null, "propertyType != null");
string mimeType = GetMimeTypeForMappedMember(member);
ResourceProperty resourceProperty = new ResourceProperty(propertyInfo, kind, mimeType, propertyType, resourceContainer);
resourceType.AddProperty(resourceProperty);
}
resourceType.SortKeyMembers();
}
/// Gets the NavigationProperty for the member of a set type if available.
/// Set for which to return navigation property.
/// Relationship end member for the navigation property.
/// The NavigationProperty for the member of a set type if available; null otherwise.
private static NavigationProperty PropertyForEnd(EntitySet set, RelationshipEndMember member)
{
Debug.Assert(set != null, "set != null");
Debug.Assert(member != null, "member != null");
foreach (NavigationProperty p in set.ElementType.NavigationProperties)
{
if (p.FromEndMember == member)
{
return p;
}
}
return null;
}
/// Creates a string array with property names.
/// Properties to include in name.
/// A string array with property names.
private static string[] PropertyNamesToStrings(ReadOnlyMetadataCollection properties)
{
Debug.Assert(properties != null, "properties != null");
string[] result = new string[properties.Count];
for (int i = 0; i < properties.Count; i++)
{
result[i] = properties[i].Name;
}
return result;
}
///
/// Write the child element of the referential constraint
///
/// xmlWriter to which metadata needs to be written
/// name of the xmlnode : Principal or Dependent
/// role name
/// list of properties
private static void WriteReferentialConstraintChildElement(
XmlWriter xmlWriter,
string nodeName,
string roleName,
ReadOnlyMetadataCollection properties)
{
// Write the principal role
xmlWriter.WriteStartElement(nodeName);
xmlWriter.WriteAttributeString(XmlConstants.Role, roleName);
foreach (EdmProperty property in properties)
{
xmlWriter.WriteStartElement(XmlConstants.PropertyRef);
xmlWriter.WriteAttributeString(XmlConstants.Name, property.Name);
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement();
}
///
/// Write the metadata for the given members in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// type whose members metadata needs to be written
/// Metadata for type as a web data service resource.
private static void WriteMembers(XmlWriter xmlWriter, StructuralType declaringType, ResourceType resourceType)
{
foreach (EdmMember member in declaringType.Members)
{
// Ignore members which are not defined on this type
if (member.DeclaringType != declaringType)
{
continue;
}
// Ignore properties with no metadata.
if (resourceType.TryResolvePropertyName(member.Name) == null)
{
continue;
}
if (member.BuiltInTypeKind == BuiltInTypeKind.EdmProperty)
{
xmlWriter.WriteStartElement(XmlConstants.Property);
xmlWriter.WriteAttributeString(XmlConstants.Name, member.Name);
xmlWriter.WriteAttributeString(XmlConstants.Type, member.TypeUsage.EdmType.FullName);
WriteFacets(xmlWriter, member.TypeUsage);
}
else
{
Debug.Assert(
BuiltInTypeKind.NavigationProperty == member.BuiltInTypeKind,
"Invalid Member type encountered");
NavigationProperty navProperty = (NavigationProperty)member;
xmlWriter.WriteStartElement(XmlConstants.NavigationProperty);
xmlWriter.WriteAttributeString(XmlConstants.Name, navProperty.Name);
xmlWriter.WriteAttributeString(XmlConstants.Relationship, navProperty.RelationshipType.FullName);
xmlWriter.WriteAttributeString(XmlConstants.FromRole, navProperty.FromEndMember.Name);
xmlWriter.WriteAttributeString(XmlConstants.ToRole, navProperty.ToEndMember.Name);
}
WriteUserDefinedAnnotations(xmlWriter, member.MetadataProperties);
xmlWriter.WriteEndElement();
}
}
///
/// Write the metadata for the given facets in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// typeusage whose metadata needs to be written
private static void WriteFacets(XmlWriter xmlWriter, TypeUsage typeUsage)
{
foreach (Facet facet in typeUsage.Facets)
{
if (facet.Value != null)
{
WriteFacetValue(xmlWriter, facet.Name, facet.Value);
}
}
}
///
/// Write the metadata for the given facet
///
/// xmlWriter to which metadata needs to be written
/// name of the facet
/// value of the facet
private static void WriteFacetValue(XmlWriter xmlWriter, string facetName, object facetValue)
{
Type facetValueType = facetValue.GetType();
// We need to special case the boolean facets, since ToString returns True and False as values
// and for xml, they need to be lower case
if (facetValueType == typeof(bool))
{
if ((bool)facetValue)
{
xmlWriter.WriteAttributeString(facetName, XmlConstants.XmlTrueLiteral);
}
else
{
xmlWriter.WriteAttributeString(facetName, XmlConstants.XmlFalseLiteral);
}
}
else if (facetValueType == typeof(int))
{
xmlWriter.WriteAttributeString(facetName, XmlConvert.ToString((int)facetValue));
}
else if (facetValueType.IsEnum)
{
xmlWriter.WriteAttributeString(facetName, facetValue.ToString());
}
else if (facetValueType == typeof(byte))
{
xmlWriter.WriteAttributeString(facetName, XmlConvert.ToString((byte)facetValue));
}
else
{
xmlWriter.WriteAttributeString(facetName, facetValue.ToString());
}
}
///
/// Write the user annotations to the csdl
///
/// xmlWriter to which metadata needs to be written
/// list of metadata properties from which extended ones needs to be written
private static void WriteUserDefinedAnnotations(XmlWriter xmlWriter, ReadOnlyMetadataCollection metadataProperties)
{
Debug.Assert(xmlWriter != null, "xmlWriter != null");
Debug.Assert(metadataProperties != null, "metadataProperties != null");
Debug.Assert(xmlWriter.WriteState == WriteState.Element, "xmlWriter.WriteState == WriteState.Element - annotations are written on elements");
foreach (MetadataProperty metadataProperty in metadataProperties)
{
if (metadataProperty.PropertyKind == PropertyKind.Extended)
{
int index = metadataProperty.Name.LastIndexOf(":", StringComparison.Ordinal);
Debug.Assert(index != -1, "empty space is a reserved namespace for edm. So this should never be the case");
string xmlNamespace = metadataProperty.Name.Substring(0, index);
string attributeName = metadataProperty.Name.Substring(index + 1);
xmlWriter.WriteAttributeString(attributeName, xmlNamespace, (string)metadataProperty.Value);
}
}
}
///
/// Returns the entity set name for the given entity set. If this entity set belongs to the default container name,
/// then it returns the entity set name, otherwise qualifies it with the entitycontainer name
///
/// entity set name
/// entity container name
/// true if the given entity set belongs to the default entity container
/// returns the entity set name
private static string GetEntitySetName(string entitySetName, string entityContainerName, bool containedInDefaultEntityContainer)
{
if (containedInDefaultEntityContainer)
{
return entitySetName;
}
else
{
return entityContainerName + "." + entitySetName;
}
}
///
/// Returns the escaped entity set name for the given entity set. If this entity set belongs to the default container name,
/// then it returns the escaped entity set name, otherwise it escapes both the container and set name
///
/// qualified entity set name whose name needs to be escaped
/// returns the escaped entityset name
private static string GetEscapedEntitySetName(string qualifiedEntitySetName)
{
int indexOfLastPeriod = qualifiedEntitySetName.LastIndexOf('.');
if (-1 == indexOfLastPeriod)
{
return "[" + qualifiedEntitySetName + "]";
}
else
{
return "[" + qualifiedEntitySetName.Substring(0, indexOfLastPeriod) + "].[" + qualifiedEntitySetName.Substring(indexOfLastPeriod + 1) + "]";
}
}
///
/// Returns the edm schema multiplicity value for the given multiplicity enum
///
/// enum multiplicity value
/// returns edm schema multiplicity value
private static string GetMultiplicity(RelationshipMultiplicity multiplicity)
{
string multiplicityValue;
if (RelationshipMultiplicity.Many == multiplicity)
{
multiplicityValue = XmlConstants.Many;
}
else if (RelationshipMultiplicity.One == multiplicity)
{
multiplicityValue = XmlConstants.One;
}
else
{
Debug.Assert(
RelationshipMultiplicity.ZeroOrOne == multiplicity,
"Invalid value for multiplicity encountered");
multiplicityValue = XmlConstants.ZeroOrOne;
}
return multiplicityValue;
}
/// Initializes metadata for the given object context.
/// Instance of data source to use if pure static analysis isn't possible.
/// assembly whose metadata needs to be loaded.
private static void InitializeObjectItemCollection(ObjectContext objectContext, Assembly assembly)
{
objectContext.MetadataWorkspace.LoadFromAssembly(assembly);
}
///
/// Get the entity set metadata object given the qualified entity set name
///
/// qualified entity set name i.e. if the entity set
/// belongs to entity container other than the default one, then the entity container name should
/// be part of the qualified name
/// the entity set metadata object
private EntitySet GetEntitySet(string qualifiedEntitySetName)
{
Debug.Assert(
!String.IsNullOrEmpty(qualifiedEntitySetName),
"!String.IsNullOrEmpty(qualifiedEntitySetName) -- otherwise qualifiedEntitySetName didn't come from internal metadata");
string entityContainerName;
string entitySetName;
// entity set name is fully qualified
int index = qualifiedEntitySetName.LastIndexOf('.');
if (index != -1)
{
entityContainerName = qualifiedEntitySetName.Substring(0, index - 1);
entitySetName = qualifiedEntitySetName.Substring(index + 1);
}
else
{
entityContainerName = this.ObjectContext.DefaultContainerName;
entitySetName = qualifiedEntitySetName;
}
EntityContainer entityContainer = this.ObjectContext.MetadataWorkspace.GetEntityContainer(entityContainerName, DataSpace.CSpace);
Debug.Assert(
entityContainer != null,
"entityContainer != null -- otherwise entityContainerName '" + entityContainerName + "' didn't come from metadata");
EntitySet entitySet = entityContainer.GetEntitySetByName(entitySetName, false /*ignoreCase*/);
Debug.Assert(
entitySet != null,
"entitySet != null -- otherwise entitySetName '" + entitySetName + "' didn't come from metadata");
return entitySet;
}
///
/// Finds a with the same CLR type as the
/// specified .
///
/// EDM type to look up.
///
/// A with the same CLR type as the
/// specified ; possibly null.
///
private ResourceType FindResourceType(StructuralType edmType)
{
// Skip entity types with no metadata.
Type type = GetClrTypeForCSpaceType(this.ObjectContext.MetadataWorkspace, edmType);
foreach (ResourceType resourceType in this.Types)
{
if (resourceType.Type == type)
{
return resourceType;
}
}
return null;
}
///
/// Get the default entity container
///
/// returns the default entity container
private EntityContainer GetDefaultEntityContainer()
{
EntityContainer entityContainer;
if (String.IsNullOrEmpty(this.ObjectContext.DefaultContainerName))
{
throw new InvalidOperationException(Strings.ObjectContext_DefaultEntityContainerNameMissing);
}
if (!this.ObjectContext.MetadataWorkspace.TryGetEntityContainer(
this.ObjectContext.DefaultContainerName, DataSpace.CSpace, out entityContainer))
{
throw new InvalidOperationException(
Strings.ObjectContext_InvalidDefaultEntityContainerName(this.ObjectContext.DefaultContainerName));
}
return entityContainer;
}
/// Creates the object query for the given resource container and returns it.
/// Resource container for which a query instance needs to be created.
/// Returns the ObjectQuery instance for the given resource container.
private ObjectQuery InternalGetResourceContainerInstance(ResourceContainer container)
{
Debug.Assert(container != null, "container != null");
if (container.ReadFromContextDelegate == null)
{
Type[] parameterTypes = new Type[] { typeof(object) };
string escapedEntitySetName = GetEscapedEntitySetName(container.Name);
MethodInfo genericMethod = typeof(ObjectContext).GetMethod("CreateQuery", WebUtil.PublicInstanceBindingFlags).MakeGenericMethod(container.ElementType);
// ((ObjectContext)arg0).CreateQuery("escapedEntitySetName", new ObjectParameter[0]);
System.Reflection.Emit.DynamicMethod readerMethod = new System.Reflection.Emit.DynamicMethod("queryable_reader", typeof(IQueryable), parameterTypes, false);
var generator = readerMethod.GetILGenerator();
generator.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
generator.Emit(System.Reflection.Emit.OpCodes.Castclass, typeof(ObjectContext));
generator.Emit(System.Reflection.Emit.OpCodes.Ldstr, escapedEntitySetName);
generator.Emit(System.Reflection.Emit.OpCodes.Ldc_I4_0);
generator.Emit(System.Reflection.Emit.OpCodes.Newarr, typeof(ObjectParameter));
generator.Emit(System.Reflection.Emit.OpCodes.Call, genericMethod);
generator.Emit(System.Reflection.Emit.OpCodes.Ret);
container.ReadFromContextDelegate = (Func)readerMethod.CreateDelegate(typeof(Func));
}
return (ObjectQuery)container.ReadFromContextDelegate(this.ObjectContext);
}
///
/// Write the metadata for the given association set element in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// association set whose metadata needs to be written
/// Name of container for the entity set.
private void WriteAssociationSet(XmlWriter xmlWriter, AssociationSet associationSet, string containerName)
{
// Skip association sets where any end is hidden.
foreach (AssociationSetEnd end in associationSet.AssociationSetEnds)
{
string lookupName = end.EntitySet.Name;
if (!String.IsNullOrEmpty(containerName))
{
lookupName = containerName + "." + lookupName;
}
if (((IDataServiceProvider)this).TryResolveContainerName(lookupName) == null)
{
return;
}
}
xmlWriter.WriteStartElement(XmlConstants.AssociationSet);
xmlWriter.WriteAttributeString(XmlConstants.Name, associationSet.Name);
xmlWriter.WriteAttributeString(
XmlConstants.Association, associationSet.ElementType.FullName);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, associationSet.MetadataProperties);
for (int i = 0; i < associationSet.AssociationSetEnds.Count; i++)
{
xmlWriter.WriteStartElement(XmlConstants.End);
xmlWriter.WriteAttributeString(XmlConstants.Role, associationSet.AssociationSetEnds[i].Name);
xmlWriter.WriteAttributeString(XmlConstants.EntitySet, associationSet.AssociationSetEnds[i].EntitySet.Name);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, associationSet.AssociationSetEnds[i].MetadataProperties);
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement();
}
///
/// Write the entity set element in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// entity set whose metadata needs to be written
/// Name of the container for the entity set.
private void WriteEntitySet(XmlWriter xmlWriter, EntitySet entitySet, string containerName)
{
// Skip entity sets with no backing metadata.
string lookupName = entitySet.Name;
if (!String.IsNullOrEmpty(containerName))
{
lookupName = containerName + "." + lookupName;
}
if (((IDataServiceProvider)this).TryResolveContainerName(lookupName) == null)
{
return;
}
xmlWriter.WriteStartElement(XmlConstants.EntitySet);
xmlWriter.WriteAttributeString(XmlConstants.Name, entitySet.Name);
xmlWriter.WriteAttributeString(XmlConstants.EntityType, entitySet.ElementType.FullName);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, entitySet.MetadataProperties);
xmlWriter.WriteEndElement();
}
///
/// Writes the metadata for all the edmTypes
///
/// xmlWriter to which metadata needs to be written
/// edmtypes whose metadata needs to be written
private void WriteEdmTypes(XmlWriter xmlWriter, List edmTypes)
{
// Top Level EdmTypes are EntityType, ComplexType and AssociationType.
foreach (EdmType edmType in edmTypes)
{
switch (edmType.BuiltInTypeKind)
{
case BuiltInTypeKind.EntityType:
this.WriteEntityType(xmlWriter, (EntityType)edmType);
break;
case BuiltInTypeKind.ComplexType:
this.WriteComplexType(xmlWriter, (ComplexType)edmType);
break;
case BuiltInTypeKind.AssociationType:
this.WriteAssociationType(xmlWriter, (AssociationType)edmType);
break;
default:
Debug.Assert(false, "Unexpected EdmType encountered");
break;
}
}
}
///
/// Write the metadata for the given EntityType element in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// entity type whose metadata needs to be written
private void WriteEntityType(XmlWriter xmlWriter, EntityType entityType)
{
// Skip entity types with no metadata.
ResourceType resourceType = this.FindResourceType(entityType);
if (resourceType == null)
{
return;
}
xmlWriter.WriteStartElement(XmlConstants.EntityType);
xmlWriter.WriteAttributeString(XmlConstants.Name, entityType.Name);
if (entityType.Abstract)
{
xmlWriter.WriteAttributeString(XmlConstants.Abstract, XmlConstants.XmlTrueLiteral);
}
if (entityType.BaseType != null)
{
xmlWriter.WriteAttributeString(XmlConstants.BaseType, entityType.BaseType.FullName);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, entityType.MetadataProperties);
}
else
{
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, entityType.MetadataProperties);
// If there is no base type, then the key must be defined on this entity type.
xmlWriter.WriteStartElement(XmlConstants.Key);
foreach (EdmMember member in entityType.KeyMembers)
{
xmlWriter.WriteStartElement(XmlConstants.PropertyRef);
xmlWriter.WriteAttributeString(XmlConstants.Name, member.Name);
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement();
}
WriteMembers(xmlWriter, entityType, resourceType);
xmlWriter.WriteEndElement();
}
///
/// Write the metadata for the given complex type element in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// complex type whose metadata needs to be written
private void WriteComplexType(XmlWriter xmlWriter, ComplexType complexType)
{
// Skip complex types with no metadata.
ResourceType resourceType = this.FindResourceType(complexType);
if (resourceType == null)
{
return;
}
xmlWriter.WriteStartElement(XmlConstants.ComplexType);
xmlWriter.WriteAttributeString(XmlConstants.Name, complexType.Name);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, complexType.MetadataProperties);
// Write the metadata for complex type properties
WriteMembers(xmlWriter, complexType, resourceType);
xmlWriter.WriteEndElement();
}
///
/// Write the metadata for the given association type element in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// association type whose metadata needs to be written
private void WriteAssociationType(XmlWriter xmlWriter, AssociationType associationType)
{
// Skip association types were any ends are not visible.
foreach (AssociationEndMember member in associationType.AssociationEndMembers)
{
RefType reference = (RefType)member.TypeUsage.EdmType;
ResourceType referenceResourceType = this.FindResourceType(reference.ElementType);
if (referenceResourceType == null)
{
return;
}
}
xmlWriter.WriteStartElement(XmlConstants.Association);
xmlWriter.WriteAttributeString(XmlConstants.Name, associationType.Name);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, associationType.MetadataProperties);
foreach (AssociationEndMember end in associationType.RelationshipEndMembers)
{
xmlWriter.WriteStartElement(XmlConstants.End);
xmlWriter.WriteAttributeString(XmlConstants.Role, end.Name);
// The ends are of reference type
xmlWriter.WriteAttributeString(XmlConstants.Type, ((RefType)end.TypeUsage.EdmType).ElementType.FullName);
// Write the multiplicity value
xmlWriter.WriteAttributeString(XmlConstants.Multiplicity, GetMultiplicity(end.RelationshipMultiplicity));
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, end.MetadataProperties);
// write the action value
if (OperationAction.None != end.DeleteBehavior)
{
xmlWriter.WriteStartElement(XmlConstants.OnDelete);
xmlWriter.WriteAttributeString(XmlConstants.Action, end.DeleteBehavior.ToString());
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement();
}
foreach (ReferentialConstraint referentialConstraint in associationType.ReferentialConstraints)
{
xmlWriter.WriteStartElement(XmlConstants.ReferentialConstraint);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, referentialConstraint.MetadataProperties);
WriteReferentialConstraintChildElement(xmlWriter, XmlConstants.Principal, referentialConstraint.FromRole.Name, referentialConstraint.FromProperties);
WriteReferentialConstraintChildElement(xmlWriter, XmlConstants.Dependent, referentialConstraint.ToRole.Name, referentialConstraint.ToProperties);
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement();
}
///
/// Writes all the entity container definition in the given xml writer.
/// Also emits a special annotation for the default entity container so that
/// client can figure out which one is the default entity container name
///
/// xmlWriter to which metadata needs to be written
/// list of entity containers
/// default entity container
private void WriteEntityContainers(
XmlWriter xmlWriter,
List entityContainers,
EntityContainer defaultEntityContainer)
{
for (int i = 0; i < entityContainers.Count; i++)
{
EntityContainer current = entityContainers[i];
xmlWriter.WriteStartElement(XmlConstants.EntityContainer);
xmlWriter.WriteAttributeString(XmlConstants.Name, current.Name);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, current.MetadataProperties);
if (current == defaultEntityContainer)
{
BaseServiceProvider.WriteDataWebMetadata(
xmlWriter, XmlConstants.IsDefaultEntityContainerAttribute, XmlConstants.XmlTrueLiteral);
this.WriteServiceOperations(xmlWriter, this.ServiceOperations);
}
string containerName = (current == defaultEntityContainer) ? "" : current.Name;
foreach (EntitySetBase entitySetBase in current.BaseEntitySets)
{
if (BuiltInTypeKind.EntitySet == entitySetBase.BuiltInTypeKind)
{
this.WriteEntitySet(xmlWriter, (EntitySet)entitySetBase, containerName);
}
else if (BuiltInTypeKind.AssociationSet == entitySetBase.BuiltInTypeKind)
{
this.WriteAssociationSet(xmlWriter, (AssociationSet)entitySetBase, containerName);
}
}
xmlWriter.WriteEndElement();
}
}
///
/// Stores the types as per the namespaces they belong to
///
private class TypeManager
{
///
/// To keep track of the all the namespace encountered
///
private Dictionary> namespaces = new Dictionary>(StringComparer.Ordinal);
///
/// List of entity containers in the metadataworkspace
///
private List entityContainers = new List();
///
/// Populates the type manager with the types in the workspace
///
/// workspace containing the metadata
internal TypeManager(MetadataWorkspace workspace)
{
//
foreach (GlobalItem globalItem in workspace.GetItems(DataSpace.CSpace))
{
// ignore all the primitive types and functions
if (BuiltInTypeKind.PrimitiveType == globalItem.BuiltInTypeKind ||
BuiltInTypeKind.EdmFunction == globalItem.BuiltInTypeKind)
{
continue;
}
if (globalItem.BuiltInTypeKind == BuiltInTypeKind.EntityContainer)
{
this.entityContainers.Add((EntityContainer)globalItem);
}
else
{
List edmTypesInSameNamespace;
EdmType edmType = (EdmType)globalItem;
// Check if the namespace is already present
if (!this.namespaces.TryGetValue(edmType.NamespaceName, out edmTypesInSameNamespace))
{
edmTypesInSameNamespace = new List();
this.namespaces.Add(edmType.NamespaceName, edmTypesInSameNamespace);
}
edmTypesInSameNamespace.Add(edmType);
}
}
}
///
/// Returns IEnumerable containing the namespace and list of types in that namespace
///
/// Returns IEnumerable containing the namespace and list of types in that namespace
internal IEnumerable>> NamespaceAlongWithTypes
{
get
{
return this.namespaces;
}
}
///
/// Returns the list of entity containers in the workspace
///
/// returns the list of entity containers
internal List GetEntityContainers()
{
return this.entityContainers;
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//----------------------------------------------------------------------
//
// 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;
using System.Data.EntityClient;
using System.Data.Metadata.Edm;
using System.Data.Objects;
using System.Data.Services.Caching;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
#endregion Namespaces.
///
/// Provides a reflection-based IDataServiceProvider implementation.
///
[DebuggerDisplay("ObjectContextServiceProvider: {Type}")]
internal class ObjectContextServiceProvider : BaseServiceProvider
{
#region Private fields.
///
/// List of objects that we need to be replaced. The key value
/// indicates the new instance of the type with whatever values
/// are specified in the payload. The value is the actual object
/// which is attached to the object context
///
private Dictionary objectsToBeReplaced = new Dictionary();
///
/// List of cspace types for which ospace metadata couldn't be found.
///
private List typesWithoutOSpaceMetadata;
#endregion Private fields.
///
/// Initializes a new System.Data.Services.ReflectionServiceProvider instance.
///
/// Metadata for this provider.
/// instance of the data source provider.
internal ObjectContextServiceProvider(MetadataCacheItem metadata, object dataSourceInstance)
: base(metadata, dataSourceInstance)
{
this.typesWithoutOSpaceMetadata = new List();
}
/// Gets a value indicating whether null propagation is required in expression trees.
public override bool NullPropagationRequired
{
get { return false; }
}
///
/// Provides a name for the context in which all resource containers are.
///
public override string ResourceContextName
{
get
{
return this.ObjectContext.DefaultContainerName;
}
}
/// Strongly-types instance being reflected upon.
private ObjectContext ObjectContext
{
get
{
return (ObjectContext)this.CurrentDataSource;
}
}
///
/// Gets the container 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 override ResourceContainer GetContainer(string declaringTypeContainerName, Type declaringResourceType, ResourceProperty resourceProperty)
{
Debug.Assert(!String.IsNullOrEmpty(declaringTypeContainerName), "!String.IsNullOrEmpty(declaringTypeContainerName)");
Debug.Assert(declaringResourceType != null && resourceProperty != null, "declaringResourceType != null && resourceProperty != null");
EntitySet entitySet = this.GetEntitySet(declaringTypeContainerName);
Debug.Assert(entitySet != null, "entitySet != null -- GetEntitySet should never return null");
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(declaringResourceType);
EntityType entityType = this.ObjectContext.MetadataWorkspace.GetItem(resourceType.FullName, DataSpace.CSpace);
Debug.Assert(entityType != null, "entityType != null");
NavigationProperty navigationProperty;
entityType.NavigationProperties.TryGetValue(resourceProperty.Name, false /*ignoreCase*/, out navigationProperty);
Debug.Assert(navigationProperty != null, "navigationProperty != null");
ResourceContainer result = null;
foreach (EntitySetBase set in entitySet.EntityContainer.BaseEntitySets)
{
if (set.BuiltInTypeKind != BuiltInTypeKind.AssociationSet)
{
continue;
}
AssociationSet associationSet = (AssociationSet)set;
if (associationSet.ElementType == navigationProperty.RelationshipType)
{
AssociationSetEnd setEnd = associationSet.AssociationSetEnds[navigationProperty.FromEndMember.Name];
if (setEnd.EntitySet == entitySet)
{
entitySet = associationSet.AssociationSetEnds[navigationProperty.ToEndMember.Name].EntitySet;
string entitySetName = GetEntitySetName(entitySet.Name, entitySet.EntityContainer.Name, this.ObjectContext.DefaultContainerName == entitySet.EntityContainer.Name);
result = ((IDataServiceProvider)this).TryResolveContainerName(entitySetName);
break;
}
}
}
Debug.Assert(result != null, "result != null -- we should be able to find the entity set for the given navigation property");
return result;
}
/// 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 override ResourceContainer GetContainerForResourceType(Type resourceType)
{
throw Error.NotSupported();
}
///
/// Returns the metadata in edm format
///
/// Writer to which metadata XML should be written.
public override void GetMetadata(XmlWriter xmlWriter)
{
Debug.Assert(xmlWriter != null, "xmlWriter != null");
InitializeObjectItemCollection(this.ObjectContext, this.Type.Assembly);
var typeManager = new TypeManager(this.ObjectContext.MetadataWorkspace);
BaseServiceProvider.WriteTopLevelSchemaElements(xmlWriter);
bool entityContainerDefinitionWritten = false;
// Write all the types in their respective namespaces
foreach (KeyValuePair> typesInNamespace in typeManager.NamespaceAlongWithTypes)
{
BaseServiceProvider.WriteSchemaElement(xmlWriter, typesInNamespace.Key, this.CompatibleWithV1Schema);
// Write the entity container in the same namespace as that of the type
if (!entityContainerDefinitionWritten && typesInNamespace.Key == this.Type.Namespace)
{
this.WriteEntityContainers(xmlWriter, typeManager.GetEntityContainers(), this.GetDefaultEntityContainer());
entityContainerDefinitionWritten = true;
}
this.WriteEdmTypes(xmlWriter, typesInNamespace.Value);
xmlWriter.WriteEndElement();
}
// If the entity container is in a different namespace than the types, then write the entity container definition
// in a different namespace
if (!entityContainerDefinitionWritten)
{
BaseServiceProvider.WriteSchemaElement(xmlWriter, this.Type.Namespace, this.CompatibleWithV1Schema);
this.WriteEntityContainers(xmlWriter, typeManager.GetEntityContainers(), this.GetDefaultEntityContainer());
xmlWriter.WriteEndElement();
}
// These end elements balance the elements written out in WriteTopLevelSchemaElements
xmlWriter.WriteEndElement();
xmlWriter.WriteEndElement();
xmlWriter.Flush();
}
///
/// Get the list of etag property names given the entity set name and the instance of the resource
///
/// name of the entity set
/// clr type of the resource whose etag properties need to be fetched
/// list of etag property names
public override ICollection GetETagProperties(string containerName, Type resourceClrType)
{
Debug.Assert(!String.IsNullOrEmpty(containerName), "container name must not be empty");
Debug.Assert(resourceClrType != null, "clrType cannot be null");
EntitySetBase entitySet = this.GetEntitySet(containerName);
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(resourceClrType);
EntityType entityType = this.ObjectContext.MetadataWorkspace.GetItem(resourceType.FullName, DataSpace.CSpace);
Debug.Assert(entityType != null, "entityType != null");
List etagProperties = new List();
// Workspace associated directly with the ObjectContext has metadata only about OSpace, CSpace and OCSpace.
// Since GetRequiredOriginalValueMembers depends on mapping information (CSSpace metadata),
// we need to make sure we call this API on a workspace which has information about the CS Mapping.
// Hence getting workspace from the underlying Entity connection.
MetadataWorkspace workspace = ((EntityConnection)this.ObjectContext.Connection).GetMetadataWorkspace();
foreach (EdmMember member in workspace.GetRequiredOriginalValueMembers(entitySet, entityType))
{
ResourceProperty property = resourceType.TryResolvePropertyName(member.Name);
Debug.Assert(property != null, "property != null");
Debug.Assert(property.TypeKind == ResourceTypeKind.Primitive, "property.TypeKind == ResourceTypeKind.Primitive");
// Ignore key properties if they are part of etag, since the uri already has the key information
// and it makes no sense to duplicate them in etag
if (!property.IsOfKind(ResourcePropertyKind.Key))
{
etagProperties.Add(property);
}
}
return etagProperties;
}
#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 override object CreateResource(string containerName, string fullTypeName)
{
ResourceType resourceType = ((IDataServiceProvider)this).TryResolveTypeName(fullTypeName);
Debug.Assert(resourceType != null, "resourceType != null");
if (resourceType.Type.IsAbstract)
{
throw DataServiceException.CreateBadRequestError(Strings.CannotCreateInstancesOfAbstractType(resourceType.FullName));
}
object resource = resourceType.ConstructorDelegate();
if (containerName != null)
{
this.ObjectContext.AddObject(containerName, resource);
}
return resource;
}
///
/// 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 override object GetResource(IQueryable query, string fullTypeName)
{
Debug.Assert(query != null, "query != null");
ObjectQuery objectQuery = query as ObjectQuery;
Debug.Assert(objectQuery != null, "objectQuery != null - otherwise we're passed an IQueryable we didn't produce");
objectQuery.MergeOption = MergeOption.AppendOnly;
object result = null;
foreach (object resource in objectQuery)
{
if (result != null)
{
throw new InvalidOperationException(Strings.SingleResourceExpected);
}
result = resource;
}
if (result != null && fullTypeName != null)
{
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(result.GetType());
Debug.Assert(resourceType != null, "the result must return a known type");
if (resourceType.FullName != fullTypeName)
{
throw DataServiceException.CreateBadRequestError(Strings.TargetElementTypeOfTheUriSpecifiedDoesNotMatchWithTheExpectedType(resourceType.FullName, fullTypeName));
}
}
return result;
}
///
/// 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 override object ResetResource(object resource)
{
Debug.Assert(resource != null, "resource != null");
Type resourceClrType = resource.GetType();
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(resourceClrType);
if (resourceType.ResourceTypeKind == ResourceTypeKind.EntityType)
{
// For entity types, do the following:
// create a new instance of the same type and set the key values on it
// detach the given instance and attach the new instance
// mark all the properties as modified on the new instance so that the default values get persisted
object newInstance = resourceType.ConstructorDelegate();
// set the key value on the new instance
foreach (ResourceProperty property in resourceType.KeyProperties)
{
object propertyValue = property.GetValue(resource);
property.SetValue(newInstance, propertyValue);
}
this.objectsToBeReplaced.Add(newInstance, resource);
return newInstance;
}
else if (resourceType.ResourceTypeKind == ResourceTypeKind.ComplexType)
{
// For complex types, just return a brand new instance.
return resourceType.ConstructorDelegate();
}
return 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 override void SetValue(object targetResource, string propertyName, object propertyValue)
{
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(targetResource.GetType());
Debug.Assert(resourceType != null, "resourceType != null");
ResourceProperty resourceProperty = resourceType.TryResolvePropertyName(propertyName);
Debug.Assert(resourceProperty != null, "resourceProperty != null");
resourceProperty.SetValue(targetResource, 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 override object GetValue(object targetResource, string propertyName)
{
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(targetResource.GetType());
Debug.Assert(resourceType != null, "resourceType != null");
ResourceProperty resourceProperty = resourceType.TryResolvePropertyName(propertyName);
Debug.Assert(resourceProperty != null, "resourceProperty != null");
object resource = resourceProperty.GetValue(targetResource);
return resource;
}
///
/// 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 override void SetReference(object targetResource, string propertyName, object propertyValue)
{
// Set the value of the reference property
ResourceType resourceType = ((IDataServiceProvider)this).GetResourceType(targetResource.GetType());
Debug.Assert(resourceType != null, "resourceType != null");
ResourceProperty resourceProperty = resourceType.TryResolvePropertyName(propertyName);
Debug.Assert(resourceProperty != null, "resourceProperty != null");
resourceProperty.SetValue(targetResource, 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 override void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
{
Debug.Assert(targetResource != null, "propertyValue != null");
Debug.Assert(!String.IsNullOrEmpty(propertyName), "!String.IsNullOrEmpty(propertyName)");
Debug.Assert(resourceToBeAdded != null, "propertyValue != null");
var entityToBeAdded = (System.Data.Objects.DataClasses.IEntityWithRelationships)resourceToBeAdded;
ObjectStateEntry stateEntry = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(targetResource);
try
{
// Get the EntityCollection value
object collection = targetResource.GetType().InvokeMember(
propertyName,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty,
null,
targetResource,
new object[0],
CultureInfo.InvariantCulture);
Debug.Assert(collection != null, "collection != null");
var end = (System.Data.Objects.DataClasses.IRelatedEnd)collection;
if (stateEntry.State == EntityState.Added || stateEntry.State == EntityState.Unchanged)
{
end.Add(entityToBeAdded);
}
else
{
end.Attach(entityToBeAdded);
}
}
catch (TargetInvocationException e)
{
ErrorHandler.HandleTargetInvocationException(e);
throw;
}
}
/// 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 override IEnumerable ApplyExpansions(IQueryable queryable, ICollection expandPaths)
{
IExpandProvider provider = this.CurrentDataSource as IExpandProvider;
if (provider != null)
{
return provider.ApplyExpansions(queryable, expandPaths);
}
else
{
// Start by adding all the containers for the root type, in case the query originates from
// a service operation and we don't know what the origin is.
HashSet containers = new HashSet(EqualityComparer.Default);
foreach (var set in this.EntitySets)
{
if (set.Value.ElementType.IsAssignableFrom(queryable.ElementType))
{
containers.Add(set.Value);
}
}
bool useBasicExpandProvider = false;
foreach (ExpandSegmentCollection path in expandPaths)
{
if (!useBasicExpandProvider)
{
if (path.Any(segment => segment.HasFilterOrMaxResults))
{
useBasicExpandProvider = true;
}
else
{
// If we expand properties that come from a container we've already encountered,
// we should use the basic provider; otherwise the EF materializer will throw because
// tracking is turned off for querying.
foreach (ExpandSegment segment in path)
{
Debug.Assert(
segment.Container != null,
"segment.Container != null -- otherwise we have an open property in EF or forgot to bind a segment");
if (!containers.Add(segment.Container))
{
useBasicExpandProvider = true;
break;
}
}
}
}
if (path.Count > 8)
{
throw DataServiceException.CreateBadRequestError(Strings.ObjectContext_ExpandTooDeep);
}
}
if (useBasicExpandProvider)
{
return new BasicExpandProvider(this, false).ApplyExpansions(queryable, expandPaths);
}
MethodInfo includeMethod = typeof(ObjectContextServiceProvider).GetMethod("Include", BindingFlags.Static | BindingFlags.NonPublic);
includeMethod = includeMethod.MakeGenericMethod(queryable.ElementType);
IQueryable result = queryable;
foreach (ExpandSegmentCollection path in expandPaths)
{
string dottedPath = JoinIdentifiers(path);
result = (IQueryable)includeMethod.Invoke(null, new object[] { result, dottedPath });
}
return result;
}
}
///
/// 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 override void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
{
Debug.Assert(targetResource != null, "targetResource != null");
Debug.Assert(!String.IsNullOrEmpty(propertyName), "!String.IsNullOrEmpty(propertyName)");
Debug.Assert(resourceToBeRemoved != null, "resourceToBeRemoved != null");
try
{
// Get the EntityCollection value
object collection = targetResource.GetType().InvokeMember(
propertyName,
BindingFlags.Public | BindingFlags.Instance | BindingFlags.GetProperty,
null,
targetResource,
new object[0],
CultureInfo.InvariantCulture);
Debug.Assert(collection != null, "collection != null");
// For many to many relationships, we need to attach the other end first, otherwise
// the collection is null and hence removing it won't have any effect.
collection.GetType().InvokeMember(
"Attach",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod,
null,
collection,
new object[] { resourceToBeRemoved },
CultureInfo.InvariantCulture);
collection.GetType().InvokeMember(
"Remove",
BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod,
null,
collection,
new object[] { resourceToBeRemoved },
CultureInfo.InvariantCulture);
}
catch (TargetInvocationException exception)
{
ErrorHandler.HandleTargetInvocationException(exception);
throw;
}
}
///
/// Delete the given resource
///
/// resource that needs to be deleted
public override void DeleteResource(object resource)
{
this.ObjectContext.DeleteObject(resource);
}
///
/// Saves all the pending changes made till now
///
public override void SaveChanges()
{
// handle the resource which need to be replaced.
foreach (KeyValuePair objectToBeReplaced in this.objectsToBeReplaced)
{
// Get the key from the actual object
EntityKey entityKey = this.ObjectContext.ObjectStateManager.GetObjectStateEntry(objectToBeReplaced.Value).EntityKey;
// Apply property changes as specified in the new object.
this.ObjectContext.ApplyPropertyChanges(entityKey.EntitySetName, objectToBeReplaced.Key);
}
// clear all these once we have processed all the entities that need to be replaced.
this.objectsToBeReplaced.Clear();
// Save Changes
this.ObjectContext.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 override object ResolveResource(object resource)
{
Debug.Assert(resource != null, "resource != null");
return resource;
}
///
/// Revert all the pending changes.
///
public override void ClearChanges()
{
// Detach all the existing entries in the object context
foreach (ObjectStateEntry entry in this.ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added | EntityState.Deleted | EntityState.Modified | EntityState.Unchanged))
{
// entry.State != Entry.Detached: Also, if they are stub entries (no entity object
// associated with the entry yet - these get automatically populated when we query the related entity),
// they also get automatically detached once we detach the associated entity).
// Both Entity and IsRelationship property throws if the entry is detached.
// !entry.IsRelationship: We just need to remove the key entries.
// While detaching the key entries, the relationship entries associated
// with the key entries also get detached.
// entry.Entity != null: Since they are no ordering gaurantees, the stub key entries
// can come first, we need to skip these.
if (entry.State != EntityState.Detached && !entry.IsRelationship && entry.Entity != null)
{
this.ObjectContext.Detach(entry.Entity);
}
}
// clear the list of objects that need to be special handled during save changes for replace semantics
this.objectsToBeReplaced.Clear();
}
#endregion
#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 override IEnumerable EnumerateAccessPathAttributes()
{
// To synthesize AccessPathAttribute instances, we look for Association types with the
// ReferentialConstraint property set. Additionally we have attributes on EntitySet
// to determine whether top-level visibility is enabled, and on NavigationProperty to
// determine whether an access path is canonical or non-canonical.
Debug.Assert(this.ObjectContext != null, "this.ObjectContext != null");
MetadataWorkspace workspace = this.ObjectContext.MetadataWorkspace;
foreach (EntityContainer container in workspace.GetItems(DataSpace.CSpace))
{
foreach (AssociationSet association in container.BaseEntitySets.OfType())
{
AssociationType type = association.ElementType;
foreach (ReferentialConstraint constraint in type.ReferentialConstraints)
{
EntitySet fromSet = association.AssociationSetEnds[constraint.ToRole.Name].EntitySet;
NavigationProperty fromNavigationProperty = PropertyForEnd(fromSet, constraint.ToRole);
if (fromNavigationProperty == null)
{
// If there is no navigation property, no traversal is possible - simply ignore.
continue;
}
bool canonical = GetNavigationPropertyCanonical(fromNavigationProperty);
EntitySet targetSet = association.AssociationSetEnds[constraint.FromRole.Name].EntitySet;
bool topLevelAccess = GetEntitySetTopLevelAccess(targetSet);
AccessPathAttribute attribute;
if (canonical)
{
CanonicalAccessPathAttribute ca = new CanonicalAccessPathAttribute();
ca.TopLevelAccess = topLevelAccess;
attribute = ca;
}
else
{
attribute = new AccessPathAttribute();
}
attribute.AnnotatedContainer = this.EntitySets[targetSet.Name];
attribute.InternalChildKeyMapping = PropertyNamesToStrings(constraint.FromProperties);
attribute.InferredFromSchema = true;
attribute.Parent = fromSet.Name;
attribute.InternalParentKeyMapping = PropertyNamesToStrings(constraint.ToProperties);
attribute.ParentNavigationProperty = fromNavigationProperty.Name;
yield return attribute;
}
}
}
}
#endif
/// 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 override void CheckConfigurationConsistency(object dataSourceInstance)
{
base.CheckConfigurationConsistency(dataSourceInstance);
// Check that rights are consistent in MEST scenarios.
//
// Strictly we should only check for consistent visibility
// for all entity sets of a given type, however the current
// metadata design doesn't differentiate between resource
// container types and resource container instances on
// associations, and therefore all rights are checked at
// the resource type level, which forces this check to have
// consistent rights.
//
// The only exception could be references which are not connected
// (technically those that are not targets, but in EDM all
// associations are two-way). These can have entity sets
// with different rights, enforced at the container level.
// Discover connected types.
HashSet connectedTypes = new HashSet(EqualityComparer.Default);
foreach (ResourceType type in this.Types)
{
foreach (ResourceProperty property in type.PropertiesDeclaredInThisType)
{
if (property.TypeKind == ResourceTypeKind.EntityType)
{
connectedTypes.Add(property.ResourceType);
}
}
}
// Discover containers of same type with conflicting rights.
Dictionary typeRights = new Dictionary();
foreach (KeyValuePair containerEntry in this.EntitySets)
{
Debug.Assert(containerEntry.Key != null, "containerEntry.Key != null");
Debug.Assert(containerEntry.Value != null, "containerEntry.Value != null");
ResourceType resourceType = containerEntry.Value.ResourceType;
// Disregard types that are not connected to any other types.
if (!connectedTypes.Contains(resourceType))
{
continue;
}
ResourceContainer previouslyFoundContainer;
if (typeRights.TryGetValue(resourceType, out previouslyFoundContainer))
{
if (containerEntry.Value.Rights != previouslyFoundContainer.Rights)
{
throw new InvalidOperationException(Strings.ObjectContext_DifferentContainerRights(
previouslyFoundContainer.Name,
previouslyFoundContainer.Rights,
containerEntry.Value.Name,
containerEntry.Value.Rights));
}
}
else
{
typeRights.Add(resourceType, containerEntry.Value);
}
}
CheckNavigationPropertiesBound(dataSourceInstance);
}
///
/// Populates metadata from the given object context
///
/// dictionary of already known types
/// list of already known entity sets
protected override void PopulateMetadata(
IDictionary knownTypes, IDictionary entitySets)
{
Debug.Assert(knownTypes != null, "knownTypes != null");
Debug.Assert(entitySets != null, "entitySets != null");
Debug.Assert(this.ObjectContext != null, "this.ObjectContext != null");
InitializeObjectItemCollection(this.ObjectContext, this.Type.Assembly);
MetadataWorkspace metadataWorkspace = this.ObjectContext.MetadataWorkspace;
// Create Resource types for all the top level entity types and complexTypes
foreach (StructuralType edmType in metadataWorkspace.GetItems(DataSpace.CSpace))
{
if (edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType ||
edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType)
{
// Populates metadata for the given types and all its base types
if (PopulateTypeMetadata(metadataWorkspace, edmType, knownTypes) == null)
{
this.typesWithoutOSpaceMetadata.Add(edmType);
}
}
}
foreach (EntityContainer entityContainer in metadataWorkspace.GetItems(DataSpace.CSpace))
{
bool defaultEntityContainer = entityContainer.Name == this.ObjectContext.DefaultContainerName;
// Get the list of entity sets (Ignore the relationship sets, since we won't allow that to be queried directly
foreach (EntitySetBase entitySetBase in entityContainer.BaseEntitySets)
{
// Ignore all the association sets for the type being, since we are caching only entity sets
if (entitySetBase.BuiltInTypeKind != BuiltInTypeKind.EntitySet)
{
continue;
}
EntitySet entitySet = (EntitySet)entitySetBase;
Type elementType = GetClrTypeForCSpaceType(metadataWorkspace, entitySet.ElementType);
ResourceType resourceType = knownTypes[elementType];
string entitySetName = GetEntitySetName(entitySet.Name, entitySet.EntityContainer.Name, defaultEntityContainer);
ResourceContainer resourceContainer = new ResourceContainer(entitySetName, resourceType);
entitySets.Add(entitySetName, resourceContainer);
}
}
// Now go and populate the member information for each resource type
foreach (ResourceType resourceType in knownTypes.Values)
{
if (resourceType.ResourceTypeKind == ResourceTypeKind.Primitive)
{
continue;
}
PopulateMemberMetadata(resourceType, metadataWorkspace, entitySets, knownTypes);
}
}
///
/// 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 override IQueryable GetResourceContainerInstance(ResourceContainer resourceContainer)
{
Debug.Assert(resourceContainer != null, "resourceContainer != null");
ObjectQuery result = this.InternalGetResourceContainerInstance(resourceContainer);
result.MergeOption = MergeOption.NoTracking;
return result;
}
///
/// 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 override void PopulateMetadataForUserSpecifiedTypes(
IEnumerable userSpecifiedTypes, IDictionary knownTypes, IEnumerable entitySets)
{
foreach (Type type in userSpecifiedTypes)
{
if (this.PopulateMetadataForType(type, knownTypes, entitySets) == null)
{
throw new InvalidOperationException(Strings.BadProvider_InvalidTypeSpecified(type.FullName));
}
}
// If there is a type in the model, for which we couldn't load the metadata, we should throw.
if (this.typesWithoutOSpaceMetadata.Count != 0)
{
throw new InvalidOperationException(Strings.ObjectContext_UnableToLoadMetadataForType(this.typesWithoutOSpaceMetadata[0].FullName));
}
this.typesWithoutOSpaceMetadata = null;
}
///
/// 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 override ResourceType PopulateMetadataForType(
Type type,
IDictionary knownTypes,
IEnumerable entitySets)
{
ResourceType resourceType;
if (!knownTypes.TryGetValue(type, out resourceType))
{
InitializeObjectItemCollection(this.ObjectContext, type.Assembly);
ObjectItemCollection objectItemCollection = (ObjectItemCollection)this.ObjectContext.MetadataWorkspace.GetItemCollection(DataSpace.OSpace);
StructuralType ospaceType, cspaceType;
if (objectItemCollection.TryGetItem(type.FullName, out ospaceType))
{
if (this.ObjectContext.MetadataWorkspace.TryGetEdmSpaceType(ospaceType, out cspaceType))
{
ResourceType baseType = null;
if (cspaceType.BaseType != null)
{
baseType = this.PopulateMetadataForType(type.BaseType, knownTypes, entitySets);
}
resourceType = CreateResourceType(cspaceType, type, baseType, knownTypes);
this.typesWithoutOSpaceMetadata.Remove(cspaceType);
}
}
}
return resourceType;
}
///
/// Checks that all navigation properties are bound to some association set for every entity set.
///
/// Instance of the data source for the provider.
private static void CheckNavigationPropertiesBound(object dataSourceInstance)
{
// For every navigation property, ensure that all of the EntitySets that can
// take their EntityType have an AssociationSet of the appropriate Association type.
Debug.Assert(dataSourceInstance != null, "dataSourceInstance != null");
MetadataWorkspace workspace = ((ObjectContext)dataSourceInstance).MetadataWorkspace;
foreach (EntityType type in workspace.GetItems(DataSpace.CSpace))
{
foreach (NavigationProperty navigationProperty in type.NavigationProperties)
{
foreach (EntitySet entitySet in GetEntitySetsForType(workspace, type))
{
IEnumerable entitySetsWithAssocation = GetEntitySetsWithAssociationSets(
workspace,
navigationProperty.RelationshipType,
navigationProperty.FromEndMember);
if (!entitySetsWithAssocation.Contains(entitySet))
{
throw new InvalidOperationException(Strings.ObjectContext_NavigationPropertyUnbound(
navigationProperty.Name,
type.FullName,
entitySet.Name));
}
}
}
}
}
/// Finds a target container for the specified .
/// CLR type to find.
/// Containers to examine.
/// The resolved ; never null.
private static ResourceContainer FindSampleTargetContainer(Type clrType, IEnumerable containers)
{
Debug.Assert(clrType != null, "clrType != null");
Debug.Assert(containers != null, "clrType != containers");
ResourceContainer result = null;
foreach (ResourceContainer container in containers)
{
if (container.ElementType.IsAssignableFrom(clrType))
{
result = container;
break;
}
}
Debug.Assert(result != null, "result != null - otherwise unable to find container for " + clrType);
return result;
}
/// Gets the CLR type mapped to the specified C-Space type.
/// Workspace in which the type is defined.
/// C-Space type whose matching clr type needs to be looked up.
/// The resolved for the given .
private static Type GetClrTypeForCSpaceType(MetadataWorkspace workspace, StructuralType edmType)
{
Debug.Assert(workspace != null, "workspace != null");
Debug.Assert(edmType != null, "edmType != null");
Debug.Assert(
edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType || edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType,
"Must be entityType or complexType");
StructuralType ospaceType;
if (workspace.TryGetObjectSpaceType(edmType, out ospaceType))
{
ObjectItemCollection objectItemCollection = (ObjectItemCollection)workspace.GetItemCollection(DataSpace.OSpace);
return objectItemCollection.GetClrType(ospaceType);
}
return null;
}
///
/// Gets all instance that may hold an entity of type .
///
/// Workspace with metadata.
/// Entity type to get entity sets for.
/// An enumeration of instances that can hold .
private static IEnumerable GetEntitySetsForType(MetadataWorkspace workspace, EntityType type)
{
Debug.Assert(type != null, "type != null");
Debug.Assert(workspace != null, "workspace != null");
foreach (EntityContainer container in workspace.GetItems(DataSpace.CSpace))
{
foreach (EntitySet entitySet in container.BaseEntitySets.OfType())
{
if (IsAssignableFrom(entitySet.ElementType, type))
{
yield return entitySet;
}
}
}
}
///
/// Gets all entity sets that participate as members for the specified .
///
/// Workspace with metadata.
/// Type of assocation to check.
/// Member of association to check.
///
/// All instances that are are on the role for
/// some association of .
///
private static IEnumerable GetEntitySetsWithAssociationSets(
MetadataWorkspace workspace,
RelationshipType associationType,
RelationshipEndMember member)
{
Debug.Assert(workspace != null, "workspace != null");
Debug.Assert(associationType != null, "associationType != null");
Debug.Assert(member != null, "member != null");
foreach (EntityContainer container in workspace.GetItems(DataSpace.CSpace))
{
foreach (AssociationSet associationSet in container.BaseEntitySets.OfType())
{
if (associationSet.ElementType == associationType)
{
foreach (AssociationSetEnd end in associationSet.AssociationSetEnds)
{
if (end.CorrespondingAssociationEndMember == member)
{
yield return end.EntitySet;
}
}
}
}
}
}
/// Reads the TopLevelAccess property from the specified .
/// Set to read attribute from.
/// true or false depending on the value; true if the value is missing.
private static bool GetEntitySetTopLevelAccess(EntitySet set)
{
Debug.Assert(set != null, "set != null");
const string TopLevelAttributeName = XmlConstants.DataWebMetadataNamespace + ":" + XmlConstants.DataWebAccessTopLevelAccessAttribute;
bool result = true;
MetadataProperty property;
if (set.MetadataProperties.TryGetValue(TopLevelAttributeName, false /* ignoreCase */, out property))
{
string text = (string)property.Value;
if (String.IsNullOrEmpty(text))
{
throw new InvalidOperationException(Strings.ObjectContext_TopLevelEmpty(set.Name));
}
if (!bool.TryParse(text, out result))
{
throw new InvalidOperationException(Strings.ObjectContext_TopLevelIncorrect(property.Name, text));
}
}
return result;
}
/// Gets the MIME type specified for the specified member.
/// C-Space member for which we need to find the C-Space mime type attribute.
/// The MIME type for the specified member, null if none deifned.
private static string GetMimeTypeForMappedMember(EdmMember csdlMember)
{
const string MimePropertyName = XmlConstants.DataWebMetadataNamespace + ":" + XmlConstants.DataWebMimeTypeAttributeName;
string mimeType = null;
MetadataProperty property;
if (csdlMember.MetadataProperties.TryGetValue(MimePropertyName, false /* ignoreCase */, out property))
{
mimeType = (string)property.Value;
if (mimeType != null && mimeType.Length == 0)
{
throw new InvalidOperationException(Strings.ObjectContext_MimeTypeAttributeEmpty(csdlMember.Name, csdlMember.DeclaringType.FullName));
}
if (!IsPrimitiveType(csdlMember.TypeUsage.EdmType))
{
throw new InvalidOperationException(Strings.ObjectContext_MimeTypeAttributeOnNonPrimitive(csdlMember.DeclaringType.FullName, csdlMember.Name, csdlMember.TypeUsage.EdmType.FullName));
}
}
return mimeType;
}
#if ASTORIA_CONTAINMENT
///
/// Reads the AccessPath canonical-ness property from the specified .
///
/// Property to read attribute from.
/// true or false depending on the value; false if the value is missing.
private static bool GetNavigationPropertyCanonical(NavigationProperty property)
{
Debug.Assert(property != null, "property != null");
const string AccessPathAttributeName = XmlConstants.DataWebMetadataNamespace + ":" + XmlConstants.DataWebAccessPathAttribute;
bool result = false;
MetadataProperty metadata;
if (property.MetadataProperties.TryGetValue(AccessPathAttributeName, false /* ignoreCase */, out metadata))
{
string text = (string)metadata.Value;
if (String.IsNullOrEmpty(text))
{
throw new InvalidOperationException(Strings.ObjectContext_AccessPathEmpty(property.Name));
}
if (text == XmlConstants.DataWebAccessPathCanonicalValue)
{
result = true;
}
else if (text == XmlConstants.DataWebAccessPathNonCanonicalValue)
{
result = false;
}
else
{
throw new InvalidOperationException(Strings.ObjectContext_AccessPathIncorrect(property.Name, text));
}
}
return result;
}
#endif
/// Generic method to invoke an Include method on an ObjectQuery source.
/// Element type of the source.
/// Source query.
/// Path to include.
/// A new query that includes in .
private static ObjectQuery Include(IQueryable query, string dottedPath)
{
Debug.Assert(query != null, "query != null");
Debug.Assert(dottedPath != null, "dottedPath != null");
ObjectQuery typedQuery = (ObjectQuery)query;
return typedQuery.Include(dottedPath);
}
/// Checks whether may be assigned to .
/// Type to check assignment to.
/// Type to check assignment from.
///
/// true if an instance of can be assigned to a variable of
/// ; false otherwise.
///
private static bool IsAssignableFrom(EntityType baseType, EntityType derivedType)
{
while (derivedType != null)
{
if (derivedType == baseType)
{
return true;
}
derivedType = (EntityType)derivedType.BaseType;
}
return false;
}
/// Checks whether the specified type is a known primitive type.
/// Type to check.
/// true if the specified type is known to be a primitive type; false otherwise.
private static bool IsPrimitiveType(EdmType type)
{
Debug.Assert(type != null, "type != null");
if (type.BuiltInTypeKind != BuiltInTypeKind.PrimitiveType)
{
return false;
}
else
{
Debug.Assert(
WebUtil.IsPrimitiveType(((PrimitiveType)type).ClrEquivalentType),
"WebUtil.IsPrimitiveType(((PrimitiveType)type).ClrEquivalentType) - all EDM primitive types are Astoria primitive types");
return true;
}
}
/// Joins the list of segment identifiers by dots.
/// List of segments to join.
/// A string with the identifiers joined by dots.
private static string JoinIdentifiers(List segments)
{
Debug.Assert(segments != null, "segments != null");
int capacity = 0;
foreach (ExpandSegment segment in segments)
{
capacity += segment.Name.Length;
}
capacity += segments.Count - 1;
StringBuilder builder = new StringBuilder(capacity);
foreach (ExpandSegment segment in segments)
{
if (builder.Length > 0)
{
builder.Append('.');
}
builder.Append(segment.Name);
}
return builder.ToString();
}
///
/// Populates the metadata for the given type and its base type
///
/// metadata workspace containing all the metadata information
/// type whose metadata needs to be populated
/// list of known types
/// returns the resource type corresponding to the given edmType
private static ResourceType PopulateTypeMetadata(
MetadataWorkspace workspace,
StructuralType edmType,
IDictionary knownTypes)
{
Debug.Assert(
edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType ||
edmType.BuiltInTypeKind == BuiltInTypeKind.ComplexType,
"type must be entity or complex type");
ResourceType resourceType = null;
Type clrType = GetClrTypeForCSpaceType(workspace, edmType);
if (clrType != null && !knownTypes.TryGetValue(clrType, out resourceType))
{
ResourceType baseType = null;
if (edmType.BaseType != null)
{
baseType = PopulateTypeMetadata(workspace, (StructuralType)edmType.BaseType, knownTypes);
}
resourceType = CreateResourceType(edmType, clrType, baseType, knownTypes);
}
return resourceType;
}
///
/// Creates a new instance of resource type given the cspace structural type and mapping clr type.
///
/// cspace structural type.
/// mapping clr type for the given structural type.
/// the base resource type for the given resource type.
/// list of already known resource types.
/// the new resource type instance created for the given cspace type.
private static ResourceType CreateResourceType(
StructuralType cspaceType,
Type clrType,
ResourceType baseResourceType,
IDictionary knownTypes)
{
ResourceTypeKind resourceTypeKind = cspaceType.BuiltInTypeKind == BuiltInTypeKind.EntityType ? ResourceTypeKind.EntityType : ResourceTypeKind.ComplexType;
ResourceType resourceType = new ResourceType(clrType, resourceTypeKind, baseResourceType, cspaceType.FullName, cspaceType.Name);
knownTypes.Add(clrType, resourceType);
return resourceType;
}
///
/// Populates the member metadata for the given type
///
/// resource type whose member metadata needs to be filled
/// workspace containing the metadata information
/// Available entity sets.
/// list of already known types
private static void PopulateMemberMetadata(
ResourceType resourceType,
MetadataWorkspace workspace,
IDictionary entitySets,
IDictionary knownTypes)
{
Debug.Assert(resourceType != null, "resourceType != null");
Debug.Assert(workspace != null, "workspace != null");
Debug.Assert(entitySets != null, "entitySets != null");
// Find the type from the OSpace
StructuralType edmType = workspace.GetItem(resourceType.FullName, DataSpace.CSpace);
foreach (EdmMember member in edmType.Members)
{
// only look at the instance members
if (member.DeclaringType != edmType)
{
continue;
}
ResourceContainer resourceContainer = null;
ResourcePropertyKind kind = (ResourcePropertyKind)(-1);
PropertyInfo propertyInfo = resourceType.Type.GetProperty(member.Name);
ResourceType propertyType = null;
switch (member.TypeUsage.EdmType.BuiltInTypeKind)
{
case BuiltInTypeKind.PrimitiveType:
propertyType = knownTypes[Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType];
if (edmType.BuiltInTypeKind == BuiltInTypeKind.EntityType &&
((EntityType)edmType).KeyMembers.Contains(member))
{
kind = ResourcePropertyKind.Key | ResourcePropertyKind.Primitive;
}
else
{
kind = ResourcePropertyKind.Primitive;
}
break;
case BuiltInTypeKind.ComplexType:
kind = ResourcePropertyKind.ComplexType;
propertyType = knownTypes[propertyInfo.PropertyType];
break;
case BuiltInTypeKind.EntityType:
kind = ResourcePropertyKind.ResourceReference;
propertyType = knownTypes[propertyInfo.PropertyType];
resourceContainer = FindSampleTargetContainer(propertyType.Type, entitySets.Values);
break;
case BuiltInTypeKind.CollectionType:
kind = ResourcePropertyKind.ResourceSetReference;
Type propertyClrType = GetClrTypeForCSpaceType(workspace, (EntityType)((CollectionType)member.TypeUsage.EdmType).TypeUsage.EdmType);
propertyType = knownTypes[propertyClrType];
resourceContainer = FindSampleTargetContainer(propertyType.Type, entitySets.Values);
break;
default:
Debug.Assert(false, "Invalid member type encountered on " + member.Name + " - " + member.TypeUsage.EdmType.BuiltInTypeKind);
break;
}
Debug.Assert(propertyType != null, "propertyType != null");
string mimeType = GetMimeTypeForMappedMember(member);
ResourceProperty resourceProperty = new ResourceProperty(propertyInfo, kind, mimeType, propertyType, resourceContainer);
resourceType.AddProperty(resourceProperty);
}
resourceType.SortKeyMembers();
}
/// Gets the NavigationProperty for the member of a set type if available.
/// Set for which to return navigation property.
/// Relationship end member for the navigation property.
/// The NavigationProperty for the member of a set type if available; null otherwise.
private static NavigationProperty PropertyForEnd(EntitySet set, RelationshipEndMember member)
{
Debug.Assert(set != null, "set != null");
Debug.Assert(member != null, "member != null");
foreach (NavigationProperty p in set.ElementType.NavigationProperties)
{
if (p.FromEndMember == member)
{
return p;
}
}
return null;
}
/// Creates a string array with property names.
/// Properties to include in name.
/// A string array with property names.
private static string[] PropertyNamesToStrings(ReadOnlyMetadataCollection properties)
{
Debug.Assert(properties != null, "properties != null");
string[] result = new string[properties.Count];
for (int i = 0; i < properties.Count; i++)
{
result[i] = properties[i].Name;
}
return result;
}
///
/// Write the child element of the referential constraint
///
/// xmlWriter to which metadata needs to be written
/// name of the xmlnode : Principal or Dependent
/// role name
/// list of properties
private static void WriteReferentialConstraintChildElement(
XmlWriter xmlWriter,
string nodeName,
string roleName,
ReadOnlyMetadataCollection properties)
{
// Write the principal role
xmlWriter.WriteStartElement(nodeName);
xmlWriter.WriteAttributeString(XmlConstants.Role, roleName);
foreach (EdmProperty property in properties)
{
xmlWriter.WriteStartElement(XmlConstants.PropertyRef);
xmlWriter.WriteAttributeString(XmlConstants.Name, property.Name);
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement();
}
///
/// Write the metadata for the given members in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// type whose members metadata needs to be written
/// Metadata for type as a web data service resource.
private static void WriteMembers(XmlWriter xmlWriter, StructuralType declaringType, ResourceType resourceType)
{
foreach (EdmMember member in declaringType.Members)
{
// Ignore members which are not defined on this type
if (member.DeclaringType != declaringType)
{
continue;
}
// Ignore properties with no metadata.
if (resourceType.TryResolvePropertyName(member.Name) == null)
{
continue;
}
if (member.BuiltInTypeKind == BuiltInTypeKind.EdmProperty)
{
xmlWriter.WriteStartElement(XmlConstants.Property);
xmlWriter.WriteAttributeString(XmlConstants.Name, member.Name);
xmlWriter.WriteAttributeString(XmlConstants.Type, member.TypeUsage.EdmType.FullName);
WriteFacets(xmlWriter, member.TypeUsage);
}
else
{
Debug.Assert(
BuiltInTypeKind.NavigationProperty == member.BuiltInTypeKind,
"Invalid Member type encountered");
NavigationProperty navProperty = (NavigationProperty)member;
xmlWriter.WriteStartElement(XmlConstants.NavigationProperty);
xmlWriter.WriteAttributeString(XmlConstants.Name, navProperty.Name);
xmlWriter.WriteAttributeString(XmlConstants.Relationship, navProperty.RelationshipType.FullName);
xmlWriter.WriteAttributeString(XmlConstants.FromRole, navProperty.FromEndMember.Name);
xmlWriter.WriteAttributeString(XmlConstants.ToRole, navProperty.ToEndMember.Name);
}
WriteUserDefinedAnnotations(xmlWriter, member.MetadataProperties);
xmlWriter.WriteEndElement();
}
}
///
/// Write the metadata for the given facets in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// typeusage whose metadata needs to be written
private static void WriteFacets(XmlWriter xmlWriter, TypeUsage typeUsage)
{
foreach (Facet facet in typeUsage.Facets)
{
if (facet.Value != null)
{
WriteFacetValue(xmlWriter, facet.Name, facet.Value);
}
}
}
///
/// Write the metadata for the given facet
///
/// xmlWriter to which metadata needs to be written
/// name of the facet
/// value of the facet
private static void WriteFacetValue(XmlWriter xmlWriter, string facetName, object facetValue)
{
Type facetValueType = facetValue.GetType();
// We need to special case the boolean facets, since ToString returns True and False as values
// and for xml, they need to be lower case
if (facetValueType == typeof(bool))
{
if ((bool)facetValue)
{
xmlWriter.WriteAttributeString(facetName, XmlConstants.XmlTrueLiteral);
}
else
{
xmlWriter.WriteAttributeString(facetName, XmlConstants.XmlFalseLiteral);
}
}
else if (facetValueType == typeof(int))
{
xmlWriter.WriteAttributeString(facetName, XmlConvert.ToString((int)facetValue));
}
else if (facetValueType.IsEnum)
{
xmlWriter.WriteAttributeString(facetName, facetValue.ToString());
}
else if (facetValueType == typeof(byte))
{
xmlWriter.WriteAttributeString(facetName, XmlConvert.ToString((byte)facetValue));
}
else
{
xmlWriter.WriteAttributeString(facetName, facetValue.ToString());
}
}
///
/// Write the user annotations to the csdl
///
/// xmlWriter to which metadata needs to be written
/// list of metadata properties from which extended ones needs to be written
private static void WriteUserDefinedAnnotations(XmlWriter xmlWriter, ReadOnlyMetadataCollection metadataProperties)
{
Debug.Assert(xmlWriter != null, "xmlWriter != null");
Debug.Assert(metadataProperties != null, "metadataProperties != null");
Debug.Assert(xmlWriter.WriteState == WriteState.Element, "xmlWriter.WriteState == WriteState.Element - annotations are written on elements");
foreach (MetadataProperty metadataProperty in metadataProperties)
{
if (metadataProperty.PropertyKind == PropertyKind.Extended)
{
int index = metadataProperty.Name.LastIndexOf(":", StringComparison.Ordinal);
Debug.Assert(index != -1, "empty space is a reserved namespace for edm. So this should never be the case");
string xmlNamespace = metadataProperty.Name.Substring(0, index);
string attributeName = metadataProperty.Name.Substring(index + 1);
xmlWriter.WriteAttributeString(attributeName, xmlNamespace, (string)metadataProperty.Value);
}
}
}
///
/// Returns the entity set name for the given entity set. If this entity set belongs to the default container name,
/// then it returns the entity set name, otherwise qualifies it with the entitycontainer name
///
/// entity set name
/// entity container name
/// true if the given entity set belongs to the default entity container
/// returns the entity set name
private static string GetEntitySetName(string entitySetName, string entityContainerName, bool containedInDefaultEntityContainer)
{
if (containedInDefaultEntityContainer)
{
return entitySetName;
}
else
{
return entityContainerName + "." + entitySetName;
}
}
///
/// Returns the escaped entity set name for the given entity set. If this entity set belongs to the default container name,
/// then it returns the escaped entity set name, otherwise it escapes both the container and set name
///
/// qualified entity set name whose name needs to be escaped
/// returns the escaped entityset name
private static string GetEscapedEntitySetName(string qualifiedEntitySetName)
{
int indexOfLastPeriod = qualifiedEntitySetName.LastIndexOf('.');
if (-1 == indexOfLastPeriod)
{
return "[" + qualifiedEntitySetName + "]";
}
else
{
return "[" + qualifiedEntitySetName.Substring(0, indexOfLastPeriod) + "].[" + qualifiedEntitySetName.Substring(indexOfLastPeriod + 1) + "]";
}
}
///
/// Returns the edm schema multiplicity value for the given multiplicity enum
///
/// enum multiplicity value
/// returns edm schema multiplicity value
private static string GetMultiplicity(RelationshipMultiplicity multiplicity)
{
string multiplicityValue;
if (RelationshipMultiplicity.Many == multiplicity)
{
multiplicityValue = XmlConstants.Many;
}
else if (RelationshipMultiplicity.One == multiplicity)
{
multiplicityValue = XmlConstants.One;
}
else
{
Debug.Assert(
RelationshipMultiplicity.ZeroOrOne == multiplicity,
"Invalid value for multiplicity encountered");
multiplicityValue = XmlConstants.ZeroOrOne;
}
return multiplicityValue;
}
/// Initializes metadata for the given object context.
/// Instance of data source to use if pure static analysis isn't possible.
/// assembly whose metadata needs to be loaded.
private static void InitializeObjectItemCollection(ObjectContext objectContext, Assembly assembly)
{
objectContext.MetadataWorkspace.LoadFromAssembly(assembly);
}
///
/// Get the entity set metadata object given the qualified entity set name
///
/// qualified entity set name i.e. if the entity set
/// belongs to entity container other than the default one, then the entity container name should
/// be part of the qualified name
/// the entity set metadata object
private EntitySet GetEntitySet(string qualifiedEntitySetName)
{
Debug.Assert(
!String.IsNullOrEmpty(qualifiedEntitySetName),
"!String.IsNullOrEmpty(qualifiedEntitySetName) -- otherwise qualifiedEntitySetName didn't come from internal metadata");
string entityContainerName;
string entitySetName;
// entity set name is fully qualified
int index = qualifiedEntitySetName.LastIndexOf('.');
if (index != -1)
{
entityContainerName = qualifiedEntitySetName.Substring(0, index - 1);
entitySetName = qualifiedEntitySetName.Substring(index + 1);
}
else
{
entityContainerName = this.ObjectContext.DefaultContainerName;
entitySetName = qualifiedEntitySetName;
}
EntityContainer entityContainer = this.ObjectContext.MetadataWorkspace.GetEntityContainer(entityContainerName, DataSpace.CSpace);
Debug.Assert(
entityContainer != null,
"entityContainer != null -- otherwise entityContainerName '" + entityContainerName + "' didn't come from metadata");
EntitySet entitySet = entityContainer.GetEntitySetByName(entitySetName, false /*ignoreCase*/);
Debug.Assert(
entitySet != null,
"entitySet != null -- otherwise entitySetName '" + entitySetName + "' didn't come from metadata");
return entitySet;
}
///
/// Finds a with the same CLR type as the
/// specified .
///
/// EDM type to look up.
///
/// A with the same CLR type as the
/// specified ; possibly null.
///
private ResourceType FindResourceType(StructuralType edmType)
{
// Skip entity types with no metadata.
Type type = GetClrTypeForCSpaceType(this.ObjectContext.MetadataWorkspace, edmType);
foreach (ResourceType resourceType in this.Types)
{
if (resourceType.Type == type)
{
return resourceType;
}
}
return null;
}
///
/// Get the default entity container
///
/// returns the default entity container
private EntityContainer GetDefaultEntityContainer()
{
EntityContainer entityContainer;
if (String.IsNullOrEmpty(this.ObjectContext.DefaultContainerName))
{
throw new InvalidOperationException(Strings.ObjectContext_DefaultEntityContainerNameMissing);
}
if (!this.ObjectContext.MetadataWorkspace.TryGetEntityContainer(
this.ObjectContext.DefaultContainerName, DataSpace.CSpace, out entityContainer))
{
throw new InvalidOperationException(
Strings.ObjectContext_InvalidDefaultEntityContainerName(this.ObjectContext.DefaultContainerName));
}
return entityContainer;
}
/// Creates the object query for the given resource container and returns it.
/// Resource container for which a query instance needs to be created.
/// Returns the ObjectQuery instance for the given resource container.
private ObjectQuery InternalGetResourceContainerInstance(ResourceContainer container)
{
Debug.Assert(container != null, "container != null");
if (container.ReadFromContextDelegate == null)
{
Type[] parameterTypes = new Type[] { typeof(object) };
string escapedEntitySetName = GetEscapedEntitySetName(container.Name);
MethodInfo genericMethod = typeof(ObjectContext).GetMethod("CreateQuery", WebUtil.PublicInstanceBindingFlags).MakeGenericMethod(container.ElementType);
// ((ObjectContext)arg0).CreateQuery("escapedEntitySetName", new ObjectParameter[0]);
System.Reflection.Emit.DynamicMethod readerMethod = new System.Reflection.Emit.DynamicMethod("queryable_reader", typeof(IQueryable), parameterTypes, false);
var generator = readerMethod.GetILGenerator();
generator.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
generator.Emit(System.Reflection.Emit.OpCodes.Castclass, typeof(ObjectContext));
generator.Emit(System.Reflection.Emit.OpCodes.Ldstr, escapedEntitySetName);
generator.Emit(System.Reflection.Emit.OpCodes.Ldc_I4_0);
generator.Emit(System.Reflection.Emit.OpCodes.Newarr, typeof(ObjectParameter));
generator.Emit(System.Reflection.Emit.OpCodes.Call, genericMethod);
generator.Emit(System.Reflection.Emit.OpCodes.Ret);
container.ReadFromContextDelegate = (Func)readerMethod.CreateDelegate(typeof(Func));
}
return (ObjectQuery)container.ReadFromContextDelegate(this.ObjectContext);
}
///
/// Write the metadata for the given association set element in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// association set whose metadata needs to be written
/// Name of container for the entity set.
private void WriteAssociationSet(XmlWriter xmlWriter, AssociationSet associationSet, string containerName)
{
// Skip association sets where any end is hidden.
foreach (AssociationSetEnd end in associationSet.AssociationSetEnds)
{
string lookupName = end.EntitySet.Name;
if (!String.IsNullOrEmpty(containerName))
{
lookupName = containerName + "." + lookupName;
}
if (((IDataServiceProvider)this).TryResolveContainerName(lookupName) == null)
{
return;
}
}
xmlWriter.WriteStartElement(XmlConstants.AssociationSet);
xmlWriter.WriteAttributeString(XmlConstants.Name, associationSet.Name);
xmlWriter.WriteAttributeString(
XmlConstants.Association, associationSet.ElementType.FullName);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, associationSet.MetadataProperties);
for (int i = 0; i < associationSet.AssociationSetEnds.Count; i++)
{
xmlWriter.WriteStartElement(XmlConstants.End);
xmlWriter.WriteAttributeString(XmlConstants.Role, associationSet.AssociationSetEnds[i].Name);
xmlWriter.WriteAttributeString(XmlConstants.EntitySet, associationSet.AssociationSetEnds[i].EntitySet.Name);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, associationSet.AssociationSetEnds[i].MetadataProperties);
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement();
}
///
/// Write the entity set element in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// entity set whose metadata needs to be written
/// Name of the container for the entity set.
private void WriteEntitySet(XmlWriter xmlWriter, EntitySet entitySet, string containerName)
{
// Skip entity sets with no backing metadata.
string lookupName = entitySet.Name;
if (!String.IsNullOrEmpty(containerName))
{
lookupName = containerName + "." + lookupName;
}
if (((IDataServiceProvider)this).TryResolveContainerName(lookupName) == null)
{
return;
}
xmlWriter.WriteStartElement(XmlConstants.EntitySet);
xmlWriter.WriteAttributeString(XmlConstants.Name, entitySet.Name);
xmlWriter.WriteAttributeString(XmlConstants.EntityType, entitySet.ElementType.FullName);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, entitySet.MetadataProperties);
xmlWriter.WriteEndElement();
}
///
/// Writes the metadata for all the edmTypes
///
/// xmlWriter to which metadata needs to be written
/// edmtypes whose metadata needs to be written
private void WriteEdmTypes(XmlWriter xmlWriter, List edmTypes)
{
// Top Level EdmTypes are EntityType, ComplexType and AssociationType.
foreach (EdmType edmType in edmTypes)
{
switch (edmType.BuiltInTypeKind)
{
case BuiltInTypeKind.EntityType:
this.WriteEntityType(xmlWriter, (EntityType)edmType);
break;
case BuiltInTypeKind.ComplexType:
this.WriteComplexType(xmlWriter, (ComplexType)edmType);
break;
case BuiltInTypeKind.AssociationType:
this.WriteAssociationType(xmlWriter, (AssociationType)edmType);
break;
default:
Debug.Assert(false, "Unexpected EdmType encountered");
break;
}
}
}
///
/// Write the metadata for the given EntityType element in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// entity type whose metadata needs to be written
private void WriteEntityType(XmlWriter xmlWriter, EntityType entityType)
{
// Skip entity types with no metadata.
ResourceType resourceType = this.FindResourceType(entityType);
if (resourceType == null)
{
return;
}
xmlWriter.WriteStartElement(XmlConstants.EntityType);
xmlWriter.WriteAttributeString(XmlConstants.Name, entityType.Name);
if (entityType.Abstract)
{
xmlWriter.WriteAttributeString(XmlConstants.Abstract, XmlConstants.XmlTrueLiteral);
}
if (entityType.BaseType != null)
{
xmlWriter.WriteAttributeString(XmlConstants.BaseType, entityType.BaseType.FullName);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, entityType.MetadataProperties);
}
else
{
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, entityType.MetadataProperties);
// If there is no base type, then the key must be defined on this entity type.
xmlWriter.WriteStartElement(XmlConstants.Key);
foreach (EdmMember member in entityType.KeyMembers)
{
xmlWriter.WriteStartElement(XmlConstants.PropertyRef);
xmlWriter.WriteAttributeString(XmlConstants.Name, member.Name);
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement();
}
WriteMembers(xmlWriter, entityType, resourceType);
xmlWriter.WriteEndElement();
}
///
/// Write the metadata for the given complex type element in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// complex type whose metadata needs to be written
private void WriteComplexType(XmlWriter xmlWriter, ComplexType complexType)
{
// Skip complex types with no metadata.
ResourceType resourceType = this.FindResourceType(complexType);
if (resourceType == null)
{
return;
}
xmlWriter.WriteStartElement(XmlConstants.ComplexType);
xmlWriter.WriteAttributeString(XmlConstants.Name, complexType.Name);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, complexType.MetadataProperties);
// Write the metadata for complex type properties
WriteMembers(xmlWriter, complexType, resourceType);
xmlWriter.WriteEndElement();
}
///
/// Write the metadata for the given association type element in the given xml writer
///
/// xmlWriter to which metadata needs to be written
/// association type whose metadata needs to be written
private void WriteAssociationType(XmlWriter xmlWriter, AssociationType associationType)
{
// Skip association types were any ends are not visible.
foreach (AssociationEndMember member in associationType.AssociationEndMembers)
{
RefType reference = (RefType)member.TypeUsage.EdmType;
ResourceType referenceResourceType = this.FindResourceType(reference.ElementType);
if (referenceResourceType == null)
{
return;
}
}
xmlWriter.WriteStartElement(XmlConstants.Association);
xmlWriter.WriteAttributeString(XmlConstants.Name, associationType.Name);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, associationType.MetadataProperties);
foreach (AssociationEndMember end in associationType.RelationshipEndMembers)
{
xmlWriter.WriteStartElement(XmlConstants.End);
xmlWriter.WriteAttributeString(XmlConstants.Role, end.Name);
// The ends are of reference type
xmlWriter.WriteAttributeString(XmlConstants.Type, ((RefType)end.TypeUsage.EdmType).ElementType.FullName);
// Write the multiplicity value
xmlWriter.WriteAttributeString(XmlConstants.Multiplicity, GetMultiplicity(end.RelationshipMultiplicity));
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, end.MetadataProperties);
// write the action value
if (OperationAction.None != end.DeleteBehavior)
{
xmlWriter.WriteStartElement(XmlConstants.OnDelete);
xmlWriter.WriteAttributeString(XmlConstants.Action, end.DeleteBehavior.ToString());
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement();
}
foreach (ReferentialConstraint referentialConstraint in associationType.ReferentialConstraints)
{
xmlWriter.WriteStartElement(XmlConstants.ReferentialConstraint);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, referentialConstraint.MetadataProperties);
WriteReferentialConstraintChildElement(xmlWriter, XmlConstants.Principal, referentialConstraint.FromRole.Name, referentialConstraint.FromProperties);
WriteReferentialConstraintChildElement(xmlWriter, XmlConstants.Dependent, referentialConstraint.ToRole.Name, referentialConstraint.ToProperties);
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement();
}
///
/// Writes all the entity container definition in the given xml writer.
/// Also emits a special annotation for the default entity container so that
/// client can figure out which one is the default entity container name
///
/// xmlWriter to which metadata needs to be written
/// list of entity containers
/// default entity container
private void WriteEntityContainers(
XmlWriter xmlWriter,
List entityContainers,
EntityContainer defaultEntityContainer)
{
for (int i = 0; i < entityContainers.Count; i++)
{
EntityContainer current = entityContainers[i];
xmlWriter.WriteStartElement(XmlConstants.EntityContainer);
xmlWriter.WriteAttributeString(XmlConstants.Name, current.Name);
// Write the user defined annotations
WriteUserDefinedAnnotations(xmlWriter, current.MetadataProperties);
if (current == defaultEntityContainer)
{
BaseServiceProvider.WriteDataWebMetadata(
xmlWriter, XmlConstants.IsDefaultEntityContainerAttribute, XmlConstants.XmlTrueLiteral);
this.WriteServiceOperations(xmlWriter, this.ServiceOperations);
}
string containerName = (current == defaultEntityContainer) ? "" : current.Name;
foreach (EntitySetBase entitySetBase in current.BaseEntitySets)
{
if (BuiltInTypeKind.EntitySet == entitySetBase.BuiltInTypeKind)
{
this.WriteEntitySet(xmlWriter, (EntitySet)entitySetBase, containerName);
}
else if (BuiltInTypeKind.AssociationSet == entitySetBase.BuiltInTypeKind)
{
this.WriteAssociationSet(xmlWriter, (AssociationSet)entitySetBase, containerName);
}
}
xmlWriter.WriteEndElement();
}
}
///
/// Stores the types as per the namespaces they belong to
///
private class TypeManager
{
///
/// To keep track of the all the namespace encountered
///
private Dictionary> namespaces = new Dictionary>(StringComparer.Ordinal);
///
/// List of entity containers in the metadataworkspace
///
private List entityContainers = new List();
///
/// Populates the type manager with the types in the workspace
///
/// workspace containing the metadata
internal TypeManager(MetadataWorkspace workspace)
{
//
foreach (GlobalItem globalItem in workspace.GetItems(DataSpace.CSpace))
{
// ignore all the primitive types and functions
if (BuiltInTypeKind.PrimitiveType == globalItem.BuiltInTypeKind ||
BuiltInTypeKind.EdmFunction == globalItem.BuiltInTypeKind)
{
continue;
}
if (globalItem.BuiltInTypeKind == BuiltInTypeKind.EntityContainer)
{
this.entityContainers.Add((EntityContainer)globalItem);
}
else
{
List edmTypesInSameNamespace;
EdmType edmType = (EdmType)globalItem;
// Check if the namespace is already present
if (!this.namespaces.TryGetValue(edmType.NamespaceName, out edmTypesInSameNamespace))
{
edmTypesInSameNamespace = new List();
this.namespaces.Add(edmType.NamespaceName, edmTypesInSameNamespace);
}
edmTypesInSameNamespace.Add(edmType);
}
}
}
///
/// Returns IEnumerable containing the namespace and list of types in that namespace
///
/// Returns IEnumerable containing the namespace and list of types in that namespace
internal IEnumerable>> NamespaceAlongWithTypes
{
get
{
return this.namespaces;
}
}
///
/// Returns the list of entity containers in the workspace
///
/// returns the list of entity containers
internal List GetEntityContainers()
{
return this.entityContainers;
}
}
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.