/// <summary> /// Invokes a widget synchronously. /// </summary> /// <param name="writer">The target text writer.</param> /// <param name="descriptor">The widget descriptor.</param> /// <param name="values">The set of values to provide to the widget.</param> private void InvokeCore(TextWriter writer, WidgetDescriptor descriptor, object values = null) { var context = new WidgetContext(descriptor, new RouteValueDictionary(values), _viewContext, writer); var invoker = _invokerFactory.CreateInstance(context); invoker.Invoke(context); }
/// <summary> /// Finds a synchronous method to execute. /// </summary> /// <param name="context">The widget context.</param> /// <param name="widgetType">The widget type.</param> /// <returns>The synchronous method.</returns> public static MethodInfo FindSyncMethod(WidgetContext context, TypeInfo widgetType) { string httpMethod = ResolveHttpMethod(context); string state = string.Empty; // Resolve a widget state? MethodInfo method = null; for (int i = 0; i < SyncMethodNames.Length; i++) { string name = string.Format(SyncMethodNames[i], state, httpMethod); method = GetMethod(name, widgetType); if (method != null) { break; } } if (method == null) { return null; } if (method.ReturnType == typeof(void)) { throw new InvalidOperationException($"Sync method '{method.Name}' should return a value."); } if (method.ReturnType.IsAssignableFrom(typeof(Task))) { throw new InvalidOperationException($"Sync method '{method.Name}' cannot return a task."); } return method; }
/// <inheritdoc /> public async Task ExecuteAsync(WidgetContext context) { var viewEngine = ViewEngine ?? ResolveViewEngine(context); var viewData = ViewData ?? context.ViewData; bool isNullOrEmptyViewName = string.IsNullOrEmpty(ViewName); string state = null; // TODO: Resolve from value provider? string qualifiedViewName; if (!isNullOrEmptyViewName && (ViewName[0] == '~' || ViewName[0] == '/')) { qualifiedViewName = ViewName; } else { qualifiedViewName = string.Format(ViewPath, context.WidgetDescriptor.ShortName, isNullOrEmptyViewName ? (state ?? DefaultViewName) : ViewName); } var view = FindView(context.ViewContext, viewEngine, qualifiedViewName); var childViewContext = new ViewContext( context.ViewContext, view, viewData, context.Writer); using (view as IDisposable) { await view.RenderAsync(childViewContext); } }
/// <summary> /// Finds an asynchronous method to execute. /// </summary> /// <param name="context">The widget context.</param> /// <param name="widgetType">The widget type.</param> /// <returns>The asynchronous method.</returns> public static MethodInfo FindAsyncMethod(WidgetContext context, TypeInfo widgetType) { string httpMethod = ResolveHttpMethod(context); string state = string.Empty; // Resolve a widget state? MethodInfo method = null; for (int i = 0; i < AsyncMethodNames.Length; i++) { string name = string.Format(AsyncMethodNames[i], state, httpMethod); method = GetMethod(name, widgetType); if (method != null) { break; } } if (method == null) { return null; } if (!method.ReturnType.GetTypeInfo().IsGenericType || method.ReturnType.GetGenericTypeDefinition() != typeof(Task<>)) { throw new InvalidOperationException($"Async method '{method.Name}' must return a task."); } return method; }
/// <inheritdoc /> public virtual void Activate(object widget, WidgetContext context) { var propertiesToActivate = _injectActions.GetOrAdd(widget.GetType(), _getPropertiesToActivate); for (int i = 0; i < propertiesToActivate.Length; i++) { var activateInfo = propertiesToActivate[i]; activateInfo.Activate(widget, context); } }
/// <summary> /// Creates a widget instance. /// </summary> /// <param name="context">The widget context.</param> /// <returns>The wiget instance.</returns> private object CreateWidget(WidgetContext context) { var services = context.ViewContext.HttpContext.RequestServices; var widget = _typeActivatorCache.CreateInstance <object>( services, context.WidgetDescriptor.Type); _widgetActivator.Activate(widget, context); return(widget); }
/// <summary> /// Invokes an asynchronous method. /// </summary> /// <param name="method">The method to invoke.</param> /// <param name="context">The widget context.</param> /// <returns>The widget result.</returns> private async Task <IWidgetResult> InvokeAsyncCore(MethodInfo method, WidgetContext context) { var widget = CreateWidget(context); var arguments = await GetArgumentsAsync(context, method, context.Values); var result = await ControllerActionExecutor.ExecuteAsync(method, widget, arguments); var widgetResult = CoerceToWidgetResult(result); return(widgetResult); }
/// <inheritdocs /> public async Task <IDictionary <string, object> > BindArgumentsAsync(WidgetContext context, MethodInfo method, IDictionary <string, object> values) { var bindingContext = new OperationBindingContext { HttpContext = context.ViewContext.HttpContext, InputFormatters = _options.InputFormatters, MetadataProvider = _modelMetadataProvider, ModelBinder = new CompositeModelBinder(_options.ModelBinders), ValidatorProvider = new CompositeModelValidatorProvider(_options.ModelValidatorProviders), ValueProvider = await CompositeValueProvider.CreateAsync(_options.ValueProviderFactories, new ValueProviderFactoryContext(context.ViewContext.HttpContext, context.ViewContext.RouteData.Values)) }; var arguments = new Dictionary <string, object>(StringComparer.Ordinal); var parameters = method.GetParameters(); foreach (var parameter in parameters) { if (values.ContainsKey(parameter.Name)) { arguments.Add(parameter.Name, values[parameter.Name]); } else { var attribute = parameter.GetCustomAttributes().OfType <IBindingSourceMetadata>().FirstOrDefault(); var bindingInfo = new BindingInfo() { BindingSource = attribute?.BindingSource }; var metadata = _modelMetadataProvider.GetMetadataForType(parameter.ParameterType); var modelBindingContext = ModelBindingContext.CreateBindingContext( bindingContext, context.ModelState, metadata, bindingInfo, parameter.Name); var result = await bindingContext.ModelBinder.BindModelAsync(modelBindingContext); if (result.IsModelSet) { _objectModelValidator.Validate(bindingContext.ValidatorProvider, context.ModelState, modelBindingContext.ValidationState, result.Key, result.Model); arguments.Add(parameter.Name, result.Model); } else { arguments.Add(parameter.Name, Activator.CreateInstance(parameter.ParameterType)); } } } return(arguments); }
/// <inheritdocs /> public async Task<IDictionary<string, object>> BindArgumentsAsync(WidgetContext context, MethodInfo method, IDictionary<string, object> values) { var bindingContext = new OperationBindingContext { HttpContext = context.ViewContext.HttpContext, InputFormatters = _options.InputFormatters, MetadataProvider = _modelMetadataProvider, ModelBinder = new CompositeModelBinder(_options.ModelBinders), ValidatorProvider = new CompositeModelValidatorProvider(_options.ModelValidatorProviders), ValueProvider = await CompositeValueProvider.CreateAsync(_options.ValueProviderFactories, new ValueProviderFactoryContext(context.ViewContext.HttpContext, context.ViewContext.RouteData.Values)) }; var arguments = new Dictionary<string, object>(StringComparer.Ordinal); var parameters = method.GetParameters(); foreach (var parameter in parameters) { if (values.ContainsKey(parameter.Name)) { arguments.Add(parameter.Name, values[parameter.Name]); } else { var attribute = parameter.GetCustomAttributes().OfType<IBindingSourceMetadata>().FirstOrDefault(); var bindingInfo = new BindingInfo() { BindingSource = attribute?.BindingSource }; var metadata = _modelMetadataProvider.GetMetadataForType(parameter.ParameterType); var modelBindingContext = ModelBindingContext.CreateBindingContext( bindingContext, context.ModelState, metadata, bindingInfo, parameter.Name); var result = await bindingContext.ModelBinder.BindModelAsync(modelBindingContext); if (result.IsModelSet) { _objectModelValidator.Validate(bindingContext.ValidatorProvider, context.ModelState, modelBindingContext.ValidationState, result.Key, result.Model); arguments.Add(parameter.Name, result.Model); } else { arguments.Add(parameter.Name, Activator.CreateInstance(parameter.ParameterType)); } } } return arguments; }
/// <inheritdoc /> public void Invoke(WidgetContext context) { var method = WidgetMethodSelector.FindSyncMethod(context, context.WidgetDescriptor.Type.GetTypeInfo()); if (method == null) { throw new InvalidOperationException("Cannot find an appropriate method."); } var result = InvokeSyncCore(method, context); result.Execute(context); }
/// <inheritdoc /> public void Execute(WidgetContext context) { var serializerSettings = _serializerSettings; if (serializerSettings == null) { serializerSettings = context.ViewContext.HttpContext.RequestServices.GetRequiredService<IOptions<MvcJsonOptions>>().Value.SerializerSettings; } using (var jsonWriter = new JsonTextWriter(context.Writer)) { jsonWriter.CloseOutput = false; var jsonSerializer = JsonSerializer.Create(serializerSettings); jsonSerializer.Serialize(jsonWriter, Value); } }
/// <summary> /// Invokes a synchronous method. /// </summary> /// <param name="method">The method to invoke.</param> /// <param name="context">The widget context.</param> /// <returns>The widget result.</returns> private IWidgetResult InvokeSyncCore(MethodInfo method, WidgetContext context) { var widget = CreateWidget(context); object result = null; var arguments = GetArgumentsAsync(context, method, context.Values).GetAwaiter().GetResult(); try { result = method.Invoke(widget, arguments); var widgetResult = CoerceToWidgetResult(result); return(widgetResult); } catch (TargetInvocationException ex) { var exceptionInfo = ExceptionDispatchInfo.Capture(ex.InnerException); exceptionInfo.Throw(); return(null); // Unreachable } }
/// <inheritdoc /> public async Task InvokeAsync(WidgetContext context) { IWidgetResult result; var asyncMethod = WidgetMethodSelector.FindAsyncMethod(context, context.WidgetDescriptor.Type.GetTypeInfo()); if (asyncMethod == null) { var syncMethod = WidgetMethodSelector.FindSyncMethod(context, context.WidgetDescriptor.Type.GetTypeInfo()); if (syncMethod == null) { throw new InvalidOperationException("Cannot find an appropriate method."); } result = InvokeSyncCore(syncMethod, context); } else { result = await InvokeAsyncCore(asyncMethod, context); } await result.ExecuteAsync(context); }
/// <inheritdoc /> public IWidgetInvoker CreateInstance(WidgetContext context) { return new DefaultWidgetInvoker(_typeActivatorCache, _widgetActivator, _argumentBinder); }
/// <summary> /// Resolves the view engine. /// </summary> /// <param name="context">The widget context.</param> /// <returns>The view engine.</returns> private static IViewEngine ResolveViewEngine(WidgetContext context) { return context.ViewContext.HttpContext.RequestServices.GetRequiredService<ICompositeViewEngine>(); }
/// <inheritdoc /> public void Execute(WidgetContext context) { var task = ExecuteAsync(context); task.GetAwaiter().GetResult(); }
/// <summary> /// Gets the arguments for the target method. /// </summary> /// <remarks> /// The argument binder will provide a mixed-result of values from the provided dictionary, and those from model binding. /// </remarks> /// <param name="context">The widget context.</param> /// <param name="method">The target method.</param> /// <param name="values">The set of provided values.</param> /// <returns>The bound arguments.</returns> private async Task<object[]> GetArgumentsAsync(WidgetContext context, MethodInfo method, RouteValueDictionary values) { var arguments = await _argumentBinder.BindArgumentsAsync(context, method, values); return arguments.Values.ToArray(); }
/// <summary> /// Resolves the HTTP method used to discover which action to execute. /// </summary> /// <param name="context">The widget context.</param> /// <returns>The HTTP method.</returns> private static string ResolveHttpMethod(WidgetContext context) { string httpMethod = context.ViewContext?.HttpContext?.Request?.Method; if (string.Equals(httpMethod, "get", StringComparison.OrdinalIgnoreCase)) { httpMethod = "Get"; } else { httpMethod = "Post"; } if (httpMethod == "Post" && !string.IsNullOrEmpty(context.WidgetId)) { // MA - We only want to use Post if we are the active widget. // TODO - This needs to come from the value provider. var form = context.ViewContext?.HttpContext?.Request?.Form; if (!string.Equals(context.WidgetId, form[WidgetConventions.WidgetTarget], StringComparison.CurrentCultureIgnoreCase)) { httpMethod = "Get"; } } return httpMethod; }
/// <inheritdoc /> public Task ExecuteAsync(WidgetContext context) { Execute(context); return Task.FromResult(true); }
/// <summary> /// Invokes an asynchronous method. /// </summary> /// <param name="method">The method to invoke.</param> /// <param name="context">The widget context.</param> /// <returns>The widget result.</returns> private async Task<IWidgetResult> InvokeAsyncCore(MethodInfo method, WidgetContext context) { var widget = CreateWidget(context); var arguments = await GetArgumentsAsync(context, method, context.Values); var result = await ControllerActionExecutor.ExecuteAsync(method, widget, arguments); var widgetResult = CoerceToWidgetResult(result); return widgetResult; }
/// <summary> /// Invokes a synchronous method. /// </summary> /// <param name="method">The method to invoke.</param> /// <param name="context">The widget context.</param> /// <returns>The widget result.</returns> private IWidgetResult InvokeSyncCore(MethodInfo method, WidgetContext context) { var widget = CreateWidget(context); object result = null; var arguments = GetArgumentsAsync(context, method, context.Values).GetAwaiter().GetResult(); try { result = method.Invoke(widget, arguments); var widgetResult = CoerceToWidgetResult(result); return widgetResult; } catch (TargetInvocationException ex) { var exceptionInfo = ExceptionDispatchInfo.Capture(ex.InnerException); exceptionInfo.Throw(); return null; // Unreachable } }
/// <inheritdoc /> public void Execute(WidgetContext context) { context.Writer.Write(EncodedContent.ToString()); }
/// <inheritdoc /> public Task ExecuteAsync(WidgetContext context) { return context.Writer.WriteAsync(EncodedContent.ToString()); }
/// <inheritdoc /> public IWidgetInvoker CreateInstance(WidgetContext context) { return(new DefaultWidgetInvoker(_typeActivatorCache, _widgetActivator, _argumentBinder)); }
/// <summary> /// Creates a widget instance. /// </summary> /// <param name="context">The widget context.</param> /// <returns>The wiget instance.</returns> private object CreateWidget(WidgetContext context) { var services = context.ViewContext.HttpContext.RequestServices; var widget = _typeActivatorCache.CreateInstance<object>( services, context.WidgetDescriptor.Type); _widgetActivator.Activate(widget, context); return widget; }
/// <summary> /// Gets the arguments for the target method. /// </summary> /// <remarks> /// The argument binder will provide a mixed-result of values from the provided dictionary, and those from model binding. /// </remarks> /// <param name="context">The widget context.</param> /// <param name="method">The target method.</param> /// <param name="values">The set of provided values.</param> /// <returns>The bound arguments.</returns> private async Task <object[]> GetArgumentsAsync(WidgetContext context, MethodInfo method, RouteValueDictionary values) { var arguments = await _argumentBinder.BindArgumentsAsync(context, method, values); return(arguments.Values.ToArray()); }