MVC using ASP.NET webforms - model-view-controller

I've inherited an ASP.NET application built on top of webforms, and the application suffers from having all of its business logic embedded in the codebehind. As a result, this application can't be unit tested.
I want to break out the functionality of every form into an MVC style, but I've found that ASP.NET resists every effort on my part to refactor it. In general, I like to seperate my MVC classes as follows:
public class LoginModel
{
public string Username, Password;
public bool IsAuthenticated;
}
public interface ILoginView
{
event Action UserLoggedIn;
void SetMode(bool isAuthenticated);
}
public class LoginController
{
ILoginView View;
LoginModel Model;
public LoginController(ILoginView view, LoginModel model)
{
this.View = view;
this.Model = model;
// hook onto view events
}
}
Once I've got my classes set up and nicely unit tested, I can implement the ILoginView interface on my usercontrol or page:
public class LoginView : UserControl, ILoginView
{
public LoginView() : base()
{
new LoginController(this); // registers view with the controller
}
}
If this were a winform application, it would work beautifullly. But the ASP.NET lifecycle causes this style to break down.
ASP.NET creates and destroys the view on every page load. Since my Controller is held by the View, the Model is held by the Controller, every postback causes my page to lose its state.
I can remedy the problem above by holding my Controller in the users session, but that introduces a world of problems. In particular, putting Controllers in the session causes memory issues because Models and Controllersaren't reclaimed by garbage collection until the session expires. Navigating from page to page creates dozens of controllers, but the controllers don't dispose of themselves when the user navigates away from a page.
Since a view is destroyed/recreated on every postback, then I have to re-register the view with the controller on every postback. This is harder to do than it sounds, because the state of the Model needs to be copied back to the View on every postback, but simultaneously we don't want to overwrite a users changes to the View which were made in the previous postback. You have no idea what kind of additional nightmare this turns into when dealing with dynamically created or AJAXed controls using this MVC style.
I know I'm overthinking this and there is an easier way to get the results I want, but how do I implement an MVC style properly using webforms?

Is it not easier to re-write this using asp.net mvc?

As it seems you'll have to re-write. Either completely to MVC (and stop using DNN), or to a better WebForms implementation (meaning, separate logic from display and take into account the page life-cycle issues). There's a third option - combining MVC and ASP.NET WebForms, but you should look at it carefully taking into account all the variables in your platform.

Well the nature of the POSTback is dictating your state changes, so you should react on that. Any framework you use would work pretty much the same way, it'll rebuild/bind the state with each request. You should look into savind the state (read data) into your user's session.

ViewState should be used to store all the loaded data in WebForms, so the Controller would be instancied only when the page is created, eliminating the need to store any object in the user session.

Related

How to set global viewmodels in ASP .NET Core 3.1?

I am new to ASP.NET Core and have some trouble with binding global models and viewmodels to razor views. My application is a mixture of Web API and MVC with razor views.
My goal
When I open an ASP.NET MVC page, I need to instantiate a model by loading it from the database (DbContext service) based on an id received in a cookie. I want to use this model object globally in every view or partial view.
Current Implementation
I can access the cookies in action methods of page controllers, so that I have to load the model from the DbContext in every action method and bind it as viewmodel to target view. This is not practical, because I have to do this in every page controller, because I need that model object on all pages in my navigation pane.
Idea
I think it should be possible to access to Cookies and dbcontext within Startup.cs and bind the model object to _ViewStart.cshtml, so that it is accessible globally in every view or partial view. Even this approach were correct, I do not have any idea how the code would look like. Because I am learning Web-Apps with .NET Core by learning by doing and try and error at the moment. :(
UPDATE
I have a layout page _Layout.cshtml, which includes partial views like the _NavPane.cshtml. My goal is to pass a Model object to the _Layout, which is instantiated via loading from the database (I have a service IMandantRepository for this purpose) and dependent on a cookie.
That model object is needed on every page request. That's why it would be a better practice to load the model object outside the MVC page controllers and pass it to them (what I can not implement technically).
I tried to find a solution by myself and ended up in following interim ugly solution. Following is the content of the _ViewStart file. On the bottom I assign the needed global variables, which I can use in every view or partial view.
This solution has at least two disadvantages:
The model object is possibly loaded redundantly.
Too many program logic in a view file.
#inject MyProject.Data.IMandantRepository mandantRepo
#{
// Main layout template
Layout = "_Layout";
// Define default values
bool showAdminSection = false;
string src = "/images/logos/nologo.png";
// Read cookie value
string currentMandantUid;
Context.Request.Cookies.TryGetValue("currentMandant", out currentMandantUid);
// Load mandant from the database
var thisMandant = mandantRepo.GetMandantByUid(currentMandantUid);
if(thisMandant is Mandant){
src = "data:image/*;base64," + thisMandant.GetBase64Logo();
showAdminSection = thisMandant.Abbr == "AdminMandant";
}
// Assing global variables to ViewData
ViewData["CurrentMandant"] = thisMandant;
ViewData["logoSrc"] = src;
ViewData["showAdminSection"] = showAdminSection;
}
This is an example code in ConfigureService() of Startup.cs. You can register your dbContext class in this way.
services.AddDbContext<BookStoreContext>( options =>
options.UseSqlServer(_configuration.GetConnectionString("DefaultConnection")));

ASP.NET Core populate model only once for all views

I'm new with ASP.Net Core (3.0 in this case) and I´m trying to create a menu that is visible on all views of a WebApplication, is created dynamically and must be populated only once. Below i explain the steps and try outs i did to reach the goal needed (if required i can share the code I'm using).
This is what i did:
In a simple way, using the "_Layout.cshtml" page, i created a static HTML menu and made all other views simply inherit that layout. So far, so good;
Next challenge comes from the fact that the menu items are dynamically created after a User has logged-in, which i managed to overcome by setting a ModelView inside a controller (HomeController.cs with Index action in this case), and then delivering it to the view. For this case works OK, because the default page is ~\Home\Index\, problem is when i change to a different view with a different controller, the menu has to be rendered again, and so i have to replicate the code (a problem dealt create a BaseController and BaseModel based on this post along side the OnActionExecuted to host the menu generating code)
Now, the biggest problem is the fact that i can only populate the menu once, after the user logs-in. Each time there is a redirect between different controllers/views (post-back of same controller/view works fine), the model is null inside the OnActionExecuted, I tried using ViewData, ViewBag, TemData, but all are null.
So, my question is, how to keep that specific data alive and shared, basically across all the views, and only gets populated once (after each user login) between redirects from different views?
I have been reading around and found several solutions besides the one i did, but i did not found any that could keep data alive throughout the user session the way I need:
ViewBag, ViewData and TempData
Can the shared layout view have a controller in ASP.NET MVC?
Pass data to layout that are common to all pages
To sum up, my flow at this moment, is like this:
User Logged-in
Redirect to default: ~\Home\Index
MenuModelView.cs for the menu gets built and HomeController.cs returns to Index.cshtml with the model attached to it.
Index.cshtml receives the populated ModelView and it uses _Layout.cshtml
The _Layout.cshtml builds the HTML tags for the menu based on the MenuModelView.cs data
User navigates to a different view and steps 3 to 5 are repeated from a specific controller/view
If you want to create a control that can be accessible in all pages without changing every controller, I strongly suggest creating a view component. For a view component has no relationship with your controller, but can access dependencies like database and full HTTP context.
For example, you want to build a custom nav menu, you can just create a view component named NavHeader
using Microsoft.AspNetCore.Mvc;
namespace YourProject.Views.Shared.Components.NavHeader
{
public class NavHeader : ViewComponent
{
public NavHeader(YourDbContext context)
{
// you can access your dependencies, like database.
}
public IViewComponentResult Invoke()
{
// your own logic. You can access HTTPContext here.
var model = new YourOwnModel();
return View(model);
}
}
}
And just call it in any view or layout.
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
</head>
<body>
#*Render your component like this*#
<vc:nav-header></vc:nav-header>
</body>
For more details about view component, please reference:
https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-components?view=aspnetcore-3.1
https://anduin.aiursoft.com/post/2020/1/4/share-view-component-between-different-aspnet-core-web-project

The point of MVC

First, just to say, this is not a troll posting, I'm genuinely completely and utterly at a loss as to why MVC is where the industry is going/gone. If MVC wasn't on every job advert I've seen for the last 4 years I'd not bother but as it is, it looks like I'm going to have to use it.
What is the point of MVC? Yes you get the pretty URL e.g. www.amazon.co.uk/books/horror/ but I can't see any other advantage. I was reading the ScorrGu blog where other's where asking the same
https://weblogs.asp.net/scottgu/introducing-razor
This is the copy+paste reasoning from the ScottGu blog concerning Razor
Some people like a controls model with code-behind for code/content separation, and others like a MVC model where there is separation between the controller and view. In an MVC model your application/business logic is contained within the controller and model layers - and does not live in your view. Your view file instead just focuses on generating HTML rendering.
In the controls model there is "separation between the controller and view" in that there is the code behind(controller) and the content (view).
Application/business logic is sprinkled throughout the view (depending on what's on that view obviously) from the examples and tutorials I've been looking at logic like "Does this user have permissions to see this link?"
"Your view file instead just focuses on generating HTML rendering."
It isn't
#foreach (var item in Model.Items) {
var style = item.IsArchived ? "archived-validation-item" : "";
#Html.DisplayFor(modelItem => item.Code)
their is logic here about how many times a loop is performed and retrieving the right item code.
In the controls model, a page has two files one containing the logic and one the layout.
In MVC, a page has three files, the controller and model containing logic and the view which contains layout and logic.
The reason classic ASP was left behind was so that the code and the markup were seperated, the front end developer could get on with making it look pretty while the back end developer worked on getting the right things onto that page. Their roles and responsibilities were clear and seperate. With MVC, both developers must know about the model (and viewmodel too if that's used) as well as the view and controller (as well as the routes, etc.) so that everything is linked up together. From the examples I've seen for example, the decision on when a link should be shown could be made by the view, the controller or the model. This adds layers of complexity and confusion.
What am I missing apart from some well paid jobs? ;)
Thanks
You are correct in that the old *.aspx + Code-Behind Class was an example of separating concerns and logic - however it was not true MVC separation because the code-behind class was still concerned with aspects of presentation, and similarly it's possible for the .aspx file to have business logic in it, as an example, this:
ASPX:
<!-- this data-source is arguably a piece of business logic in the view layer: -->
<asp:SqlDataSource runat="server" id="ds1" selectCommand="SELECT * FROM someTable" />
<asp:GridView runat="server" id="gv1" dataSource="ds1" />
<asp:Literal runat="server" id="lit1" />
Code-behind:
void Page_Load(Object sender, EventArgs e) {
this.lit1.Text = "<div class=\"something\"><p>some HTML goes here</p></div>"; // this is presentation logic in the code-behind
}
In MVC separation, you remove the "tight-coupling" between the two, as well as enforcing the separation of concerns because you cannot (feasibly) generate HTML from within a Controller Action, and similarly you should not be able to execute business rules in a View because for example, all database connections are closed at that point in the request lifecycle.
One core tenet of MVC is the "view-model" which is a class that encapsulates the entirety of data represented (and manipulated) by the view (alternatively, the tuple of the ViewModel with the ViewData). As this class is well-defined it means the page is almost self-describing, there is no need to manually examine the view to see what's going on and what data is being displayed.
In summary, it's this:
WebForms:
[query database] -> [populate view directly]
MVC:
[query database] -> [populate viewmodel] -> [view renders viewmodel]
One advantage to this approach is that you can turn a website into a web-service by only changing one line of code:
From this:
public ActionResult Index() {
IndexViewModel viewModel = new IndexViewModel();
// (populate ViewModel here)
return this.View( viewModel );
}
To this:
public ActionResult Index() {
IndexViewModel viewModel = new IndexViewModel();
// (populate ViewModel here)
return this.Json( viewModel );
}
You can't do that with WebForms :)
You can write good code, or bad code independent of the frameworks involved. IMHO MVC is a pattern that better lends itself to test-ability due the view separation. Putting logic in the correct places is still the responsibility of all the team members. MVC won't guarantee a great architecture.
And honestly if your application is correctly designed and your webforms are all calling logic in services, maybe MVC isn't that big of a deal for you personally. Also, I agree with what Vincent mentioned about the shift toward SPA's. Personally I've seen several applications written as spas that aren't interactive that would have been just fine as MVC apps. As always, "It depends" is still the best software dogma.

How does an MVC system work?

I'm trying to learn the MVC pattern, but each place say something different. So now I don't know whats the true MVC.
So I'm guessing its the most pure MVC:
Model is just data and notify data changes.
View reads the messages of the Model to update the view.
Controller reads the user input from View and changes the Model according.
Implementing
Model knows no one.
View knows the Model.
Controller knows both View and Model.
Pseudocode:
/* Model */
class Color{
color = blue;
setColor(color);
notifyUpdate();
}
/* View */
class ColorPicker(model){
model.register(update);
update(){
this.colorToExhibit = model.color;
}
}
/* Controller */
class Colorize(view, model){
view.register(update);
update(color){
model.setColor(color);
}
}
Some questions:
Is that right?
I can't see why the View cannot change the Model directly, but through Controller.
Suppose I have animations to be performed after an action. Who must handle this animation: the Model, the View, or the Controller? Also: the animation logic is part of the Model, View, or Controller? More: Suppose a Poker game. After the user choose an action (say, 'Raise'), the system must play an animation (say, the chips going from player spot to the desk). How can I see this poker example (with animation) as a MVC? Can you explain and give a pseudocode about that?
Thank you.
Model is just data and notify data changes.
View reads the messages of the Model to update the view.
Controller reads the user input from View and changes the Model according.
The Model is more than just data. The model is also the business logic. It contains all of the intelligence of the system, or at least an abstraction of behind-the-scenes intelligence (such as database calls or other service calls). Consider the saying, "Keep your models heavy and your controllers light."
Model knows no one.
View knows the Model.
Controller knows both View and Model.
The Model knows no one, that it correct. The Model should be portable between applications and shouldn't depend on UI concerns in any way. (The View and the Controller are UI concerns in this case.)
The View knows the Model, also correct. The View basically "binds" to the Model. It presents all of the UI elements and places Model data within the UI elements accordingly.
The Controller kind of "knows the View." It knows which View to which it should direct control, but it doesn't know anything about that View. Nor does it know which View from which control previously came. The Controller responds to events. An event comes in from the UI, carrying some kind of state information with it (a ViewModel, perhaps), directs logical control through the Models (where the business logic happens), and responds with a Model (or a ViewModel, if the shape of the data specific to a particular View is different than the Models) and a View.
I can't see why the View cannot change the Model directly, but through Controller.
The View can manipulate the Model within the context of the user interaction, but shouldn't expect those changes to persist in any way. The View should be considered "client-side" and doesn't know anything "server-side." (Even if you're talking about a native application and not a web application.) Persisting any change is considered a UI "action" or "event" and would go to a Controller to make it happen.
Suppose I have animations to be performed after an action. Who must handle this animation: the Model, the View, or the Controller? Also: the animation logic is part of the Model, View, or Controller?
An animation sounds like an entirely UI-based operation. It would be within the View. Is there more happening than just a UI animation? Does the animation change anything in the back-end? For example, if I have a web application and, when a page loads, I want to fade-in some data (an animation)... that's entirely in the View. The data would be delivered to the View like any other data, and the animation takes place entirely within the UI (View). It doesn't do anything from the perspective of the Model or the Controller.
Suppose a Poker game. After the user choose an action (say, 'Raise'), the system must play an animation (say, the chips going from player spot to the desk). How can I see this poker example (with animation) as a MVC? Can you explain and give a pseudocode about that?
The action ("Raise") is a Controller event. The UI would contact the controller to perform the "raise". So the Controller might have a method like this:
View Raise(GameState state)
{
// Interact with the Models to update the known state of the game.
// The Models would perform the actual Poker game logic.
// Respond with a View bound to updated Models.
}
Once the Controller responds to the UI with a new View, that View would contain any animations to display to the user. (After all, you don't want to perform the animation unless the action was successful, right? When the Controller responds to the UI with a new View indicating a successful action, then the animation would play. It may instead respond to the UI with a View indicating an error, in which case that View would show something else.)
I'll go with the simple Bank analogy.
Tellers are Views.
Runners are Controllers.
Bankers are Models.
The Bankers are the smart ones, they know all of the business logic and do all of the complex calculations.
The Runners are used to transport the money (data) from the Bankers to the Tellers.
The Teller presents the money to the Customer.
A simple representation:
Model
public class BankAccount
{
public int ID;
public int Balance;
public BankAccount(int id)
{
ID = id;
Balance = DetermineAmount();
}
public int DetermineAmount()
{
// Gather transaction info, debits, credits and return a
// sum of the amount left in the account depending on the
// id provided.
}
}
Controller
public class BankAccountController
{
public ViewResult Index(int id)
{
BankAccount account = new BankAccount(id);
return View(account);
}
}
View
<ul id="account-info">
<li>Account ID: `#Model.ID`</li>
<li>Balance: `#Model.Balance`</li>
</ul>
If you are interested in the historical true MVC, then start with Trygve Reenskaug. He created (observed?, catalogued??) it in the late 1970s. For a start, read "Models-Views-Controllers" from 1979. It defines the terminology. Take careful note of it's title - all three roles are pluralized. This is the first thing that most people seem to get wrong.
The best description I've found of the original use of MVC is actually in a presentation dated 2004 entitled "Inside Smalltalk MVC". I would guess that the canonical papers that describe the Smalltalk 80 final version of MVC are Krasner & Pope's "A Cookbook for Using the Model-View-Controller User Interface Paradigm in the Smalltalk-80" and
Steve Burbeck's "Applications Programming in Smalltalk-80: How to use Model-View-Controller (MVC)". Both papers are well worth the read.
If you have some time to kill and don't mind listening to Robert Martin, he did a good keynote at Ruby Midwest 2011 that touched on MVC. It is a little over an hour, but quite entertaining and enlightening. I tend to follow with his opinion that most implementations get MVC wrong. I spent a little time looking around and finally found a diagram that I can link to which describes MVC. The one that I like came from Pope and Krasner.
(source: as3dp.com)
From my point of view, the following are the key points:
a model instance is responsible for notifying the interested objects of changes. Note that these can be any object instances. The diagram shows both views and controllers receiving updates here.
views are responsible for querying the current state and displaying the results. They usually perform filtering or data transformation as well.
controllers are responsible for accepting user input and forwarding view messages along to the view.
View messages are a common theme in MVC. It is important that these are independent of the UI world - these are not mouse clicks and what not but a view-specific language of events. This brings us to the next point.
The view does not depend on the controller in any way. Controller's are responsible for arranging and creating views and providing the interface between the rest of the world and the view.
In a perfect world, the view is responsible for making the model representation visible. This is how it worked when MVC was applied to desktop applications.
The reality is that MVC has been twisted and rewritten for the web world. It's not really MVC anymore or maybe MVC was simply redefined. This is why you are seeing so many different opinions and representations of MVC out there. If you are looking into writing desktop style applications, then look at the stuff by Krasner & Pope. If you are looking into how MVC is applied to the web, then I recommend Uncle Bob's keynote for an alternative that it better suited for web applications - what he called the Interactor, Entity, Boundary Architecture for lack of a better name. Dig around for stuff associated with his talks about "the Lost Years of Architecture".

ASP.NET MVC 3 - pass data to a partial view in Layout

I'm working on a ASP.NET MVC 3 application, but I'm rather new to MVC in general.
I have a partial view in a my application layout view that needs to have data passed to it. this will appear on every page. Is there a way to make this happen so I don't have to load that data into the view model for every action in the entire site?
As in, if a user navigates to Mysite/admin/settings, I would like to have the partial view on the layout be able to somehow receive the data that it needs without me needing to put that code in the Settings action in the Admin controller.
On this same note, how do you pass data to the layout view of an application anyway?
In these situations I usually use a base ViewModel for my Views
public class ApplicationViewModel
{
public string UserName {get; set;}
....
}
public class SettingsViewModel : ApplicationViewModel
{
}
all your views would inherit from that ViewModel. Your layout would expect it as well
_layout.cshtml:
#model ApplicationViewModel
....
<h1>hello #Model.UserName</h1>
hopefully this answers your question
Partial only renders a view. You need to provide the model manually.
You can create an action for the view you want and render it with Html.Action( actionName ).
Make an action for example menu which will create a model that will be provided to the menu view.
Now you can call the #Html.Action("menu") from wherever, and it will be rendered autonomously. (you can ofcourse provide a controller name as well, and even custom routeData)
You might also want to set Layout = null; in the view to avoid using the master layout of the whole site.
This is how I pass a value to the partial view from my layout page:
Layout page code:
Html.RenderPartial("_SubMenuLeft", new ViewDataDictionary { {"category", "MMG"} });
and in my _SubMenuLeft.cshtml (partial view)
#if (ViewData["category"] == "MMG")
{
...
}
Hope it helps someone for future reference.

Resources