您的位置:首页 > 理论基础 > 计算机网络

【C#】对异步请求处理程序IHttpAsyncHandler的理解和分享一个易用性封装 【手记】走近科学之为什么明明实现了IEnumerable<T>的类型却不能调用LINQ扩展方法 【手记】手机网页弹出层后屏蔽底层的滑动响应 【手记】ASP.NET提示“未能创建类型”处理 【Web】一个非常简单的移动web消息框 【手记】解决EXCEL跑SQL遇“查询无法运行或数据库表无法打开...”

2018-05-18 18:13 2146 查看

【C#】对异步请求处理程序IHttpAsyncHandler的理解和分享一个易用性封装

在asp.net项目中,添加一个【一般处理程序】来处理请求是很自然的事,这样会得到一个实现自IHttpHandler的类,然后只需在ProcessRequest方法中写上处理逻辑就行了。但是这样的一个请求处理程序(下称ashx)是同步的,就是接待该次请求的线程会一直等待处理完才能解脱,后果就是,如果这个ashx比较耗时,并且同时对它的请求又多的话,服务器需要开启若干个线程来跑这个ashx,并且这些线程都要各自跑很久才能被收回或挪作它用,如果这样的ashx还有不少的话,那么对整个服务器资源的开销是很大的,所以有必要采用IHttpAsyncHandler来实现这种ashx,即异步请求处理程序,异步化以后,线程把请求接进来就完事了,反手就可以去处理其它请求,然后由别的线程硬件来处理具体的任务~取决于任务是CPU消耗型(密集运算,如图片处理)还是I/O型(数据库读写、网络访问等),老实说如果耗时任务总是CPU消耗型,那同步异步在资源消耗上没什么区别,因为总得有个线程来跑任务,换不换线程意义不大。但总的来说异步化没坏处,而且万一对任务类型评估错误呢。

改用IHttpAsyncHandler后,多了两个方法BeginProcessRequest和EndProcessRequest,原有的ProcessRequest事实上已经废弃,请求不会进入里面,而是改为在BeginProcessRequest中处理请求,原IsReusable属性功能不变。说回BeginProcessRequest,这是一个典型的传统异步方法(相对于.net 4.5后的async/await新式异步方法来说),逻辑相比原来的同步方法ProcessRequest有点绕,首先入参除了熟悉的HttpContext外还有两个,然后还有个IAsyncResult类型的返回值。熟悉APM(异步编程模型)套路的朋友知道该怎么搞,不熟悉的可参看MSDN,要点就是实例化一个实现IAsyncResult的类,在其中异步或起线程执行逻辑,然后返回这个对象。现成的实现IAsyncResult的类在.net 4.0后有Task,但如果项目不到4.0,你还找不到一个可以拿来就用的类,如果要为每个ashx实现一个IAsyncResult,想想都蛋疼,哪怕总共只需实现一个IAsyncResult我都不情愿,好在委托这个东西编译器会为它自动生成异步模型,于是有了下面这个简单的封装:

/// <summary>
/// 异步请求处理基类
/// <para>- 子类实现ProcessRequest方法并在其中处理请求</para>
/// <para>- 默认允许实例重用(IsReusable=true),子类可重写为false</para>
/// </summary>
public abstract class HttpAsyncHandler : IHttpAsyncHandler
{
readonly Action<HttpContext> _processRequestDel;

protected HttpAsyncHandler() => _processRequestDel = ProcessRequest;

/// <summary>
/// 处理请求
/// </summary>
//总是要有个让子类处理业务逻辑的地方,既然原来的ProcessRequest已废弃,不如废物利用
public abstract void ProcessRequest(HttpContext context);

/// <summary>
/// 多次请求是否可以重用实例。默认true
/// </summary>
public virtual bool IsReusable => true;

public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) =>
_processRequestDel.BeginInvoke(context, cb, extraData); //利用ProcessRequest委托的异步能力

//虽然不End也不会导致异步还没跑完就返回响应(HttpApplication的实现似乎保证这一点),但异步中抛出的异常会被忽略,所以需要End暴露问题
public void EndProcessRequest(IAsyncResult result) => _processRequestDel.EndInvoke(result);
}

有了这个封装好的基类,在写新的ashx时就可以把IHttpHandler改为HttpAsyncHandler,完了把ProcessRequest方法标成override就行,老ashx也可以经过简单修改异步化。举例:

public class FooHandler : HttpAsyncHandler // 替掉IHttpHandler
{
//加上override
public override void ProcessRequest(HttpContext context)
{
//在这里写逻辑
context.Response.Write("OK");
}
}

需要注意的是IsReusable在HttpAsyncHandler中已改为true,所以如果你的ashx明确需要false,请override该属性,如:

public override bool IsReusable => false;

对于.net 4.5及以上版本,微软已经写好了个HttpTaskAsyncHandler,性质一样,只不过形式上符合新式的async/await用法,总之目的都是让开发者可以优雅的使用异步ashx,不必繁琐的从IHttpAsyncHandler开始。

EOF

 

【手记】走近科学之为什么明明实现了IEnumerable<T>的类型却不能调用LINQ扩展方法

比如Json.NET的JObject明明实现了IEnumerable<T>,具体来说是IEnumerable<KeyValuePair<string, JToken>>,按说JObject类型的对象是可以直接调用Select、Where等linq扩展方法的,但偏偏就是不行,代码如下:

using System.Linq;
...
var jobj = new JObject();
var xxx = jobj.Select(x=>x); //报错:JObject未包含Select定义,也不存在第1个参数为JObject的Select扩展方法...
foreach(var x in jobj) { } //可以
var xxx = ((IEnumerable<KeyValuePair<string, JTokin>>)jobj).Select(x=>x); //也可以

究竟是人性的扭曲还是道德的沦丧?答案就在:

因为JObject除了本身实现了一个IEnumerable<KeyValuePair<string, JToken>>,它实现的另一个接口JContainer也实现了一个IEnumerable<JToken>,等于JObject直接和间接实现了两个不同的IEnumerable<T>,当.Select的时候编译器不能确定调用哪个类型的Select,所以就这样了。——爆栈

 

【手记】手机网页弹出层后屏蔽底层的滑动响应

这个需求场景很常见,但好像到目前还没有一个正统的做法,以至于一搜这个问题,出来的招数五花八门,典型的包括:

  • 给body上overflow:hidden,fixed什么的。问题在于:手机端可能没用,或者会让页面回到顶部~影响体验
  • 简单粗暴的屏蔽touchmove。问题在于:弹出层内部需要滚动就不行
  • 张鑫旭大神的招数问题在于:我感觉复杂,就没试

终于还是让我在爆栈找到一个挺满意的招:https://stackoverflow.com/a/31063201/5482608,在此分享一下。

 

【手记】ASP.NET提示“未能创建类型”处理

我是在本机启动IIS Express调试一个ashx(一般处理程序)时遇到这个报错,网上的说法普遍有这么几种:

  • 把bbb.ashx中的Class="aaa.bbb" 改为Class="bbb",即把命名空间删掉
  • 把bbb.ashx.cs中的代码复制到bbb.ashx中来,保持<% WebHandler ....%>这句在顶端,并且将其中的CodeBehind="xx"属性删除,最后删除bbb.ashx.cs文件

上述方法对我没用,问题依旧,有用的是这招:

  • 把项目的输出目录由bin\xxx改为bin\

 

【Web】一个非常简单的移动web消息框

适用:h5+jquery,移动网页最佳

最近在写个简单的公众号页面,前端验证时有些信息要提示,很简单的需求实在不想找啥现成的轮子,又不至于用alert这么粗暴,遂写了个非常简单的消息框,效果如图:

特点:

  • 有黑层遮罩
  • 点击消失,无论是点消息框还是外面

用法:

//定义函数。我个人还是习惯PascalCase命名
function MsgBox(msg,title) {
$('<div onclick="$(this).remove();"style="z-index:9999;padding:10px;display:flex;justify-content:center;align-items:center;position:fixed;top:0;left:0;width:100%;height:100%;<div><div style="color:#555;background-color:#DDD;font-size:0.8em;padding:3px;border-top-left-radius:5px;border-top-right-radius:5px;">' + (title === undefined ? '提示' : title) + '</div><div style="min-height:80px;min-width:250px;display:flex;justify-content:center;align-items:center;padding:20px;border-top:1px solid #BBB;border-bottom-left-radius:5px;border-bottom-right-radius:5px;text-align:justify">' + msg + '</div></div></div>')
.appendTo($(document.body));
}

//使用
MsgBox('内容');           //标题显示为“提示”
MsgBox('内容','我的标题'); //自定义标题

欢迎吐槽或推荐更好的方案。

-end-

 

【手记】解决EXCEL跑SQL遇“查询无法运行或数据库表无法打开...”

报错:

解决:在语句开头指定SET NOCOUNT ON

就是这么神cao奇dan。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐