贝网博客

我的分类
流水记事
源码下载
Asp.net
其它
数据库
Javascript
.Net技术
我的相册
友情链接
博客园
CSDN博客
Start0
最新回复
fasdfasdf
[:..
lz这个东西好厉害,我..
哈哈,好照片
不错,以前一直用黄色..
终于找到支持ff的修正..
终于找到支持ff的修正..
新鲜性
看看,试试,好不好使。
好东西一起学习[:img0..
 日志列表    
本站一共有博客83条,当前显示81条
回复:1  发表于:2012-03-13 17:45:13
更新于:2012-06-07 11:26:53

一直在使用WebForm,最近领导要求项目开始使用MVC,因为服务器还没有安装Framework4,先从MVC2开始吧

1、在IIS里访问MVC路由时,出现404错误,但是在Visual Studio开发服务器里正常
这是因为没有配置IIS通配符应用程序映射,开始=>运行,输入Inetmgr,进入Internet信息服务管理器,在你的网站或虚拟目录上按右键=>属性=>主目录,点击下面的“配置”,在下方的“通配符应用程序映射”里,点“插入”,可执行文件里输入“c:\windows\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll”,并去掉“确认文件是否存在”,最后点确定即可,现在IIS里访问也正常了
这里有点问题,按上面配置后,任意扩展名请求,都会交给aspnet_isapi.dll去处理了,包括html、jpg、js等常见扩展名了
即使通过routes.IgnoreRoute("{*dir}", new { dir = @".*\.html(.*)?" });忽略这些常用扩展名,也会进isapi走一趟,目前我的做法就是MVC都加一个ashx扩展名,这样IIS也不用配置了(Application_BeginRequest里加断点,可以看到所有的html等请求都会到这个断点)

我用的路由:routes.MapRoute("Default","{controller}.ashx/{action}/{*path}",new { controller = "Home", action = "Index" });

2、路由里的参数名称必须与Controller里的参数名称一致,如配置了一个路由:
routes.MapRoute("mytest","{controller}/MyTest/{id}",new { controller = "Home", action = "MyTest" });
那么HomeController里的方法必须为:
public ActionResult MyTest(string id){}// 如果参数名不是id,而是abc等其它名,就会出现找不到对应的Controller的错误
简单的作法就是配置路由时,不指定参数

3、参数约束:
routes.MapRoute("mytest","{controller}/MyTest/{id}",new { controller = "Home", action = "MyTest" },
new {id = @"\d*"} );// 这个约束表明id必须为数字,而且必须全部是数字(有点疑惑,全数字的正则应该是^\d+$,这里没有开始结束符也可以做到)

4、执行前方法OnActionExecuting:在这里可以在Action执行前做任意操作,比如登录检查等等,如:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
    string controllName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
    //string actionName = filterContext.ActionDescriptor.ActionName;
    if (!controllName.Equals("Account", StringComparison.OrdinalIgnoreCase)) {
        if (!IsLogin)  {
            filterContext.Result = RedirectToAction("LogOn", "Account"); // Redirect("/Account.ashx/LogOn");
            return;
        }
    }
            
    base.OnActionExecuting(filterContext);
}

5、Controller里的方法不允许重载,即不允许有2个方法名字一样,会造成访问时出错,不知道调用哪个方法

6、通过Post提交数据时,可以通过 方法参数名1=值&方法参数名2=值&……
以便直接把值绑定到参数里(不需要通过Request.Form去自己匹配,MVC自己搞定了),对于Class类型,可以提交json格式,MVC自己转换为这个类型的参数

7、正常情况下,POST数据类型都是:Content-Type: application/x-www-form-urlencoded; charset=UTF-8
某些客户端POST数据时,可能提交类型为:Content-Type: application/octet-stream,这会导致MVC认不到Request.Form,此时需要自行去处理POST参数,如代码:
public ActionResult MVC1(string para)
{
    if (para == null)
    {
        // 当客户端用Content-Type: application/octet-stream传递时
        if (Request.InputStream != null && Request.InputStream.Length > 0)
        {
            string postData;
            using (Stream inStream = Request.InputStream)
            using (StreamReader reader = new StreamReader(inStream, Encoding.UTF8))
            {
                postData = reader.ReadToEnd();
            }
            NameValueCollection tmpNV = HttpUtility.ParseQueryString(postData);
            if(tmpNV != null)
            {
                para = tmpNV["para"];
            }
        }
    }

 发表于:2012-03-21 09:42:15
更新于:2012-06-07 09:38:53

我们可能经常会用到 Thread.Sleep 函数来使线程挂起一段时间。那么你有没有正确的理解这个函数的用法呢?思考下面这两个问题:

  1. 假设现在是 2008-4-7 12:00:00.000,如果我调用一下 Thread.Sleep(1000) ,在 2008-4-7 12:00:01.000 的时候,这个线程会 不会被唤醒?
  2. 某人的代码中用了一句看似莫明其妙的话:Thread.Sleep(0) 。既然是 Sleep 0 毫秒,那么他跟去掉这句代码相比,有啥区别么?

我们先回顾一下操作系统原理。

操作系统中,CPU竞争有很多种策略。Unix系统使用的是时间片算法,而Windows则属于抢占式的。

在时间片算法中,所有的进程排成一个队列。操作系统按照他们的顺序,给每个进程分配一段时间,即该进程允许运行的时间。如果在 时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。调度程 序所要做的就是维护一张就绪进程列表,,当进程用完它的时间片后,它被移到队列的末尾。

所谓抢占式操作系统,就是说如果一个进程得到了 CPU 时间,除非它自己放弃使用 CPU ,否则将完全霸占 CPU 。因此可以看出,在抢 占式操作系统中,操作系统假设所有的进程都是“人品很好”的,会主动退出 CPU 。

在抢占式操作系统中,假设有若干进程,操作系统会根据他们的优先级、饥饿时间(已经多长时间没有使用过 CPU 了),给他们算出一 个总的优先级来。操作系统就会把 CPU 交给总优先级最高的这个进程。当进程执行完毕或者自己主动挂起后,操作系统就会重新计算一 次所有进程的总优先级,然后再挑一个优先级最高的把 CPU 控制权交给他。

我们用分蛋糕的场景来描述这两种算法。假设有源源不断的蛋糕(源源不断的时间),一副刀叉(一个CPU),10个等待吃蛋糕的人(10 个进程)。

如果是 Unix操作系统来负责分蛋糕,那么他会这样定规矩:每个人上来吃 1 分钟,时间到了换下一个。最后一个人吃完了就再从头开始。于是,不管这10个人是不是优先级不同、饥饿程度不同、饭量不同,每个人上来的时候都可以吃 1 分钟。当然,如果有人本来不太饿,或者饭量小,吃了30秒钟之后就吃饱了,那么他可以跟操作系统说:我已经吃饱了(挂起)。于是操作系统就会让下一个人接着来。

如果是 Windows 操作系统来负责分蛋糕的,那么场面就很有意思了。他会这样定规矩:我会根据你们的优先级、饥饿程度去给你们每个人计算一个优先级。优先级最高的那个人,可以上来吃蛋糕——吃到你不想吃为止。等这个人吃完了,我再重新根据优先级、饥饿程度来计算每个人的优先级,然后再分给优先级最高的那个人。

这样看来,这个场面就有意思了——可能有些人是PPMM,因此具有高优先级,于是她就可以经常来吃蛋糕。可能另外一个人是个丑男,而去很ws,所以优先级特别低,于是好半天了才轮到他一次(因为随着时间的推移,他会越来越饥饿,因此算出来的总优先级就会越来越高,因此总有一天会轮到他的)。而且,如果一不小心让一个大胖子得到了刀叉,因为他饭量大,可能他会霸占着蛋糕连续吃很久很久,导致旁边的人在那里咽口水。。。
而且,还可能会有这种情况出现:操作系统现在计算出来的结果,5号PPMM总优先级最高,而且高出别人一大截。因此就叫5号来吃蛋糕。5号吃了一小会儿,觉得没那么饿了,于是说“我不吃了”(挂起)。因此操作系统就会重新计算所有人的优先级。因为5号刚刚吃过,因此她的饥饿程度变小了,于是总优先级变小了;而其他人因为多等了一会儿,饥饿程度都变大了,所以总优先级也变大了。不过这时候仍然有可能5号的优先级比别的都高,只不过现在只比其他的高一点点——但她仍然是总优先级最高的啊。因此操作系统就会说:5号mm上来吃蛋糕……(5号mm心里郁闷,这不刚吃过嘛……人家要减肥……谁叫你长那么漂亮,获得了那么高的优先级)。

那么,Thread.Sleep 函数是干吗的呢?还用刚才的分蛋糕的场景来描述。上面的场景里面,5号MM在吃了一次蛋糕之后,觉得已经有8分饱了,她觉得在未来的半个小时之内都不想再来吃蛋糕了,那么她就会跟操作系统说:在未来的半个小时之内不要再叫我上来吃蛋糕了。这样,操作系统在随后的半个小时里面重新计算所有人总优先级的时候,就会忽略5号mm。Sleep函数就是干这事的,他告诉操作系统“在未来的多少毫秒内我不参与CPU竞争”。

看完了 Thread.Sleep 的作用,我们再来想想文章开头的两个问题。

对于第一个问题,答案是:不一定。因为你只是告诉操作系统:在未来的1000毫秒内我不想再参与到CPU竞争。那么1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束;况且,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。

与此相似的,Thread有个Resume函数,是用来唤醒挂起的线程的。好像上面所说的一样,这个函数只是“告诉操作系统我从现在起开始参与CPU竞争了”,这个函数的调用并不能马上使得这个线程获得CPU控制权。

对于第二个问题,答案是:有,而且区别很明显。假设我们刚才的分蛋糕场景里面,有另外一个PPMM 7号,她的优先级也非常非常高(因为非常非常漂亮),所以操作系统总是会叫道她来吃蛋糕。而且,7号也非常喜欢吃蛋糕,而且饭量也很大。不过,7号人品很好,她很善良,她没吃几口就会想:如果现在有别人比我更需要吃蛋糕,那么我就让给他。因此,她可以每吃几口就跟操作系统说:我们来重新计算一下所有人的总优先级吧。不过,操作系统不接受这个建议——因为操作系统不提供这个接口。于是7号mm就换了个说法:“在未来的0毫秒之内不要再叫我上来吃蛋糕了”。这个指令操作系统是接受的,于是此时操作系统就会重新计算大家的总优先级——注意这个时候是连7号一起计算的,因为“0毫秒已经过去了”嘛。因此如果没有比7号更需要吃蛋糕的人出现,那么下一次7号还是会被叫上来吃蛋糕。

因此,Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”。竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。这也是我们在大循环里面经常会写一句Thread.Sleep(0) ,因为这样就给了其他线程比如Paint线程获得CPU控制权的权力,这样界面就不会假死在那里。

末了说明一下,虽然上面提到说“除非它自己放弃使用 CPU ,否则将完全霸占 CPU”,但这个行为仍然是受到制约的——操作系统会监控你霸占CPU的情况,如果发现某个线程长时间霸占CPU,会强制使这个线程挂起,因此在实际上不会出现“一个线程一直霸占着 CPU 不放”的情况。至于我们的大循环造成程序假死,并不是因为这个线程一直在霸占着CPU。实际上在这段时间操作系统已经进行过多次CPU竞争了,只不过其他线程在获得CPU控制权之后很短时间内马上就退出了,于是就又轮到了这个线程继续执行循环,于是就又用了很久才被操作系统强制挂起。。。因此反应到界面上,看起来就好像这个线程一直在霸占着CPU一样。

末了再说明一下,文中线程、进程有点混乱,其实在Windows原理层面,CPU竞争都是线程级的,本文中把这里的进程、线程看成同一个东西就好了。

本文转载自http://www.cnblogs.com/ilove/archive/2008/04/07/1140419.html

 发表于:2012-02-17 16:41:56
更新于:2012-02-27 10:49:30

Assembly asms = Assembly.LoadFile(Request.Form["v"]);
object[] debugAtt = asms.GetCustomAttributes(typeof(DebuggableAttribute), false);
if (debugAtt.Length >= 1)
{
    if ((debugAtt[0] as DebuggableAttribute).IsJITTrackingEnabled)
        Response.Write("Debug版本: ");
    else
        Response.Write("Release版本: ");
}
else
{
    Response.Write("UnKnown版本: ");
}
Response.Write(asms.FullName);
 

参考:http://www.cnblogs.com/anytao/archive/2009/12/29/must_net_35.html

 发表于:2012-02-02 18:41:37
更新于:2012-02-17 16:42:10

类定义:
01、public class Teacher{// 老师类
02、private List<Student> _students;
03、    public List<Student> Students{// 这个老师的学生列表
04、        get{
05、            if(_students == null){
06、                _students = new List<Student>();
07、                // 这里是添加学生清单的逻辑
08、                _students.Add(new Student());
09、            }
10、            return _students;
11、        }
12、    }
13、}
上面的这段代码应该没啥问题,应该是在单线程访问Students属性时没有问题,在并发的时候,
比如有a、b两个用户同时访问这个属性,就会出现
a执行到第7行时,b用户进来了,执行到第5行,发现_students不为null,就直接return了,导致b用户得到的学生清单为0个

解决方案1:把get方法进行lock,只允许单线程进入,如:
public class Teacher{// 老师类
private List<Student> _students;
    public List<Student> Students{// 这个老师的学生列表
        get{
            if(_students == null){
                lock(_students){
                    if(_students == null){// 再判断一次,避免进入时已经被别的线程填充过,导致重复填充
                        _students = new List<Student>();
                        // 这里是添加学生清单的逻辑
                        _students.Add(new Student());
                    }
                }
            }
            return _students;
        }
    }
}
这个方案的缺点在于lock会导致所有的线程堵住,都要等待第一个线程的完成,其余线程才能继续

解决方案2:
public class Teacher{// 老师类
private List<Student> _students;
    public List<Student> Students{// 这个老师的学生列表
        get{
            if(_students == null){
                var tmp = new List<Student>();
                // 这里是添加学生清单的逻辑
                tmp.Add(new Student());
                _students = tmp;
            }
            return _students;
        }
    }
}
这个方案的缺点在于,首次访问Students属性时,由于瞬间_students为null,进入的所有线程都会去填充一遍_students,重复工作,如果这个填充时间短,还无所谓,如果比较长,建议使用方案1

 发表于:2012-01-31 10:31:34

以前在实例化SqlParameter时,通常都是用下面的语句,没有设置size属性:
new SqlParameter("@name", SqlDbType.Varchar) { Value = name };
根据MSDN解释:如果未在 size 参数中显式设置 Size,则从 dbType 参数的值推断出该大小。
我一直以为是从SqlDbType类型推断,实际上是从参数的值推断,比如"ab",则size值为2,"abcd",则size值为4,且经测试发现,size的值不同时,会导致执行计划不会重用,下面的代码:
string sql = "select top 1 * from tb where name = @o";
var para = new SqlParameter("@o", SqlDbType.VarChar) {Value = "ab"};
SqlHelper.ExecuteReader(ReadConnectionString, CommandType.Text, sql, para);
通过SqlProfiler捕获到的sql如下:
exec sp_executesql N'select top 1 * from tb where name = @o',N'@o nvarchar(2)',@o=N'ab'
如果把参数的值改成abcd,则通过SqlProfiler捕获到的sql如下:
exec sp_executesql N'select top 1 * from tb where name = @o',N'@o nvarchar(4)',@o=N'abcd'
通过下面的sql,可以看出执行计划是否有重用:
--先清空执行计划缓存
DBCC FREESYSTEMCACHE ('ALL')
DBCC FREEPROCCACHE
GO
SELECT * FROM sys.dm_exec_cached_plans WHERE cacheobjtype = 'Compiled Plan'
GO
exec sp_executesql N'select top 1 * from tb where name = @o',N'@o nvarchar(2)',@o=N'ab'
GO
SELECT * FROM sys.dm_exec_cached_plans WHERE cacheobjtype = 'Compiled Plan'
GO
exec sp_executesql N'select top 1 * from tb where name = @o',N'@o nvarchar(4)',@o=N'abcd'
GO
SELECT * FROM sys.dm_exec_cached_plans WHERE cacheobjtype = 'Compiled Plan'
GO

最后的说明,实例化SqlParameter时,如果是字符型,一定要指定size属性,如:
new SqlParameter("@name", SqlDbType.Varchar, 4000) { Value = name };
如果是Int、Float、Bigint之类的参数,可以不用指定size属性

 发表于:2012-01-29 16:47:47
更新于:2012-01-31 10:16:44

早上一来,公司门口猜谜抽奖,拆信,猜地名,开封嘛,然后抽奖,1块钱,nnd,蚊子肉也是肉,哈哈
结果晚上跟老婆出去吃饭,红包拿出来放桌上,吃完撤退,这一块钱也丢了……

 发表于:2011-12-26 11:44:31
更新于:2011-12-26 14:52:13

1、获取所有表的所有外键及关联表、字段信息:
SELECT tbl.name AS 表名, cstr.name AS 外键名, fk.constraint_column_id AS [ID], cfk.name AS 外键列,
rtbl.[name] AS 关联表, crk.name AS 关联列
FROM sys.tables AS tbl
INNER JOIN sys.foreign_keys AS cstr ON cstr.parent_object_id=tbl.object_id
INNER JOIN sys.foreign_key_columns AS fk ON fk.constraint_object_id=cstr.object_id
INNER JOIN sys.columns AS cfk ON fk.parent_column_id = cfk.column_id and fk.parent_object_id = cfk.object_id
INNER JOIN sys.columns AS crk ON fk.referenced_column_id = crk.column_id and fk.referenced_object_id = crk.object_id
INNER JOIN sys.tables AS rtbl ON rtbl.object_id = cstr.referenced_object_id
ORDER BY tbl.name, cstr.[name]

2、获取所有表的所有主键以及主键对应的字段信息:
SELECT a.name, b.[name],
STUFF((SELECT ',' + CAST(d.name AS NVARCHAR) FROM sysindexkeys c, syscolumns d, sysindexes e
        WHERE d.colid = c.colid AND d.id = c.id AND d.id = b.id and d.id = e.id and e.name = a.name and e.indid = c.indid
        FOR XML PATH('')),1,1,'') cols
FROM sysobjects a
INNER JOIN sysobjects b ON a.parent_obj = b.id
 WHERE a.xtype = 'PK'

3、删除当前数据库所有外键(有时效率低,通过程序维护关系,不需要外键处理):
DECLARE @tbname NVARCHAR(100), @fkname NVARCHAR(100);
DECLARE @sql NVARCHAR(MAX);
DECLARE CUR CURSOR LOCAL FORWARD_ONLY FOR SELECT b.[name] tbname, a.name fkname
FROM sysobjects a
INNER JOIN sysobjects b ON a.parent_obj = b.id
 WHERE a.xtype = 'F'
ORDER BY b.[name];
OPEN CUR
FETCH NEXT FROM CUR INTO @tbname, @fkname
WHILE @@FETCH_STATUS = 0
BEGIN 
 SET @sql = N'ALTER TABLE dbo.['+@tbname+N'] DROP CONSTRAINT ['+@fkname+N']';
    EXEC sp_executesql @sql
    FETCH NEXT FROM CUR INTO @tbname, @fkname
END
CLOSE CUR
DEALLOCATE CUR
 

 发表于:2011-12-26 13:41:56

今天在修改博客时发现,如果在程序里执行sql,传递的参数顺序与sql不一致时,导致sql执行结果不正确,如:
1、string sql = "select * from tb1 where id=@id and name=@name";
2、SqlCommand command = new SqlCommand(connection, sql);
3、command.Parameters.Add(new OleDbParameter("@name", OleDbType.VarChar){Value = "abc"});
4、command.Parameters.Add(new OleDbParameter("@id", OleDbType.VarChar){Value = 12});

上面4行,执行结果获取到的记录集为空,如果把第三行和第四行对调,结果就正确了


©2008 Beinet.cn 版权所有