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

Can we use same datatable for pagemethod and webmethod in ASP.NET?

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

Gridviews 绑定在页面加载事件上,图表通过调用 WebMethod 使用 jquery-ajax 方法(使用 amcharts 和 highcharts)显示。

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

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

static DataTable Report1;

并像这样绑定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());
        }

    }

在 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);

    }

有了这个我就可以同时显示网格和图表了。

现在请告诉我,这是处理 web 方法的正确方法吗?我读过 webmethod 与页面无关,all.Please 告诉我这个方法的缺点。

如果这是错误的,请提出一个更好的方法来提高页面性能?

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

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

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

请检查以下场景:

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

首页

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);
        }
    }
}

索引页

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

现在在每个页面加载和 运行 应用程序中放置一个断点,

  • 打开 separate tab 中的两个页面。
  • 刷新主页并检查列是否显示
  • 现在转到下一个选项卡(索引)并刷新它(为 dt 创建了一个新实例)。它会影响数据 table 现在您也可以在家里获得新数据 table。
  • 因此,如果这两个 processes/pages 同时执行,则两个页面都将获得最新值。这就是为什么我说它会在并发测试中实现这一点。

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

首页

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"];
        }
    }
}

首先,根据一般经验,不要在 Web 应用程序中使用静态变量。这些充当全局变量,不会随每个请求实例化。

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

  1. 为您尝试绑定的对象创建一个模型。

例如,如果您有一个名为 person 的 table,它具有以下字段。

Id | first_name | last_name | audit_ts

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

public class Person
{
    public int Id {get;set;}
    public string FirstName {get;set;}
    public string LastName {get;set;}
}
  1. 现在在一个单独的函数中,在某些 class 中,您可以从数据库调用存储过程,然后将 table 行转换为 table进入人物对象列表。

  2. 现在,与其调用存储过程两次来获取相同的数据(这只会降低应用程序的性能),不如将网格视图绑定到后面的代码中 Page_Load事件。在调用您的网络方法后,只需绑定 HTML table,我相信它在您的 code-behind. You can refer to this post regarding how to bind your HTML table with JSON object returned by your Ajax 调用中。

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

对于很少使用的 Cache 对象,这是一个很好的用例类似,灵活多了。

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

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

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);


}

然后,当您在其他方法中时,您可以使用 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
}

以及您的网格绑定:

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());
    }

}

现在,您将需要做一些此处未提及的事情。您应该考虑您希望缓存数据何时过期(给出的示例是 10 分钟)。您还应该考虑是希望它是绝对分钟数(绝对到期)还是自上次访问以来的分钟数(滑动到期)。在你的情况下,可能绝对到期,但只有你知道。那么在设置变量内容的时候就要设置过期了。

在此处查看缓存文档: https://msdn.microsoft.com/en-us/library/6hbbsfk6.aspx

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

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

查看您提供的代码示例(以及您传递给 GetReportGraph() 的参数 date_fromdate_to我假设:

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

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

  3. 由于数据经过筛选,不会有数千条记录。

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

拿到数据table绑定grid view后,立即序列化为JSON并注册为脚本块(定义一个JS变量,将序列化后的JSON 因为它的价值)。

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