CodeBehind Library in ASP.NET Core

Code Behind Framework

CodeBehind library is a back-end framework. This library is a programming model based on the MVC pattern, which provides the possibility of creating dynamic aspx files (similar to ASP.NET standard) in ASP.NET Core and has high serverside independence. CodeBehind is free and open-source.

Previously, in the following article, we gave explanations about the CodeBehind framework; currently, CodeBehind version 1.5 is in a stable state, and more features have been added to it, and it has passed many tests: https://www.c-sharpcorner.com/article/how-use-aspx-files-in-net-core/

Note. In every part of the text of the article where ASP.NET Core is mentioned, we mean programming with default cshtml pages in ASP.NET Core.

Note. We mean by web part in this article; it can also mean add-ons, plugins, modules, Extensions, etc.; if you read to the end of the article, you will understand what we mean by web part.

Background

First, CodeBehind was supposed to be a back-end framework for the C++ programming language; our project in C++ was going well, we built the listener structure, and we were even able to implement fast-cgi in the coding phase for the Windows operating system. Windows operating system test with nginx web server was very stable and fast, but for some reason, we stopped working and implemented CodeBehind on .NET Core version 7 (maybe we will explain about this in an article in the future).

Programming in CodeBehind is simple. The simplicity of the CodeBehind project is the result of two years of study and research on back-end frameworks and how they support web parts.

Simple and structured MVC in CodeBehind

This article contains many explanations, diagrams, and figures to describe the functionality of the CodeBehind framework, and also slightly changed the coding structure; therefore, we completely rewrite all Model, View, and Controller sections to present a complete and flawless article.

View File. Default.aspx

<%@ Page Controller="YourProjectName.DefaultController" Model="YourProjectName.DefaultModel" %><!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title><%=model.PageTitle%></title>
</head>
<body>
    <%=model.BodyValue%>
</body>
</html>

Model File. DefaultModel.cs

using CodeBehind;

namespace YourProjectName
{
    public partial class DefaultModel : CodeBehindModel
    {
        public string PageTitle { get; set; }
        public string BodyValue { get; set; }
    }
}

Controler File. DefaultController.cs

using CodeBehind;

namespace YourProjectName
{
    public partial class DefaultController : CodeBehindController
    {
        public DefaultModel model = new DefaultModel();
        public void PageLoad(HttpContext context)
        {
            model.PageTitle = "My Title";
            model.BodyValue = "HTML Body";
            View(model);
        }
    }
}

As you can see, there is an extraordinary structure in the controller section, which consists of a function named PageLoad and is called with every request of this function.

Program File. Program.cs

using CodeBehind;
using SetCodeBehind;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

+ CodeBehindCompiler.Initialization();

app.Run(async context =>
{
+    CodeBehindExecute execute = new CodeBehindExecute();
+    await context.Response.WriteAsync(execute.Run(context));
+    await context.Response.CompleteAsync();
});

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.Run();

Suppose you enter the value true in CodeBehindCompiler.Initialization(), as long as the CodeBehindLastSuccessCompiled.dll.tmp file exists next to the main dll files of the program. Recompilation will not be done. Doing this makes the response speed of the requests high after the first request since the program goes to sleep.

CodeBehindCompiler.Initialization(true);

Note. If you configure the Program.cs class like this, any changes in the aspx files, or adding new web parts or removing web parts, requires deleting the CodeBehindLastSuccessCompiled.dll.tmp file.

You can use the Write method in the model and controller classes; the Write method adds a string value to the ResponseText attribute; you can also change the values of the ResponseText attribute by accessing them directly.

In the controller class, there is an attribute named IgnoreViewAndModel attribute, and if you activate the IgnoreViewAndModel attribute, it will ignore the values of model and view, and you will only see a blank page; this feature allows you to display the values you need to the user and avoid multiple redirects and transfers.

Note. If you have set the name of a model in the aspx file, You must make sure to call View(ModelName) in the controller class at the end of the method or set the value of IgnoreViewAndModel to true.

To receive the information sent through the form, you can follow the instructions below.

public DefaultModel model = new DefaultModel();
public void PageLoad(HttpContext context)
{
    if (!string.IsNullOrEmpty(context.Request.Form["btn_Add"]))
        btn_Add_Click();

    View(model);
}

private void btn_Add_Click()
{
    model.PageTitle = "btn_Add Button Clicked";
    model.FileUploadValue = context.Request.Form.Files["upd_FileUpload"];
    model.AcceptChechedValue = context.Request.Form["cbx_AcceptCheched"] == "on";
	model.GuestNameValue = context.Request.Form["txt_GuestName"];
}

Sending data through the form is shown for you in the codes above; you can see the simplicity and comprehensibility of the code.

By using CodeBehind, you can call the output of any page on any other page (or anywhere).

aspx page

<%@ Page Controller="YourProjectName.DefaultController" Model="YourProjectName.DefaultModel" %><!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title><%=model.PageTitle%></title>
</head>
<body>
    <%=model.LeftMenuValue%>
    <div class="main_content">
        <%=model.MainContentValue%>
    </div>
    <%=model.RightMenuValue%>
</body>
</html>

Controller class

using CodeBehind;

namespace YourProjectName
{
    public partial class DefaultController : CodeBehindController
    {
        public DefaultModel model = new DefaultModel();

        public void PageLoad(HttpContext context)
        {
            model.PageTitle = "My Title";
            CodeBehindExecute execute = new CodeBehindExecute();

            // Add Left Menu Page
            model.LeftMenuValue = execute.Run(context, "/menu/left.aspx");

            // Add Right Menu Page
            model.RightMenuValue = execute.Run(context, "/menu/right.aspx");

            // Add Main Content Page
            model.MainContentValue = execute.Run(context, "/pages/main.aspx");

            View(model);
        }
    }
}

Each of the pages left.aspx, right.aspx, and main.aspx can also call several other aspx files; these calls can definitely be dynamic, and an add-on can be executed that the kernel programmers don't even know about.

You can even call pages with query strings.

model.MainContentValue = execute.Run(context, "/pages/main.aspx?template=1");

You can also call a path that is determined at runtime and may change over time.

string MainPage = Pages.GetDefaultPage();
model.MainContentValue = execute.Run(context, MainPage);

Why Code Behind

Many developers avoid ASP.NET Core and choose interpretive frameworks like Django and Laravel. And this is due to the complexities and weak default structure of ASP.NET Core and the need for complex configurations and controller classes with a chaotic and incomprehensible structure for novice programmers, as well as the difficulty of building a modular system.

ASP.Net Core vs Core Behind

Really, no matter what we tried, we couldn't find any advantages to using ASP.NET Core compared to CodeBehind; perhaps if we were to compare ASP.NET Core with frameworks such as Django and Laravel, we could introduce high execution speed and leading programming language C# as a measure of ASP.NET Core's superiority; but using CodeBehind will give us the same advantages. If you have one or more better advantages for ASP.NET Core than CodeBehind, be sure to share your thoughts with us.

CodeBehind in version 1.5

CodeBehind is currently in a stable state at version 1.5. In the future, newer versions will be released, and more features will be added to it.

CodeBehind advantages

CodeBehind is a flexible framework. CodeBehind inherits all the advantages of ASP.NET Core and gives it more simplicity, power, and flexibility.

One of the great features that Code-Behind gives you is the support for DLL libraries. You can add all the .NET Core DLL libraries that you have created into the bin directory located in wwwroot so that the Code-Behind will call all of them.

Project in Code Behind

How to add a web part?

First, copy your compiled project files to the desired path in wwwroot; then copy the main dll file to the wwwroot/bin path. You can do the copy while the process is running in the method and then call the code below to compile without restarting the program.

// Recompile
CodeBehindCompiler.Initialization();
CodeBehindCompiler.CompileAspx();

CodeBehind, like the default ASP.NET Core, supports multiple platforms and has high stability on Linux.

CodeBehind occupies less memory resources (RAM) than ASP.NET Core.

Aspx pages are compiled in CodeBehind, and their calling is done at a very high speed so that the path of the aspx file is not even referred to during the calling.

In CodeBehind, the physical executable pages (aspx) are placed in the root path, and this makes the program structured.

CodeBehind supports web parts; web parts are like other parts of the project and include aspx files.

Web Part in Code Behind

To add the web part in CodeBehind, just put the project files in the root.

In CodeBehind, you can run web parts that make changes to aspx files. You can edit all aspx files during project execution and respond to users.

In CodeBehind, the structure of web parts is the same as the structure of the main project; your main project includes aspx pages, dll files, and other client-side files (css, JS, images, etc.); web parts in CodeBehind also include aspx pages, dll files, and other client-side files.

Web Part Main

The project created by using CodeBehind is automatically a modular project. That is, it has the ability to add web parts. In addition, each web part can be used in other projects.

The system built with CodeBehind is also a web part itself. Each web part can also be a separate system! The web part adds the configuration of the Program.cs class is considered the main system.

CodeBehind stores the final values of its pages outside of the Response in the HttpContext; you can edit the output of the final values in the aspx pages before the answer. This gives you more control than ASP.NET Core.

CodeBehind produces understandable code, while the Controller part of ASP.NET Core is a messy and complex situation.

You will never experience the power that the CodeBehind framework gives you in ASP.NET Core.

.NET developers accept CodeBehind as part of the larger .NET ecosystem. Whatever benefits CodeBehind has belongs to the .NET community.

CodeBehind is similar to interpreted frameworks such as Django and Laravel, and programmers of interpreted programming language projects can easily program with CodeBehind.

Developers of interpretative frameworks can consider CodeBehind as an alternative.

The CodeBehind framework allows you to add advanced features to your project without conflicting with the complex structure of ASP.NET Core; the following list is examples of these facilities:

Note. Ability to add a module (web part) to your project without stopping or restarting the program and without deleting sessions.

You can add a page to insert a module (web part) on the admin page of your project; this page should include an input for uploading, and after copying, run the CodeBehind compilation methods again; According to the following codes:

// Recompile
CodeBehindCompiler.Initialization();
CodeBehindCompiler.CompileAspx();

Scheduled task

Example

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<scheduled_tasks_root>
  <scheduled_tasks_list>
    <scheduled_task name="empty_tmp_directory" path="/action/system_access/scheduled_tasks/empty_tmp_directory/Default.aspx" active="true" corn_hour="86400" type="load" check_type="page" last_run="20230830010903" />
    <scheduled_task name="empty_session_data_directory" path="/action/system_access/scheduled_tasks/empty_session_data_directory/Default.aspx" active="true" corn_hour="86400" type="load" check_type="page" last_run="20230830010903" />
    <scheduled_task name="active_delay_content" path="/action/system_access/scheduled_tasks/active_delay_content/Default.aspx" active="true" corn_hour="600" type="load" check_type="page" last_run="20230830010903" />
    <scheduled_task name="delete_robot_blocked_ip" path="/action/system_access/scheduled_tasks/delete_robot_blocked_ip/Default.aspx" active="true" corn_hour="600" type="load" check_type="page" last_run="20230830010903" />
  </scheduled_tasks_list>
</scheduled_tasks_root>

Scheduled task is one of the most important parts of a high-level project; you can run the Scheduled task method in the Run method in the builder located in the Program.cs class.

app.Run(async context =>
{
+  ScheduledTask.Run(context);

   CodeBehindExecute execute = new CodeBehindExecute();
   await context.Response.WriteAsync(execute.Run(context));
   await context.Response.CompleteAsync();
});

You can perform the scheduled task either by request or by calling a timer (of course, if you don't allow the system to sleep!).

Note. You can implement both of them in a combination.

Startup

Example

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<start_up_root>
  <start_up_list>
    <start_up name="start_timer" path="/action/system_access/start_up/start_timer/Default.aspx" active="true" />
    <start_up name="send_start_project_email_to_provider" path="/action/system_access/start_up/send_start_project_email_to_provider/Default.aspx" active="true" />
  </start_up_list>
</start_up_root>

Startup is a structure that allows you to initialize some things before the program is activated; you can run the Startup method before the Run method in the builder located in the Program.cs class.

+  Startup.Run();

app.Run(async context =>
{
   CodeBehindExecute execute = new CodeBehindExecute();
   await context.Response.WriteAsync(execute.Run(context));
   await context.Response.CompleteAsync();
});

Before load path reference

Example

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<before_load_path_reference_root>

  <before_load_path_reference_list>
    
    <reference type="url" check_type="page" exist="true" start_by="false" end_by="false" regex_match="false" active="false" reason="login_try_count_limitation_is_active">
      <path_value>btn_Login=</path_value>
      <load_value><![CDATA[/action/system_access/reference/check_login_try_count_limitation/Default.aspx]]></load_value>
    </reference>
    
    <reference type="form" check_type="page" exist="true" start_by="false" end_by="false" regex_match="false" active="false" reason="login_try_count_limitation_is_active">
      <path_value>btn_Login=</path_value>
      <load_value><![CDATA[/action/system_access/reference/check_login_try_count_limitation/Default.aspx]]></load_value>
    </reference>

    <reference type="url" check_type="page" exist="true" start_by="false" end_by="false" regex_match="false" active="true" reason="lock_login_is_active">
      <path_value>btn_Login=</path_value>
      <load_value><![CDATA[/action/system_access/reference/check_lock_login/Default.aspx]]></load_value>
    </reference>

    <reference type="form" check_type="page" exist="true" start_by="false" end_by="false" regex_match="false" active="true" reason="lock_login_is_active">
      <path_value>btn_Login=</path_value>
      <load_value><![CDATA[/action/system_access/reference/check_lock_login/Default.aspx]]></load_value>
    </reference>
    
    <reference type="url" check_type="page" exist="true" start_by="false" end_by="false" regex_match="false" active="true" reason="next_search_time_interval_limitation_is_active">
      <path_value>btn_Search=</path_value>
      <load_value><![CDATA[/action/system_access/reference/check_next_search_time_interval_limitation/Default.aspx]]></load_value>
    </reference>
    
    <reference type="form" check_type="page" exist="true" start_by="false" end_by="false" regex_match="false" active="true" reason="next_search_time_interval_limitation_is_active">
      <path_value>btn_Search=</path_value>
      <load_value><![CDATA[/action/system_access/reference/check_next_search_time_interval_limitation/Default.aspx]]></load_value>
    </reference>
	 
  </before_load_path_reference_list>
  
</before_load_path_reference_root>

You can control the url paths before execution. The above example shows that you can execute aspx pages before executing the route and deny access to the routes or change or add to the output hypertext. The first reference in the xml file above shows that there is a restriction to entering the login page; every time the user clicks on the login button, the aspx page is executed, and the number of login attempts is reduced by one from the session; If this value becomes 0, it will not allow entry.

you can run the Startup method in the Run method in the builder (before the execute page) located in the Program.cs class.

app.Run(async context =>
{
+  BeforeLoadPathReference.Run(context);

   CodeBehindExecute execute = new CodeBehindExecute();
   await context.Response.WriteAsync(execute.Run(context));
   await context.Response.CompleteAsync();
});

After the load path reference

Example

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<after_load_path_reference_root>

  <after_load_path_reference_list>
    
    <reference type="url" check_type="page" exist="false" start_by="true" end_by="false" regex_match="false" active="true">
      <path_value>/upload/attachment/</path_value>
      <load_value><![CDATA[/action/system_access/reference/increase_attachment_visit/Default.aspx]]></load_value>
    </reference>
    
  </after_load_path_reference_list>
  
</after_load_path_reference_root>

You also have the possibility to control the url paths after execution. The example above shows that you can run aspx pages after running the route. In the XML file above, you can see that after the requests, an aspx page is executed in the upload/attachment path, and if the path is an existing file in the path and database, the download value of the attachment file in the database is added by one number.

you can run the Startup method in the Run method in the builder (before the execute page) located in the Program.cs class.

app.Run(async context =>
{
   CodeBehindExecute execute = new CodeBehindExecute();
   await context.Response.WriteAsync(execute.Run(context));

+  AfterLoadPathReference.Run(context);

   await context.Response.CompleteAsync();
});

The advanced features mentioned above are the features that make your system adjustable. Customers of your system may want to add things that you don't even know about.

The program that is created under CodeBehind is highly extensible, and creating these features is very simple. Implementing these things in ASP.NET Core becomes very challenging. At the same time, CodeBehind has the ability to create powerful and customizable systems.

It is recommended to research the following about ASP.NET Core and then compare it with CodeBehind.

  • Load a cshtml page from another cshtml page
  • Changes in cshtml pages after publication and deployment on the web
  • Call the path of an executable page specified at runtime on the current page
  • About how to add web parts
  • Ability to add web parts without restarting the running project
  • The possibility of reusing web parts
  • Access to the route's output response
  • Adding a large project to a very large project
  • Challenges of moving the project to new versions

Reference