public string RenderToStringAsync(string viewName, object model) { var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider }; var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor()); using (var sw = new StringWriter()) { var viewResult = _razorViewEngine.FindView(actionContext, viewName, false); if (viewResult.View == null) { throw new ArgumentNullException($"{viewName} does not match any available view"); } var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { Model = model }; var viewContext = new ViewContext( actionContext, viewResult.View, viewDictionary, new TempDataDictionary(actionContext.HttpContext, _tempDataProvider), sw, new HtmlHelperOptions() ); viewResult.View.RenderAsync(viewContext); return(sw.ToString()); } }
public virtual async Task ExecuteAsync(ActionContext context, ViewComponentResult result) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (result == null) { throw new ArgumentNullException(nameof(result)); } var response = context.HttpContext.Response; var viewData = result.ViewData; if (viewData == null) { viewData = new ViewDataDictionary(_modelMetadataProvider, context.ModelState); } var tempData = result.TempData; if (tempData == null) { tempData = _tempDataDictionaryFactory.GetTempData(context.HttpContext); } ResponseContentTypeHelper.ResolveContentTypeAndEncoding( result.ContentType, response.ContentType, (ViewExecutor.DefaultContentType, Encoding.UTF8), MediaType.GetEncoding, out var resolvedContentType, out var resolvedContentTypeEncoding); response.ContentType = resolvedContentType; if (result.StatusCode != null) { response.StatusCode = result.StatusCode.Value; } await using var writer = _writerFactory.CreateWriter(response.Body, resolvedContentTypeEncoding); var viewContext = new ViewContext( context, NullView.Instance, viewData, tempData, writer, _htmlHelperOptions); OnExecuting(viewContext); // IViewComponentHelper is stateful, we want to make sure to retrieve it every time we need it. var viewComponentHelper = context.HttpContext.RequestServices.GetRequiredService <IViewComponentHelper>(); (viewComponentHelper as IViewContextAware)?.Contextualize(viewContext); var viewComponentResult = await GetViewComponentResult(viewComponentHelper, _logger, result); if (viewComponentResult is ViewBuffer viewBuffer) { // In the ordinary case, DefaultViewComponentHelper will return an instance of ViewBuffer. We can simply // invoke WriteToAsync on it. await viewBuffer.WriteToAsync(writer, _htmlEncoder); await writer.FlushAsync(); } else { await using var bufferingStream = new FileBufferingWriteStream(); await using (var intermediateWriter = _writerFactory.CreateWriter(bufferingStream, resolvedContentTypeEncoding)) { viewComponentResult.WriteTo(intermediateWriter, _htmlEncoder); } await bufferingStream.DrainBufferAsync(response.Body); } }
public ViewDataDictionary(ViewDataDictionary source, object?model) : base(source, model, declaredModelType: typeof(TModel)) { }
public ViewDataDictionary(ViewDataDictionary source) : base(source, declaredModelType: typeof(TModel)) { }
/// <summary> /// Initializes a new instance of the <see cref="ViewDataDictionary"/> class based entirely on an existing /// instance. /// </summary> /// <param name="source"><see cref="ViewDataDictionary"/> instance to copy initial values from.</param> /// <remarks> /// <para> /// For use when copying a <see cref="ViewDataDictionary"/> instance and the declared <see cref="Model"/> /// <see cref="Type"/> will not change e.g. when copying from a <see cref="ViewDataDictionary{TModel}"/> /// instance to a base <see cref="ViewDataDictionary"/> instance. /// </para> /// <para> /// This constructor should not be used in any context where <see cref="Model"/> may be set to a value /// incompatible with the declared type of <paramref name="source"/>. /// </para> /// </remarks> public ViewDataDictionary(ViewDataDictionary source) : this(source, source.Model, source._declaredModelType) { }
/// <summary> /// Initializes a new instance of the <see cref="ViewDataDictionary"/> class based in part on an existing /// instance. This constructor is careful to avoid exceptions <see cref="SetModel"/> may throw when /// <paramref name="model"/> is <c>null</c>. /// </summary> /// <param name="source"><see cref="ViewDataDictionary"/> instance to copy initial values from.</param> /// <param name="model">Value for the <see cref="Model"/> property.</param> /// <param name="declaredModelType"> /// <see cref="Type"/> of <see cref="Model"/> values expected. Used to set <see cref="ModelMetadata"/>. /// </param> /// <remarks> /// <para> /// For use when copying a <see cref="ViewDataDictionary"/> instance and new instance's declared /// <see cref="Model"/> <see cref="Type"/> and <see cref="Model"/> are known. /// </para> /// <para> /// This constructor may <c>throw</c> if <paramref name="model"/> is non-<c>null</c> and incompatible with /// <paramref name="declaredModelType"/>. /// </para> /// </remarks> // This is the core constructor called when Model is known. protected ViewDataDictionary(ViewDataDictionary source, object model, Type declaredModelType) : this(source._metadataProvider, source.ModelState, declaredModelType, data : new CopyOnWriteDictionary <string, object>(source, StringComparer.OrdinalIgnoreCase), templateInfo : new TemplateInfo(source.TemplateInfo)) { if (source == null) { throw new ArgumentNullException(nameof(source)); } // A non-null Model must always be assignable to both _declaredModelType and ModelMetadata.ModelType. // // ModelMetadata.ModelType should also be assignable to _declaredModelType. Though corner cases exist such // as a ViewDataDictionary<List<int>> holding information about an IEnumerable<int> property (because an // @model directive matched the runtime type though the view's name did not), we'll throw away the property // metadata in those cases -- preserving invariant that ModelType can be assigned to _declaredModelType. // // More generally, since defensive copies to base VDD and VDD<object> abound, it's important to preserve // metadata despite _declaredModelType changes. var modelType = model?.GetType(); var modelOrDeclaredType = modelType ?? declaredModelType; if (source.ModelMetadata.MetadataKind == ModelMetadataKind.Type && source.ModelMetadata.ModelType == typeof(object) && modelOrDeclaredType != typeof(object)) { // Base ModelMetadata on new type when there's no property information to preserve and type changes to // something besides typeof(object). ModelExplorer = _metadataProvider.GetModelExplorerForType(modelOrDeclaredType, model); } else if (!declaredModelType.IsAssignableFrom(source.ModelMetadata.ModelType)) { // Base ModelMetadata on new type when existing metadata is incompatible with the new declared type. ModelExplorer = _metadataProvider.GetModelExplorerForType(modelOrDeclaredType, model); } else if (modelType != null && !source.ModelMetadata.ModelType.IsAssignableFrom(modelType)) { // Base ModelMetadata on new type when new model is incompatible with the existing metadata. ModelExplorer = _metadataProvider.GetModelExplorerForType(modelType, model); } else if (object.ReferenceEquals(model, source.ModelExplorer.Model)) { // Source's ModelExplorer is already exactly correct. ModelExplorer = source.ModelExplorer; } else { // The existing metadata is compatible with the value and declared type but it's a new value. ModelExplorer = new ModelExplorer( _metadataProvider, source.ModelExplorer.Container, source.ModelMetadata, model); } // Ensure the given Model is compatible with _declaredModelType. Do not do this one of the following // special cases: // - Constructing a ViewDataDictionary<TModel> where TModel is a non-Nullable value type. This may for // example occur when activating a RazorPage<int> and the container is null. // - Constructing a ViewDataDictionary<object> immediately before overwriting ModelExplorer with correct // information. See TemplateBuilder.Build(). if (model != null) { EnsureCompatible(model); } }
/// <summary> /// Initializes a new instance of the <see cref="ViewDataDictionary"/> class based in part on an existing /// instance. /// </summary> /// <param name="source"><see cref="ViewDataDictionary"/> instance to copy initial values from.</param> /// <param name="declaredModelType"> /// <see cref="Type"/> of <see cref="Model"/> values expected. Used to set <see cref="ModelMetadata"/>. /// </param> /// <remarks> /// <para> /// For use when copying a <see cref="ViewDataDictionary"/> instance and new instance's declared /// <see cref="Model"/> <see cref="Type"/> is known but <see cref="Model"/> should be copied from the existing /// instance e.g. when copying from a base <see cref="ViewDataDictionary"/> instance to a /// <see cref="ViewDataDictionary{TModel}"/> instance. /// </para> /// <para> /// This constructor may <c>throw</c> if <c>source.Model</c> is non-<c>null</c> and incompatible with /// <paramref name="declaredModelType"/>. Pass <c>model: null</c> to /// <see cref="ViewDataDictionary(ViewDataDictionary, object, Type)"/> to ignore <c>source.Model</c>. /// </para> /// </remarks> protected ViewDataDictionary(ViewDataDictionary source, Type declaredModelType) : this(source, model : source.Model, declaredModelType : declaredModelType) { }