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

本文关键字:数据表 webmethod ASP NET pagememethod 中为 我们 | 更新日期: 2023-09-27 18:14:54

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

Gridviews绑定在pageload事件上,图表通过调用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);
    }

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

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

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

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

不,这不是正确的方法。由于您已将DataTable声明为static(静态变量具有应用程序作用域,不能实例化),因此所有

用户将得到相同的结果(最近更新的值)。

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

请检查以下场景:

考虑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创建一个新实例)。它将影响数据表,现在您将在家里获得新的数据表。
  • 因此,如果这两个进程/页面并发执行,将获得两个页面的最新值。这就是为什么我说它将在并发测试中实现这一点。

在这种情况下可以使用会话。考虑下面的代码:

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应用程序中使用静态变量。它们充当全局变量,不会在每个请求中实例化。

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

  1. 创建一个你想要绑定的对象的模型

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

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;}
}
  • 现在在一个单独的函数中,在某些类中,您可以从数据库调用存储过程,然后将person表中的表行转换为person Object列表。

  • 现在,而不是调用你的存储过程两次获得相同的数据,这只会降低你的应用程序的性能,你可以做的是,而不是绑定你的网格视图在你的代码后面的Page_Load事件。简单地绑定HTML表后,你使调用你的webmethod,我相信是在你的代码背后。关于如何将HTML表与Ajax调用返回的JSON对象绑定,您可以参考这篇文章。

  • 这种方式,您正在对服务器和数据库进行一次调用,以使用相同的数据来绑定您的表以及您的图表。

  • 这是很少使用的缓存对象的一个很好的用例许多用户理解ViewState和SessionState,但是缓存对象没有被广泛使用,尽管概念非常相似,但它要灵活得多。

    如果你的页面调用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

    添加Cache数据: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给出的方法。如果没有,请检查下面的替代方法:

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

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