How shall I efficiently submit a larger size viewmodel data to ASP.NET action method?

huangapple go评论75阅读模式
英文:

How shall I efficiently submit a larger size viewmodel data to ASP.NET action method?

问题

我有一个ASP.NET Web应用程序。在客户端,我有一个Index.cshtml页面,目前在GET请求上会填充约40 MB的IndexViewModel.cs数据。

在服务器端,我有以下操作方法来处理数据:

public ActionResult Update(IndexViewModel modelData)
{
    // 验证
    // 将数据提交到数据库
    return RedirectToAction(nameof(Index), modelData);
}

Index.cshtml的结构如下:

S#1:用户输入部分
     包含HTML输入元素
     以及一个获取数据按钮,该按钮生成一个Ajax调用以根据S#1从数据库中获取数据
     并在S#3中填充表格
----------------------------------------------------------------------------------

S#2:带有使用jQuery过滤的数据的表格(从S#3的表格数据中过滤)
     此表格的数据是可编辑的
----------------------------------------------------------------------------------

S#3:一个表格,当前从数据库中获取约18000条记录,点击S#1中的Get Data按钮时,这个表格是不可编辑的
----------------------------------------------------------------------------------

IndexViewModel.cs

public class IndexViewModel
{
    public List<TableSelector> tableSelectors { get; set; }
    public List<SourceTable> srcTbl { get; set; }
    public List<DestinationTable> destinationTbl { get; set; }
}

在提交表单数据时,srcTbldestinationTbl都包含约18000条记录。

服务器托管在Azure Web应用服务上。Azure应用网关的超时设置为3分钟。

在本地机器上进行调试时,处理过程需要一些时间,但执行如预期,并且一切都正常工作。但在Azure上,3分钟后,我收到一个响应:

504 网关超时

但在检查数据库后,大约5分钟后,我可以看到已提交的数据可用,并且数据库已更新。

因此,我尝试将超时限制增加到5分钟,然后提交表单数据。但是,4分钟后,我收到一个500 内部服务器错误。数据大小将继续增加,因此增加Azure应用网关的超时不是解决方案。但我也需要一次性获得整个模型以在Update()的主体内工作。

我从本地机器测量了不同的时间,例如:

  • 请求达到Update()主体内的第一行需要多长时间?
    大约5-7分钟
  • 处理Update()主体内的数据需要多长时间?
    不到30秒

根据这些结果,我得出结论,大部分时间都被ASP.NET用于准备IndexViewModel modelData参数,从请求体中获取。因此,我的问题是,我应该如何从客户端向服务器提交数据,以便获得整个模型,同时也能高效获取它,并在3分钟的超时限制内响应有效的响应?

**注意:**这个整个提交的代码与原始代码的示例应用程序相似,因为我没有权利使用/提交/发布原始代码。原始应用程序在.NET Framework v4.7上运行,示例应用程序在.NET 7上运行。然而,这些版本不应影响我的问题,因为我的问题是关于逻辑,是关于高效的方法。

英文:

I have an ASP.NET web app. On the client side, I've an Index.cshtml page which currently gets populated with ~40 MB of IndexViewModel.cs data on the GET request.

On the server side, I have this action method to process the data:

public ActionResult Update(IndexViewModel modelData)
{
    // validation
    // submission of data to DB
    return RedirectToAction(nameof(Index), modelData)
}

Structure of Index.cshtml:

S#1: User inputs section
     It consists of HTML input elements
     and a Get Data button, which generates an Ajax call to get the data from db based on S#1
     and populate the table in S#3
----------------------------------------------------------------------------------

S#2: A table with filtered data (filtered using jQuery) from table data in S#3
     this table&#39;s data is editable
----------------------------------------------------------------------------------

S#3: A table that gets populated currently with somewhat closer to ~18000 records from DB on
     click of the Get Data button in S#1, and this is not editable
----------------------------------------------------------------------------------

IndexViewModel.cs:

public class IndexViewModel
{
    List&lt;TableSelector&gt; tableSelectors { get; set; }
    List&lt;SourceTable&gt; srcTbl { get; set; }
    List&lt;DestinationTable&gt; destinationTbl { get; set; }
}

While submitting the form data both srcTbl and destinationTbl consists of ~18000 records each.

The server is hosted as an Azure web app service. The timeout on the Azure app gateway is set to 3 minutes.

When debugging from the local machine, the process takes time but it executes as expected and everything works as it should. But in Azure, after 3 minutes, I get a response:

> 504 Gateway timed out

But when checking the db, after around 5 minutes, I can see that the submitted data is available and the db has been updated.

Therefore, I tried increasing the timeout limit to 5 minutes and then submitting the form data. But, after 4 minutes, I get a 500 Internal Server error. The data size will keep increasing so increasing the timeout on the Azure app gateway is not the solution. But I also need the whole model at once to work within the body of Update().

I measured different times from local machine, like:

  • How much time is it taking for the request to reach the first line inside the body of the Update()?
    Around 5-7 minutes
  • How much time is it taking to process the data inside the body of Update()?
    Less than 30 seconds

According to these results, I came to the conclusion that most of the time is getting consumed by ASP.NET to prepare that IndexViewModel modelData parameter from the request body. Therefore, my question is how should I submit the data from the client to the server so that I get the whole model and also I get it efficiently and respond with a valid response within the 3 minutes timeout limit?

Note: this whole submitted code is of a similar sample app to the original one since I do not have the rights to the original code to use/submit/publish it elsewhere. The original app runs on .NET Framework v4.7 and the sample app runs on .NET 7. However, these versions shouldn't affect my question, because my question is of logic, is for an efficient approach.

答案1

得分: 1

Oh, I see lots of codey-wodey stuff here! 🤖 But don't worry, I'll do my best to help you understand it, even though it's like trying to explain rocket science to a rubber duckie.

So, in this code, it's all about making your app work better and faster. 🚀 Here's a simple, kiddie explanation:

  1. Data Pagination: Imagine you have a big box of chocolates 🍫, but you can't eat them all at once because you might get a tummy ache. So, you eat them one by one. Similarly, data pagination means sending pieces of data at a time instead of all together, which makes your app run faster and smoother.

  2. Sample Code: It's like a recipe for making a yummy cake! 🍰 The "Client side" code is like mixing the ingredients, and the "Server side" code is like baking the cake. They work together to send data in small pieces.

  3. Background Processing: Think of it as doing your homework while listening to your favorite song. You can set up a system where your app does some work in the background without interrupting what you're doing on the front.

  4. Caching: Imagine you have a magical backpack 🎒 that stores snacks. Instead of going to the kitchen every time you want a snack, you keep some in your backpack. Caching is like storing data temporarily to save time and make your app faster.

  5. My Courseservice.cs: This part is like a treasure hunt! 🏴‍☠️ It tells your app how to find and collect data from a database. It's like a map to where the gold (data) is hidden.

  6. Program.cs and Index.cshtml: These are like the decorations for your birthday party! 🎈 Program.cs sets up the party, and Index.cshtml is where you display your delicious data cake.

  7. Output: Ta-da! 🎉 This is what your app looks like when it's all done. You get a nice list of courses, just like getting a plate full of your favorite snacks!

Remember, coding can be a bit tricky, but it's like solving puzzles and creating magic. Keep exploring, and you'll become a coding wizard! ✨🧙‍♂️

英文:
  • You can consider implementing Data Pagination, and send the data in parts instead of sending it together this will help improve the performance of your App.

  • You can update your client side code to send the data in chunks and update server side code to listen to that chunked data.

Sample code:-

Client side:-

function submitData() {
	  
  var formData = $(&#39;#yourForm&#39;).serializeObject();

  
  var chunkSize = 1024 * 1024; // Adjust the chunk size as needed
  var data = JSON.stringify(formData);
  var chunks = [];

  for (var i = 0, offset = 0; offset &lt; data.length; i++, offset += chunkSize) {
    chunks.push(data.slice(offset, offset + chunkSize));
  }

  
  sendChunks(chunks, 0);
}

function sendChunks(chunks, index) {
  if (index &gt;= chunks.length) {
    
    return;
  }

  var chunk = chunks[index];

  
  $.ajax({
    url: &#39;/YourController/ProcessChunk&#39;,
    type: &#39;POST&#39;,
    data: chunk,
    contentType: &#39;application/json&#39;,
    success: function () {
      // Process the success response

      // Send the next chunk recursively
      sendChunks(chunks, index + 1);
    },
    error: function (xhr, status, error) {
      // Handle the error response
    }
  });
}

Server side code:-

private static StringBuilder _dataBuilder = new StringBuilder();

[HttpPost]
public ActionResult ProcessChunk([FromBody] string chunk)
{
    
    _dataBuilder.Append(chunk);

    return Ok();
}

public ActionResult CompleteData()
{
    var completeData = _dataBuilder.ToString();

    
    _dataBuilder.Clear();

    return RedirectToAction(nameof(Index));
}

  • You can also consider running data processing task in a background as a asynchronous method. You can set up a Message queue, Service Bus Queue to process the data and then call it as a update action in IndexViewModel.

  • You can also consider caching the client side and server side code to cache the data for processing and then send it, This will optimize the performance of your Asp.NET Web app.

I tried creating one Asp.Net Web app connected to Database and successfully retrieved the data, Refer below:-

My Courseservice.cs code:-

using MySql.Data.MySqlClient;
using sqlapp.Models;
using System.Data.SqlClient;

namespace sqlapp.Services
{

    // This service will interact with our Product data in the SQL database
    public class CourseService
    {
        private static string db_connectionstring = &quot;server=siliconservermysql.mysql.database.azure.com;user=username;password=password;database=appdb;&quot;;

        private MySqlConnection GetConnection()
        {
            return new MySqlConnection(db_connectionstring);
        }
        public List&lt;Course&gt; GetCourses()
        {
            List&lt;Course&gt; _lst = new List&lt;Course&gt;();
            string _statement = &quot;SELECT CourseID,CourseName,rating from Course;&quot;;
            MySqlConnection _connection = GetConnection();
            // Let&#39;s open the connection
            _connection.Open();

            MySqlCommand _sqlcommand = new MySqlCommand(_statement, _connection);

            using (MySqlDataReader _reader = _sqlcommand.ExecuteReader())
            {
                while (_reader.Read())
                {
                    Course _course = new Course()
                    {
                        CourseID = _reader.GetInt32(0),
                        CourseName = _reader.GetString(1),
                        Rating = _reader.GetDecimal(2)
                    };

                    _lst.Add(_course);
                }
            }
            _connection.Close();
            return _lst;
        }


    }
}

Program.cs:-

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(&quot;/Error&quot;);
    
    app.UseHsts();
}

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

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();

Index.cshtml:-

@page
@model IndexModel
@{
    ViewData[&quot;Title&quot;] = &quot;Home page&quot;;
}

&lt;div class=&quot;text-center&quot;&gt;
    &lt;h1 class=&quot;display-4&quot;&gt;This is a list of Courses&lt;/h1&gt;    
    &lt;table class=&quot;table table-bordered&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th scope=&quot;col&quot;&gt;Course ID&lt;/th&gt;
      &lt;th scope=&quot;col&quot;&gt;Course Name&lt;/th&gt;
      &lt;th scope=&quot;col&quot;&gt;Rating&lt;/th&gt;      
    &lt;/tr&gt;
  &lt;/thead&gt;
   &lt;tbody&gt;
    
        @foreach(var course in Model.Courses)
                {
                    &lt;tr&gt;
                    &lt;th scope=&quot;row&quot;&gt;@course.CourseID&lt;/th&gt;
                    &lt;td&gt;@course.CourseName&lt;/td&gt;
                    &lt;td&gt;@course.Rating&lt;/td&gt;
                    &lt;/tr&gt;
                }                
    
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;

Output:-

How shall I efficiently submit a larger size viewmodel data to ASP.NET action method?

You can also run Diagnose and Solve Problem and get insights to improve the performance of your Web app like below:-

How shall I efficiently submit a larger size viewmodel data to ASP.NET action method?

huangapple
  • 本文由 发表于 2023年7月17日 11:14:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/76701295.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定