我们可以在ASP.NET中为pagemethod和webmethod使用相同的数据表吗?

时间:2021-09-20 16:27:33

I am trying to create a new webpage where i need to display almost 10 different gridviews and charts.

我正在尝试创建一个新的网页,我需要显示近10个不同的网格视图和图表。

Gridviews are binded on pageload event and charts are displayed using jquery-ajax method (using amcharts as well as highcharts) by calling WebMethod.

Gridview在pageload事件中绑定,并且通过调用WebMethod使用jquery-ajax方法(使用amcharts以及highcharts)显示图表。

Initially i implemented the page in a way that after executing same set of stored procedures for gridviews(for showing grid view data) and webmethods(for drawing charts).So same sps are executed twice for this page(one for grid and another for chart).There are 10 sps required to execute for fetching the data.

最初我实现页面的方式是在执行gridview(用于显示网格视图数据)和webmethods(用于绘制图表)的同一组存储过程之后。为此页面执行两次相同的sps(一个用于网格,另一个用于图表) )。执行获取数据需要10个sps。

So for improving the page performance i have created static datatable like this

因此,为了提高页面性能,我创建了这样的静态数据表

static DataTable Report1;

and binded the gridview like this.

并且像这样捆绑了gridview。

private void gvbindReport1()
    {
        try
        {            
            Report1 = new DataTable();//refreshed datatable 
            DataSet ReportDS1 = objmvbl.GetReportGraph(ClientID, date_From, date_To);
            if (ReportDS1.Tables.Count > 0)
            {
                Report1 = ReportDS1.Tables[0];//bindinding data to static datatable

            }
            GdReport.DataSource = Report1;
            GdReport.DataBind();
        }
        catch (Exception ex)
        {
            Log.Errlog("Error Occured in  gvbindReport1 : " + ex.Message.ToString());
        }

    }

and inside the webmethod i have used the same datatable for drawing the chart like this

在webmethod内部,我使用了相同的数据表来绘制图表

 [System.Web.Services.WebMethod]
    public static string GetDataReport1()
    {
        System.Web.Script.Serialization.JavaScriptSerializer serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
        List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>();
        Dictionary<string, object> row;
        try
        {
            //processing for the data inside static datatable
            if (Report1.Rows.Count > 0)
            {
                foreach (DataRow dr in Report1.Rows)
                {
                    row = new Dictionary<string, object>();
                    foreach (DataColumn col in Report1.Columns)
                    {
                        row.Add(col.ColumnName, dr[col]);
                    }
                    rows.Add(row);
                }
            }
        }
        catch (Exception ex)
        {
            Log.Errlog("Error Occured in  GetDataReport WebMethod of Report Page : " + ex.Message.ToString());
        }

        return serializer.Serialize(rows);

    }

with this i am able to show both grid and charts.

有了这个我能够显示网格和图表。

Now Please tell me that, is this a correct approach to deal with webmethods? i have read that webmethod have no relation to the page and all.Please Tell me the drawbacks of this method.

现在请告诉我,这是处理web方法的正确方法吗?我已经读过webmethod与页面没有任何关系。请告诉我这种方法的缺点。

If this is wrong,Please suggest a better way to improve the page performance?

如果这是错误的,请建议更好的方法来改善页面性能?

4 个解决方案

#1


6  

No, this is not the correct method. Since you have declared the DataTable as static (a static variable has application scope and cannot be instantiated) all

不,这不是正确的方法。由于您已将DataTable声明为静态(静态变量具有应用程序范围且无法实例化)

users will get the same result (last updated values).

用户将获得相同的结果(上次更新的值)。

You can realize this in concurrency testing.

您可以在并发测试中实现这一点。

Please check the following scenario:

请检查以下场景:

Consider dtbl is the static dataTable which is initialized on the home page, and you create another instance instance of `datatable on the index page (both are in page load as given below).

考虑dtbl是在主页上初始化的静态dataTable,并且您在索引页面上创建了另一个`datatable实例实例(两者都在页面加载中,如下所示)。

Home

public static DataTable dtbl;
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        dtbl = new DataTable();
        dtbl.Columns.Add("id");
        dtbl.Columns.Add("name");
        for (int i = 0; i < 10; i++)
        {
            DataRow dr = dtbl.NewRow();
            dr["id"] = i.ToString();
            dr["name"] = i + 1;
            dtbl.Rows.Add(dr);
        }
    }
}

Index page

索引页面

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        home.dtbl = new DataTable();
    }
}

Now put a breakpoint in each page load and run the application,

现在在每个页面加载一个断点并运行应用程序,

  • Open both the pages in separate tab.
  • 在单独的选项卡中打开两个页面。
  • Refresh the home page and check whether the columns are showing
  • 刷新主页并检查列是否显示
  • Now go to next tab (index) and refresh it (a new instance is created for dt). It will affect the datatable now you will get the new datatable in home also.
  • 现在转到下一个选项卡(索引)并刷新它(为dt创建一个新实例)。现在,您将在家中获得新数据表,这将影响数据表。
  • So if these two processes/pages are concurrently executed the latest value will get for both the pages. That's why I am saying it will realize this in concurrency testing.
  • 因此,如果同时执行这两个进程/页面,则两个页面都将获得最新值。这就是为什么我说它会在并发测试中实现这一点。

You can make use of a session in this case. Consider the following code:

在这种情况下,您可以使用会话。请考虑以下代码:

Home

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        dtbl = new DataTable();
        dtbl.Columns.Add("id");
        dtbl.Columns.Add("name");
        for (int i = 0; i < 10; i++)
        {
            DataRow dr = dtbl.NewRow();
            dr["id"] = i.ToString();
            dr["name"] = i + 1;
            dtbl.Rows.Add(dr);
        }
        if (((DataTable)Session["MyDatatable"]).Columns.Count < 0)
        {
            Session["MyDatatable"] = dtbl;
        }
        else
        {
            dtbl = (DataTable)Session["MyDatatable"];
        }
    }
}

#2


5  

First off, do not use, as a general rule of thumb, static variables in an web application. These act as global variables and are not instantiated with each request.

首先,不要在Web应用程序中使用静态变量作为一般经验法则。它们充当全局变量,并且不会针对每个请求进行实例化。

I wouldn't also suggest you using DataTables all the way up to your UI layer. Instead, work with strongly-typed objects.

我也不建议你一直使用DataTables到你的UI层。相反,使用强类型对象。

  1. Make a Model of the object you are trying to bind.
  2. 创建您尝试绑定的对象的模型。

Like for example if you have a table called person that has the following fields.

例如,如果您有一个名为person的表,其中包含以下字段。

Id | first_name | last_name | audit_ts

You can create an object as such:

您可以这样创建一个对象:

public class Person
{
    public int Id {get;set;}
    public string FirstName {get;set;}
    public string LastName {get;set;}
}
  1. Now in a separate functions, in some class you can call your stored procedure from the database and then cast your table rows in the person table into the list of Person Object.

    现在在一个单独的函数中,在某些类中,您可以从数据库中调用存储过程,然后将person表中的表行转换为Person对象列表。

  2. Now, instead of calling your stored procedure twice to get the same data, which only reduces your application's performance, what you can do is to instead of binding your grid view in your code behind at Page_Load event. Simply bind the HTML table after you make the call to your webmethod which I believe is in your code-behind. You can refer to this post regarding how to bind your HTML table with JSON object returned by your Ajax call.

    现在,您可以做的不是在两次调用存储过程来获取相同的数据(仅降低应用程序的性能),而是在Page_Load事件中代替绑定代码中的网格视图。只需在调用webmethod之后绑定HTML表格,我认为这是在您的代码隐藏中。您可以参考这篇文章,了解如何将HTML表与Ajax调用返回的JSON对象绑定。

  3. This way, you are making one call to the server and to the database to use the same data to bind your table as well as your charts.

    这样,您将对服务器和数据库进行一次调用,以使用相同的数据来绑定表格和图表。

#3


2  

This is a good use case for the little used Cache Object Many users understand ViewState and SessionState, however the Cache object is not as widely utilized, and although the concept is very similar, it is much more flexible.

对于很少使用的缓存对象来说,这是一个很好的用例。许多用户理解ViewState和SessionState,但是Cache对象并没有被广泛使用,虽然这个概念非常相似,但它更灵活。

If your page is calling 10 stored procedures twice (once for your grids and a second time for your charts) then lets improve the performance by roughly 100% by eliminating the extra calls with the Cache Object

如果您的页面调用了10次存储过程两次(一次用于网格,第二次用于图表),那么通过使用Cache对象消除额外调用,可以将性能提高大约100%

Have one call to the stored procedures in a separate method that populate your data tables cache object, which is then reused throughout your application.

在一个单独的方法中调用存储过程,该方法填充数据表缓存对象,然后在整个应用程序中重用。

private void loadReport1IntoCache()
{
  //...load your data from DB into the Report1 variable here


  //this line is new, and it saves your data into a global Cache variable
  //with an absolute expiration of 10 minutes
  Cache.Insert("Report1", Report1, null,
  DateTime.Now.AddMinutes(10d), 
  System.Web.Caching.Cache.NoSlidingExpiration);


}

Then, when you are inside your other methods, you can use the Cache variable instead of calling stored procedures again. For example:

然后,当您在其他方法中时,可以使用Cache变量而不是再次调用存储过程。例如:

[System.Web.Services.WebMethod]
public static string GetDataReport1()
{
   //first load the application variable before performing your other work
   DataTable myCachedReport1Data = (DataTable)Cache["Report1"];
   //did the Cache expire?
   if (myCachedReport1Data == null)
   {
   //if so refresh it
   loadReport1IntoCache();
   //and then assign the variable the contents of the refresh and proceed
   myCachedReport1Data = (DataTable)Cache["Report1"];
   }

   //other work here, utilizing the myCachedReport1Data variable
}

and for your grid binding:

并为您的网格绑定:

private void gvbindReport1()
{
    try
    {            
        DataTable myCachedReport1Data = (DataTable)Cache["Report1"];
        //did the Cache expire?
        if (myCachedReport1Data == null)
        {
          //if so refresh it
          loadReport1IntoCache();
          //and then assign the variable the contents of the refresh
          myCachedReport1Data = (DataTable)Cache["Report1"];
        }

        GdReport.DataSource = myCachedReport1Data ;
        GdReport.DataBind();
    }
    catch (Exception ex)
    {
        Log.Errlog("Error Occured in  gvbindReport1 : " +  ex.Message.ToString());
    }

}

Now, you will have to do a few things not mentioned here. You should consider when you want your Cache data to expire (the example given is 10 minutes). Also you should consider if you want it to be an Absolute Number of minutes (Absolute Expiry) or a number of minutes since last access (Sliding Expiry). In your case, probably absolute expiry, but only you know that. Then you will set the expiration when you are setting the variable contents.

现在,你将不得不做一些这里没有提到的事情。您应该考虑何时希望缓存数据过期(给出的示例为10分钟)。此外,您应该考虑是否要求它是绝对分钟数(绝对到期时间)或自上次访问后的分钟数(滑动到期时间)。在你的情况下,可能绝对到期,但只有你知道。然后,您将在设置变量内容时设置到期时间。

See the Cache documentation here: https://msdn.microsoft.com/en-us/library/6hbbsfk6.aspx

请参阅此处的缓存文档:https://msdn.microsoft.com/en-us/library/6hbbsfk6.aspx

Adding Cache data: https://msdn.microsoft.com/en-us/library/18c1wd61.aspx

添加缓存数据:https://msdn.microsoft.com/en-us/library/18c1wd61.aspx

Retrieving Cache data: https://msdn.microsoft.com/en-us/library/xhy3h9f9.aspx

检索缓存数据:https://msdn.microsoft.com/en-us/library/xhy3h9f9.aspx

#4


2  

Looking at the code sample that you have given (and the parameters date_from and date_to that you are passing to GetReportGraph()) I assume:

查看您给出的代码示例(以及您传递给GetReportGraph()的参数date_from和date_to)我假设:

  1. you have 2 input fields where user is specifying the date range and then submitting the data (causing postback), based on which you are filtering the records and showing in grid as well as chart.

    您有2个输入字段,其中用户指定日期范围,然后提交数据(导致回发),您将根据该字段过滤记录并显示在网格和图表中。

  2. as different users would be providing different date ranges, you don't want to show the same data to all users.

    由于不同的用户将提供不同的日期范围,您不希望向所有用户显示相同的数据。

  3. as the data is filtered, its not going to have thousands of records.

    随着数据被过滤,它将不会有数千条记录。

I'm not sure what functionality of grid view you are using. Is it used only to show read only tabular data? If yes, you can consider the approach given by @Nabin Karki Thapa. If not check the alternate approach below:

我不确定您使用的网格视图的功能。它仅用于显示只读表格数据吗?如果是,您可以考虑@Nabin Karki Thapa给出的方法。如果没有,请检查以下替代方法:

After you have got the data table and bound it to grid view, immediately serialize it to JSON and register it as a script block (define a JS variable and assign the serialized JSON as it's value).

获得数据表并将其绑定到网格视图后,立即将其序列化为JSON并将其注册为脚本块(定义JS变量并将序列化的JSON指定为其值)。

On the client side, while charting, instead of invoking webmethod, to get the JSON object use the JS variable that you have registered. This way you will avoid the call to web method (AJAX) and extra stored procedure call altogether.

在客户端,绘制而不是调用webmethod来获取JSON对象时使用已注册的JS变量。这样,您将完全避免调用Web方法(AJAX)和额外的存储过程调用。

#1


6  

No, this is not the correct method. Since you have declared the DataTable as static (a static variable has application scope and cannot be instantiated) all

不,这不是正确的方法。由于您已将DataTable声明为静态(静态变量具有应用程序范围且无法实例化)

users will get the same result (last updated values).

用户将获得相同的结果(上次更新的值)。

You can realize this in concurrency testing.

您可以在并发测试中实现这一点。

Please check the following scenario:

请检查以下场景:

Consider dtbl is the static dataTable which is initialized on the home page, and you create another instance instance of `datatable on the index page (both are in page load as given below).

考虑dtbl是在主页上初始化的静态dataTable,并且您在索引页面上创建了另一个`datatable实例实例(两者都在页面加载中,如下所示)。

Home

public static DataTable dtbl;
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        dtbl = new DataTable();
        dtbl.Columns.Add("id");
        dtbl.Columns.Add("name");
        for (int i = 0; i < 10; i++)
        {
            DataRow dr = dtbl.NewRow();
            dr["id"] = i.ToString();
            dr["name"] = i + 1;
            dtbl.Rows.Add(dr);
        }
    }
}

Index page

索引页面

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        home.dtbl = new DataTable();
    }
}

Now put a breakpoint in each page load and run the application,

现在在每个页面加载一个断点并运行应用程序,

  • Open both the pages in separate tab.
  • 在单独的选项卡中打开两个页面。
  • Refresh the home page and check whether the columns are showing
  • 刷新主页并检查列是否显示
  • Now go to next tab (index) and refresh it (a new instance is created for dt). It will affect the datatable now you will get the new datatable in home also.
  • 现在转到下一个选项卡(索引)并刷新它(为dt创建一个新实例)。现在,您将在家中获得新数据表,这将影响数据表。
  • So if these two processes/pages are concurrently executed the latest value will get for both the pages. That's why I am saying it will realize this in concurrency testing.
  • 因此,如果同时执行这两个进程/页面,则两个页面都将获得最新值。这就是为什么我说它会在并发测试中实现这一点。

You can make use of a session in this case. Consider the following code:

在这种情况下,您可以使用会话。请考虑以下代码:

Home

protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        dtbl = new DataTable();
        dtbl.Columns.Add("id");
        dtbl.Columns.Add("name");
        for (int i = 0; i < 10; i++)
        {
            DataRow dr = dtbl.NewRow();
            dr["id"] = i.ToString();
            dr["name"] = i + 1;
            dtbl.Rows.Add(dr);
        }
        if (((DataTable)Session["MyDatatable"]).Columns.Count < 0)
        {
            Session["MyDatatable"] = dtbl;
        }
        else
        {
            dtbl = (DataTable)Session["MyDatatable"];
        }
    }
}

#2


5  

First off, do not use, as a general rule of thumb, static variables in an web application. These act as global variables and are not instantiated with each request.

首先,不要在Web应用程序中使用静态变量作为一般经验法则。它们充当全局变量,并且不会针对每个请求进行实例化。

I wouldn't also suggest you using DataTables all the way up to your UI layer. Instead, work with strongly-typed objects.

我也不建议你一直使用DataTables到你的UI层。相反,使用强类型对象。

  1. Make a Model of the object you are trying to bind.
  2. 创建您尝试绑定的对象的模型。

Like for example if you have a table called person that has the following fields.

例如,如果您有一个名为person的表,其中包含以下字段。

Id | first_name | last_name | audit_ts

You can create an object as such:

您可以这样创建一个对象:

public class Person
{
    public int Id {get;set;}
    public string FirstName {get;set;}
    public string LastName {get;set;}
}
  1. Now in a separate functions, in some class you can call your stored procedure from the database and then cast your table rows in the person table into the list of Person Object.

    现在在一个单独的函数中,在某些类中,您可以从数据库中调用存储过程,然后将person表中的表行转换为Person对象列表。

  2. Now, instead of calling your stored procedure twice to get the same data, which only reduces your application's performance, what you can do is to instead of binding your grid view in your code behind at Page_Load event. Simply bind the HTML table after you make the call to your webmethod which I believe is in your code-behind. You can refer to this post regarding how to bind your HTML table with JSON object returned by your Ajax call.

    现在,您可以做的不是在两次调用存储过程来获取相同的数据(仅降低应用程序的性能),而是在Page_Load事件中代替绑定代码中的网格视图。只需在调用webmethod之后绑定HTML表格,我认为这是在您的代码隐藏中。您可以参考这篇文章,了解如何将HTML表与Ajax调用返回的JSON对象绑定。

  3. This way, you are making one call to the server and to the database to use the same data to bind your table as well as your charts.

    这样,您将对服务器和数据库进行一次调用,以使用相同的数据来绑定表格和图表。

#3


2  

This is a good use case for the little used Cache Object Many users understand ViewState and SessionState, however the Cache object is not as widely utilized, and although the concept is very similar, it is much more flexible.

对于很少使用的缓存对象来说,这是一个很好的用例。许多用户理解ViewState和SessionState,但是Cache对象并没有被广泛使用,虽然这个概念非常相似,但它更灵活。

If your page is calling 10 stored procedures twice (once for your grids and a second time for your charts) then lets improve the performance by roughly 100% by eliminating the extra calls with the Cache Object

如果您的页面调用了10次存储过程两次(一次用于网格,第二次用于图表),那么通过使用Cache对象消除额外调用,可以将性能提高大约100%

Have one call to the stored procedures in a separate method that populate your data tables cache object, which is then reused throughout your application.

在一个单独的方法中调用存储过程,该方法填充数据表缓存对象,然后在整个应用程序中重用。

private void loadReport1IntoCache()
{
  //...load your data from DB into the Report1 variable here


  //this line is new, and it saves your data into a global Cache variable
  //with an absolute expiration of 10 minutes
  Cache.Insert("Report1", Report1, null,
  DateTime.Now.AddMinutes(10d), 
  System.Web.Caching.Cache.NoSlidingExpiration);


}

Then, when you are inside your other methods, you can use the Cache variable instead of calling stored procedures again. For example:

然后,当您在其他方法中时,可以使用Cache变量而不是再次调用存储过程。例如:

[System.Web.Services.WebMethod]
public static string GetDataReport1()
{
   //first load the application variable before performing your other work
   DataTable myCachedReport1Data = (DataTable)Cache["Report1"];
   //did the Cache expire?
   if (myCachedReport1Data == null)
   {
   //if so refresh it
   loadReport1IntoCache();
   //and then assign the variable the contents of the refresh and proceed
   myCachedReport1Data = (DataTable)Cache["Report1"];
   }

   //other work here, utilizing the myCachedReport1Data variable
}

and for your grid binding:

并为您的网格绑定:

private void gvbindReport1()
{
    try
    {            
        DataTable myCachedReport1Data = (DataTable)Cache["Report1"];
        //did the Cache expire?
        if (myCachedReport1Data == null)
        {
          //if so refresh it
          loadReport1IntoCache();
          //and then assign the variable the contents of the refresh
          myCachedReport1Data = (DataTable)Cache["Report1"];
        }

        GdReport.DataSource = myCachedReport1Data ;
        GdReport.DataBind();
    }
    catch (Exception ex)
    {
        Log.Errlog("Error Occured in  gvbindReport1 : " +  ex.Message.ToString());
    }

}

Now, you will have to do a few things not mentioned here. You should consider when you want your Cache data to expire (the example given is 10 minutes). Also you should consider if you want it to be an Absolute Number of minutes (Absolute Expiry) or a number of minutes since last access (Sliding Expiry). In your case, probably absolute expiry, but only you know that. Then you will set the expiration when you are setting the variable contents.

现在,你将不得不做一些这里没有提到的事情。您应该考虑何时希望缓存数据过期(给出的示例为10分钟)。此外,您应该考虑是否要求它是绝对分钟数(绝对到期时间)或自上次访问后的分钟数(滑动到期时间)。在你的情况下,可能绝对到期,但只有你知道。然后,您将在设置变量内容时设置到期时间。

See the Cache documentation here: https://msdn.microsoft.com/en-us/library/6hbbsfk6.aspx

请参阅此处的缓存文档:https://msdn.microsoft.com/en-us/library/6hbbsfk6.aspx

Adding Cache data: https://msdn.microsoft.com/en-us/library/18c1wd61.aspx

添加缓存数据:https://msdn.microsoft.com/en-us/library/18c1wd61.aspx

Retrieving Cache data: https://msdn.microsoft.com/en-us/library/xhy3h9f9.aspx

检索缓存数据:https://msdn.microsoft.com/en-us/library/xhy3h9f9.aspx

#4


2  

Looking at the code sample that you have given (and the parameters date_from and date_to that you are passing to GetReportGraph()) I assume:

查看您给出的代码示例(以及您传递给GetReportGraph()的参数date_from和date_to)我假设:

  1. you have 2 input fields where user is specifying the date range and then submitting the data (causing postback), based on which you are filtering the records and showing in grid as well as chart.

    您有2个输入字段,其中用户指定日期范围,然后提交数据(导致回发),您将根据该字段过滤记录并显示在网格和图表中。

  2. as different users would be providing different date ranges, you don't want to show the same data to all users.

    由于不同的用户将提供不同的日期范围,您不希望向所有用户显示相同的数据。

  3. as the data is filtered, its not going to have thousands of records.

    随着数据被过滤,它将不会有数千条记录。

I'm not sure what functionality of grid view you are using. Is it used only to show read only tabular data? If yes, you can consider the approach given by @Nabin Karki Thapa. If not check the alternate approach below:

我不确定您使用的网格视图的功能。它仅用于显示只读表格数据吗?如果是,您可以考虑@Nabin Karki Thapa给出的方法。如果没有,请检查以下替代方法:

After you have got the data table and bound it to grid view, immediately serialize it to JSON and register it as a script block (define a JS variable and assign the serialized JSON as it's value).

获得数据表并将其绑定到网格视图后,立即将其序列化为JSON并将其注册为脚本块(定义JS变量并将序列化的JSON指定为其值)。

On the client side, while charting, instead of invoking webmethod, to get the JSON object use the JS variable that you have registered. This way you will avoid the call to web method (AJAX) and extra stored procedure call altogether.

在客户端,绘制而不是调用webmethod来获取JSON对象时使用已注册的JS变量。这样,您将完全避免调用Web方法(AJAX)和额外的存储过程调用。