/// <summary> /// Initializes a new instance of the <see cref="MetadataProviderEdmActionImport"/> class. /// </summary> /// <param name="model">The model this instance belongs to.</param> /// <param name="operation">The resource operation underlying this action import.</param> /// <param name="namespaceName">The namespace of the EdmOperation.</param> /// <remarks> /// This constructor assumes that the entity set for this service operation has already be created. /// </remarks> internal MetadataProviderEdmAction( MetadataProviderEdmModel model, OperationWrapper operation, string namespaceName) : base(model, operation, namespaceName) { }
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(); }
public OperationSerializerTests() { ResourceType intType = ResourceType.GetPrimitiveResourceType(typeof(int)); var customerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "FQ.NS", "Customer", false); customerType.CanReflectOnInstanceType = false; customerType.AddProperty(new ResourceProperty("Id", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, intType) { CanReflectOnInstanceTypeProperty = false }); customerType.SetReadOnly(); var operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType), new ServiceActionParameter("P2", intType) }, null); operation.SetReadOnly(); this.baseTypeOperation = new OperationWrapper(operation); var bestCustomerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, customerType, "FQ.NS", "BestCustomer", false); bestCustomerType.SetReadOnly(); operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", bestCustomerType) }, null); operation.SetReadOnly(); this.derivedTypeOperation = new OperationWrapper(operation); operation = new ServiceAction("Unambiguous", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType) }, null); operation.SetReadOnly(); this.unambiguousOperation = new OperationWrapper(operation); this.entityToSerialize = EntityToSerialize.CreateFromExplicitValues(new object(), bestCustomerType, new TestSerializedEntityKey("http://odata.org/Service.svc/Customers(0)/", bestCustomerType.FullName)); this.testSubject = CreateOperationSerializer(AlwaysAdvertiseActions); }
public OperationLinkBuilderTests() { ResourceType intType = ResourceType.GetPrimitiveResourceType(typeof(int)); var customerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "FQ.NS", "Customer", false); customerType.CanReflectOnInstanceType = false; customerType.AddProperty(new ResourceProperty("Id", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, intType) { CanReflectOnInstanceTypeProperty = false }); customerType.SetReadOnly(); var operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType), new ServiceActionParameter("P2", intType) }, null); operation.SetReadOnly(); this.operationWithParameters = new OperationWrapper(operation); var typeWithEscapedName = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "FQ NS", "+ /", false); typeWithEscapedName.CanReflectOnInstanceType = false; typeWithEscapedName.AddProperty(new ResourceProperty("Number", ResourcePropertyKind.Primitive, intType) { CanReflectOnInstanceTypeProperty = false }); typeWithEscapedName.SetReadOnly(); operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType), new ServiceActionParameter("P2", typeWithEscapedName) }, null); operation.SetReadOnly(); this.operationWithEscapedParameter = new OperationWrapper(operation); var bestCustomerType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, customerType, "FQ.NS", "BestCustomer", false); bestCustomerType.SetReadOnly(); operation = new ServiceAction("Action", intType, OperationParameterBindingKind.Sometimes, new[] { new ServiceActionParameter("P1", customerType) }, null); operation.SetReadOnly(); this.operationBoundToBaseType = new OperationWrapper(operation); this.entityToSerialize = EntityToSerialize.CreateFromExplicitValues(new object(), bestCustomerType, new TestSerializedEntityKey("http://odata.org/Service.svc/Customers/", bestCustomerType.FullName)); var metadataUri = new Uri("http://odata.org/Service.svc/$metadata"); this.testSubject = new OperationLinkBuilder("MyContainer", metadataUri); }
internal bool AdvertiseServiceAction(OperationWrapper serviceAction, object resourceInstance, bool resourceInstanceInFeed, ref ODataAction actionToSerialize) { Debug.Assert(resourceInstance != null, "resourceInstance != null"); Debug.Assert(serviceAction != null && serviceAction.Kind == OperationKind.Action, "serviceOperation != null && serviceAction.Kind == OperationKind.Action"); Debug.Assert(actionToSerialize != null, "actionToSerialize != null."); return(this.ActionProvider.AdvertiseServiceAction(this.OperationContext, serviceAction.ServiceAction, resourceInstance, resourceInstanceInFeed, ref actionToSerialize)); }
/// <summary> /// Builds up an instance of <see cref="IDataServiceInvokable"/> for the given <paramref name="serviceAction"/> with the provided <paramref name="parameterTokens"/>. /// </summary> /// <param name="serviceAction">The service action to invoke.</param> /// <param name="parameterTokens">The parameter tokens required to invoke the service action.</param> /// <returns>An instance of <see cref="IDataServiceInvokable"/> to invoke the action with.</returns> internal Expression CreateInvokable(OperationWrapper serviceAction, Expression[] parameterTokens) { Debug.Assert(serviceAction != null && serviceAction.Kind == OperationKind.Action, "serviceAction != null && serviceAction.Kind == OperationKind.Action"); return(Expression.Call( DataServiceExecutionProviderMethods.CreateServiceActionInvokableMethodInfo, Expression.Constant(this.OperationContext, typeof(DataServiceOperationContext)), Expression.Constant(this.ActionProvider, typeof(IDataServiceActionProvider)), Expression.Constant(serviceAction.ServiceAction, typeof(ServiceAction)), Expression.NewArrayInit(typeof(object), parameterTokens))); }
/// <summary> /// Initializes a new instance of the <see cref="MetadataProviderEdmFunctionImport"/> class. /// </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> internal MetadataProviderEdmFunction(MetadataProviderEdmModel model, OperationWrapper operation, string namespaceName) : base(model, operation, namespaceName) { this.IsComposable = DefaultIsComposable; // By default everything is composable except functions that return IEnumerable if (operation.ReturnInstanceType != null && !(typeof(IEnumerable).IsAssignableFrom(operation.ReturnInstanceType) && !typeof(IQueryable).IsAssignableFrom(operation.ReturnInstanceType))) { this.IsComposable = true; } }
/// <summary> /// Add a function import to the entity container. /// </summary> /// <param name="serviceOperation">The service operation to add to the entity container.</param> /// <returns>The newly added or cached function import instance.</returns> internal IEdmOperationImport EnsureOperationImport(OperationWrapper serviceOperation) { Debug.Assert(serviceOperation != null, "serviceOperation != null"); string functionImportName = serviceOperation.Name; Debug.Assert(!string.IsNullOrEmpty(functionImportName), "!string.IsNullOrEmpty(functionImportName)"); List <IEdmOperationImport> operationImports; IEdmOperationImport operationImport = null; if (this.operationImportCache.TryGetValue(functionImportName, out operationImports)) { operationImport = operationImports.Cast <MetadataProviderEdmOperationImport>().SingleOrDefault(f => f.ServiceOperation == serviceOperation); } if (operationImport == null) { MetadataProviderEdmOperation operation = null; if (serviceOperation.Kind == OperationKind.Action || serviceOperation.Method == XmlConstants.HttpMethodPost) { operation = new MetadataProviderEdmAction(this.model, serviceOperation, this.Namespace); if (serviceOperation.OperationParameterBindingKind == OperationParameterBindingKind.Never) { operationImport = new MetadataProviderEdmActionImport(this.model, this, (MetadataProviderEdmAction)operation); } } else { Debug.Assert(serviceOperation.Method == XmlConstants.HttpMethodGet, "Method should be a get"); operation = new MetadataProviderEdmFunction(this.model, serviceOperation, this.Namespace); if (serviceOperation.OperationParameterBindingKind == OperationParameterBindingKind.Never) { operationImport = new MetadataProviderEdmFunctionImport(this.model, this, (MetadataProviderEdmFunction)operation); } } if (operationImport != null) { if (operationImports == null) { operationImports = new List <IEdmOperationImport>(); this.operationImportCache.Add(functionImportName, operationImports); } operationImports.Add(operationImport); } this.model.AddOperation(operation); } return(operationImport); }
internal Uri BuildMetadataLink(OperationWrapper operation, bool entityHasMultipleActionsWithSameName) { Debug.Assert(!String.IsNullOrEmpty(operation.Name), "!string.IsNullOrEmpty(operation.Name)"); StringBuilder builder = new StringBuilder(); builder.Append(UriUtil.UriToString(this.metadataUri)); builder.Append('#'); builder.Append(Uri.EscapeDataString(namespaceName)); builder.Append('.'); builder.Append(Uri.EscapeDataString(operation.Name)); return new Uri(builder.ToString()); }
/// <summary> /// Load operation imports from model's metadata provider. /// </summary> /// <param name="qualifiedName">The name of the entity set to be loaded.</param> /// <returns>Operation imports that are loaded.</returns> internal List <IEdmOperationImport> LazyLoadServiceOperationImports(string qualifiedName) { List <IEdmOperationImport> operationImports = new List <IEdmOperationImport>(); OperationWrapper operationWrapper = this.model.MetadataProvider.TryResolveServiceOperation(qualifiedName); if (operationWrapper != null) { IEdmOperationImport foundOperationImport = this.model.EnsureDefaultEntityContainer().EnsureOperationImport(operationWrapper); if (foundOperationImport != null) { operationImports.Add(foundOperationImport); } } else { var operationWrapperQaulified = this.model.MetadataProvider.TryResolveServiceOperation(this.containerName + "." + qualifiedName); if (operationWrapperQaulified != null) { IEdmOperationImport foundOperationImport = this.model.EnsureDefaultEntityContainer().EnsureOperationImport(operationWrapperQaulified); if (foundOperationImport != null) { operationImports.Add(foundOperationImport); } } } // metadata interface in addition to the action provider interface. if (this.model.ActionProviderWrapper != null) { bool nameIsContainerQualified; var operationName = this.model.MetadataProvider.GetNameFromContainerQualifiedName(qualifiedName, out nameIsContainerQualified); var operation = this.model.ActionProviderWrapper.TryResolveServiceAction(operationName, MetadataProviderUtils.GetResourceType((IEdmType)null)); if (operation != null) { // Only top level actions will have an operation import. IEdmOperationImport foundOperationImport = this.model.EnsureDefaultEntityContainer().EnsureOperationImport(operation); if (foundOperationImport != null) { operationImports.Add(foundOperationImport); } } } return(operationImports); }
/// <summary> /// Validates if a service action is advertisable. /// </summary> /// <param name="resourceType">Resource type to which the service action is bound to.</param> /// <param name="serviceAction">Service action to be validated for advertisement.</param> /// <param name="existingOperations">The current set of actions. Used to avoid duplicate actions.</param> /// <returns>Validated service operation to be advertised. Null, if the service operation is not suppose to be advertised.</returns> private OperationWrapper ValidateCanAdvertiseServiceAction(ResourceType resourceType, ServiceAction serviceAction, OperationCache existingOperations) { Debug.Assert(resourceType != null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType, "resourceType != null && resourceType.ResourceTypeKind == ResourceTypeKind.EntityType"); if (serviceAction == null) { return(null); } Debug.Assert(!String.IsNullOrEmpty(serviceAction.Name), "The name of the service operation was null or empty"); if (existingOperations.Contains(serviceAction)) { throw new DataServiceException(500, Strings.DataServiceActionProviderWrapper_DuplicateAction(serviceAction.Name)); } ServiceActionParameter bindingParameter = (ServiceActionParameter)serviceAction.BindingParameter; if (bindingParameter == null) { Debug.Assert(!String.IsNullOrEmpty(serviceAction.Name), "The name of the service action was null or empty"); throw new DataServiceException(500, Strings.DataServiceActionProviderWrapper_ServiceActionBindingParameterNull(serviceAction.Name)); } ResourceType bindingParameterType = bindingParameter.ParameterType; Debug.Assert(bindingParameterType != null, "bindingParameterType != null"); // We only support advertising actions for entities and not entity collections. Since resourceType must be an entity type, // IsAssignableFrom will fail when the bindingParameterType is an entity collection type. if (!bindingParameterType.IsAssignableFrom(resourceType)) { throw new DataServiceException(500, Strings.DataServiceActionProviderWrapper_ResourceTypeMustBeAssignableToBindingParameterResourceType(serviceAction.Name, bindingParameterType.FullName, resourceType.FullName)); } Debug.Assert(bindingParameterType.ResourceTypeKind == ResourceTypeKind.EntityType, "We only support advertising actions for entities and not entity collections."); OperationWrapper operationWrapper = this.provider.ValidateOperation(serviceAction); if (operationWrapper != null) { existingOperations.Add(operationWrapper); } return(operationWrapper); }
public OperationCacheTests() { this.entityType1 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "My.Namespace", "Entity1", false); this.entityType2 = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "My.Namespace", "Entity2", false); var intType = ResourceType.GetPrimitiveResourceType(typeof(int)); this.actionWithBindingParameter1 = CreateAction("SameName", intType, this.entityType1); this.actionWithBindingParameter2 = CreateAction("SameName", intType, this.entityType2); this.serviceOperation = CreateServiceOperation("SameName", intType); this.actionWithBindingParameter1.SetReadOnly(); this.actionWithBindingParameter2.SetReadOnly(); this.serviceOperation.SetReadOnly(); this.actionWithBindingParameterWrapper1 = new OperationWrapper(this.actionWithBindingParameter1); this.actionWithBindingParameterWrapper2 = new OperationWrapper(this.actionWithBindingParameter2); this.serviceOperationWrapper = new OperationWrapper(this.serviceOperation); }
/// <summary> /// Gets the target link value for an <see cref="ODataOperation"/> /// </summary> /// <param name="entityToSerialize">The current entity being serialized.</param> /// <param name="operation">The operation to generate the link for.</param> /// <param name="entityHasMultipleActionsWithSameName">Whether or not there are multiple operations in the current scope with the same name as the current operation.</param> /// <returns>Uri representing link to use for invoking this operation.</returns> internal Uri BuildTargetLink(EntityToSerialize entityToSerialize, OperationWrapper operation, bool entityHasMultipleActionsWithSameName) { Debug.Assert(entityToSerialize != null, "entityToSerialize != null"); Debug.Assert(operation != null, "operation != null"); Debug.Assert(operation.BindingParameter != null, "operation.BindingParameter != null"); Debug.Assert(operation.BindingParameter.ParameterType != null, "operation.BindingParameter.ParameterType != null"); string targetSegment = operation.GetActionTargetSegmentByResourceType(entityToSerialize.ResourceType, this.namespaceName); // If there are multiple operations with the same name, then using the edit link of the entry would cause the target to potentially resolve to the wrong // operation. Instead, use the actual binding type of the specific operation. if (entityHasMultipleActionsWithSameName) { Uri editLinkWithBindingType = RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLinkWithoutSuffix, operation.BindingParameter.ParameterType.FullName); return RequestUriProcessor.AppendUnescapedSegment(editLinkWithBindingType, targetSegment); } return RequestUriProcessor.AppendUnescapedSegment(entityToSerialize.SerializedKey.AbsoluteEditLink, targetSegment); }
/// <summary>Checks whether this request has the specified rights.</summary> /// <param name="operation">Operation 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 CheckServiceOperationRights(OperationWrapper operation, bool singleResult) { Debug.Assert(operation != null, "operation != null"); if (operation.ResultKind != ServiceOperationResultKind.Void) { ServiceOperationRights requiredRights = singleResult ? ServiceOperationRights.ReadSingle : ServiceOperationRights.ReadMultiple; CheckServiceOperationRights(operation, requiredRights); } }
/// <summary>Checks whether this request has the specified rights.</summary> /// <param name="operation">Operation to check.</param> /// <param name="requiredRights">Required rights.</param> /// <exception cref="DataServiceException">Thrown if <paramref name="requiredRights"/> aren't available.</exception> internal static void CheckServiceOperationRights(OperationWrapper operation, ServiceOperationRights requiredRights) { Debug.Assert(operation != null, "operation != null"); Debug.Assert(requiredRights != ServiceOperationRights.None, "requiredRights != EntitySetRights.None"); ServiceOperationRights effectiveRights = operation.ServiceOperationRights; if ((requiredRights & effectiveRights) == 0) { throw DataServiceException.CreateForbidden(); } }
/// <summary> /// Tries to find a wrapper for the given operation. /// </summary> /// <param name="operation">The operation to find a wrapper for.</param> /// <param name="wrapper">The wrapper, if found.</param> /// <returns>Whether or not a wrapper was found.</returns> internal bool TryGetWrapper(Operation operation, out OperationWrapper wrapper) { var cacheKey = GetCacheKey(operation); return this.underlyingCache.TryGetValue(cacheKey, out wrapper); }
private static bool AppendToLinks(OperationWrapper serviceAction, object resourceInstance, bool resourceInstanceInFeed, ref ODataAction actionToSerialize) { actionToSerialize.Metadata = new Uri(actionToSerialize.Metadata.OriginalString + "/SomethingThatWasAppended"); actionToSerialize.Target = new Uri(actionToSerialize.Target.OriginalString + "/SomethingThatWasAppended"); return true; }
/// <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> /// Creates a cache-key for the given operation wrapper. /// </summary> /// <param name="operation">The operation wrapper.</param> /// <returns>The cache-key.</returns> private static string GetCacheKey(OperationWrapper operation) { Debug.Assert(operation != null, "operation != null"); ResourceType bindingType = operation.BindingParameter == null ? null : operation.BindingParameter.ParameterType; return GetCacheKey(operation.Name, bindingType); }
/// <summary> /// Add the given service operation to the model. /// </summary> /// <param name="operationWrapper">ServiceOperationWrapper instance to add.</param> internal void AddServiceOperation(OperationWrapper operationWrapper) { Debug.Assert(operationWrapper != null, "operationWrapper != null"); // All the service operations live in the default entity container MetadataProviderEdmEntityContainer defaultEntityContainer = this.EnsureDefaultEntityContainer(); defaultEntityContainer.EnsureOperationImport(operationWrapper); }
/// <summary> /// Determines whether the given operation has already been cached. /// </summary> /// <param name="operationWrapper">The operation wrapper to look for.</param> /// <returns>Whether or not the operation has been cached.</returns> internal bool Contains(OperationWrapper operationWrapper) { return this.underlyingCache.ContainsKey(GetCacheKey(operationWrapper)); }
/// <summary> /// Gets the IEdmOperationI for a specified <paramref name="serviceOperation"/>. /// </summary> /// <param name="serviceOperation">The service action or function to get the function import for.</param> /// <returns>The function import.</returns> protected IEdmOperation GetOperation(OperationWrapper serviceOperation) { var model = this.Service.Provider.GetMetadataProviderEdmModel(); model.EnsureDefaultEntityContainer().EnsureOperationImport(serviceOperation); return model.GetRelatedOperation(serviceOperation); }
/// <summary> /// Adds the given operation wrapper to the cache. /// </summary> /// <param name="wrapper">The wrapper to add.</param> internal void Add(OperationWrapper wrapper) { var cacheKey = GetCacheKey(wrapper); this.underlyingCache.Add(cacheKey, wrapper); }
/// <summary> /// Tries to find a wrapper for an operation with the given name and binding parameter type. /// </summary> /// <param name="operationName">The operation name.</param> /// <param name="bindingType">The operation's binding parameter's type, or null.</param> /// <param name="wrapper">The wrapper, if found.</param> /// <returns>Whether or not a wrapper was found.</returns> internal bool TryGetWrapper(string operationName, ResourceType bindingType, out OperationWrapper wrapper) { var cacheKey = GetCacheKey(operationName, bindingType); return(this.underlyingCache.TryGetValue(cacheKey, out wrapper)); }
/// <summary> /// Add the given service operation to the model. /// </summary> /// <param name="operationWrapper">ServiceOperationWrapper instance to add.</param> public void AddOperationToEdmModel(OperationWrapper operationWrapper) { this.provider.GetMetadataProviderEdmModel().AddServiceOperation(operationWrapper); }
/// <summary> /// Tries to serialize the operation. /// </summary> /// <param name="entityToSerialize">The entity to serialize.</param> /// <param name="resourceInstanceInFeed">Whether or not the entity is being serialized in a feed.</param> /// <param name="entityHasMultipleActionsWithSameName">Whether or not there are multiple operations in the current scope with the same name as the current operation.</param> /// <param name="serviceOperationWrapper">The service operation wrapper.</param> /// <param name="odataAction">The ODL object-model representation of the action.</param> /// <returns>Whether or not to serialize the operation.</returns> private bool TrySerializeOperation(EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, bool entityHasMultipleActionsWithSameName, OperationWrapper serviceOperationWrapper, out ODataAction odataAction) { Debug.Assert(serviceOperationWrapper != null, "serviceOperationWrapper != null"); // We only advertise actions. This is a debug assert because GetServiceOperationsByResourceType only returns actions. Debug.Assert(serviceOperationWrapper.Kind == OperationKind.Action, "Only actions can be advertised"); Uri metadata = this.operationLinkBuilder.BuildMetadataLink(serviceOperationWrapper, entityHasMultipleActionsWithSameName); // If the action has OperationParameterBindingKind set to "Always" then we advertise the action without calling "AdvertiseServiceAction". bool isAlwaysAvailable = serviceOperationWrapper.OperationParameterBindingKind == OperationParameterBindingKind.Always; odataAction = new ODataAction { Metadata = metadata }; // There is some subtlety to the interaction between action advertisement and whether or not to include title/target on the wire. // // 1) If an action is always available: // The provider author does not get a chance to customize the title/target values... // so the values will be based on conventions... // so by default do not write them on the wire // 2) If it is only sometimes available: // The values need to be computed to provide them on the instance given to the provider... // but they might not be changed by the provider author... // so compare them to the computed values, and do not write them if they match. // TODO: Action provider should be able to customize title/target even if the action is 'always' advertised // If this gets fixed, then all the behavior should collapse to emulate case #2 above // Create a lazy Uri for the target, because we may need it more than once (see case #2 above). SimpleLazy<Uri> lazyActionTargetUri = new SimpleLazy<Uri>(() => this.operationLinkBuilder.BuildTargetLink(entityToSerialize, serviceOperationWrapper, entityHasMultipleActionsWithSameName)); this.metadataPropertyManager.SetTitle(odataAction, isAlwaysAvailable, serviceOperationWrapper.Name); this.metadataPropertyManager.SetTarget(odataAction, isAlwaysAvailable, () => lazyActionTargetUri.Value); // If the operation is always available, // 1. Return true for MetadataQueryOption.All. // 2. Return false for MetadataQueryOption.None. // 3. Return false for MetadataQueryOption.Default. if (isAlwaysAvailable) { return this.payloadMetadataParameterInterpreter.ShouldIncludeAlwaysAvailableOperation(); } return this.AskProviderIfActionShouldBeAdvertised(entityToSerialize, resourceInstanceInFeed, serviceOperationWrapper, lazyActionTargetUri, entityHasMultipleActionsWithSameName, ref odataAction); }
/// <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> /// Gets the related edm operation. /// </summary> /// <param name="operationWrapper">The operation wrapper.</param> /// <returns>Returns the EdmOperation that is associated with the specified Operation Wrapper.</returns> internal IEdmOperation GetRelatedOperation(OperationWrapper operationWrapper) { return this.operationWrapperOperationLookUp[operationWrapper]; }
/// <summary> /// Add a function import to the entity container. /// </summary> /// <param name="serviceOperation">The service operation to add to the entity container.</param> /// <returns>The newly added or cached function import instance.</returns> internal IEdmOperationImport EnsureOperationImport(OperationWrapper serviceOperation) { Debug.Assert(serviceOperation != null, "serviceOperation != null"); string functionImportName = serviceOperation.Name; Debug.Assert(!string.IsNullOrEmpty(functionImportName), "!string.IsNullOrEmpty(functionImportName)"); List<IEdmOperationImport> operationImports; IEdmOperationImport operationImport = null; if (this.operationImportCache.TryGetValue(functionImportName, out operationImports)) { operationImport = operationImports.Cast<MetadataProviderEdmOperationImport>().SingleOrDefault(f => f.ServiceOperation == serviceOperation); } if (operationImport == null) { MetadataProviderEdmOperation operation = null; if (serviceOperation.Kind == OperationKind.Action || serviceOperation.Method == XmlConstants.HttpMethodPost) { operation = new MetadataProviderEdmAction(this.model, serviceOperation, this.Namespace); if (serviceOperation.OperationParameterBindingKind == OperationParameterBindingKind.Never) { operationImport = new MetadataProviderEdmActionImport(this.model, this, (MetadataProviderEdmAction)operation); } } else { Debug.Assert(serviceOperation.Method == XmlConstants.HttpMethodGet, "Method should be a get"); operation = new MetadataProviderEdmFunction(this.model, serviceOperation, this.Namespace); if (serviceOperation.OperationParameterBindingKind == OperationParameterBindingKind.Never) { operationImport = new MetadataProviderEdmFunctionImport(this.model, this, (MetadataProviderEdmFunction)operation); } } if (operationImport != null) { if (operationImports == null) { operationImports = new List<IEdmOperationImport>(); this.operationImportCache.Add(functionImportName, operationImports); } operationImports.Add(operationImport); } this.model.AddOperation(operation); } return operationImport; }
/// <summary> /// Tries to find a wrapper for an operation with the given name and binding parameter type. /// </summary> /// <param name="operationName">The operation name.</param> /// <param name="bindingType">The operation's binding parameter's type, or null.</param> /// <param name="wrapper">The wrapper, if found.</param> /// <returns>Whether or not a wrapper was found.</returns> internal bool TryGetWrapper(string operationName, ResourceType bindingType, out OperationWrapper wrapper) { var cacheKey = GetCacheKey(operationName, bindingType); return this.underlyingCache.TryGetValue(cacheKey, out wrapper); }
/// <summary> /// Asks the provider if the action should be advertised in payloads. /// </summary> /// <param name="entityToSerialize">The entity to serialize.</param> /// <param name="resourceInstanceInFeed">Whether or not the entity is being serialized in a feed.</param> /// <param name="serviceOperationWrapper">The service operation wrapper.</param> /// <param name="lazyActionTargetUri">Target uri of the action, which will only be generated if needed.</param> /// <param name="entityHasMultipleActionsWithSameName">Whether or not there are multiple operations in the current scope with the same name as the current operation.</param> /// <param name="odataAction">The ODL object-model representation of the action.</param> /// <returns>Whether or not the action should be advertised.</returns> private bool AskProviderIfActionShouldBeAdvertised(EntityToSerialize entityToSerialize, bool resourceInstanceInFeed, OperationWrapper serviceOperationWrapper, SimpleLazy<Uri> lazyActionTargetUri, bool entityHasMultipleActionsWithSameName, ref ODataAction odataAction) { if (this.advertiseServiceAction(serviceOperationWrapper, entityToSerialize.Entity, resourceInstanceInFeed, ref odataAction)) { if (odataAction == null) { throw new DataServiceException(500, Microsoft.OData.Service.Strings.DataServiceActionProviderWrapper_AdvertiseServiceActionCannotReturnNullActionToSerialize); } // Always set target and title if there are overloaded actions. if (!entityHasMultipleActionsWithSameName) { this.metadataPropertyManager.CheckForUnmodifiedTitle(odataAction, serviceOperationWrapper.Name); this.metadataPropertyManager.CheckForUnmodifiedTarget(odataAction, () => lazyActionTargetUri.Value); } // make the target link relative this.MakeOperationTargetRelativeFromMetadataUriIfJsonLight(odataAction); return true; } odataAction = null; return false; }
private static bool AlwaysAdvertiseActions(OperationWrapper serviceAction, object resourceInstance, bool resourceInstanceInFeed, ref ODataAction actionToSerialize) { // the links should always be provided and always be absolute, even if they will be written relative in JSON-Light. actionToSerialize.Metadata.Should().NotBeNull().And.Subject.As<Uri>().IsAbsoluteUri.Should().BeTrue(); actionToSerialize.Target.Should().NotBeNull().And.Subject.As<Uri>().IsAbsoluteUri.Should().BeTrue(); return true; }
/// <summary> /// Tries to find a cached wrapper for an operation with the given name and binding parameter type. /// </summary> /// <param name="operationName">The operation name.</param> /// <param name="bindingType">The operation's binding parameter's type, or null.</param> /// <param name="wrapper">The wrapper, if found.</param> /// <returns>Whether or not a wrapper was found.</returns> public bool TryGetCachedOperationWrapper(string operationName, ResourceType bindingType, out OperationWrapper wrapper) { return(this.provider.OperationWrapperCache.TryGetWrapper(operationName, bindingType, out wrapper)); }
/// <summary> /// Creates a segment for a service operation /// </summary> /// <param name="serviceOperation">The service operation for the segment.</param> /// <returns>A fully populated PathSegment representing the service operation</returns> private static SegmentInfo CreateSegmentForServiceOperation(OperationWrapper serviceOperation) { Debug.Assert(serviceOperation != null, "serviceOperation != null"); WebUtil.DebugEnumIsDefined(serviceOperation.ResultKind); SegmentInfo segment = new SegmentInfo { Identifier = serviceOperation.Name, Operation = serviceOperation, TargetSource = RequestTargetSource.ServiceOperation, TargetResourceSet = serviceOperation.ResourceSet }; if (serviceOperation.ResultKind != ServiceOperationResultKind.Void) { segment.TargetResourceType = serviceOperation.ResultType; segment.TargetKind = TargetKindFromType(segment.TargetResourceType); // this service operation returns results // we should check service operation rights // SingleResult operations are service operations defined with [SingleResult] attribute, returns a DirectValue // or a service operation returning multiple results, but contains key predicates in the query portion. // For these operations, you MUST have ReadSingle defined // For multiple-result-operations(IQueryable/IEnumerable), you MUST have ReadMultiple defined. segment.SingleResult = (serviceOperation.ResultKind == ServiceOperationResultKind.QueryWithSingleResult || serviceOperation.ResultKind == ServiceOperationResultKind.DirectValue); } else { segment.TargetResourceType = null; segment.TargetKind = RequestTargetKind.VoidOperation; } return segment; }
/// <summary> /// Tries to find a wrapper for the given operation. /// </summary> /// <param name="operation">The operation to find a wrapper for.</param> /// <param name="wrapper">The wrapper, if found.</param> /// <returns>Whether or not a wrapper was found.</returns> internal bool TryGetWrapper(Operation operation, out OperationWrapper wrapper) { var cacheKey = GetCacheKey(operation); return(this.underlyingCache.TryGetValue(cacheKey, out wrapper)); }
/// <summary> /// Creates a segment for the given service action. /// </summary> /// <param name="previousSegment">The previous segment before the operation to be invoked.</param> /// <param name="serviceAction">The service action to create the segment for.</param> /// <returns>A fully populated PathSegment representing the service action</returns> private SegmentInfo CreateSegmentForServiceAction(SegmentInfo previousSegment, OperationWrapper serviceAction) { Debug.Assert(serviceAction != null && serviceAction.Kind == OperationKind.Action, "serviceAction != null && serviceAction.Kind == OperationKind.Action"); SegmentInfo segment = new SegmentInfo() { Identifier = serviceAction.Name, Operation = serviceAction }; WebUtil.DebugEnumIsDefined(serviceAction.ResultKind); Debug.Assert(segment.IsServiceActionSegment, "IsServiceActionSegment(segment)"); if (previousSegment != null && previousSegment.TargetKind == RequestTargetKind.Link) { throw DataServiceException.CreateBadRequestError(Strings.RequestUriProcessor_LinkSegmentMustBeFollowedByEntitySegment(serviceAction.Name, XmlConstants.UriLinkSegment)); } segment.TargetSource = RequestTargetSource.ServiceOperation; if (serviceAction.ResultKind != ServiceOperationResultKind.Void) { segment.TargetResourceSet = serviceAction.GetResultSet(this.providerWrapper, previousSegment == null ? null : previousSegment.TargetResourceSet); segment.TargetResourceType = serviceAction.ReturnType; segment.TargetKind = TargetKindFromType(segment.TargetResourceType); if (segment.TargetKind == RequestTargetKind.Resource && segment.TargetResourceSet == null) { // Service actions are either visible (ServiceActionRights.Invoke) or not (ServiceActionRight.None). The fact that // DataServiceActionProviderWrapper.TryResolveServiceAction() returns a non-null value means the action is visible. // If the result of the action is of entity type, we need to make sure the target set is visible or else the self // and edit links of the entities in the response payload would not be usable. Debug.Assert(serviceAction.IsVisible, "serviceAction.IsVisible"); throw DataServiceException.CreateForbidden(); } segment.SingleResult = serviceAction.ResultKind == ServiceOperationResultKind.DirectValue; Debug.Assert(serviceAction.ResultKind != ServiceOperationResultKind.QueryWithSingleResult, "QueryWithSingleResult is not applicable for Actions."); } else { segment.TargetResourceSet = null; segment.TargetResourceType = null; segment.TargetKind = RequestTargetKind.VoidOperation; } return segment; }
/// <summary> /// Determines whether the given operation has already been cached. /// </summary> /// <param name="operationWrapper">The operation wrapper to look for.</param> /// <returns>Whether or not the operation has been cached.</returns> internal bool Contains(OperationWrapper operationWrapper) { return(this.underlyingCache.ContainsKey(GetCacheKey(operationWrapper))); }
/// <summary> /// Reads the parameters for the specified <paramref name="operation"/> from the <paramref name="host"/>. /// </summary> /// <param name="host">RequestMessage with request information.</param> /// <param name="operation">Operation with parameters to be read.</param> /// <returns>A new object[] with parameter values.</returns> private static object[] ReadOperationParameters(AstoriaRequestMessage host, OperationWrapper operation) { Debug.Assert(host != null, "host != null"); Debug.Assert(operation != null, "operation != null"); Debug.Assert(operation.Kind == OperationKind.ServiceOperation, "operation.Kind == OperationKind.ServiceOperation"); object[] operationParameters = new object[operation.Parameters.Count]; for (int i = 0; i < operation.Parameters.Count; i++) { Type parameterType = operation.Parameters[i].ParameterType.InstanceType; string queryStringValue = host.GetQueryStringItem(operation.Parameters[i].Name); operationParameters[i] = ParseOperationParameter(parameterType, queryStringValue); } return operationParameters; }