/// <summary>Creates new root node for the projection tree.</summary> /// <param name="resourceSetWrapper">The resource set of the root level of the query.</param> /// <param name="orderingInfo">The ordering info for this node. null means no ordering to be applied.</param> /// <param name="filter">The filter for this node. null means no filter to be applied.</param> /// <param name="skipCount">Number of results to skip. null means no results to be skipped.</param> /// <param name="takeCount">Maximum number of results to return. null means return all available results.</param> /// <param name="maxResultsExpected">Maximum number of expected results. Hint that the provider should return /// at least maxResultsExpected + 1 results (if available).</param> /// <param name="expandPaths">The list of expanded paths.</param> /// <param name="baseResourceType">The resource type for all entities in this query.</param> /// <param name="selectExpandClause">The select expand clause for the current node from the URI Parser.</param> internal RootProjectionNode( ResourceSetWrapper resourceSetWrapper, OrderingInfo orderingInfo, Expression filter, int? skipCount, int? takeCount, int? maxResultsExpected, List<ExpandSegmentCollection> expandPaths, ResourceType baseResourceType, SelectExpandClause selectExpandClause) : base( String.Empty, null, null, resourceSetWrapper, orderingInfo, filter, skipCount, takeCount, maxResultsExpected, selectExpandClause) { Debug.Assert(baseResourceType != null, "baseResourceType != null"); this.expandPaths = expandPaths; this.baseResourceType = baseResourceType; }
public void Init() { this.baseType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "Fake.NS", "BaseType", false) {CanReflectOnInstanceType = false}; this.baseType.AddProperty(new ResourceProperty("Id", ResourcePropertyKind.Key | ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int))) { CanReflectOnInstanceTypeProperty = false }); this.baseType.SetReadOnly(); this.entityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, this.baseType, "Fake.NS", "Type", false) {CanReflectOnInstanceType = false}; this.entityType.SetReadOnly(); this.derivedType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, this.entityType, "Fake.NS", "DerivedType", false) { CanReflectOnInstanceType = false }; this.derivedType.SetReadOnly(); var resourceSet = new ResourceSet("Set", this.entityType); resourceSet.SetReadOnly(); this.resourceSetWrapper = ResourceSetWrapper.CreateForTests(resourceSet); this.action = new ServiceAction("Fake", ResourceType.GetPrimitiveResourceType(typeof(int)), OperationParameterBindingKind.Sometimes, new[] {new ServiceActionParameter("p1", this.entityType)}, null); this.action.SetReadOnly(); this.actionWrapper = new OperationWrapper(action); this.derivedAction = new ServiceAction("Fake", ResourceType.GetPrimitiveResourceType(typeof(int)), OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("p1", this.derivedType) }, null); this.derivedAction.SetReadOnly(); this.derivedActionWrapper = new OperationWrapper(derivedAction); this.testSubject = new SelectedOperationsCache(); }
/// <summary> /// Gets the target set from the binding set and path expression. /// </summary> /// <param name="provider">Provider instance to resolve the association set.</param> /// <param name="bindingSet">Binding resource set.</param> /// <returns>Returns the target resource set for the path expression.</returns> internal ResourceSetWrapper GetTargetSet(DataServiceProviderWrapper provider, ResourceSetWrapper bindingSet) { Debug.Assert(provider != null, "provider != null"); Debug.Assert(bindingSet != null, "sourceSet != null"); Debug.Assert(this.pathSegments != null && this.pathSegments.Length > 0, "InitializePathSegments() must be called before this."); Debug.Assert(bindingSet.ResourceType.IsAssignableFrom(this.pathSegments[0].SourceType), "bindingSet.ResourceType.IsAssignableFrom(this.pathSegments[0].SourceType)"); ResourceSetWrapper resultSet = bindingSet; for (int idx = 0; resultSet != null && idx < this.pathSegments.Length; idx++) { PathSegment segment = this.pathSegments[idx]; if (segment.Property != null) { resultSet = provider.GetResourceSet(resultSet, segment.SourceType, segment.Property); } #if DEBUG else { Debug.Assert(idx == 0, "Path cannot end with type identifier."); } #endif } return(resultSet); }
/// <summary>Fires the notification for a single action.</summary> /// <param name="service">Service on which methods should be invoked.</param> /// <param name="target">Object to be tracked.</param> /// <param name="container">Container in which object is changed.</param> /// <param name="action">Action affecting target.</param> internal static void FireNotification(IDataService service, object target, ResourceSetWrapper container, UpdateOperations action) { Debug.Assert(service != null, "service != null"); AssertActionValues(target, container); MethodInfo[] methods = container.ChangeInterceptors; if (methods != null) { object[] parameters = new object[2]; parameters[0] = target; parameters[1] = action; for (int i = 0; i < methods.Length; i++) { try { methods[i].Invoke(service.Instance, parameters); } catch (TargetInvocationException exception) { ErrorHandler.HandleTargetInvocationException(exception); throw; } } } }
/// <summary>Creates new root node for the projection tree.</summary> /// <param name="resourceSetWrapper">The resource set of the root level of the query.</param> /// <param name="orderingInfo">The ordering info for this node. null means no ordering to be applied.</param> /// <param name="filter">The filter for this node. null means no filter to be applied.</param> /// <param name="skipCount">Number of results to skip. null means no results to be skipped.</param> /// <param name="takeCount">Maximum number of results to return. null means return all available results.</param> /// <param name="maxResultsExpected">Maximum number of expected results. Hint that the provider should return /// at least maxResultsExpected + 1 results (if available).</param> /// <param name="expandPaths">The list of expanded paths.</param> /// <param name="baseResourceType">The resource type for all entities in this query.</param> /// <param name="selectExpandClause">The select expand clause for the current node from the URI Parser.</param> internal RootProjectionNode( ResourceSetWrapper resourceSetWrapper, OrderingInfo orderingInfo, Expression filter, int?skipCount, int?takeCount, int?maxResultsExpected, List <ExpandSegmentCollection> expandPaths, ResourceType baseResourceType, SelectExpandClause selectExpandClause) : base( String.Empty, null, null, resourceSetWrapper, orderingInfo, filter, skipCount, takeCount, maxResultsExpected, selectExpandClause) { Debug.Assert(baseResourceType != null, "baseResourceType != null"); this.expandPaths = expandPaths; this.baseResourceType = baseResourceType; }
/// <summary>Creates new instance of node representing expanded navigation property.</summary> /// <param name="propertyName">The name of the property to project and expand.</param> /// <param name="property">The <see cref="ResourceProperty"/> for this property. Can only be null for the root node.</param> /// <param name="targetResourceType">Target resource type on which the expansion needs to happen.</param> /// <param name="resourceSetWrapper">The resource set to which the expansion leads.</param> /// <param name="orderingInfo">The ordering info for this node. null means no ordering to be applied.</param> /// <param name="filter">The filter for this node. null means no filter to be applied.</param> /// <param name="skipCount">Number of results to skip. null means no results to be skipped.</param> /// <param name="takeCount">Maximum number of results to return. null means return all available results.</param> /// <param name="maxResultsExpected">Maximum number of expected results. Hint that the provider should return /// at least maxResultsExpected + 1 results (if available).</param> /// <param name="selectExpandClause">The select expand clause for the current node from the URI Parser.</param> internal ExpandedProjectionNode( string propertyName, ResourceProperty property, ResourceType targetResourceType, ResourceSetWrapper resourceSetWrapper, OrderingInfo orderingInfo, Expression filter, int?skipCount, int?takeCount, int?maxResultsExpected, SelectExpandClause selectExpandClause) : base(propertyName, property, targetResourceType) { Debug.Assert(resourceSetWrapper != null, "resourceSetWrapper != null"); Debug.Assert(property != null || (propertyName.Length == 0 && targetResourceType == null), "We don't support open navigation properties."); Debug.Assert(property == null || targetResourceType.TryResolvePropertyName(property.Name) != null, "Property must exist on the target resource type"); Debug.Assert(selectExpandClause != null, "selectExpandClause != null"); this.resourceSetWrapper = resourceSetWrapper; this.orderingInfo = orderingInfo; this.filter = filter; this.skipCount = skipCount; this.takeCount = takeCount; this.maxResultsExpected = maxResultsExpected; this.nodes = new List <ProjectionNode>(); this.hasExpandedPropertyOnDerivedType = false; this.selectExpandClause = selectExpandClause; }
/// <summary> /// Gets the result set for the operation. /// </summary> /// <param name="provider">Provider instance to resolve the path expression.</param> /// <param name="bindingSet">Binding resource set.</param> /// <returns>Returns the result resource set for the operation.</returns> internal ResourceSetWrapper GetResultSet(DataServiceProviderWrapper provider, ResourceSetWrapper bindingSet) { #if DEBUG Debug.Assert(this.isReadOnly, "Wrapper class has not been initialized yet."); #endif if (this.resourceSet != null) { Debug.Assert(this.resourceSet.ResourceSet == this.operation.ResourceSet, "this.resourceSet.ResourceSet == this.serviceOperation.ResourceSet"); Debug.Assert(this.ResultSetPathExpression == null, "this.ResultSetPathExpression == null"); return(this.resourceSet); } if (this.ResultSetPathExpression != null) { Debug.Assert(provider != null, "provider != null"); if (bindingSet == null) { throw new InvalidOperationException(Strings.OperationWrapper_PathExpressionRequiresBindingSet(this.Name)); } ResourceSetWrapper resultSet = this.ResultSetPathExpression.GetTargetSet(provider, bindingSet); if (resultSet == null) { throw new InvalidOperationException(Strings.OperationWrapper_TargetSetFromPathExpressionNotNotVisible(this.Name, this.ResultSetPathExpression.PathExpression, bindingSet.Name)); } return(resultSet); } return(null); }
/// <summary> /// Constructor. /// </summary> /// <param name="model">The edm model this instance belongs to.</param> /// <param name="entityContainer">Entity container instance that this set belongs to.</param> /// <param name="resourceSet">ResourceSet that this entity set represents.</param> internal MetadataProviderEdmEntitySet(MetadataProviderEdmModel model, MetadataProviderEdmEntityContainer entityContainer, ResourceSetWrapper resourceSet) { Debug.Assert(model != null, "model != null"); Debug.Assert(entityContainer != null, "entityContainer != null"); Debug.Assert(resourceSet != null, "resourceSet != null"); this.model = model; this.entityContainer = entityContainer; this.resourceSet = resourceSet; }
/// <summary> /// Constructor. /// </summary> /// <param name="model">The edm model this instance belongs to.</param> /// <param name="entityContainer">Entity container instance that this set belongs to.</param> /// <param name="resourceSet">ResourceSet that this entity set represents.</param> internal MetadataProviderEdmEntitySet(MetadataProviderEdmModel model, MetadataProviderEdmEntityContainer entityContainer, ResourceSetWrapper resourceSet) { Debug.Assert(model != null, "model != null"); Debug.Assert(entityContainer != null, "entityContainer != null"); Debug.Assert(resourceSet != null, "resourceSet != null"); this.model = model; this.entityContainer = entityContainer; this.resourceSet = resourceSet; }
/// <summary> /// Adds an entity set backed by the <paramref name="resourceSet"/> to the entity container. /// </summary> /// <param name="entitySetName">The name of the entity set.</param> /// <param name="resourceSet">The resource set backing the entity set to be created.</param> /// <returns>an instance of IEdmEntitySet that just got added.</returns> /// <remarks> /// This method will also create the association sets and associations for the entity set. /// Materialization state: EntityContainers required. No change in materialization state. /// </remarks> internal IEdmEntitySet AddEntitySet(string entitySetName, ResourceSetWrapper resourceSet) { Debug.Assert(!string.IsNullOrEmpty(entitySetName), "!string.IsNullOrEmpty(entitySetName)"); Debug.Assert(resourceSet != null, "resourceSet != null"); IEdmEntitySet entitySet = new MetadataProviderEdmEntitySet(this.model, this, resourceSet); MetadataProviderUtils.ConvertCustomAnnotations(this.model, resourceSet.CustomAnnotations, entitySet); this.entitySetCache.Add(entitySetName, entitySet); return(entitySet); }
/// <summary> /// Creates the wrapper from the given resource set for use in unit tests. /// </summary> /// <param name="resourceSet">resource set instance whose wrapper needs to get created.</param> /// <param name="rights">Optional rights for the set. Defaults to None.</param> /// <returns>Wrapper for the given resource set.</returns> internal static ResourceSetWrapper CreateForTests(ResourceSet resourceSet, EntitySetRights rights = EntitySetRights.None) { var resourceSetWrapper = new ResourceSetWrapper(resourceSet) { rights = rights, #if DEBUG isReadOnly = true #endif }; return(resourceSetWrapper); }
/// <summary> /// Creates the wrapper from the given resource set. This method returns null, if the given resource set is not visible. /// It also checks for the resource set metadata to make sure that the MPV in the configuration is set correctly /// </summary> /// <param name="resourceSet">resource set instance whose wrapper needs to get created.</param> /// <param name="provider">DataServiceProviderWrapper instance.</param> /// <param name="resourceTypeValidator">resource type validator.</param> /// <returns>Wrapper for the given resource set, if the resource set/resource type metadata is valid and adheres to the protocol version in the server.</returns> internal static ResourceSetWrapper CreateResourceSetWrapper(ResourceSet resourceSet, DataServiceProviderWrapper provider, Func <ResourceType, ResourceType> resourceTypeValidator) { ResourceSetWrapper resourceSetWrapper = new ResourceSetWrapper(resourceSet); resourceSetWrapper.ApplyConfiguration(provider.Configuration, provider.StaticConfiguration); if (!resourceSetWrapper.IsVisible) { return(null); } // Only validate the resource type if the set is visible. resourceSetWrapper.resourceType = resourceTypeValidator(resourceSet.ResourceType); return(resourceSetWrapper); }
/// <summary> /// Retrieve the related end for the given resource set, type and property. /// </summary> /// <param name="resourceSet">resource set for the source end</param> /// <param name="resourceType">resource type for the source end</param> /// <param name="resourceProperty">resource property for the source end</param> /// <returns>Related resource association set end for the given parameters</returns> internal ResourceAssociationSetEnd GetRelatedResourceAssociationSetEnd(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty resourceProperty) { Debug.Assert(resourceSet != null, "resourceSet != null"); Debug.Assert(resourceType != null, "resourceType != null"); ResourceAssociationSetEnd thisEnd = this.GetResourceAssociationSetEnd(resourceSet, resourceType, resourceProperty); if (thisEnd != null) { return(thisEnd == this.End1 ? this.End2 : this.End1); } return(null); }
/// <summary>Initializes a new <see cref="ExpandSegment"/> instance.</summary> /// <param name="name">Segment name.</param> /// <param name="filter">Filter expression for segment, possibly null.</param> /// <param name="maxResultsExpected"> /// Expand providers may choose to return at most MaxResultsExpected + 1 elements to allow the /// data service to detect a failure to meet this constraint. /// </param> /// <param name="container">Container to which the segment belongs; possibly null.</param> /// <param name="targetResourceType">Target resource type on which the expansion needs to happen.</param> /// <param name="expandedProperty">Property expanded by this expand segment</param> /// <param name="orderingInfo">Collection of ordering information for this segment, used for paging</param> internal ExpandSegment( string name, Expression filter, int maxResultsExpected, ResourceSetWrapper container, ResourceType targetResourceType, ResourceProperty expandedProperty, OrderingInfo orderingInfo) { WebUtil.CheckArgumentNull(name, "name"); CheckFilterType(filter); this.name = name; this.filter = filter; this.container = container; this.maxResultsExpected = maxResultsExpected; this.expandedProperty = expandedProperty; this.orderingInfo = orderingInfo; this.targetResourceType = targetResourceType; }
/// <summary> /// Retrieve the end for the given resource set, type and property. /// </summary> /// <param name="resourceSet">resource set for the end</param> /// <param name="resourceType">resource type for the end</param> /// <param name="resourceProperty">resource property for the end</param> /// <returns>Resource association set end for the given parameters</returns> internal ResourceAssociationSetEnd GetResourceAssociationSetEnd(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty resourceProperty) { Debug.Assert(resourceSet != null, "resourceSet != null"); Debug.Assert(resourceType != null, "resourceType != null"); foreach (ResourceAssociationSetEnd end in new[] { this.end1, this.end2 }) { if (end.ResourceSet.Name == resourceSet.Name && end.ResourceType.IsAssignableFrom(resourceType)) { if ((end.ResourceProperty == null && resourceProperty == null) || (end.ResourceProperty != null && resourceProperty != null && end.ResourceProperty.Name == resourceProperty.Name)) { return(end); } } } return(null); }
/// <summary> /// Creates a new instance of <see cref="EntityToSerialize"/>. /// </summary> /// <param name="entity">The entity itself.</param> /// <param name="resourceType">The type of the entity.</param> /// <param name="resourceSetWrapper">The resource set the entity belongs to.</param> /// <param name="provider">The wrapper for the current service provider.</param> /// <param name="absoluteServiceUri">The absolute service URI.</param> /// <returns>The new instance of <see cref="EntityToSerialize"/></returns> internal static EntityToSerialize Create(object entity, ResourceType resourceType, ResourceSetWrapper resourceSetWrapper, DataServiceProviderWrapper provider, Uri absoluteServiceUri) { Debug.Assert(provider != null, "provider != null"); Debug.Assert(provider.DataService != null, "provider.DataService != null"); Debug.Assert(provider.DataService.Configuration != null, "provider.DataService.Configuration != null"); KeySerializer keySerializer = KeySerializer.Create(UrlConvention.Create(provider.DataService)); Func<ResourceProperty, object> getPropertyValue = p => { object keyValue = WebUtil.GetPropertyValue(provider, entity, resourceType, p, null); if (keyValue == null) { throw new InvalidOperationException(Service.Strings.Serializer_NullKeysAreNotSupported(p.Name)); } return keyValue; }; bool includeTypeSegment = resourceSetWrapper.ResourceType != resourceType; return Create(entity, resourceType, resourceSetWrapper.Name, includeTypeSegment, getPropertyValue, keySerializer, absoluteServiceUri); }
/// <summary> /// Apply the given configuration to the resource set. /// </summary> /// <param name="configuration">data service configuration instance.</param> /// <param name="provider">data service provider wrapper instance for accessibility validation.</param> public void ApplyConfiguration(DataServiceConfiguration configuration, DataServiceProviderWrapper provider) { #if DEBUG Debug.Assert(!this.isReadOnly, "Can only apply the configuration once."); #endif if (this.Kind == OperationKind.ServiceOperation) { this.serviceOperationRights = configuration.GetServiceOperationRights(this.ServiceOperation); } else { Debug.Assert(this.Kind == OperationKind.Action, "this.Kind == OperationKind.Action"); this.serviceActionRights = configuration.GetServiceActionRights(this.ServiceAction); } if ((this.Kind == OperationKind.ServiceOperation && (this.serviceOperationRights & ~ServiceOperationRights.OverrideEntitySetRights) != ServiceOperationRights.None) || (this.Kind == OperationKind.Action && this.serviceActionRights != Service.ServiceActionRights.None)) { if (this.operation.ResourceSet != null) { // If the result type is an entity type, we need to make sure its entity set is visible. // If the entity set is hidden, we need to make sure that we throw an exception. this.resourceSet = provider.TryResolveResourceSet(this.operation.ResourceSet.Name); if (this.resourceSet == null) { throw new InvalidOperationException(Strings.OperationWrapper_OperationResourceSetNotVisible(this.Name, this.operation.ResourceSet.Name)); } } else if (this.ResultSetPathExpression != null) { this.ResultSetPathExpression.InitializePathSegments(provider); } } #if DEBUG this.isReadOnly = true; #endif }
/// <summary>Updates the request and response versions based on response format and the target resource set</summary> /// <param name="resourceSet">resourceSet to check for friendly feeds presence</param> /// <param name="service">data service instance</param> internal void UpdateVersion(ResourceSetWrapper resourceSet, IDataService service) { Debug.Assert(service != null, "service is null"); Debug.Assert(service.Provider != null, "provider != null"); AstoriaRequestMessage host = service.OperationContext.RequestMessage; if (host.HttpVerb.IsQuery() || (host.HttpVerb == HttpVerbs.POST && this.TargetSource == RequestTargetSource.ServiceOperation)) { // Update the response DSV for GET according to the features used by the resource set being requested // // Note the response DSV is payload specific and since for GET we won't know what DSV the instances to be // serialized will require until serialization time which happens after the headers are written, // the best we can do is to determin this at the set level. // // For CUD operations we'll raise the version based on the actual instances at deserialization time. if (this.TargetKind == RequestTargetKind.Resource) { Debug.Assert(resourceSet != null, "Must have valid resource set"); if (!this.LinkUri) { this.InitializeVersion(service); Version version = resourceSet.MinimumResponsePayloadVersion(service); this.VerifyAndRaiseResponseVersion(version, service); } } else if (this.TargetResourceType != null && this.CountOption != RequestQueryCountOption.CountSegment && this.TargetKind != RequestTargetKind.MediaResource) { // Except $count requests and named stream requests, we need to bump up the version // based on the target resource type. // For named streams, the response version is always 1.0 since we supported media streams in V1. // For $count requests, the response version is always 1.0 since the result is an Int64 this.VerifyAndRaiseResponseVersion(this.TargetResourceType.MetadataVersion, service); } else if (this.TargetKind == RequestTargetKind.OpenProperty) { this.InitializeVersion(service); // When we encounter open properties, raise the response version to the highest version possible, // since we do not know what the metadata is going to be. this.VerifyAndRaiseResponseVersion(this.effectiveMaxResponseVersion, service); } } }
/// <summary> /// Returns the last segment info whose target request kind is resource /// </summary> /// <param name="description">description about the target request</param> /// <param name="service">data service type to which the request was made</param> /// <param name="allowCrossReferencing">whether cross-referencing is allowed for the resource in question.</param> /// <param name="entityResource">entity resource which is getting modified.</param> /// <param name="entityContainer">entity container of the entity which is getting modified.</param> /// <param name="checkETag">whether to check the etag for the entity resource that is getting modified.</param> /// <returns>Returns the object that needs to get modified</returns> internal static object GetResourceToModify( RequestDescription description, IDataService service, bool allowCrossReferencing, out object entityResource, out ResourceSetWrapper entityContainer, bool checkETag) { // Set the index of the modifying resource int modifyingResourceIndex; if ( description.TargetKind == RequestTargetKind.OpenPropertyValue || description.TargetKind == RequestTargetKind.PrimitiveValue) { modifyingResourceIndex = description.SegmentInfos.Count - 3; } else { modifyingResourceIndex = description.SegmentInfos.Count - 2; } int entityResourceIndex; entityResource = Deserializer.GetEntityResourceToModify( description, service, allowCrossReferencing, out entityContainer, out entityResourceIndex); // now walk from the entity resource to the resource to modify. // for open types, as you walk, if the intermediate resource is an entity, // update the entityResource accordingly. object resourceToModify = entityResource; for (int i = entityResourceIndex + 1; i <= modifyingResourceIndex; i++) { // If the segment is a type identifier segment, skip the segment. There is no need to issue one more query // to the provider. if (description.SegmentInfos[i].IsTypeIdentifierSegment) { continue; } #if DEBUG SegmentInfo segmentInfo = description.SegmentInfos[i]; Debug.Assert(segmentInfo.TargetKind != RequestTargetKind.Resource, "segmentInfo.TargetKind != RequestTargetKind.Resource"); #endif resourceToModify = service.Updatable.GetValue(resourceToModify, description.SegmentInfos[i].Identifier); } // If checkETag is true, then we need to check the etag for the resource // Note that MediaResource has a separate etag, we don't need to check the MLE etag if the target kind is MediaResource if (checkETag && !WebUtil.IsCrossReferencedSegment(description.SegmentInfos[modifyingResourceIndex], service) && description.TargetKind != RequestTargetKind.MediaResource) { service.Updatable.SetETagValues(entityResource, entityContainer); } return resourceToModify; }
/// <summary> /// Returns the entity that need to get modified /// </summary> /// <param name="description">description about the target request</param> /// <param name="service">data service type to which the request was made</param> /// <param name="allowCrossReferencing">whether cross-referencing is allowed for the resource in question.</param> /// <param name="entityContainer">entity container of the entity which is getting modified.</param> /// <returns>Returns the entity that needs to get modified</returns> internal static object GetEntityResourceToModify( RequestDescription description, IDataService service, bool allowCrossReferencing, out ResourceSetWrapper entityContainer) { int entityResourceIndex; return Deserializer.GetEntityResourceToModify( description, service, allowCrossReferencing, out entityContainer, out entityResourceIndex); }
private static void AssertActionValues(object target, ResourceSetWrapper container) { Debug.Assert(target != null, "target != null"); Debug.Assert(container != null, "container != null"); }
/// <summary> /// Gets a string representation of allowed methods on the container (with the specified target cardinality), /// suitable for an 'Allow' header. /// </summary> /// <param name="configuration">configuration object which has the data</param> /// <param name="container">Targetted container, possibly null.</param> /// <param name="description">Description with target.</param> /// <returns>A value for an 'Allow' header; null if <paramref name="container"/> is null.</returns> internal static string GetAllowedMethods(DataServiceConfiguration configuration, ResourceSetWrapper container, RequestDescription description) { if (container == null) { return null; } System.Text.StringBuilder result = new System.Text.StringBuilder(); EntitySetRights rights = configuration.GetResourceSetRights(container.ResourceSet); if (description.IsSingleResult) { AppendRight(rights, EntitySetRights.ReadSingle, XmlConstants.HttpMethodGet, result); AppendRight(rights, EntitySetRights.WriteReplace, XmlConstants.HttpMethodPut, result); if (description.TargetKind != RequestTargetKind.MediaResource) { AppendRight(rights, EntitySetRights.WriteMerge, XmlConstants.HttpMethodPatch, result); AppendRight(rights, EntitySetRights.WriteDelete, XmlConstants.HttpMethodDelete, result); } } else { AppendRight(rights, EntitySetRights.ReadMultiple, XmlConstants.HttpMethodGet, result); AppendRight(rights, EntitySetRights.WriteAppend, XmlConstants.HttpMethodPost, result); } return result.ToString(); }
/// <summary>Checks whether this request has the specified rights.</summary> /// <param name="container">Container to check.</param> /// <param name="requiredRights">Required rights.</param> /// <exception cref="DataServiceException">Thrown if <paramref name="requiredRights"/> aren't available.</exception> internal static void CheckResourceRights(ResourceSetWrapper container, EntitySetRights requiredRights) { Debug.Assert(container != null, "container != null"); Debug.Assert(requiredRights != EntitySetRights.None, "requiredRights != EntitySetRights.None"); if ((requiredRights & container.Rights) == 0) { throw DataServiceException.CreateForbidden(); } }
/// <summary> /// Apply the given configuration to the resource set. /// </summary> /// <param name="configuration">data service configuration instance.</param> /// <param name="provider">data service provider wrapper instance for accessibility validation.</param> public void ApplyConfiguration(DataServiceConfiguration configuration, DataServiceProviderWrapper provider) { #if DEBUG Debug.Assert(!this.isReadOnly, "Can only apply the configuration once."); #endif if (this.Kind == OperationKind.ServiceOperation) { this.serviceOperationRights = configuration.GetServiceOperationRights(this.ServiceOperation); } else { Debug.Assert(this.Kind == OperationKind.Action, "this.Kind == OperationKind.Action"); this.serviceActionRights = configuration.GetServiceActionRights(this.ServiceAction); } if ((this.Kind == OperationKind.ServiceOperation && (this.serviceOperationRights & ~ServiceOperationRights.OverrideEntitySetRights) != ServiceOperationRights.None) || (this.Kind == OperationKind.Action && this.serviceActionRights != Service.ServiceActionRights.None)) { if (this.operation.ResourceSet != null) { // If the result type is an entity type, we need to make sure its entity set is visible. // If the entity set is hidden, we need to make sure that we throw an exception. this.resourceSet = provider.TryResolveResourceSet(this.operation.ResourceSet.Name); if (this.resourceSet == null) { throw new InvalidOperationException(Strings.OperationWrapper_OperationResourceSetNotVisible(this.Name, this.operation.ResourceSet.Name)); } } else if (this.ResultSetPathExpression != null) { this.ResultSetPathExpression.InitializePathSegments(provider); } } #if DEBUG this.isReadOnly = true; #endif }
/// <summary>Creates a new SegmentInfo for the specified <paramref name="property"/>.</summary> /// <param name="property">Property to create segment info for (possibly null).</param> /// <param name="propertyName">Name for the property.</param> /// <param name="propertySet">Target resource set for the property.</param> /// <param name="singleResult">Whether a single result is expected.</param> /// <returns> /// A new <see cref="SegmentInfo"/> instance that describes the specfied <paramref name="property"/> /// as a target, or an open proprty if <paramref name="property"/> is null. /// </returns> protected static SegmentInfo CreateSegment(ResourceProperty property, string propertyName, ResourceSetWrapper propertySet, bool singleResult) { SegmentInfo result = new SegmentInfo(); result.TargetSource = RequestTargetSource.Property; result.SingleResult = singleResult; result.Identifier = propertyName; if (property == null) { result.TargetKind = RequestTargetKind.OpenProperty; } else { result.TargetKind = RequestTargetKind.Resource; result.Identifier = propertyName; result.ProjectedProperty = property; result.TargetResourceType = property.ResourceType; result.TargetResourceSet = propertySet; } return result; }
/// <summary> /// Retrieve the end for the given resource set, type and property. /// </summary> /// <param name="resourceSet">resource set for the end</param> /// <param name="resourceType">resource type for the end</param> /// <param name="resourceProperty">resource property for the end</param> /// <returns>Resource association set end for the given parameters</returns> internal ResourceAssociationSetEnd GetResourceAssociationSetEnd(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty resourceProperty) { Debug.Assert(resourceSet != null, "resourceSet != null"); Debug.Assert(resourceType != null, "resourceType != null"); foreach (ResourceAssociationSetEnd end in new[] { this.end1, this.end2 }) { if (end.ResourceSet.Name == resourceSet.Name && end.ResourceType.IsAssignableFrom(resourceType)) { if ((end.ResourceProperty == null && resourceProperty == null) || (end.ResourceProperty != null && resourceProperty != null && end.ResourceProperty.Name == resourceProperty.Name)) { return end; } } } return null; }
/// <summary> /// Gets the entity set for the specified <paramref name="resourceSet"/> /// </summary> /// <param name="resourceSet">ResourceSet instance.</param> /// <returns>an IEdmEntitySet instance for the given resource set.</returns> protected IEdmEntitySet GetEntitySet(ResourceSetWrapper resourceSet) { return this.Service.Provider.GetMetadataProviderEdmModel().EnsureEntitySet(resourceSet); }
/// <summary> /// Constructor. /// </summary> /// <param name="model">The model this instance belongs to.</param> /// <param name="operation">The resource operation underlying this function import.</param> /// <param name="namespaceName">The namespace of the operation.</param> /// <remarks>This constructor assumes that the entity set for this service operation has already be created.</remarks> protected internal MetadataProviderEdmOperation(MetadataProviderEdmModel model, OperationWrapper operation, string namespaceName) { Debug.Assert(model != null, "model != null"); Debug.Assert(operation != null, "operation != null"); this.model = model; this.ServiceOperation = operation; this.Namespace = namespaceName; if (operation.Kind == OperationKind.Action) { this.isBound = this.ServiceOperation.BindingParameter != null; } else { Debug.Assert(operation.Kind == OperationKind.ServiceOperation, "serviceOperation.Kind == OperationKind.ServiceOperation"); Debug.Assert(operation.OperationParameterBindingKind == OperationParameterBindingKind.Never, "operation.OperationParameterBindingKind == OperationParameterBindingKind.Never"); this.isBound = DefaultIsBindable; } // EntitySetPath=<path string> ResourceSetPathExpression resultSetPathExpression = operation.ResultSetPathExpression; this.entitySetPath = resultSetPathExpression == null ? null : resultSetPathExpression.PathExpression; #if DEBUG ResourceType returnType = operation.ReturnType; ResourceSetWrapper resultSet = operation.ResourceSet; Debug.Assert( returnType == null || returnType.ResourceTypeKind == ResourceTypeKind.EntityCollection || returnType.ResourceTypeKind == ResourceTypeKind.EntityType || (resultSet == null && resultSetPathExpression == null), "resultSet and resultSetPathExpression must be both null when the return type is not an entity type or an entity collection type."); Debug.Assert( (returnType == null || returnType.ResourceTypeKind != ResourceTypeKind.EntityCollection && returnType.ResourceTypeKind != ResourceTypeKind.EntityType) || (resultSet != null || resultSetPathExpression != null), "One of resultSet or resultSetPathExpression must be set when the return type is either an entity type or an entity collection type."); Debug.Assert(resultSet == null || resultSetPathExpression == null, "resultSet and resultSetPathExpression cannot be both set."); #endif string mimeType = operation.MimeType; if (!string.IsNullOrEmpty(mimeType)) { model.SetMimeType(this, mimeType); } switch (this.ServiceOperation.OperationParameterBindingKind) { case OperationParameterBindingKind.Always: break; default: Debug.Assert( this.ServiceOperation.OperationParameterBindingKind == OperationParameterBindingKind.Sometimes || this.ServiceOperation.OperationParameterBindingKind == OperationParameterBindingKind.Never, "this.ServiceOperation.OperationParameterBindingKind == OperationParameterBindingKind.Sometimes || this.ServiceOperation.OperationParameterBindingKind == OperationParameterBindingKind.Never"); break; } ReadOnlyCollection <OperationParameter> operationParameters = operation.Parameters; if (operationParameters != null && operationParameters.Count > 0) { List <IEdmOperationParameter> list = new List <IEdmOperationParameter>(operationParameters.Count); foreach (OperationParameter parameter in operationParameters) { IEdmTypeReference parameterType = this.model.EnsureTypeReference(parameter.ParameterType, /*annotations*/ null); EdmOperationParameter edmParameter = new EdmOperationParameter(this, parameter.Name, parameterType); list.Add(edmParameter); } this.parameters = new ReadOnlyCollection <IEdmOperationParameter>(list); } this.ReturnType = this.CreateReturnTypeReference(); }
/// <summary> /// Retrieve the related end for the given resource set, type and property. /// </summary> /// <param name="resourceSet">resource set for the source end</param> /// <param name="resourceType">resource type for the source end</param> /// <param name="resourceProperty">resource property for the source end</param> /// <returns>Related resource association set end for the given parameters</returns> internal ResourceAssociationSetEnd GetRelatedResourceAssociationSetEnd(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty resourceProperty) { Debug.Assert(resourceSet != null, "resourceSet != null"); Debug.Assert(resourceType != null, "resourceType != null"); ResourceAssociationSetEnd thisEnd = this.GetResourceAssociationSetEnd(resourceSet, resourceType, resourceProperty); if (thisEnd != null) { return thisEnd == this.End1 ? this.End2 : this.End1; } return null; }
/// <summary>Composes all query interceptors into a single expression.</summary> /// <param name="serviceInstance">Web service instance.</param> /// <param name="container">Container for which interceptors should run.</param> /// <returns>An expression the filter for query interceptors, possibly null.</returns> internal static Expression ComposeQueryInterceptors(object serviceInstance, ResourceSetWrapper container) { Debug.Assert(container != null, "container != null"); MethodInfo[] methods = container.QueryInterceptors; if (methods == null || methods.Length == 0) { return null; } LambdaExpression filter = null; for (int i = 0; i < methods.Length; i++) { Expression predicate; try { predicate = (Expression)methods[i].Invoke(serviceInstance, WebUtil.EmptyObjectArray); } catch (TargetInvocationException tie) { ErrorHandler.HandleTargetInvocationException(tie); throw; } if (predicate == null) { throw new InvalidOperationException(Strings.DataService_AuthorizationReturnedNullQuery(methods[i].Name, methods[i].DeclaringType.FullName)); } // predicate is LambdaExpression -- otherwise signature check missed something and following cast would throw LambdaExpression lambdaPredicate = ((LambdaExpression)predicate); if (filter == null) { filter = lambdaPredicate; } else { ParameterExpression parameter = filter.Parameters[0]; Expression adjustedPredicate = ParameterReplacerVisitor.Replace( lambdaPredicate.Body, // expression lambdaPredicate.Parameters[0], // oldParameter parameter); // newParameter filter = Expression.Lambda(Expression.And(filter.Body, adjustedPredicate), parameter); } } return filter; }
/// <summary> /// Composes the specified <paramref name="queryExpression"/> for the /// given <paramref name="container"/> with authorization /// callbacks. /// </summary> /// <param name="service">Data service on which to invoke method.</param> /// <param name="container">resource set to compose with.</param> /// <param name="queryExpression">Query to compose.</param> /// <returns>The resulting composed query.</returns> internal static Expression ComposeResourceContainer(IDataService service, ResourceSetWrapper container, Expression queryExpression) { Debug.Assert(service != null, "service != null"); Debug.Assert(container != null, "container != null"); Debug.Assert(queryExpression != null, "queryExpression != null"); MethodInfo[] methods = container.QueryInterceptors; if (methods != null) { for (int i = 0; i < methods.Length; i++) { Expression predicate; try { predicate = (Expression)methods[i].Invoke(service.Instance, WebUtil.EmptyObjectArray); } catch (TargetInvocationException tie) { ErrorHandler.HandleTargetInvocationException(tie); throw; } if (predicate == null) { throw new InvalidOperationException(Strings.DataService_AuthorizationReturnedNullQuery(methods[i].Name, methods[i].DeclaringType.FullName)); } // predicate is LambdaExpression -- otherwise signature check missed something and following cast would throw queryExpression = queryExpression.QueryableWhere((LambdaExpression)predicate); } } return queryExpression; }
/// <summary> /// Match navigation properties with their partners for the given resource set. /// </summary> /// <param name="resourceSet">The resource set to supply the necessary data for matchign up the navigation properties.</param> /// <remarks> /// Materialization state: Full required. No change in materialization state. /// Cache state: none required. No change in cache state. /// </remarks> private void PairUpNavigationPropertiesForEntitySet(ResourceSetWrapper resourceSet) { Debug.Assert(resourceSet != null, "resourceSet != null"); this.AssertMaterializationState(MetadataProviderState.Full); // Populate for derived types foreach (ResourceType derivedType in this.MetadataProvider.GetDerivedTypes(resourceSet.ResourceType)) { this.PairUpNavigationPropertiesForEntitySetAndType(resourceSet, derivedType); } // Populate for this type and its base types ResourceType resourceType = resourceSet.ResourceType; while (resourceType != null) { this.PairUpNavigationPropertiesForEntitySetAndType(resourceSet, resourceType); resourceType = resourceType.BaseType; } }
/// <summary>Checks whether this request has the specified reading rights.</summary> /// <param name="container">Container to check.</param> /// <param name="singleResult">Whether a single or multiple resources are requested.</param> /// <exception cref="DataServiceException">Thrown if <paramref name="singleResult"/> aren't available.</exception> internal static void CheckResourceRightsForRead(ResourceSetWrapper container, bool singleResult) { Debug.Assert(container != null, "container != null"); EntitySetRights requiredRights = singleResult ? EntitySetRights.ReadSingle : EntitySetRights.ReadMultiple; CheckResourceRights(container, requiredRights); }
/// <summary> /// Add the given entity set to the model. /// </summary> /// <param name="resourceSet">ResourceSetWrapper instance to add.</param> /// <returns>an instance of IEdmEntitySet for the given <paramref name="resourceSet"/>.</returns> internal IEdmEntitySet EnsureEntitySet(ResourceSetWrapper resourceSet) { this.EnsureDefaultEntityContainer(); // Make sure if the entity container exists string entityContainerName = resourceSet.EntityContainerName ?? this.metadataProvider.ContainerName; MetadataProviderEdmEntityContainer entityContainer = this.FindExistingEntityContainer(entityContainerName); if (entityContainer == null) { entityContainer = new MetadataProviderEdmEntityContainer(this, entityContainerName, this.GetContainerNamespace()); MetadataProviderUtils.ConvertCustomAnnotations(this, this.metadataProvider.GetEntityContainerAnnotations(entityContainerName), entityContainer); this.entityContainerCache.Add(entityContainerName, entityContainer); this.entityContainerCache.Add(entityContainer.FullName(), entityContainer); } string entitySetName = MetadataProviderUtils.GetEntitySetName(resourceSet.ResourceSet); IEdmEntitySet entitySet = entityContainer.FindEntitySet(entitySetName); if (entitySet == null) { entitySet = entityContainer.AddEntitySet(entitySetName, resourceSet); } return entitySet; }
/// <summary>Creates new instance of node representing expanded navigation property.</summary> /// <param name="propertyName">The name of the property to project and expand.</param> /// <param name="property">The <see cref="ResourceProperty"/> for this property. Can only be null for the root node.</param> /// <param name="targetResourceType">Target resource type on which the expansion needs to happen.</param> /// <param name="resourceSetWrapper">The resource set to which the expansion leads.</param> /// <param name="orderingInfo">The ordering info for this node. null means no ordering to be applied.</param> /// <param name="filter">The filter for this node. null means no filter to be applied.</param> /// <param name="skipCount">Number of results to skip. null means no results to be skipped.</param> /// <param name="takeCount">Maximum number of results to return. null means return all available results.</param> /// <param name="maxResultsExpected">Maximum number of expected results. Hint that the provider should return /// at least maxResultsExpected + 1 results (if available).</param> /// <param name="selectExpandClause">The select expand clause for the current node from the URI Parser.</param> internal ExpandedProjectionNode( string propertyName, ResourceProperty property, ResourceType targetResourceType, ResourceSetWrapper resourceSetWrapper, OrderingInfo orderingInfo, Expression filter, int? skipCount, int? takeCount, int? maxResultsExpected, SelectExpandClause selectExpandClause) : base(propertyName, property, targetResourceType) { Debug.Assert(resourceSetWrapper != null, "resourceSetWrapper != null"); Debug.Assert(property != null || (propertyName.Length == 0 && targetResourceType == null), "We don't support open navigation properties."); Debug.Assert(property == null || targetResourceType.TryResolvePropertyName(property.Name) != null, "Property must exist on the target resource type"); Debug.Assert(selectExpandClause != null, "selectExpandClause != null"); this.resourceSetWrapper = resourceSetWrapper; this.orderingInfo = orderingInfo; this.filter = filter; this.skipCount = skipCount; this.takeCount = takeCount; this.maxResultsExpected = maxResultsExpected; this.nodes = new List<ProjectionNode>(); this.hasExpandedPropertyOnDerivedType = false; this.selectExpandClause = selectExpandClause; }
/// <summary> /// If the provider implements IConcurrencyProvider, then this method passes the etag values /// to the provider, otherwise compares the etag itself. /// </summary> /// <param name="resourceCookie">etag values for the given resource.</param> /// <param name="container">container for the given resource.</param> internal void SetETagValues(object resourceCookie, ResourceSetWrapper container) { Debug.Assert(resourceCookie != null, "resourceCookie != null"); Debug.Assert(container != null, "container != null"); AstoriaRequestMessage host = this.service.OperationContext.RequestMessage; Debug.Assert(String.IsNullOrEmpty(host.GetRequestIfNoneMatchHeader()), "IfNoneMatch header cannot be specified for Update/Delete operations"); // Resolve the cookie first to the actual resource type object actualEntity = this.ResolveResource(resourceCookie); Debug.Assert(actualEntity != null, "actualEntity != null"); ResourceType resourceType = WebUtil.GetNonPrimitiveResourceType(this.service.Provider, actualEntity); Debug.Assert(resourceType != null, "resourceType != null"); IList<ResourceProperty> etagProperties = this.service.Provider.GetETagProperties(container.Name, resourceType); if (etagProperties.Count == 0) { if (!String.IsNullOrEmpty(host.GetRequestIfMatchHeader())) { throw DataServiceException.CreateBadRequestError(Strings.Serializer_NoETagPropertiesForType); } // If the type has no etag properties, then we do not need to do any etag checks return; } // If the provider implements IConcurrencyProvider, then we need to call the provider // and pass the etag values. Else, we need to compare the etag values ourselves. IDataServiceUpdateProvider concurrencyProvider = this.updateProvider as IDataServiceUpdateProvider; if (concurrencyProvider != null) { bool? checkForEquality = null; IEnumerable<KeyValuePair<string, object>> etagValues; if (!String.IsNullOrEmpty(host.GetRequestIfMatchHeader())) { checkForEquality = true; etagValues = ParseETagValue(etagProperties, host.GetRequestIfMatchHeader()); } else { etagValues = WebUtil.EmptyKeyValuePairStringObject; } concurrencyProvider.SetConcurrencyValues(resourceCookie, checkForEquality, etagValues); } else if (String.IsNullOrEmpty(host.GetRequestIfMatchHeader())) { throw DataServiceException.CreateBadRequestError(Strings.DataService_CannotPerformOperationWithoutETag(resourceType.FullName)); } else if (host.GetRequestIfMatchHeader() != XmlConstants.HttpAnyETag) { // Compare If-Match header value with the current etag value, if the If-Match header value is not equal to '*' string etagValue = WebUtil.GetETagValue(resourceCookie, resourceType, etagProperties, this.service, false /*getMethod*/); Debug.Assert(!String.IsNullOrEmpty(etagValue), "etag value can never be null"); if (etagValue != host.GetRequestIfMatchHeader()) { throw DataServiceException.CreatePreConditionFailedError(Strings.Serializer_ETagValueDoesNotMatch); } } }
/// <summary> /// Gets the result set for the operation. /// </summary> /// <param name="provider">Provider instance to resolve the path expression.</param> /// <param name="bindingSet">Binding resource set.</param> /// <returns>Returns the result resource set for the operation.</returns> internal ResourceSetWrapper GetResultSet(DataServiceProviderWrapper provider, ResourceSetWrapper bindingSet) { #if DEBUG Debug.Assert(this.isReadOnly, "Wrapper class has not been initialized yet."); #endif if (this.resourceSet != null) { Debug.Assert(this.resourceSet.ResourceSet == this.operation.ResourceSet, "this.resourceSet.ResourceSet == this.serviceOperation.ResourceSet"); Debug.Assert(this.ResultSetPathExpression == null, "this.ResultSetPathExpression == null"); return this.resourceSet; } if (this.ResultSetPathExpression != null) { Debug.Assert(provider != null, "provider != null"); if (bindingSet == null) { throw new InvalidOperationException(Strings.OperationWrapper_PathExpressionRequiresBindingSet(this.Name)); } ResourceSetWrapper resultSet = this.ResultSetPathExpression.GetTargetSet(provider, bindingSet); if (resultSet == null) { throw new InvalidOperationException(Strings.OperationWrapper_TargetSetFromPathExpressionNotNotVisible(this.Name, this.ResultSetPathExpression.PathExpression, bindingSet.Name)); } return resultSet; } return null; }
/// <summary> /// Adds an entity set backed by the <paramref name="resourceSet"/> to the entity container. /// </summary> /// <param name="entitySetName">The name of the entity set.</param> /// <param name="resourceSet">The resource set backing the entity set to be created.</param> /// <returns>an instance of IEdmEntitySet that just got added.</returns> /// <remarks> /// This method will also create the association sets and associations for the entity set. /// Materialization state: EntityContainers required. No change in materialization state. /// </remarks> internal IEdmEntitySet AddEntitySet(string entitySetName, ResourceSetWrapper resourceSet) { Debug.Assert(!string.IsNullOrEmpty(entitySetName), "!string.IsNullOrEmpty(entitySetName)"); Debug.Assert(resourceSet != null, "resourceSet != null"); IEdmEntitySet entitySet = new MetadataProviderEdmEntitySet(this.model, this, resourceSet); MetadataProviderUtils.ConvertCustomAnnotations(this.model, resourceSet.CustomAnnotations, entitySet); this.entitySetCache.Add(entitySetName, entitySet); return entitySet; }
/// <summary>Match navigation properties with their partners for the given set and type</summary> /// <param name="resourceSet">Resource type to inspect.</param> /// <param name="resourceType">Resource set to inspect.</param> /// <remarks> /// Materialization state: Full required. No change in materialization state. /// Cache state: none required. No change in cache state. /// </remarks> private void PairUpNavigationPropertiesForEntitySetAndType(ResourceSetWrapper resourceSet, ResourceType resourceType) { Debug.Assert(resourceSet != null, "resourceSet != null"); Debug.Assert(resourceType != null, "resourceType != null"); this.AssertMaterializationState(MetadataProviderState.Full); IEnumerable<ResourceProperty> resourceProperties = resourceType.PropertiesDeclaredOnThisType; if (resourceProperties != null) { foreach (ResourceProperty navigationProperty in resourceProperties.Where(p => p.TypeKind == ResourceTypeKind.EntityType)) { // For every nav property, hook it up to its partner. this.PairUpNavigationProperty(resourceSet, resourceType, navigationProperty); } } }
/// <summary> /// Tracks the specified <paramref name="target"/> for a /// given <paramref name="action "/> on the <paramref name="container"/>. /// </summary> /// <param name="target">Object to be tracked.</param> /// <param name="container">Container in which object is changed.</param> /// <param name="action">Action affecting target.</param> /// <remarks> /// If <paramref name="target"/> was already being tracked, the actions are OR'ed together. /// </remarks> internal void TrackAction(object target, ResourceSetWrapper container, UpdateOperations action) { AssertActionValues(target, container); Debug.Assert(this.items != null, "this.items != null - otherwise FireNotification has already been called"); // If it won't be necessary for us to fire authorizatio methods, // skip tracking altogether. if (container.ChangeInterceptors == null) { return; } // Get the container for which the change has taken place. Dictionary<object, UpdateOperations> changedItems; if (!this.items.TryGetValue(container, out changedItems)) { // In order to mantain backwards compatibility, we are going to use default comparer for V1 // providers. However, for V2 providers we are going to do reference equality comparisons. if (this.service.Provider.HasReflectionOrEFProviderQueryBehavior) { changedItems = new Dictionary<object, UpdateOperations>(EqualityComparer<object>.Default); } else { changedItems = new Dictionary<object, UpdateOperations>(ReferenceEqualityComparer<object>.Instance); } this.items.Add(container, changedItems); } UpdateOperations existingAction; if (changedItems.TryGetValue(target, out existingAction)) { if ((action | existingAction) != existingAction) { changedItems[target] = action | existingAction; } } else { changedItems.Add(target, action); } }
/// <summary> /// Fills in the rest of required information for navigation properties. /// </summary> /// <param name="resourceSet">Resource set to inspect.</param> /// <param name="resourceType">Resource type to inspect.</param> /// <param name="navigationProperty">Navigation property to inspect.</param> internal void PairUpNavigationProperty(ResourceSetWrapper resourceSet, ResourceType resourceType, ResourceProperty navigationProperty) { Debug.Assert(resourceSet != null, "resourceSet != null"); Debug.Assert(resourceType != null, "resourceType != null"); Debug.Assert(navigationProperty != null && navigationProperty.ResourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "navigationProperty != null && navigationProperty.ResourceType.ResourceTypeKind == ResourceTypeKind.EntityType"); Debug.Assert(resourceType.TryResolvePropertiesDeclaredOnThisTypeByName(navigationProperty.Name) != null, "navigationProperty must be declared on resourceType."); string associationSetKey = resourceSet.Name + '_' + resourceType.FullName + '_' + navigationProperty.Name; // We changed the association logic on the server to create the correct navigation property for V4 to run the tests. // The association logic on server side needs to be removed. // Check the cache first; we might already have visited the partiner of this navigation property. if (this.associationSetByKeyCache.ContainsKey(associationSetKey)) { return; } ResourceAssociationSet resourceAssociationSet = this.MetadataProvider.GetResourceAssociationSet(resourceSet, resourceType, navigationProperty); if (resourceAssociationSet != null) { this.PairUpNavigationPropertyWithResourceAssociationSet(resourceAssociationSet); this.associationSetByKeyCache.Add(associationSetKey, resourceAssociationSet.Name); } }
/// <summary> /// Determines the minimum version that can be used for this specific type. /// /// Note: if you don't know the specific type only the set, you will need to find the maximum of this /// for all types in the hierarchy for the set type see ResourceSetWrapper.MinimumPayloadVersion /// </summary> /// <param name="service">The data service instance</param> /// <param name="resourceSet">The set that the type belongs to.</param> /// <returns>The minimum version that can be used for a payload of this specific type.</returns> internal Version GetMinimumResponseVersion(IDataService service, ResourceSetWrapper resourceSet) { Debug.Assert(resourceSet.ResourceType.IsAssignableFrom(this), "The passed in resourceSet does not include this type"); Version minimumVersion = VersionUtil.Version4Dot0; // If target set contains collection properties we need v4.0 // If target type contains named streams, we need v4.0 minimumVersion = VersionUtil.RaiseVersion(minimumVersion, this.GetMinimumProtocolVersion()); // If the resource type is an open type, then we do not know the metadata of the open property and hence cannot // predict the response version. Hence we need to bump the version to the maximum, // and if we encounter during serialization, anything greater than the computed response version, we will throw instream error. if (this.IsOpenType) { Version maxProtocolVersion = service.Configuration.DataServiceBehavior.MaxProtocolVersion.ToVersion(); Version requestMaxVersion = service.OperationContext.RequestMessage.RequestMaxVersion; Version responseVersion = (requestMaxVersion < maxProtocolVersion) ? requestMaxVersion : maxProtocolVersion; minimumVersion = VersionUtil.RaiseVersion(minimumVersion, responseVersion); } return minimumVersion; }