-
Notifications
You must be signed in to change notification settings - Fork 10k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Supporting open generic models in Views #4865
Comments
Intro Data Layer Model objects
This structure provides us the following features:
This is our model classes:
We don't add a BlogDate, NewsDate or Author property to Blog and News models. We use CreatedDate and CreatedBy properties to satisfy this need. |
For the administration panel we create an "Admin" area in our MVC application. There, for each module, we create a controller. So, we have a BlogController, NewsController, PhotoController and ProductController. I don't go through the code of the data layer, repositories, the controllers and their actions. They are not our concern. I go right to the Views which is the topic of this post. Views This is the View of Index action in our BlogController:
The View receives and shows one page of data. Thus, a pager is displayed at the bottom of the page using an extension method named Html.Pager() and the arguments are passed to it from ViewBag. This suffices the whole structure of the page. Not surprisingly, the Views for the Index action in all our other controllers, (NewsController, PhotoController and ProductController) follow a similar code. For example, this is the view for the Index action of ProductController.
|
The IdeaNow let's go for the presented idea. It would be a great help if we could define a generic View like AdminGrid.cstml like below:
We are in fact extracting the repetitive or common HTML code out of all of our views into a single View. This is somehow following the DRY principle. Notice that I used two generic Html.Partial() method calls in this AdminGrid View, namely AdminGridHead and AdminGridBody to provide a way for the developer to implement custom HTML codes based on each type for the generic T parameter. It's important to know that, despite we have a single AdminGrid.cshtml View, we should have separate AdminGridHead and AdminGridBody partial views, each one for a type that will be used for the generic T parameter in AdminGrid's model part, i.e. Blog, News, Photo, Product, etc. added at 2017-12-23
The partial views for other models have a similar code. These views could be defined using a file naming convention like below (use the name of the concrete type in the partial view filename):
added at 2017-12-23
Although, each of these Views will have a concrete model not a generic model, based on the fact that they are called using a generic Html.Partial<T>() extension method (which we don't have at the moment), they in turn aim to the topic of this post (supporting generic models in Views). added at 2017-12-23 Now, supposing we have the ability of defining Views with generic models, in our Index actions we should return the model using a generic View<T>().
It is important to call a generic View<T>() in place of the non-generic View() method which is inherited to controllers. I explain the reason a little further. |
This was the whole scenario. I will explain potential problems we have in front of us when implementing this feature in future comments. Because, when I looked at MVC's source code, I noticed that it's not that easy to implement this feature as it seems in the first glance (what I personally thought too at first!). The key point is, we need to carry the generic T, from the top api like Html.Partial() extension method down to the ViewEngine, ViewPageActivator and other classes that play role in creating the C# class of the views, finding the files of views and instantiating from the view class. |
Now, why we need a generic View<T>() method in our controllers? Why we can't call the old View() method in our actions to return a View whose model is generic? The reason is that, the View cannot be instantiated without its generic parameters known. Suppose we have the following View named AdminGrid.cshtml which has a generic IEnumerable model.
Let's suppose such a View is supported. The C# class that is required to be generated for this View which should inherit WebViewPage would be something like this:
The important thing in this class is that despite classes that are generated for other Views it is still a generic class with a generic T parameter. This class cannot be instantiated unless we provide its generic parameter when instantiating it. This is why we need a generic View<T>() method in our controllers and specify its generic parameter, so that the ViewEngine (and ViewPageActivator at the end) are able to instantiate the view from its generated class. |
I was thinking that what happens if our view has a generic model with multiple generic types.
Clearly, we can no longer use the generic View<()> method in Controller class. I think the correct way to implement this feature is to add overload methods with a Type[] array argument to which we can pass an array of types that are required when instiating from the class of the generic Views. This way we can support a model with any number of generic type parameters. This also applies to IViewPageActivator that is responsible for instantiating views. In fact, we need to add another Create() method to the interface with a Type[] parameter.
The following could be the implementation of this new method in the DefaultViewPageActivator:
|
+1 to this, I just spent a little while puzzling out that you can't do this (trying to setup a partial view to render pagination controls for various pages with different models, taking a PaginatedList). You can do this without explicitly specifying the @model type but it would be nice to have support for @model to check it. |
From a Milestone to the Backlog on May 14? Where is this? I can't believe I haven't seen what I consider to be an overwhelming Use Case for this, albeit one at which I am fully aware of the countless eyes that will roll. Send in a generic model collection, reflect on its items' property names for column headers and then iterate through the list to display those items' values. Reflection, slow, I get it. But how much dev time would be saved for simple key/value lookup lists, like Country? Let the eye rolling begin. |
+1 |
Actually I made my hands dirty a little to implement this feature. As far as the codes were in MVC source code I handled the changes and applied appropriate changes to provide open generics feature in views. But when I went deeper I reached to Razor Engine source code and there I stuck. The Razor Engine source code was very sophisticated. I couldn't invest more time on that. So, I dropped the case. Clearly the folks in Razor Engine project can apply the needed changes faster. |
@mansoor-omrani Can you upload your progress to GitHub? And point out where in the Razor Engine it's complicated? |
Yes. Sure. I'll upload the changes I applied. |
I tried to push the changes. But it failed. This is the messages I received.
By the way, I was working on "https://github.com/aspnet/AspNetWebStack" repository. |
I opened a new issue in the aforementioned branch. |
I created a pull request for code changes in my fork. I was working on legacy MVC. |
I strongly support the need for passing a generic item into the model of a view. This is very much needed in all future versions of Asp.Net Core. |
This is not something we plan to do. |
The story:
The idea came to my mind when summarizing multiple views using partials. I thought it would be great if we could define partial views with open generic models.
I thought at first to post the idea in ASP.NET uservoice website. When I searched there, I noticed the same idea was suggested a few years ago https://aspnet.uservoice.com/forums/41201-asp-net-mvc/suggestions/2706166-support-for-generics-in-razor-view.
There, @danroth27 had directed users to a new track ( #727 ) in ASP.NET github and asked users to comment practical examples/use cases of the suggestion.
After visiting the issue, I noticed that it was closed, because nobody had given any comment. I didn't know if someone was still allowed to comment on a closed issue or not, but I dared to try it.
I was happy to see @Eilon's quick reply some hours later still welcoming use cases for this idea.
As the scenario I wanted to mention was a little long, he recommended me to post it as new issue.
So, here am I.
The scenario involves multiple pieces of code fragments (C#, Razor views), but I do my best to make it short and only mention the meat.
The text was updated successfully, but these errors were encountered: