ASP.NET 2.0中的异步页面

简介:

http://msdn.microsoft.com/zh-cn/site/cc163725#S2

当ASP.Net收到一个页请求,它会从线程池中抓取一个线程,然后把这个请求交给这个线程处理。一个普通的,或者说是同步的页在请求期间占据了这个线程,阻止了线程被其它请求占用。如果同步请求变成IO绑定,例如,如果他调用远程的WEB服务或者查询远程数据库,并且等待数据返回,这个线程就被卡在那里,什么也不做直到返回值出现。 
这个阻碍了可伸缩性,因为线程池只有有限的线程可以得到。如果所有的请求处理线程都被阻塞来等待I/O操作完成,额外的请求将会被放入一个队列来等到线程空闲。顺利的话,问题也就是由于请求等待时间太久导致效率低下,如果糟糕点,那就是队列也满了,ASP.NET对接下来的请求会抛出503 "Server Unavailable"的错误。

异步页面提供一个小巧干净的解决方案来解决这种由于对I/O受阻的请求的问题。页处理由线程池线程开始,但是一旦一个异步I/O操作开始对ASP.NET来的信号做出响应,这个线程会返回到线程池。当操作完成,ASP.NET从线程池抓取另外一个线程,并完成处理这个请求。可伸缩性增加了,因为线程池线程更有效的使用了。线程本来是会被卡住等待I/O完成操作,现在可以用来服务于其他请求。最直接的受益者就是请求本身,不需要执行长期的I/O操作可以由此快速的进出管道。长时间的等待进入管道对性能有很大的负面影响。 
ASP.NET 2.0异步页面架构

ASP.NET 1.x的异步页面 
ASP.NET 1.x本身不支持异步页面,但是可以通过一些技巧来实现。可以看这个文章"Use Threads and Build Asynchronous Handlers in Your Server-Side Web Code"

 

这个窍门就是在页面的后台代码类中现实IHttpAsyncHandler ,让ASP.NET 不通过调用页面的IHttpHandler.ProcessRequest方法处理请求,而是通过调用IHttpAsyncHandler.BeginProcessRequest方法。你的BeginProcessRequest然后可以启动另一个线程。该线程调用base.ProcessRequest,使页经历了普通的请求处理周期(由事件,Load,Render完成),但是是在一个非线程池线程上。与此同时,BeginProcessRequest在调用新线程后立即返回,返回线程池。

这个是基本的方法,但是在细节上很折磨人。在这些东西中,你需要实现IAsyncResult接口,从BeginProcessRequest返回它。这通常意味着创建一个ManualResetEvent对象,当ProcessRequest 返回到后台线程时,设置信号量。另外,你必须提供线程调用base.ProcessRequest。不幸的是,大多数常见的把工作移到后台线程的方法,包括Thread.Start,ThreadPool.QueueUserWorkItem和异步委托在ASP.NET 应用程序中会起反作用,因为他们或者会从线程池偷走线程,或者会有不受约束的线程增长的风险,一个合适的异步页面实现使用一个自定义的线程池,自定义线程池类写起来不是小事。(for more information, see the .NET Matters column in the February 2005 issue of MSDN Magazine).

总之,在ASP.NET1.x中实现异步页面不是不可能,只是太复杂。在实现它一两次后,你会禁不住想最好有更好的方法。现在有了,ASP.NET 2.0。

ASP.NET2.0中的异步页面

ASP.NET 2.0极大的简化了创建异步页面。只需要在page指令中包含Async="true"属性。就像这样:

<%@ Page Async="true" ... %>

它告诉ASP.NET在页面实现IHttpAsyncHandler接口。接下来,你在页面生命周期的早期调用Page.AddOnPreRenderCompleteAsync方法,比如Page_Load方法,注册开始方法和结束方法,如下:

  1. AddOnPreRenderCompleteAsync (
  2.     new BeginEventHandler(MyBeginMethod),
  3.     new EndEventHandler (MyEndMethod)
  4. );

 

接下来的事情更意思,这个页面会经历常规的页面生命周期,直到在PreRender事件触发之后。然后ASP.NET调用AddOnPreRenderCompleteAsync所注册的Begin方法。这个Begin方法的工作就是启动一个异步操作,比如数据库查询或者web服务,并且马上返回。这时,分配去处理这个请求的线程返回到线程池,此外,Begin方法返回一个IAsyncResult对象,让ASP.NET决定当异步操作完成了,何时再从线程池抽取线程来调用End方法。在End方法返回之后,ASP.NET执行剩下部分的页面生命周期,包括rendering步骤,在Begin返回和End被调用这段时间内,处理请求的线程是空闲的,能够再处理其他的请求,直到End被调用。rendering被延期。由于Framework2.0提供了多种方法来完成一步操作,所以通常你不需要去实现IAsyncResult,Framework 会为你实现。

下面的隐藏类代码提供了一个实例。相应的页面包含一个Label控件,ID是Output。这个页使用System.Net.HttpWebRequest类来从http://msdn.microsoft.com抓取数据,然后解析转换返回的HTML,把所有找到的超链接写到Label控件上。

  1. using System;
  2. using System.Web;
  3. using System.Web.UI;
  4. using System.Web.UI.WebControls;
  5. using System.Net;
  6. using System.IO;
  7. using System.Text;
  8. using System.Text.RegularExpressions;
  9.  
  10. public partial class AsyncPage : System.Web.UI.Page
  11. {
  12.     private WebRequest _request;
  13.  
  14.     void Page_Load (object sender, EventArgs e)
  15.     {
  16.         AddOnPreRenderCompleteAsync (
  17.             new BeginEventHandler(BeginAsyncOperation),
  18.             new EndEventHandler (EndAsyncOperation)
  19.         );
  20.     }
  21.  
  22.     IAsyncResult BeginAsyncOperation (object sender, EventArgs e,
  23.         AsyncCallback cb, object state)
  24.     {
  25.         _request = WebRequest.Create("http://msdn.microsoft.com");
  26.         return _request.BeginGetResponse (cb, state);
  27.     }
  28.     void EndAsyncOperation (IAsyncResult ar)
  29.     {
  30.         string text;
  31.         using (WebResponse response = _request.EndGetResponse(ar))
  32.         {
  33.             using (StreamReader reader =
  34.                 new StreamReader(response.GetResponseStream()))
  35.             {
  36.                 text = reader.ReadToEnd();
  37.             }
  38.         }
  39.  
  40.         Regex regex = new Regex ("href\\s*=\\s*\"([^\"]*)\"",
  41.             RegexOptions.IgnoreCase);
  42.         MatchCollection matches = regex.Matches(text);
  43.  
  44.         StringBuilder builder = new StringBuilder(1024);
  45.         foreach (Match match in matches)
  46.         {
  47.             builder.Append (match.Groups[1]);
  48.             builder.Append("<br/>");
  49.         }
  50.  
  51.         Output.Text = builder.ToString ();
  52.     }
  53. }

由于HTTP request要花很久才能返回,AsyncPage.aspx.cs异步执行处理。它在Page_Load方法注册了Begin和End方法,在Begin方法中,调用HttpWebRequest.BeginGetResponse来启动一个异步的HTTP请求。BeginAsyncOperation由BeginGetResponse返回给ASP.NET一个IAsyncResult,结果就是当HTTP request完成后ASP.NET调用EndAsyncOperation。EndAsyncOperation解析内容,把结果输出到Label控件,之后触发rendering,HTTP请求返回到浏览器。

Figure 2 Synchronous vs. Asynchronous Page Processing

图2描述了ASP.NET2.0中同步页面和异步页面的不同点。当请求同步页面时,ASP.NET从线程池中取出一个线程来处理请求。如果请求暂停了,去执行I/O操作,这个线程就被占用了,直到操作完成,并且页面生命周期也结束。一个异步页面,对照来看,和普通的页面执行一样,通过PreRender事件。然后由AddOnPreRenderCompleteAsync注册的Begin方法被调用,之后,请求处理线程返回到线程池。Begin方法启动异步I/O操作,当操作完成,ASP.NET抓取线程池中的另外的线程,然后调用End方法,执行剩下页面生命周期。

Figure 3 Trace Output Shows Async Page's Async Point

Begin的调用标记了页面的异步点。图3的trace准确的显示了异步点的发生。如果要调用,AddOnPreRenderCompleteAsync必须在异步点之前调用,也就是说,不能在页面的PreRender事件之后。

异步数据绑定

ASP.NET页面使用HttpWebRequest直接请求其他页面不是很常见,但是查询数据,绑定数据集确很常见。所以,怎样使用异步页面执行异步数据绑定?图4展示了实现此功能的后台隐藏代码。

Figure 4 AsyncDataBind.aspx.cs

  1. using System;
  2. using System.Data;
  3. using System.Data.SqlClient;
  4. using System.Web;
  5. using System.Web.UI;
  6. using System.Web.UI.WebControls;
  7. using System.Web.Configuration;
  8.  
  9. public partial class AsyncDataBind : System.Web.UI.Page
  10. {
  11.     private SqlConnection _connection;
  12.     private SqlCommand _command;
  13.     private SqlDataReader _reader;
  14.  
  15.  
  16.     protected void Page_Load(object sender, EventArgs e)
  17.     {
  18.         if (!IsPostBack)
  19.         {
  20.             // Hook PreRenderComplete event for data binding
  21.             this.PreRenderComplete +=
  22.                 new EventHandler(Page_PreRenderComplete);
  23.  
  24.             // Register async methods
  25.             AddOnPreRenderCompleteAsync(
  26.                 new BeginEventHandler(BeginAsyncOperation),
  27.                 new EndEventHandler(EndAsyncOperation)
  28.             );
  29.         }
  30.     }
  31.     IAsyncResult BeginAsyncOperation (object sender, EventArgs e,
  32.         AsyncCallback cb, object state)
  33.     {
  34.         string connect = WebConfigurationManager.ConnectionStrings
  35.             ["PubsConnectionString"].ConnectionString;
  36.         _connection = new SqlConnection(connect);
  37.         _connection.Open();
  38.         _command = new SqlCommand(
  39.             "SELECT title_id, title, price FROM titles", _connection);
  40.         return _command.BeginExecuteReader (cb, state);
  41.     }
  42.  
  43.     void EndAsyncOperation(IAsyncResult ar)
  44.     {
  45.         _reader = _command.EndExecuteReader(ar);
  46.     }
  47.  
  48.     protected void Page_PreRenderComplete(object sender, EventArgs e)
  49.     {
  50.         Output.DataSource = _reader;
  51.         Output.DataBind();
  52.     }
  53.  
  54.     public override void Dispose()
  55.     {
  56.         if (_connection != null) _connection.Close();
  57.         base.Dispose();
  58.     }
  59. }

 

AsyncDataBind.aspx.cs也使用AddOnPreRenderCompleteAsync模式,但不是调用HttpWebRequest.BeginGetResponse,它的BeginAsyncOperation方法调用SqlCommand.BeginExecuteReader(ADO.NET 2.0中新带的),执行异步数据库查询。当调用完成,EndAsyncOperation调用SqlCommand.EndExecuteReader获取一个SqlDataReader,存储了一个私有字段。PreRenderComplete事件,在异步操作完成后,页面render之前除非,绑定SqlDataReader到输出Gridview控件。从外部看,页面看上去就像普通同步页面显数据一样,但是从内部卡,这个页面更有伸缩性,因为他不需要占用线程池线程来等待查询完成。

Calling Web Services Asynchronously

异步调用web服务

其他的通常由ASP.NETweb页面执行的IO任务是Web服务。既然web服务调用要花费很长的时间来返回,那么执行该操作的页面可以使用异步操作。

下面展示了一种创建异步页面调用web服务的方法。同样使用AddOnPreRenderCompleteAsync机制。这个页面的Begin方法启动一个异步web服务,通过调用web服务代理的异步Begin方法实现。这个页的End方法捕获一个由web方法返回的私有字段,PreRenderComplete处理绑定dataset到Gridview。

  1. [WebMethod]
  2. public DataSet GetTitles ()
  3. {
  4.     string connect = WebConfigurationManager.ConnectionStrings
  5.         ["PubsConnectionString"].ConnectionString;
  6.     SqlDataAdapter adapter = new SqlDataAdapter
  7.         ("SELECT title_id, title, price FROM titles", connect);
  8.     DataSet ds = new DataSet();
  9.     adapter.Fill(ds);
  10.     return ds;
  11. }

Figure 5 AsyncWSInvoke1.aspx.cs

  1. using System;
  2. using System.Data;
  3. using System.Configuration;
  4. using System.Web;
  5. using System.Web.UI;
  6. using System.Web.UI.WebControls;
  7.  
  8. public partial class AsyncWSInvoke1 : System.Web.UI.Page
  9. {
  10.     private WS.PubsWebService _ws;
  11.     private DataSet _ds;
  12.  
  13.     protected void Page_Load(object sender, EventArgs e)
  14.     {
  15.         if (!IsPostBack)
  16.         {
  17.             // Hook PreRenderComplete event for data binding
  18.             this.PreRenderComplete +=
  19.                 new EventHandler(Page_PreRenderComplete);
  20.  
  21.             // Register async methods
  22.             AddOnPreRenderCompleteAsync(
  23.                 new BeginEventHandler(BeginAsyncOperation),
  24.                 new EndEventHandler(EndAsyncOperation)
  25.             );
  26.         }
  27.     }
  28.  
  29.     IAsyncResult BeginAsyncOperation (object sender, EventArgs e,
  30.         AsyncCallback cb, object state)
  31.     {
  32.         _ws = new WS.PubsWebService();
  33.         // Fix up URL for call to local VWD-hosted Web service
  34.         _ws.Url = new Uri(Request.Url, "Pubs.asmx").ToString();
  35.         _ws.UseDefaultCredentials = true;
  36.         return _ws.BeginGetTitles (cb, state);
  37.     }
  38.  
  39.     void EndAsyncOperation(IAsyncResult ar)
  40.     {
  41.         _ds = _ws.EndGetTitles(ar);
  42.     }
  43.  
  44.     protected void Page_PreRenderComplete(object sender, EventArgs e)
  45.     {
  46.         Output.DataSource = _ds;
  47.         Output.DataBind();
  48.     }
  49.  
  50.     public override void Dispose()
  51.     {
  52.         if (_ws != null) _ws.Dispose();
  53.         base.Dispose();
  54.     }
  55. }

这是实现异步调用的一种方法,但不是唯一的方法,.NET Framework 2.0 web服务代理类支持2中机制实现异步调用web服务。一种是每个方法的Begin和End方法,Framework1.x和2.0web服务代理类都能实现。另一种是新的MethodAsync 方法和MethodCompleted 事件,仅在.NET Framework 2.0中提供。

假如Web服务有一个方法Foo,那么额外的就会有方法BeginFoo和EndFoo,一个.Net Framework 2.0web服务代理包括一个方法FooAsync和一个事件FooCompleted。你可以异步的调用Foo,如下:

  1. proxy.FooCompleted += new FooCompletedEventHandler (OnFooCompleted);
  2. proxy.FooAsync (...);
  3. ...
  4. void OnFooCompleted (Object source, FooCompletedEventArgs e)
  5. {
  6.     // Called when Foo completes
  7. }

由FooAsync 开始的异步调用结束时,FooCompleted事件触发,致使FooCompleted事件处理被调用。委托包裹的事件处理(FooCompletedEventHandler)和传递给他的第二个参数随着web服务代理产生。你可以通过FooCompletedEventArgs.Result访问Foo的返回值。

下面展示了后台代码,异步的调用web服务的GetTitles方法,采用MethodAsync模式。功能上来说,和上例的也没是等价的。而本质上,是非常不同的。AsyncWSInvoke2.aspx包括一个@ Page Async="true" 指令,就像AsyncWSInvoke1.aspx页面。但是AsyncWSInvoke2.aspx.cs不调用AddOnPreRenderCompleteAsync方法;它为GetTitlesCompleted事件注册处理函数,调用web服务代理上的GetTitlesAsync。ASP.NET仍然延迟显示页面直到GetTitlesAsync完成。它使用System.Threading.SynchronizationContext的一个实例——.NET Framework 2.0中的另外的一个新的类——当异步调用开始和结束的时候获取通知。

Figure 6 AsyncWSInvoke2.aspx.cs

  1. using System;
  2. using System.Data;
  3. using System.Configuration;
  4. using System.Web;
  5. using System.Web.UI;
  6. using System.Web.UI.WebControls;
  7.  
  8. public partial class AsyncWSInvoke2 : System.Web.UI.Page
  9. {
  10.     private WS.PubsWebService _ws;
  11.     private DataSet _ds;
  12.  
  13.     protected void Page_Load(object sender, EventArgs e)
  14.     {
  15.         if (!IsPostBack)
  16.         {
  17.             // Hook PreRenderComplete event for data binding
  18.             this.PreRenderComplete +=
  19.                 new EventHandler(Page_PreRenderComplete);
  20.  
  21.             // Call the Web service asynchronously
  22.             _ws = new WS.PubsWebService();
  23.             _ws.GetTitlesCompleted += new
  24.                 WS.GetTitlesCompletedEventHandler(GetTitlesCompleted);
  25.             _ws.Url = new Uri(Request.Url, "Pubs.asmx").ToString();
  26.             _ws.UseDefaultCredentials = true;
  27.             _ws.GetTitlesAsync();
  28.         }
  29.     }
  30.  
  31.     void GetTitlesCompleted(Object source,
  32.         WS.GetTitlesCompletedEventArgs e)
  33.     {
  34.         _ds = e.Result;
  35.     }
  36.  
  37.     protected void Page_PreRenderComplete(object sender, EventArgs e)
  38.     {
  39.         Output.DataSource = _ds;
  40.         Output.DataBind();
  41.     }
  42.  
  43.     public override void Dispose()
  44.     {
  45.         if (_ws != null) _ws.Dispose();
  46.         base.Dispose();
  47.     }
  48. }

使用MethodAsync而不是AddOnPreRenderCompleteAsync来实现异步页面有2个优势。第一,MethodAsync将impersonation,culture和HttpContext.Current流向MethodCompleted事件处理。AddOnPreRenderCompleteAsync 则不能。第二,如果页面实现多个异步调用,并且必须延迟显示直到所有的调用完成,使用AddOnPreRenderCompleteAsync需要你构造一个IAsyncResult 接口,直到调用完成。使用MethodAsync,不需要这种麻烦。妮子还需要简单的调用,随便多少个,ASP.NET引擎会延时直到最后的调用返回。

异步任务

MethodAsync是一个便利的方法来实现多个异步web服务,延迟显示直到所有的调用完成。但是如果你要在一个异步页面执行多个异步IO操作,而且这些操作不引入WEB服务?这是不是意味着你要返回到构造IAsyncResult接口,这样的话,你能返回给ASP.NET一个让他直到何时最后一个调用被完成了。幸运的是,不必。

在ASP.NET 2.0, the System.Web.UI.Page类引入另外一个方法来促进异步实现:RegisterAsyncTask。RegisterAsyncTask相对AddOnPreRenderCompleteAsync有四个优势。首先,除了Begin方法和End方法,RegisterAsyncTask允许你注册超时方法,如果异步操作花费太长时间才能完成,则会调用此方法。你可以通过在页面的@ Page 指令的AsyncTimeout属性设置超时声明。AsyncTimeout="5" 将超时时间设置为5秒。

第二个优势是,你能在一个请求中注册多个异步操作,多次调用RegisterAsyncTask 。使用MethodAsync,ASP.NET延迟呈现页面直到所有的操作完成。第三,你能使用RegisterAsyncTask的第四个参数传递Begin方法的状态。最后,RegisterAsync将Taskimpersonation, culture, 和HttpContext.Current流向End方法和超时方法。就如之前提及的。

在其他方面,一个依靠RegisterAsyncTask的异步页面和依靠AddOnPreRenderCompleteAsync的异步页面很相似。它仍然需要@ Page directive指令指定 Async="true"(或者再程序中将页面的AsyncMode属性设置为True),仍然和普通的页面一样执行到PreRender时间,在这时,用RegisterAsyncTask注册的Begin方法被调用,进一步的请求处理暂停了直到最后一步操作完成。为了演示,如下的代码在功能上等同于Figure 1,但是它使用RegisterTaskAsync代替AddOnPreRenderCompleteAsync。注意,命名为TimeoutAsyncOperation的超时处理,如果HttpWebRequest.BeginGetRequest花费太多时间,就会调用它。相应的.aspx文件包括一个AsyncTimeout属性将超时间隔设置为5秒。同时也要注意,RegisterAsyncTask的第四个参数传入的是null,这也能用来将数据传递给Begin方法。

Figure 7 AsyncPageTask.aspx.cs

  1. using System;
  2. using System.Web;
  3. using System.Web.UI;
  4. using System.Web.UI.WebControls;
  5. using System.Net;
  6. using System.IO;
  7. using System.Text;
  8. using System.Text.RegularExpressions;
  9.  
  10. public partial class AsyncPageTask : System.Web.UI.Page
  11. {
  12.     private WebRequest _request;
  13.  
  14.     protected void Page_Load(object sender, EventArgs e)
  15.     {
  16.         PageAsyncTask task = new PageAsyncTask(
  17.             new BeginEventHandler(BeginAsyncOperation),
  18.             new EndEventHandler(EndAsyncOperation),
  19.             new EndEventHandler(TimeoutAsyncOperation),
  20.             null
  21.         );
  22.         RegisterAsyncTask(task);
  23.     }
  24.  
  25.     IAsyncResult BeginAsyncOperation(object sender, EventArgs e,
  26.         AsyncCallback cb, object state)
  27.     {
  28.         _request = WebRequest.Create("http://msdn.microsoft.com");
  29.         return _request.BeginGetResponse(cb, state);
  30.     }
  31.  
  32.     void EndAsyncOperation(IAsyncResult ar)
  33.     {
  34.         string text;
  35.         using (WebResponse response = _request.EndGetResponse(ar))
  36.         {
  37.             using (StreamReader reader =
  38.                 new StreamReader(response.GetResponseStream()))
  39.             {
  40.                 text = reader.ReadToEnd();
  41.             }
  42.         }
  43.  
  44.         Regex regex = new Regex("href\\s*=\\s*\"([^\"]*)\"",
  45.             RegexOptions.IgnoreCase);
  46.         MatchCollection matches = regex.Matches(text);
  47.  
  48.         StringBuilder builder = new StringBuilder(1024);
  49.         foreach (Match match in matches)
  50.         {
  51.             builder.Append(match.Groups[1]);
  52.             builder.Append("<br/>");
  53.         }
  54.  
  55.         Output.Text = builder.ToString();
  56.     }
  57.  
  58.     void TimeoutAsyncOperation(IAsyncResult ar)
  59.     {
  60.         Output.Text = "Data temporarily unavailable";
  61.     }
  62. }

RegisterAsyncTask的主要优势是它允许异步页面触发多个异步回调,并且延迟Rendering直到所有的调用完成。并且他能提供一个超时设置,AddOnPreRenderCompleteAsync则没有。如果你创建一个异步页面只有一个异步调用,你能使用RegisterAsyncTask的AddOnPreRenderCompleteAsync,但是对那些要放置2个或者更多的异步调用的异步页面,RegisterAsyncTask更方便。

由于超时值在每页设置,而不是在每个调用上设置,你可能想知道是否可以在单个的调用上改变超时的值。回答是No。你可以改变通过程序修改页面的AsyncTimeout属性来改变每个请求的超时值,但是你不能对同一个请求上的不同的调用设置不同的超时值。

现在你已经知道了ASP.NET2.0异步页面的一些知识。在后续版本中的将会更加简单。

一个最终的要点,你必须记住,你创建异步页面时候,你启动异步操作所用的线程,不应该从和ASP.NET相同的线程池上借来的,例如,在页面的异步点上调用ThreadPool.QueueUserWorkItem是起反作用的,因为此方法从线程池拖出来,导致没有线程处理请求。对应的,调用在Framework上建立的异步方法,比如HttpWebRequest.BeginGetResponse 或者SqlCommand.BeginExecuteReader通常认为非常安全,因为这些方法趋向于使用完成端口来实现异步行为。



















本文转自cnn23711151CTO博客,原文链接:http://blog.51cto.com/cnn237111/596283 ,如需转载请自行联系原作者



相关文章
|
2月前
|
开发框架 .NET 中间件
七天.NET 8操作SQLite入门到实战 - (2)第七天Blazor班级管理页面编写和接口对接
七天.NET 8操作SQLite入门到实战 - (2)第七天Blazor班级管理页面编写和接口对接
|
1月前
|
SQL 设计模式 开发框架
.NET异步有多少种实现方式?(异步编程提高系统性能、改善用户体验)
想要知道.NET异步有多少种实现方式,首先我们要知道.NET提供的执行异步操作的三种模式,然后再去了解.NET异步实现的四种方式。
|
12月前
|
开发框架 数据可视化 前端开发
ASP.NET Core MVC+Quartz实现定时任务可视化管理页面
ASP.NET Core MVC+Quartz实现定时任务可视化管理页面
444 0
|
开发框架 JavaScript .NET
Asp.net C#页面传参的几种方式
Asp.net C#页面传参的几种方式
122 0
mvc.net分页查询案例——前台页面(Index.aspx)
mvc.net分页查询案例——前台页面(Index.aspx)
|
开发框架 JSON 前端开发
【C#】.net core2.1,自定义全局类对API接口和视图页面产生的异常统一处理
在开发一个网站项目时,异常处理和过滤功能是最基础的模块 本篇文章就来讲讲,如何自定义全局异常类来统一处理
225 0
|
开发框架 程序员 API
【C#】.net core2.1,通过扩展状态代码页方法对404页面进行全局捕抓并响应信息
在开发一个网站项目时,除了异常过滤功能模块,还需要有针对404不存在的api接口和页面处理功能 本篇文章就来讲讲,如何自定义全局请求状态类来统一处理
198 0
|
开发框架 .NET Windows
真正解决ASP.NET每一个页面首次访问超级慢的问题
真正解决ASP.NET每一个页面首次访问超级慢的问题
241 0
|
2月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
115 0