Example #1
0
        /// <summary>
        /// Invokes the Update method and gets the result.
        /// </summary>
        protected virtual object GetUpdateMethodResult(IDictionary keys, IDictionary values, IDictionary oldValues)
        {
            ModelDataSourceMethod method = EvaluateUpdateMethodParameters(keys, values, oldValues);
            ModelDataMethodResult result = InvokeMethod(method);

            return(result.ReturnValue);
        }
Example #2
0
        /// <summary>
        /// Invokes the Insert method and gets the result.
        /// </summary>
        protected virtual object GetInsertMethodResult(IDictionary values)
        {
            ModelDataSourceMethod method = EvaluateInsertMethodParameters(values);
            ModelDataMethodResult result = InvokeMethod(method);

            return(result.ReturnValue);
        }
Example #3
0
 //Evaluates the select method parameters using the custom value provides. This is done after page load so that
 //we raise the DataSourceChanged event if the values of parameters change.
 private void EvaluateSelectParameters()
 {
     if (!String.IsNullOrEmpty(SelectMethod))
     {
         ModelDataSourceMethod method = FindMethod(SelectMethod);
         EvaluateMethodParameters(DataSourceOperation.Select, method, controlValues: null, isPageLoadComplete: true);
     }
 }
Example #4
0
        /// <summary>
        /// Invokes the select method and gets the result. Also handles auto paging and sorting when required.
        /// </summary>
        /// <param name="arguments">The DataSourceSelectArguments for the select operation.
        /// When applicable, this method sets the TotalRowCount out parameter in the arguments.
        /// </param>
        /// <returns>The return value from the select method.</returns>
        protected virtual object GetSelectMethodResult(DataSourceSelectArguments arguments)
        {
            if (SelectMethod.Length == 0)
            {
                throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_SelectNotSupported));
            }

            DataSourceSelectResultProcessingOptions options = null;
            ModelDataSourceMethod method = EvaluateSelectMethodParameters(arguments, out options);
            ModelDataMethodResult result = InvokeMethod(method);

            return(ProcessSelectMethodResult(arguments, options, result));
        }
Example #5
0
        protected virtual ModelDataSourceMethod EvaluateInsertMethodParameters(IDictionary values)
        {
            if (!CanInsert)
            {
                throw new NotSupportedException(SR.GetString(SR.ModelDataSourceView_InsertNotSupported));
            }

            IDictionary caseInsensitiveNewValues = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);

            MergeDictionaries(values, caseInsensitiveNewValues);

            ModelDataSourceMethod method = FindMethod(InsertMethod);

            EvaluateMethodParameters(DataSourceOperation.Insert, method, caseInsensitiveNewValues);
            return(method);
        }
Example #6
0
        protected virtual ModelDataSourceMethod EvaluateDeleteMethodParameters(IDictionary keys, IDictionary oldValues)
        {
            if (!CanDelete)
            {
                throw new NotSupportedException(SR.GetString(SR.ModelDataSourceView_DeleteNotSupported));
            }

            IDictionary caseInsensitiveOldValues = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);

            MergeDictionaries(keys, caseInsensitiveOldValues);
            MergeDictionaries(oldValues, caseInsensitiveOldValues);

            ModelDataSourceMethod method = FindMethod(DeleteMethod);

            EvaluateMethodParameters(DataSourceOperation.Delete, method, caseInsensitiveOldValues);
            return(method);
        }
Example #7
0
        protected virtual ModelDataSourceMethod EvaluateUpdateMethodParameters(IDictionary keys, IDictionary values, IDictionary oldValues)
        {
            if (!CanUpdate)
            {
                throw new NotSupportedException(SR.GetString(SR.ModelDataSourceView_UpdateNotSupported));
            }

            IDictionary caseInsensitiveNewValues = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);

            // We start out with the old values, just to pre-populate the list with items
            // that might not have corresponding new values. For example if a GridView has
            // a read-only field, there will be an old value, but no new value. The data object
            // still has to have *some* value for a given field, so we just use the old value.
            MergeDictionaries(oldValues, caseInsensitiveNewValues);
            MergeDictionaries(keys, caseInsensitiveNewValues);
            MergeDictionaries(values, caseInsensitiveNewValues);

            ModelDataSourceMethod method = FindMethod(UpdateMethod);

            EvaluateMethodParameters(DataSourceOperation.Update, method, caseInsensitiveNewValues);
            return(method);
        }
Example #8
0
        /// <summary>
        /// Invokes the data method in a secure fashion.
        /// </summary>
        /// <param name="method">
        /// The ModelDataSouceMethod object specifying the Instance on which the method should be invoked (null for static methods),
        /// the MethodInfo of the method to be invoked and the Parameters for invoking the method.
        /// All the above properties should be populated before this method is called.
        /// </param>
        /// <returns>
        /// A ModelDataSouceResult object containing the ReturnValue of the method and any out parameters.
        /// </returns>
        protected virtual ModelDataMethodResult InvokeMethod(ModelDataSourceMethod method)
        {
            object returnValue = null;

            object[] parameterValues = null;
            if (method.Parameters != null && method.Parameters.Count > 0)
            {
                parameterValues = new object[method.Parameters.Count];
                for (int i = 0; i < method.Parameters.Count; i++)
                {
                    parameterValues[i] = method.Parameters[i];
                }
            }

            returnValue = _methodInvokerDispatcher(method.MethodInfo, method.Instance, parameterValues);
            OrderedDictionary outputParameters = GetOutputParameters(method.MethodInfo.GetParameters(), parameterValues);

            method.Instance = null;

            //Data Method is done executing, turn off the TryUpdateModel
            _owner.DataControl.Page.SetActiveValueProvider(null);

            return(new ModelDataMethodResult(returnValue, outputParameters));
        }
        private object GetUpdateMethodResult(IDictionary keys, IDictionary values, IDictionary oldValues, ModelDataSourceMethod method, bool isAsyncMethod, CancellationToken? cancellationToken) {
            method = method == null ? EvaluateUpdateMethodParameters(keys, values, oldValues) : EvaluateUpdateMethodParameters(keys, values, oldValues, method);
            SetCancellationTokenIfRequired(method, isAsyncMethod, cancellationToken);
            ModelDataMethodResult result = InvokeMethod(method, isAsyncMethod);

            return result.ReturnValue;
        }
        private ModelDataSourceMethod EvaluateDeleteMethodParameters(IDictionary keys, IDictionary oldValues, ModelDataSourceMethod method) {
            if (!CanDelete) {
                throw new NotSupportedException(SR.GetString(SR.ModelDataSourceView_DeleteNotSupported));
            }

            IDictionary caseInsensitiveOldValues = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);

            MergeDictionaries(keys, caseInsensitiveOldValues);
            MergeDictionaries(oldValues, caseInsensitiveOldValues);

            method = method ?? FindMethod(DeleteMethod);
            EvaluateMethodParameters(DataSourceOperation.Delete, method, caseInsensitiveOldValues);
            return method;
        }
        private ModelDataSourceMethod EvaluateInsertMethodParameters(IDictionary values, ModelDataSourceMethod method) {
            if (!CanInsert) {
                throw new NotSupportedException(SR.GetString(SR.ModelDataSourceView_InsertNotSupported));
            }

            IDictionary caseInsensitiveNewValues = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);

            MergeDictionaries(values, caseInsensitiveNewValues);

            method = method ?? FindMethod(InsertMethod);
            EvaluateMethodParameters(DataSourceOperation.Insert, method, caseInsensitiveNewValues);
            return method;
        }
        private ModelDataSourceMethod EvaluateSelectMethodParameters(DataSourceSelectArguments arguments, ModelDataSourceMethod method, bool isAsyncSelect, out DataSourceSelectResultProcessingOptions selectResultProcessingOptions) {
            IOrderedDictionary mergedParameters = MergeSelectParameters(arguments);
            // Resolve the method
            method = method ?? FindMethod(SelectMethod);

            Type selectMethodReturnType = method.MethodInfo.ReturnType;
            if (isAsyncSelect) {
                selectMethodReturnType = ExtractAsyncSelectReturnType(selectMethodReturnType);
            }

            Type modelType = ModelType;
            if (modelType == null) {
                //When ModelType is not specified but SelectMethod returns IQueryable<T>, we treat T as model type for auto paging and sorting.
                //If the return type is something like CustomType<U,T> : IQueryable<T>, we should use T for paging and sorting, hence
                //we walk over the return type's generic arguments for a proper match.
                foreach (Type typeParameter in selectMethodReturnType.GetGenericArguments()) {
                    if (typeof(IQueryable<>).MakeGenericType(typeParameter).IsAssignableFrom(selectMethodReturnType)) {
                        modelType = typeParameter;
                    }
                }
            }
            Type queryableModelType = (modelType != null) ? typeof(IQueryable<>).MakeGenericType(modelType) : null;

            //We do auto paging or auto sorting when the select method is returning an IQueryable and does not have parameters for paging or sorting.
            bool isReturningQueryable = queryableModelType != null && queryableModelType.IsAssignableFrom(selectMethodReturnType);

            if (isAsyncSelect && isReturningQueryable) {
                // async select method does not support returning IQueryable<>.
                throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_InvalidAsyncSelectReturnType, modelType));
            }

            bool autoPage = false;
            bool autoSort = false;

            if (arguments.StartRowIndex >= 0 && arguments.MaximumRows > 0) {
                autoPage = IsAutoPagingRequired(method.MethodInfo, isReturningQueryable, isAsyncSelect);

                if (isAsyncSelect) {
                    Debug.Assert(!autoPage, "auto-paging should not be true when using async select method");

                    // custom paging is not supported if the return type is not SelectResult
                    if (typeof(SelectResult) != selectMethodReturnType) {
                        throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_MustUseSelectResultAsReturnType));
                    }
                }
            }

            if (!String.IsNullOrEmpty(arguments.SortExpression)) {
                autoSort = IsAutoSortingRequired(method.MethodInfo, isReturningQueryable);
            }

            selectResultProcessingOptions = new DataSourceSelectResultProcessingOptions() { ModelType = modelType, AutoPage = autoPage, AutoSort = autoSort };
            EvaluateMethodParameters(DataSourceOperation.Select, method, mergedParameters);
            return method;
        }
        private void SetCancellationTokenIfRequired(ModelDataSourceMethod method, bool isAsyncMethod, CancellationToken? cancellationToken) {
            string cancellationTokenParameterName;
            if (isAsyncMethod && IsCancellationRequired(method.MethodInfo, out cancellationTokenParameterName)) {
                if (null == cancellationToken) {
                    throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_CancellationTokenIsNotSupported));
                }

                method.Parameters[cancellationTokenParameterName] = cancellationToken;
            }
        }
Example #14
0
 /// <summary>
 /// Evaluates the method parameters using model binding.
 /// </summary>
 /// <param name="dataSourceOperation">The datasource operation for which parameters are being evaluated.</param>
 /// <param name="modelDataSourceMethod">The ModelDataSourceMethod object for which the Parameter collection is being evaluated. The MethodInfo property should already be set on this object.</param>
 /// <param name="controlValues">The values from the data bound control.</param>
 protected virtual void EvaluateMethodParameters(DataSourceOperation dataSourceOperation, ModelDataSourceMethod modelDataSourceMethod, IDictionary controlValues)
 {
     EvaluateMethodParameters(dataSourceOperation, modelDataSourceMethod, controlValues, isPageLoadComplete: false);
 }
 /// <summary>
 /// Evaluates the method parameters using model binding.
 /// </summary>
 /// <param name="dataSourceOperation">The datasource operation for which parameters are being evaluated.</param>
 /// <param name="modelDataSourceMethod">The ModelDataSourceMethod object for which the Parameter collection is being evaluated. The MethodInfo property should already be set on this object.</param>
 /// <param name="controlValues">The values from the data bound control.</param>
 protected virtual void EvaluateMethodParameters(DataSourceOperation dataSourceOperation, ModelDataSourceMethod modelDataSourceMethod, IDictionary controlValues) {
     EvaluateMethodParameters(dataSourceOperation, modelDataSourceMethod, controlValues, isPageLoadComplete: false);
 }
        private Func<object, Task> GetSelectAsyncFunc(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback, ModelDataSourceMethod method) {
            Func<object, Task> func = async _ => {
                ValidateAsyncModelBindingRequirements();
                CancellationTokenSource cancellationTokenSource = _owner.DataControl.Page.CreateCancellationTokenFromAsyncTimeout();
                CancellationToken cancellationToken = cancellationTokenSource.Token;
                DataSourceSelectResultProcessingOptions selectResultProcessingOptions = null;
                ModelDataSourceMethod modelMethod = EvaluateSelectMethodParameters(arguments, method, true/*isAsyncSelect*/, out selectResultProcessingOptions);
                SetCancellationTokenIfRequired(modelMethod, true/*isAsyncMethod*/, cancellationToken);
                ModelDataMethodResult result = InvokeMethod(modelMethod);
                IEnumerable finalResult = null;
                if (result.ReturnValue != null) {
                    await (Task)result.ReturnValue;
                    var returnValue = GetPropertyValueByName(result.ReturnValue, "Result");

                    if (null == returnValue) {
                        // do nothing
                    }
                    // Users needs to use SelectResult as return type to use
                    // custom paging.
                    else if (returnValue is SelectResult) {
                        var viewOperationTask = _viewOperationTask;
                        if (viewOperationTask != null) {
                            await viewOperationTask;
                        }

                        var selectResult = (SelectResult)returnValue;
                        arguments.TotalRowCount = selectResult.TotalRowCount;
                        finalResult = CreateSelectResult(selectResult.Results, true/*isAsyncSelect*/);
                    }
                    else {
                        // The returnValue does not have to run through ProcessSelectMethodResult() as we
                        // don't support auto-paging or auto-sorting when using async select.
                        finalResult = CreateSelectResult(returnValue, true/*isAsyncSelect*/);
                    }
                }

                callback(finalResult);
                if (cancellationToken.IsCancellationRequested) {
                    throw new TimeoutException(SR.GetString(SR.Async_task_timed_out));
                }
            };

            return func;
        }
        private ModelDataSourceMethod EvaluateUpdateMethodParameters(IDictionary keys, IDictionary values, IDictionary oldValues, ModelDataSourceMethod method) {
            if (!CanUpdate) {
                throw new NotSupportedException(SR.GetString(SR.ModelDataSourceView_UpdateNotSupported));
            }

            IDictionary caseInsensitiveNewValues = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);

            // We start out with the old values, just to pre-populate the list with items
            // that might not have corresponding new values. For example if a GridView has
            // a read-only field, there will be an old value, but no new value. The data object
            // still has to have *some* value for a given field, so we just use the old value.
            MergeDictionaries(oldValues, caseInsensitiveNewValues);
            MergeDictionaries(keys, caseInsensitiveNewValues);
            MergeDictionaries(values, caseInsensitiveNewValues);

            method = method ?? FindMethod(UpdateMethod);
            EvaluateMethodParameters(DataSourceOperation.Update, method, caseInsensitiveNewValues);
            return method;
        }
Example #18
0
        /// <summary>
        /// Evaluates the method parameters using model binding.
        /// </summary>
        /// <param name="dataSourceOperation">The datasource operation for which parameters are being evaluated.</param>
        /// <param name="modelDataSourceMethod">The ModelDataSourceMethod object for which the Parameter collection is being evaluated. The MethodInfo property should already be set on this object.</param>
        /// <param name="controlValues">The values from the data bound control.</param>
        /// <param name="isPageLoadComplete">This must be set to true only when this method is called in Page's LoadComplete event handler
        /// to evaluate the select method parameters that use custom value providers so that we can identify any changes
        /// to those and mark the data-bound control for data binding if necessary.</param>
        protected virtual void EvaluateMethodParameters(DataSourceOperation dataSourceOperation, ModelDataSourceMethod modelDataSourceMethod, IDictionary controlValues, bool isPageLoadComplete)
        {
            Debug.Assert(_owner.DataControl.Page != null);
            Debug.Assert(_owner.DataControl.TemplateControl != null);

            MethodInfo actionMethod = modelDataSourceMethod.MethodInfo;

            IModelBinder binder = ModelBinders.Binders.DefaultBinder;

            IValueProvider dataBoundControlValueProvider = GetValueProviderFromDictionary(controlValues);

            ModelBindingExecutionContext modelBindingExecutionContext = _owner.DataControl.Page.ModelBindingExecutionContext;

            //This is used by ControlValueProvider later.
            modelBindingExecutionContext.PublishService <Control>(_owner.DataControl);

            //This is done for the TryUpdateModel to work inside a Data Method.
            if (dataSourceOperation != DataSourceOperation.Select)
            {
                _owner.DataControl.Page.SetActiveValueProvider(dataBoundControlValueProvider);
            }

            foreach (ParameterInfo parameterInfo in actionMethod.GetParameters())
            {
                object value     = null;
                string modelName = parameterInfo.Name;

                if (parameterInfo.ParameterType == typeof(ModelMethodContext))
                {
                    //ModelMethodContext is a special parameter we pass in for enabling developer to call
                    //TryUpdateModel when Select/Update/Delete/InsertMethods are on a custom class.
                    value = new ModelMethodContext(_owner.DataControl.Page);
                }
                //Do not attempt model binding the out parameters
                else if (!parameterInfo.IsOut)
                {
                    bool           validateRequest;
                    IValueProvider customValueProvider = GetCustomValueProvider(modelBindingExecutionContext, parameterInfo, ref modelName, out validateRequest);

                    //When we are evaluating the parameter at the time of page load, we do not want to populate the actual ModelState
                    //because there will be another evaluation at data-binding causing duplicate errors if model validation fails.
                    ModelStateDictionary modelState = isPageLoadComplete ? new ModelStateDictionary() : _owner.DataControl.Page.ModelState;

                    ModelBindingContext bindingContext = new ModelBindingContext()
                    {
                        ModelBinderProviders = ModelBinderProviders.Providers,
                        ModelMetadata        = ModelMetadataProviders.Current.GetMetadataForType(null, parameterInfo.ParameterType),
                        ModelState           = modelState,
                        ModelName            = modelName,
                        ValueProvider        = customValueProvider,
                        ValidateRequest      = validateRequest
                    };

                    //Select parameters that take custom values providers are tracked by ViewState so that
                    //we can detect any changes from previous page request and mark the data bound control for data binding if necessary.
                    if (dataSourceOperation == DataSourceOperation.Select && customValueProvider != null && parameterInfo.ParameterType.IsSerializable)
                    {
                        if (!SelectParameters.ContainsKey(parameterInfo.Name))
                        {
                            SelectParameters.Add(parameterInfo.Name, new MethodParameterValue());
                        }

                        if (binder.BindModel(modelBindingExecutionContext, bindingContext))
                        {
                            value = bindingContext.Model;
                        }
                        SelectParameters[parameterInfo.Name].UpdateValue(value);
                    }
                    else
                    {
                        if (isPageLoadComplete)
                        {
                            Debug.Assert(dataSourceOperation == DataSourceOperation.Select, "Only Select Operation should have been done immediately after page load");
                            //When this method is called as part of Page's LoadComplete event handler we do not have values in defaultValueProvider
                            //(i.e., values from DataBoundControl), so we need not evaluate the parameters values.
                            continue;
                        }

                        if (customValueProvider == null)
                        {
                            bindingContext.ValueProvider = dataBoundControlValueProvider;
                        }

                        if (binder.BindModel(modelBindingExecutionContext, bindingContext))
                        {
                            value = bindingContext.Model;
                        }
                    }

                    if (!isPageLoadComplete)
                    {
                        ValidateParameterValue(parameterInfo, value, actionMethod);
                    }
                }
                modelDataSourceMethod.Parameters.Add(parameterInfo.Name, value);
            }
        }
        private void SelectAsync(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback, ModelDataSourceMethod method) {
            Func<object, Task> func = GetSelectAsyncFunc(arguments, callback, method);

            var syncContext = _owner.DataControl.Page.Context.SyncContext as AspNetSynchronizationContext;
            if (null == syncContext) {
                throw new InvalidOperationException(SR.GetString(SR.ModelDataSourceView_UseAsyncMethodMustBeUsingAsyncPage));
            }

            // The first edition of the async model binding feature was implemented by registering async binding 
            // function as PageAsyncTask. We, however, decided not to do that because postponing data binding
            // to page async point changed the order of page events and caused many problems.
            // See the comment on SynchronizationHelper.QueueAsynchronousAsync for more details regarding to the PostAsync
            // function.
            syncContext.PostAsync(func, null);
        }
 /// <summary>
 /// Invokes the data method in a secure fashion.
 /// </summary>
 /// <param name="method">
 /// The ModelDataSouceMethod object specifying the Instance on which the method should be invoked (null for static methods), 
 /// the MethodInfo of the method to be invoked and the Parameters for invoking the method.
 /// All the above properties should be populated before this method is called.
 /// </param>
 /// <returns>
 /// A ModelDataSouceResult object containing the ReturnValue of the method and any out parameters.
 /// </returns>
 protected virtual ModelDataMethodResult InvokeMethod(ModelDataSourceMethod method) {
     return InvokeMethod(method, false/*isAsyncMethod*/);
 }
 // For unit testing.
 // Return the select async func that we use in the SelectAsync method.
 internal Func<object, Task> SelectAsyncInternal(DataSourceSelectArguments arguments, DataSourceViewSelectCallback callback, ModelDataSourceMethod method) {
     return GetSelectAsyncFunc(arguments, callback, method);
 }
        private ModelDataMethodResult InvokeMethod(ModelDataSourceMethod method, bool isAsyncMethod) {
            object returnValue = null;

            object[] parameterValues = null;
            if (method.Parameters != null && method.Parameters.Count > 0) {
                parameterValues = new object[method.Parameters.Count];
                for (int i = 0; i < method.Parameters.Count; i++) {
                    parameterValues[i] = method.Parameters[i];
                }
            }

            returnValue = _methodInvokerDispatcher(method.MethodInfo, method.Instance, parameterValues);
            OrderedDictionary outputParameters = GetOutputParameters(method.MethodInfo.GetParameters(), parameterValues);
            method.Instance = null;

            // Data Method is done executing, turn off the TryUpdateModel
            // Do not turn off the TryUpdateModel at this point when the method is async
            if (!isAsyncMethod) {
                _owner.DataControl.Page.SetActiveValueProvider(null);
            }

            return new ModelDataMethodResult(returnValue, outputParameters);
        }
        /// <summary>
        /// Evaluates the method parameters using model binding.
        /// </summary>
        /// <param name="dataSourceOperation">The datasource operation for which parameters are being evaluated.</param>
        /// <param name="modelDataSourceMethod">The ModelDataSourceMethod object for which the Parameter collection is being evaluated. The MethodInfo property should already be set on this object.</param>
        /// <param name="controlValues">The values from the data bound control.</param>
        /// <param name="isPageLoadComplete">This must be set to true only when this method is called in Page's LoadComplete event handler
        /// to evaluate the select method parameters that use custom value providers so that we can identify any changes
        /// to those and mark the data-bound control for data binding if necessary.</param>
        protected virtual void EvaluateMethodParameters(DataSourceOperation dataSourceOperation, ModelDataSourceMethod modelDataSourceMethod, IDictionary controlValues, bool isPageLoadComplete) {

            Debug.Assert(_owner.DataControl.Page != null);
            Debug.Assert(_owner.DataControl.TemplateControl != null);

            MethodInfo actionMethod = modelDataSourceMethod.MethodInfo;
            
            IModelBinder binder = ModelBinders.Binders.DefaultBinder;

            IValueProvider dataBoundControlValueProvider = GetValueProviderFromDictionary(controlValues);
            
            ModelBindingExecutionContext modelBindingExecutionContext = _owner.DataControl.Page.ModelBindingExecutionContext;

            Control previousDataControl = null;
            if (BinaryCompatibility.Current.TargetsAtLeastFramework46) {
                // DevDiv 1087698: a child control overwrites its parent controls's modelBindingExecutionContext,
                // which may cause a problem for the parent control to find control by controlId.
                Control dataControl = modelBindingExecutionContext.TryGetService<Control>();
                if (dataControl != _owner.DataControl) {
                    previousDataControl = dataControl;
                }
            }

            //This is used by ControlValueProvider later.
            modelBindingExecutionContext.PublishService<Control>(_owner.DataControl);

            //This is done for the TryUpdateModel to work inside a Data Method. 
            if (dataSourceOperation != DataSourceOperation.Select) {
                _owner.DataControl.Page.SetActiveValueProvider(dataBoundControlValueProvider);
            }

            var methodParameters = actionMethod.GetParameters();
            ParameterInfo lastParameter = null;
            if (methodParameters.Length > 0) {
                lastParameter = methodParameters[methodParameters.Length - 1];
            }

            foreach (ParameterInfo parameterInfo in methodParameters) {
                object value = null;
                string modelName = parameterInfo.Name;

                if (parameterInfo.ParameterType == typeof(ModelMethodContext)) {
                    //ModelMethodContext is a special parameter we pass in for enabling developer to call
                    //TryUpdateModel when Select/Update/Delete/InsertMethods are on a custom class.
                    value = new ModelMethodContext(_owner.DataControl.Page);
                }
                //Do not attempt model binding the out parameters
                else if (!parameterInfo.IsOut) {
                    bool validateRequest;
                    IValueProvider customValueProvider = GetCustomValueProvider(modelBindingExecutionContext, parameterInfo, ref modelName, out validateRequest);

                    //When we are evaluating the parameter at the time of page load, we do not want to populate the actual ModelState
                    //because there will be another evaluation at data-binding causing duplicate errors if model validation fails.
                    ModelStateDictionary modelState = isPageLoadComplete ? new ModelStateDictionary() : _owner.DataControl.Page.ModelState;

                    ModelBindingContext bindingContext = new ModelBindingContext() {
                        ModelBinderProviders = ModelBinderProviders.Providers,
                        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, parameterInfo.ParameterType),
                        ModelState = modelState,
                        ModelName = modelName,
                        ValueProvider = customValueProvider,
                        ValidateRequest = validateRequest
                    };

                    //Select parameters that take custom values providers are tracked by ViewState so that 
                    //we can detect any changes from previous page request and mark the data bound control for data binding if necessary.
                    if (dataSourceOperation == DataSourceOperation.Select && customValueProvider != null && parameterInfo.ParameterType.IsSerializable) {
                        if (!SelectParameters.ContainsKey(parameterInfo.Name)) {
                            SelectParameters.Add(parameterInfo.Name, new MethodParameterValue());
                        }

                        if (binder.BindModel(modelBindingExecutionContext, bindingContext)) {
                            value = bindingContext.Model;
                        }
                        SelectParameters[parameterInfo.Name].UpdateValue(value);
                    }
                    else {
                        if (isPageLoadComplete) {
                            Debug.Assert(dataSourceOperation == DataSourceOperation.Select, "Only Select Operation should have been done immediately after page load");
                            //When this method is called as part of Page's LoadComplete event handler we do not have values in defaultValueProvider 
                            //(i.e., values from DataBoundControl), so we need not evaluate the parameters values.
                            continue;
                        }

                        if (customValueProvider == null) {
                            bindingContext.ValueProvider = dataBoundControlValueProvider;
                        }

                        if (binder.BindModel(modelBindingExecutionContext, bindingContext)) {
                            value = bindingContext.Model;
                        }
                    }

                    // We set the CancellationToken after EvaluateMethodParameters(). 
                    // We don't want to set a null value to a CancellationToken variable.
                    if (parameterInfo == lastParameter && typeof(CancellationToken) == parameterInfo.ParameterType && value == null) {
                        value = CancellationToken.None;
                    }

                    if (!isPageLoadComplete) {
                        ValidateParameterValue(parameterInfo, value, actionMethod);
                    }
                }
                modelDataSourceMethod.Parameters.Add(parameterInfo.Name, value);
            }

            if (previousDataControl != null) {
                modelBindingExecutionContext.PublishService<Control>(previousDataControl);
            }
        }
        private bool RequireAsyncModelBinding(string methodName, out ModelDataSourceMethod method) {
            if (!AppSettings.EnableAsyncModelBinding) {
                method = null;
                return false;
            }

            method = FindMethod(methodName);
            if (null == method) {
                return false;
            }

            MethodInfo methodInfo = method.MethodInfo;
            bool returnTypeIsTask = typeof(Task).IsAssignableFrom(methodInfo.ReturnType);

            return returnTypeIsTask;
        }