I am have a view model which has a property
public IRichTextContent Body { get; set; }
This interface inherits IEnumerable<IRichTextBlock> and there are 3 interfaces that inherit IRichTextBlock: IHtmlContent, IInlineImage and IInlineContentItem. These are all part of the Kentico Kontent Delivery .NET SDK. The normal recommended rendering approach for this property is:
#Html.DisplayFor(vm => vm.Body)
and everything works fine. For the IInlineImage and IHtmlContent types, with no display templates in the solution, ASP.NET MVC calls the ToString() method on them. If I place display templates for the types in the solution then these are picked up and used. The IInlineContentItem has a property of type object that can hold various actual types and ASP.NET MVC correctly resolves the right display template for this object, presumably due to the IEnumerable<object> implementation (see the InlineContentItem). Happy days so far, the meta data template resolution pixie magic works.
In some scenarios I want to be able to use different display templates, so a single display template for the type will not work. As the model property is a collection of different types I can't do this as it stands. So I figured I would enumerate the IEnumerable<IRichTextBlock> and then call DisplayFor() on the types passing a template where required. Something like this:
#foreach (var block in Model.Body)
{
#switch (block)
{
case Kentico.Kontent.Delivery.Abstractions.IInlineImage image:
#Html.DisplayFor(vm => image, "AmpInlineImage")
break;
default:
#Html.DisplayFor(vm => block)
break;
}
}
For the case where I specify the template this works fine, the correct type is sent to the template. However, the default switch case without a template now does not resolve either the underlying type ToString() or the display templates in my solution. Instead it seems the default ASP.NET MVC object template is used for the IHtmlContent and nothing is rendered for the IInlineContentItem.
What is the difference here between the case where ASP.NET MVC correctly resolves the underlying types when enumerating the collection itself and the case where I am doing this? People do not normally seem to have issues with a foreach over a collection, but I presume the issue here is the polymorphism?
Your presumption is correct: based on the ASP.NET Core MVC source, the difference is the polymorphism, or specifically that template resolution does not handle inheritance of interface types. Here is an abridged summary of the method that finds the template name from the type:
public static IEnumerable<string> GetTypeNames(ModelMetadata modelMetadata, Type fieldType)
{
// ...
var fieldTypeInfo = fieldType.GetTypeInfo();
if (typeof(IEnumerable<IFormFile>) != fieldType)
{
yield return fieldType.Name;
}
if (fieldType == typeof(string))
{
// ...
}
else if (!modelMetadata.IsComplexType)
{
// A complex type is defined as a Type without a
// TypeConverter that can convert from string
}
else if (!fieldTypeInfo.IsInterface)
{
var type = fieldType;
while (true)
{
type = type.GetTypeInfo().BaseType;
if (type == null || type == typeof(object))
{
break;
}
yield return type.Name;
}
}
if (typeof(IEnumerable).IsAssignableFrom(fieldType))
{
if (typeof(IEnumerable<IFormFile>).IsAssignableFrom(fieldType))
{
// ...
}
yield return "Collection";
}
else if (typeof(IFormFile) != fieldType && typeof(IFormFile).IsAssignableFrom(fieldType))
{
yield return nameof(IFormFile);
}
yield return "Object";
}
Note how:
The runtime type name is returned except for one special scenario.
There is a condition when the type is not an interface that returns the type names in the hierarchy.
Nothing else until the end where "Object" is returned as a generic template name.
This occurs regardless of the Kentico Kontent Delivery .NET SDK, and you can test it by creating a model property using an IEnumerable of a simple interface and setting it to a List of objects of a type implementing an interface that inherits that interface. If you do the foreach and #Html.DisplayFor on each item, the generic Object template is used.
In this case, you have some options:
Always pass in a template name (even create an extension method to automatically pull it from the runtime type).
Implement the IRichTextBlock.cshtml template.
Implement the Object.cshtml template.
An example of IRichTextBlock.cshtml is this:
#model Kentico.Kontent.Delivery.Abstractions.IRichTextBlock
#switch (Model)
{
case Kentico.Kontent.Delivery.Abstractions.IInlineContentItem inlineContentItem:
// Render inlineContentItem
break;
default:
#Html.Raw(Model.ToString())
break;
}
I have returned a list of warning classes from the control class like this:
public ActionResult Ex3()
{
List<warning> warningsList = new List<warning>();
XElement xelem = XElement.Load(transformedFile);
var warnings = from elem in xelem.Descendants("warning")
select elem;
foreach (var v in warnings)
{
warning warn = new warning();
warn.id = v.Attribute("id").Value;
warningsList.Add(warn);
}
return View(warningsList);
}
i have created a view with the option "create a strongly typed view" and i have choose the warning class as a model (razor engine).
Now, in Ex3View.cshtml, i want to create and fill a gridpannel with the values in the warningsList. How can i do that ? how can i access these data in the returned list?
Please note that the view file is .cshtml and not .aspx.
You can put a Model to a Store's DataSource:
#Html.X().Store().DataSource(Model)
In a project I'm working on, I have a lot of "read-only" pages that don't have <form>s in; I also have a lot of form pages that also have a lot of readonly data pulled in from the controller.
Ordinarily you'd use ViewModels and have one ViewModel per view and the ViewModel contains all of the data for that view. That seems fair enough, except there's a problem:
In my head, I see the ViewModel as being a representation and an encapsulation of the entire data sent from the view back to the controller, but the ViewModel might contain data populated by the controller (such as SelectListItem[] Html.DropDownListFor() data) which cannot be populated by a view and sent back to the controller.
Sure, it is possible to have that data as part of the ViewModel and manually re-populate it before returning the View in the Controller's HttpPost-handling method, but I feel it needlessly complicates the controller's code (and you'd have to use UpdateModel() instead of the automatic updates that happen when you specify the model as an argument to the action method).
My solution to this is a typed ViewData object. I derive from ViewPage<TModel> to give ViewPage2<TModel,TData> where TData : ViewDataDictionary<TModel> and override (or shadow) the .ViewData property to return an instance of TData instead..
My questions are twofold:
Subclassing ViewPage seems easy enough, but where do I put the logic to handle the initialisation of my ViewPage2<TModel,TData> class?
Is there anything wrong with my approach?
I found a way to implement my ViewData / ViewModel approach without messing too much with ASP.NET MVC.
Here's how I did it:
This is my ViewPage2 class which exposes the strongly-typed ViewData object to views. Unfortunately you do need to use reflection to avoid the ViewPage's behaviour of creating a brand new ViewDataDictionary, but with statically cached FieldInfo objects it's no more expensive than MVC's dynamic controller/action lookup in routing.
public class ViewPage2<TModel,TData> : ViewPage<TModel> where TData : ViewDataDictionary<TModel> {
public ViewPage2() : base() {
}
private Boolean _dataPresent;
private TData _data;
public new TData ViewData {
get {
if( _dataPresent && _data == null ) {
_data = (TData)base.ViewData;
}
return _data;
}
}
// Cached in static class state for performance.
private static readonly FieldInfo _viewPage1ViewData;
private static readonly FieldInfo _viewPage2ViewData;
static ViewPage2() {
Type viewPage1 = typeof(ViewPage<TModel>);
_viewPage1ViewData = viewPage1.GetField("_viewData", BindingFlags.Instance | BindingFlags.NonPublic );
Type viewPage2 = typeof(ViewPage);
_viewPage2ViewData = viewPage2.GetField("_viewData", BindingFlags.Instance | BindingFlags.NonPublic );
}
protected override void SetViewData(ViewDataDictionary viewData) {
// ViewPage<TModel> creates a new ViewDataDictionary<TModel> when this method is called, even if viewData is of the correct type.
// The trick is to reimplement SetViewData and set base._viewData and basebase._viewData
if( viewData is TData ) {
_viewPage1ViewData.SetValue( this, viewData );
_viewPage2ViewData.SetValue( this, viewData );
_dataPresent = true;
} else {
base.SetViewData( viewData );
}
}
}
Then for each *.aspx file (I use the WebFormViewEngine) I just change the #Page directive:
<%# Page Language="C#" MasterPageFile="~/Site.Master" Inherits="Me.ViewPage2<Me.FormModel,Me.FormData>" %>
I'll admit the two generic type specifiers to make it a bit fiddly, but it's something you only need to set once.
Then in each controller it's just a matter of doing this:
public ActionResult Edit() {
FormData data = new FormData();
data.SomeStronglyTypedField = "foo";
this.ViewData = data;
}
In each view you can now benefit from strongly-typed view data:
<p><%= ViewData.SomeStronglyTypedField %></p>
And because ViewData is not part of the model there's the separation of concerns when dealing with POST-submitted data and the benefits of automatic binding:
[HttpPost]
public ActionResult Edit(EditModel model) {
if( !ModelState.IsValid ) {
// See how I can return the model object without modifying it. All I need to do is re-create the View data.
FormData data = new FormData();
data.SomeStronglyTypedField = "foo";
this.ViewData = data;
return View( model );
}
// persist to DB here
return RedirectToAction("View");
}
In practice I use a common BaseController class that handles the automatic creation and setting of ViewData objects so I don't need those three lines (FormData data... etc) in every action.
I've been trying to come up with an extensible search pattern for an mvc site I'm working on and I wanted some base functionality to minimize what I need to do to extend the search by adding another controller option.
I'm having a problem determining where I would go from having a base abstract searchable controller that presumes a particular entity and model type. The Mapper reference refers to AutoMapper and just maps the entity to the model. Everything seems to work ok but I'm not sure what the view would look like in terms of the model definition because the type is variable for the base model SearchModel. I could implement my own view in each controller with the #model SearchModel but I'm just wondering how much "free" plumbing I can get away with.
public abstract class SearchableController<TModel, TEntity, TRepository> : Controller where TRepository : ISearchableRepository<TEntity> where TEntity : class where TModel : class
{
protected TRepository _repository;
[HttpGet]
public ActionResult Search(string q, int? page)
{
int pageNumber = page.HasValue ? page.Value : 1;
PagedList<TEntity> entities = _repository.Search(q, 10, page);
Mapper.CreateMap<TEntity, TModel>();
var results = new List<TModel>();
foreach (TEntity entity in entities)
{
var entityModel = Mapper.Map<TEntity, TModel>(entity);
results.Add(entityModel);
}
var model = new SearchModel<TModel>();
model.searchPattern = q;
model.pageNumber = pageNumber;
model.Results = new StaticPagedList<TModel>(results, entities.PageNumber, entities.PageSize, entities.TotalItemCount);
return View(model);
}
}
I guess I don't really understand your problem.
Your model would be an #model SearchModel<WhateverYourTModelIs>. What "free plumbing" are you referring to?
Maybe i'm confused, but you have to define the static types for your derived controllers, so what is your problem with defining static type views for them?
Yeah I think that is the route I ended up with. Then I just use a master view that handles the common view portion of the search model like the search pattern rebind to a search input, pagination, current page number etc...
My current problem is that I have a partial view that I want to determine what model is being used by it.
I have had to deal with a few strange scenarios for my project so I will try to outline it here, maybe someone can offer a better way to do this.
I am designing something like the Google iGoogle page. A main page with multiple widgets that are able to move around or be configured as needed. The current system loads the actual widget's data asynchronously view a POST to a controller within my application. That controller will either render a partial view to HTML that can be returned (and then loaded into the page view JQUERY) or just straight HTML/JavaScript that is stored in a database.
This was working fine for me, I had a model for the widgets that holds a dictionary of options that are described via the database, and then used by the partial view. The problem came when I wanted to pass data to a partial view. The best solution I could come up with was having the controller determine which model the partial view in question uses, have some function that will fill the model, and then pass it, along with the partial view, to the function that will render it to HTML within the controller.
I realize this is an odd scenario for MVC (the layers are blending...) and any advice on fundamental design, or implementation of this would be greatly appreciated.
I am currently using MVC3/Razor. Feel free to ask any other questions.
I prototyped a possible solution to this, because it seemed like a fun problem. I hope it's useful to you.
Models
First, the models. I decided to create two 'widgets', one for news, and one for a clock.
public class NewsModel
{
public string[] Headlines { get; set; }
public NewsModel(params string[] headlines)
{
Headlines = headlines;
}
}
public class ClockModel
{
public DateTime Now { get; set; }
public ClockModel(DateTime now)
{
Now = now;
}
}
Controller
My controller doesn't know anything about the views. What it does is returns a single model, but that model has the ability to dynamically fetch the right model as required by the view.
public ActionResult Show(string widgetName)
{
var selector = new ModelSelector();
selector.WhenRendering<ClockModel>(() => new ClockModel(DateTime.Now));
selector.WhenRendering<NewsModel>(() => new NewsModel("Headline 1", "Headline 2", "Headline 3"));
return PartialView(widgetName, selector);
}
Delegates are used so that the correct model is only created/fetched if it is actually used.
ModelSelector
The ModelSelector that the controller uses is pretty simple - it just keeps a bag of delegates to create each model type:
public class ModelSelector
{
private readonly Dictionary<Type, Func<object>> modelLookup = new Dictionary<Type, Func<object>>();
public void WhenRendering<T>(Func<object> getter)
{
modelLookup.Add(typeof(T), getter);
}
public object GetModel(Type modelType)
{
if (!modelLookup.ContainsKey(modelType))
{
throw new KeyNotFoundException(string.Format("A provider for the model type '{0}' was not provided", modelType.FullName));
}
return modelLookup[modelType]();
}
}
The Views - Simple solution
Now, the easiest way to implement a view would be:
#model MvcApplication2.ModelSelector
#using MvcApplication2.Models
#{
var clock = (ClockModel) Model.GetModel(typeof (ClockModel));
}
<h2>The time is: #clock.Now</h2>
You could end here and use this approach.
The Views - Better solution
That's pretty ugly. I wanted my views to look like this:
#model MvcApplication2.Models.ClockModel
<h2>Clock</h2>
#Model.Now
And
#model MvcApplication2.Models.NewsModel
<h2>News Widget</h2>
#foreach (var headline in Model.Headlines)
{
<h3>#headline</h3>
}
To make this work, I had to create a custom view engine.
Custom view engine
When a Razor view is compiled, it inherits a ViewPage<T>, where T is the #model. So we can use reflection to figure out what type the view wanted, and select it.
public class ModelSelectorEnabledRazorViewEngine : RazorViewEngine
{
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
var result = base.CreateView(controllerContext, viewPath, masterPath);
if (result == null)
return null;
return new CustomRazorView((RazorView) result);
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
var result = base.CreatePartialView(controllerContext, partialPath);
if (result == null)
return null;
return new CustomRazorView((RazorView)result);
}
public class CustomRazorView : IView
{
private readonly RazorView view;
public CustomRazorView(RazorView view)
{
this.view = view;
}
public void Render(ViewContext viewContext, TextWriter writer)
{
var modelSelector = viewContext.ViewData.Model as ModelSelector;
if (modelSelector == null)
{
// This is not a widget, so fall back to stock-standard MVC/Razor rendering
view.Render(viewContext, writer);
return;
}
// We need to work out what #model is on the view, so that we can pass the correct model to it.
// We can do this by using reflection over the compiled views, since Razor views implement a
// ViewPage<T>, where T is the #model value.
var compiledViewType = BuildManager.GetCompiledType(view.ViewPath);
var baseType = compiledViewType.BaseType;
if (baseType == null || !baseType.IsGenericType)
{
throw new Exception(string.Format("When the view '{0}' was compiled, the resulting type was '{1}', with base type '{2}'. I expected a base type with a single generic argument; I don't know how to handle this type.", view.ViewPath, compiledViewType, baseType));
}
// This will be the value of #model
var modelType = baseType.GetGenericArguments()[0];
if (modelType == typeof(object))
{
// When no #model is set, the result is a ViewPage<object>
throw new Exception(string.Format("The view '{0}' needs to include the #model directive to specify the model type. Did you forget to include an #model line?", view.ViewPath));
}
var model = modelSelector.GetModel(modelType);
// Switch the current model from the ModelSelector to the value of #model
viewContext.ViewData.Model = model;
view.Render(viewContext, writer);
}
}
}
The view engine is registered by putting this in Global.asax.cs:
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new ModelSelectorEnabledRazorViewEngine());
Rendering
My home view includes the following lines to test it all out:
#Html.Action("Show", "Widget", new { widgetName = "Clock" })
#Html.Action("Show", "Widget", new { widgetName = "News" })
One option would be to extend the idea of partial requests in your application. Steve Sanderson has a fantastic example of this, although the post relates to MVC 1 & 2. I think it would still help in you v3, but I haven't investigated v3 to see if the MVC team implemented their own version. In your asynch scenario, you'll need to toy with the implementation a bit, perhaps change the PartialRequest definition to accept different information as needed, but I think this might be a good start. The net result would be better isolation of concerns, allowing individual controllers to manage a particular type of partial, and in turn be better aware of the model Type you want to work with.
I'm not 100% sure that this is what you'd be looking for, but the [ChildActionOnly] attribute can be added to a method within your controller. That requires that the method can only be called from a partial view. Then you can set up your partial view for that method that basically resembles one of your widgets. Check out the MVC Music Store example here:
http://www.asp.net/mvc/tutorials/mvc-music-store-part-10
What about a dynamic view model? Layouts in MVC3 use them, and maybe you can use something similar for your purposes:
Dynamic in C# 4.0: Introducing the ExpandoObject
Fun With Method Missing and C# 4
Dynamic View Page, MVC without a View Model
I blogged about doing exactly this. Please see http://blogs.planetcloud.co.uk/mygreatdiscovery/?tag=/widget
Essentially I built out a similar widget system. The posts also cover how to handle configuration of those widgets. This makes use of the dynamic support in Mvc3 so that any model object can be passed to the view, from a single controller action.
By default all widgets have a collection of KVP properties (I believe this is what the OP has). So for a simple widget we get access to those properties from within the view. I used for a widget that displayed some html (where the html was stored in one of those properties).
However, for more complex widgets we implement IWidgetWithDisplayModel. This tells us that before we pass the loaded widget back to the view, we need to "build" our display model.
Here's the controller action that does that. Check the posts for full details.
[HttpGet]
public ActionResult Get(string name)
{
var widget = widgetService.GetWidgetBySystemName(name, true);
if (widget == null)
return Content(string.Format("Widget [{0}] not found!", name));
if (!this.ViewExists(widget.WidgetName))
return Content(string.Format("A template for widget [{0}] was not found.", widget.WidgetName));
if (widget is IWidgetWithDisplayModel) {
(widget as IWidgetWithDisplayModel).CreateDisplayModel();
}
return PartialView(widget.WidgetName, widget);
}