/// <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);
        }
 /// <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 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>
 /// 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();
            }
        }
Example #18
0
 /// <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;
 }
Example #20
0
        /// <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();
        }
Example #21
0
 /// <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);
        }
Example #23
0
 /// <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);
 }
Example #25
0
        /// <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);
        }
Example #26
0
        /// <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;
        }
Example #32
0
 /// <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);
 }
Example #33
0
 /// <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>
        /// 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;
        }
Example #38
0
        /// <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;
        }
Example #40
0
 /// <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;
        }