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); }
/// <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); }; }