我们可以在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和页面没有任何关系。请告诉我这种方法的缺点。
如果这是错误的,请建议一个更好的方法来提高页面性能?
不,这不是正确的方法。由于您已将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层。相反,使用强类型对象。
- 创建一个你想要绑定的对象的模型
例如,如果您有一个名为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_from
和date_to
) 我假设:
-
您有2个输入字段,用户指定日期范围,然后提交数据(引起回发),基于此,您正在过滤记录并显示在网格和图表中。
-
由于不同的用户会提供不同的日期范围,所以您不希望向所有用户显示相同的数据。
-
当数据被过滤时,它不会有成千上万的记录。
我不确定你正在使用的网格视图的什么功能。它是否仅用于显示只读表格数据?如果是,你可以考虑@Nabin Karki Thapa给出的方法。如果没有,请检查下面的替代方法:
获得数据表并将其绑定到网格视图后,立即将其序列化为JSON并将其注册为脚本块(定义一个JS变量并将序列化的JSON赋值为其值)。
在客户端,当绘制图表时,不是调用webmethod,而是使用您已经注册的JS变量来获取JSON对象。这样可以避免调用web方法(AJAX)和额外的存储过程调用。