public void GetTargetSetTestsSetBindingTypeShouldPerformCorrectValidation() { ResourceType actorEntityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "foo", "Actor", false) { CanReflectOnInstanceType = false }; ResourceProperty idProperty = new ResourceProperty("ID", ResourcePropertyKind.Primitive | ResourcePropertyKind.Key, ResourceType.GetPrimitiveResourceType(typeof(int))) { CanReflectOnInstanceTypeProperty = false }; actorEntityType.AddProperty(idProperty); ResourceType addressComplexType = new ResourceType(typeof(object), ResourceTypeKind.ComplexType, null, "foo", "Address", false) { CanReflectOnInstanceType = false }; addressComplexType.AddProperty(new ResourceProperty("StreetAddress", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(string))) { CanReflectOnInstanceTypeProperty = false }); addressComplexType.AddProperty(new ResourceProperty("ZipCode", ResourcePropertyKind.Primitive, ResourceType.GetPrimitiveResourceType(typeof(int))) { CanReflectOnInstanceTypeProperty = false }); actorEntityType.AddProperty(new ResourceProperty("PrimaryAddress", ResourcePropertyKind.ComplexType, addressComplexType) { CanReflectOnInstanceTypeProperty = false }); actorEntityType.AddProperty(new ResourceProperty("OtherAddresses", ResourcePropertyKind.Collection, ResourceType.GetCollectionResourceType(addressComplexType)) { CanReflectOnInstanceTypeProperty = false }); ResourceType movieEntityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, null, "foo", "Movie", false) { CanReflectOnInstanceType = false }; movieEntityType.AddProperty(idProperty); ResourceProperty moviesNavProp = new ResourceProperty("Movies", ResourcePropertyKind.ResourceSetReference, movieEntityType) { CanReflectOnInstanceTypeProperty = false }; actorEntityType.AddProperty(moviesNavProp); ResourceProperty actorsNavProp = new ResourceProperty("Actors", ResourcePropertyKind.ResourceSetReference, actorEntityType) { CanReflectOnInstanceTypeProperty = false }; movieEntityType.AddProperty(actorsNavProp); ResourceType derivedActorEntityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, actorEntityType, "foo", "DerivedActor", false) { CanReflectOnInstanceType = false }; ResourceType derivedMovieEntityType = new ResourceType(typeof(object), ResourceTypeKind.EntityType, movieEntityType, "foo", "DerivedMovie", false) { CanReflectOnInstanceType = false }; actorEntityType.SetReadOnly(); derivedActorEntityType.SetReadOnly(); movieEntityType.SetReadOnly(); derivedMovieEntityType.SetReadOnly(); addressComplexType.SetReadOnly(); DataServiceProviderSimulator providerSimulator = new DataServiceProviderSimulator(); providerSimulator.AddResourceType(actorEntityType); providerSimulator.AddResourceType(derivedActorEntityType); providerSimulator.AddResourceType(movieEntityType); providerSimulator.AddResourceType(derivedMovieEntityType); providerSimulator.AddResourceType(addressComplexType); ResourceSet actorSet = new ResourceSet("Actors", actorEntityType); ResourceSet movieSet = new ResourceSet("Movies", movieEntityType); actorSet.SetReadOnly(); movieSet.SetReadOnly(); providerSimulator.AddResourceSet(actorSet); providerSimulator.AddResourceSet(movieSet); providerSimulator.AddResourceAssociationSet(new ResourceAssociationSet("Actors_Movies", new ResourceAssociationSetEnd(actorSet, actorEntityType, moviesNavProp), new ResourceAssociationSetEnd(movieSet, movieEntityType, actorsNavProp))); DataServiceConfiguration config = new DataServiceConfiguration(providerSimulator); config.SetEntitySetAccessRule("*", EntitySetRights.All); config.DataServiceBehavior.MaxProtocolVersion = ODataProtocolVersion.V4; var dataService = new DataServiceSimulator() { OperationContext = new DataServiceOperationContext(new DataServiceHostSimulator()) }; dataService.ProcessingPipeline = new DataServiceProcessingPipeline(); DataServiceStaticConfiguration staticConfiguration = new DataServiceStaticConfiguration(dataService.Instance.GetType(), providerSimulator); IDataServiceProviderBehavior providerBehavior = DataServiceProviderBehavior.CustomDataServiceProviderBehavior; DataServiceProviderWrapper provider = new DataServiceProviderWrapper( new DataServiceCacheItem( config, staticConfiguration), providerSimulator, providerSimulator, dataService, false); dataService.Provider = provider; provider.ProviderBehavior = providerBehavior; var testCases = new[] { new { AppendParameterName = true, Path = "", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), ErrorMessage = default(string) }, new { AppendParameterName = true, Path = "/Movies", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = ResourceSetWrapper.CreateResourceSetWrapper(movieSet, provider, set => set), ErrorMessage = default(string) }, new { AppendParameterName = true, Path = "/Movies/Actors/Movies", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = ResourceSetWrapper.CreateResourceSetWrapper(movieSet, provider, set => set), ErrorMessage = default(string) }, new { AppendParameterName = true, Path = "/foo.DerivedActor/Movies/foo.DerivedMovie/Actors/foo.DerivedActor/Movies", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = ResourceSetWrapper.CreateResourceSetWrapper(movieSet, provider, set => set), ErrorMessage = default(string) }, new { AppendParameterName = true, Path = "/", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression '{0}/' is not a valid expression because it contains an empty segment or it ends with '/'." }, new { AppendParameterName = true, Path = "/Movies/", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression '{0}/Movies/' is not a valid expression because it contains an empty segment or it ends with '/'." }, new { AppendParameterName = true, Path = "/Movies//Actors", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = ResourceSetWrapper.CreateResourceSetWrapper(movieSet, provider, set => set), ErrorMessage = "The path expression '{0}/Movies//Actors' is not a valid expression because it contains an empty segment or it ends with '/'." }, new { AppendParameterName = true, Path = "/foo.DerivedActor", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression '{0}/foo.DerivedActor' is not a valid expression because it ends with the type identifier 'foo.DerivedActor'. A valid path expression must not end in a type identifier." }, new { AppendParameterName = true, Path = "/Movies/foo.DerivedMovie", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression '{0}/Movies/foo.DerivedMovie' is not a valid expression because it ends with the type identifier 'foo.DerivedMovie'. A valid path expression must not end in a type identifier." }, new { AppendParameterName = true, Path = "/Foo", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression '{0}/Foo' is not a valid expression because the segment 'Foo' is not a type identifier or a property on the resource type 'foo.Actor'." }, new { AppendParameterName = true, Path = "/ID", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression '{0}/ID' is not a valid expression because the segment 'ID' is a property of type 'Edm.Int32'. A valid path expression must only contain properties of entity type." }, new { AppendParameterName = true, Path = "/OtherAddresses", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression '{0}/OtherAddresses' is not a valid expression because the segment 'OtherAddresses' is a property of type 'Collection(foo.Address)'. A valid path expression must only contain properties of entity type." }, new { AppendParameterName = true, Path = "/Movies/Actors/PrimaryAddress", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression '{0}/Movies/Actors/PrimaryAddress' is not a valid expression because the segment 'PrimaryAddress' is a property of type 'foo.Address'. A valid path expression must only contain properties of entity type." }, new { AppendParameterName = false, Path = "foo", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression 'foo' is not a valid path expression. A valid path expression must start with the binding parameter name '{0}'." }, new { AppendParameterName = false, Path = "abc/pqr", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression 'abc/pqr' is not a valid path expression. A valid path expression must start with the binding parameter name '{0}'." }, new { AppendParameterName = false, Path = "actorParameter", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression 'actorParameter' is not a valid path expression. A valid path expression must start with the binding parameter name '{0}'." }, new { AppendParameterName = false, Path = "actorsParameter", BindingSet = ResourceSetWrapper.CreateResourceSetWrapper(actorSet, provider, set => set), TargetSet = default(ResourceSetWrapper), ErrorMessage = "The path expression 'actorsParameter' is not a valid path expression. A valid path expression must start with the binding parameter name '{0}'." }, }; ServiceActionParameter actorParameter = new ServiceActionParameter("actor", actorEntityType); ServiceActionParameter actorsParameter = new ServiceActionParameter("actors", ResourceType.GetEntityCollectionResourceType(actorEntityType)); var parameters = new ServiceActionParameter[] { actorParameter, actorsParameter }; foreach (var parameter in parameters) { foreach (var testCase in testCases) { string pathString = testCase.AppendParameterName ? parameter.Name + testCase.Path : testCase.Path; var path = new ResourceSetPathExpression(pathString); Assert.AreEqual(pathString, path.PathExpression); string expectedErrorMessage = testCase.ErrorMessage == null ? null : string.Format(testCase.ErrorMessage, parameter.Name); try { path.SetBindingParameter(parameter); path.InitializePathSegments(provider); var targetSet = path.GetTargetSet(provider, testCase.BindingSet); Assert.IsNull(expectedErrorMessage, "Expecting exception but received none."); Assert.AreEqual(targetSet.Name, testCase.TargetSet.Name); } catch (InvalidOperationException e) { Assert.AreEqual(expectedErrorMessage, e.Message); } } } }
private object GetMarshalledParameter(DataServiceOperationContext operationContext, ServiceActionParameter serviceActionParameter, object value) { var parameterKind = serviceActionParameter.ParameterType.ResourceTypeKind; // Need to Marshall MultiValues i.e. Collection(Primitive) & Collection(ComplexType) if (parameterKind == ResourceTypeKind.EntityType) { // This entity is UNATTACHED. But people are likely to want to edit this... IDataServiceUpdateProvider2 updateProvider = operationContext.GetService(typeof(IDataServiceUpdateProvider2)) as IDataServiceUpdateProvider2; value = updateProvider.GetResource(value as IQueryable, serviceActionParameter.ParameterType.InstanceType.FullName); value = updateProvider.ResolveResource(value); // This will attach the entity. } else if (parameterKind == ResourceTypeKind.EntityCollection) { // WCF Data Services constructs an IQueryable that is NoTracking... // but that means people can't just edit the entities they pull from the Query. var query = value as ObjectQuery; query.MergeOption = MergeOption.AppendOnly; } else if (parameterKind == ResourceTypeKind.Collection) { // need to coerce into a List<> for dispatch var enumerable = value as IEnumerable; // the <T> in List<T> is the Instance type of the ItemType var elementType = (serviceActionParameter.ParameterType as CollectionResourceType).ItemType.InstanceType; // call IEnumerable.Cast<T>(); var castMethod = CastMethodGeneric.MakeGenericMethod(elementType); object marshalledValue = castMethod.Invoke(null, new[] { enumerable }); // call IEnumerable<T>.ToList(); var toListMethod = ToListMethodGeneric.MakeGenericMethod(elementType); value = toListMethod.Invoke(null, new[] { marshalledValue }); } return(value); }
private MethodInfo RetrieveOverloadedServiceAction(IEnumerable <MethodInfo> overloadedServiceActionMethods, ServiceActionParameter bindingParameterToSearch) { foreach (MethodInfo mi in overloadedServiceActionMethods) { ParameterInfo bindingParameter = mi.GetParameters().FirstOrDefault(); // handle unbound actions if (bindingParameter == null && bindingParameterToSearch == null) { return(mi); } // find the MethodInfo for the action based on the binding parameter type if (bindingParameter != null && bindingParameterToSearch != null && bindingParameter.ParameterType == bindingParameterToSearch.ParameterType.InstanceType) { return(mi); } } return(null); }
/// <summary> /// Constructs a delegate to invoke the service action with the provided parameters. /// </summary> /// <param name="operationContext">The data service operation context instance.</param> /// <param name="dataService">The data service instance.</param> /// <param name="action">The service action to invoke.</param> /// <param name="parameters">The parameters required to invoke the service action.</param> private void ConstructInvokeDelegateForServiceAction(DataServiceOperationContext operationContext, ServiceAction action, object[] parameters) { var info = action.CustomState as DSPServiceActionInfo; if (info == null) { throw new InvalidOperationException("Insufficient information to invoke the service action!"); } IDataServiceUpdateProvider2 updateProvider = (IDataServiceUpdateProvider2)operationContext.GetService(typeof(IDataServiceUpdateProvider2)); if (updateProvider == null) { throw new InvalidOperationException("DataServiceOperationContext.GetService(IDataServiceUpdateProvider2) returned null!"); } this.invokeDelegate = () => { int idx = 0; if (action.BindingParameter != null) { if (action.BindingParameter.ParameterType.ResourceTypeKind == ResourceTypeKind.EntityType) { parameters[idx] = updateProvider.GetResource((IQueryable)parameters[idx], null); } parameters[idx] = updateProvider.ResolveResource(parameters[idx]); idx++; } var methodParameters = info.Method.GetParameters(); for (; idx < parameters.Length; idx++) { ServiceActionParameter sap = action.Parameters[idx]; if (sap.ParameterType.ResourceTypeKind == ResourceTypeKind.Collection && parameters[idx] != null) { ResourceType itemResourceType = ((CollectionResourceType)sap.ParameterType).ItemType; // Need to call ResolveResource on each complex item in the collection. Type parameterType = methodParameters[idx].ParameterType; Type itemType = parameterType.GetGenericArguments()[0]; MethodInfo resolveCollectionMethod; if (parameterType.GetGenericTypeDefinition() == typeof(IQueryable <>)) { resolveCollectionMethod = DSPInvokableAction.ResolveCollectionAsQueryableMethodInfo.MakeGenericMethod(itemType); } else { resolveCollectionMethod = DSPInvokableAction.ResolveCollectionMethodInfo.MakeGenericMethod(itemType); } parameters[idx] = resolveCollectionMethod.Invoke(null, new object[] { updateProvider, parameters[idx], itemResourceType.ResourceTypeKind == ResourceTypeKind.Primitive }); } else if (sap.ParameterType.ResourceTypeKind == ResourceTypeKind.ComplexType) { parameters[idx] = updateProvider.ResolveResource(parameters[idx]); } } this.result = info.Method.Invoke(info.Instance, parameters); }; }