Curllion 的个人资料curllion照片日志列表更多 ![]() | 帮助 |
curllionMSN SPACES太慢了,我的博客搬家了 http://hi.baidu.com/curllion |
|||||
|
2006/7/19 一看就明白的线程的例子在VS2005(dotnet2.0)中测试通过
using System;
using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Threading; namespace test
{ public partial class Form1 : Form { //声明一个委托,这个委托将会在两个地方用到 public delegate void TimerStartHandle(string t); //用新定义的委托定义一个本Form的事件OnTimerStart,其实在这里不要理解为事件,理解为委托的一个实例更合适。 public event TimerStartHandle OnTimerStart; public Form1() { InitializeComponent(); } /// <summary>
/// 改变标签的文本, /// </summary> /// <param name="t"></param> private void setLableText(String t) { this.label1.Text = t; } /// <summary> /// 在主线程的基础上,执行一个委托,这一步在dotnet2.0中必不可少 /// 因为不能在别的线程中来修改主线程创建的控件的属性 /// </summary> /// <param name="t"></param> private void setLableTextThread(String t) { this.BeginInvoke(new TimerStartHandle(this.setLableText), new object[] { t }); } /// <summary> /// 线程池要执行的委托参数 /// </summary> /// <param name="info"></param> private void StartTimer(object info) { do { //执行事件委托,所以在线程中每执行一次StartTimer,就会触发一次OnTimerStart事件。 //从而会执行this.setLableTextThread中的语句 //而在this.setLableTextThread中,又会执行一个委托,即:setLableText //这样就避开了在非主线程中修改由主线程创建的控件的问题 this.OnTimerStart(System.DateTime.Now.ToString("hh:mm:ss")); Thread.Sleep(1000); } while (true); } private void Form1_Load(object sender, EventArgs e)
{ //指定响应的事件 this.OnTimerStart += new TimerStartHandle(this.setLableTextThread); //由线程池来执行this.StartTimer。这时会依次执行this.setLableTextThread,setLableText
ThreadPool.QueueUserWorkItem(new WaitCallback(this.StartTimer)); //或创建一个线程 //Thread thr = new Thread(this.StartTimer); //thr.Start(); } } } 上面的代码在VS2003中编译后可以运行,其实在vs2003(dotnet1.1)还有一个简单得多:
...
private void Form1_Load(object sender, EventArgs e)
{ //指定响应的事件 this.OnTimerStart += new TimerStartHandle(this.setLableTextThread); //由线程池来执行this.StartTimer。这时会依次执行this.setLableTextThread,setLableText
ThreadPool.QueueUserWorkItem(new WaitCallback(this.StartTimer)); //或创建一个线程 //Thread thr = new Thread(this.StartTimer); //thr.Start(); } private void StartTimer(object info) { do { this.label1.Text = System.DateTime.Now.ToString("hh:mm:ss"); Thread.Sleep(1000); } while (true); } ... 2006/4/18 能横向翻页的动态交叉查询存储过程-----------------------------------按经销售商列出报销汇总交叉表,没有必要按状态来筛-------------------------------------
CREATE PROCEDURE schmreimburse_bydealer @depid nvarchar(10), @rstime datetime, @retime datetime, @pagesize int, --输入每页的记录数 @currentpage int, --输入当前页 ,0为起始页 @totalpages int output, --返回总页数 @newcpage int output --返回新的当前页 ,0为起始页 AS declare @current as int --定义当前记录的位置 declare @total as int --定义记录总条数 declare @i as int --定义循环变量 set @total =(SELECT COUNT(DISTINCT emsdealer.jxname) FROM emsreimburse LEFT OUTER JOIN emsmperform ON emsreimburse.porno = emsmperform.orno LEFT OUTER JOIN emsdealer RIGHT OUTER JOIN emswldw ON emsdealer.jxid = emswldw.jxid ON emsmperform.wlid = emswldw.wlid WHERE (emsreimburse.reidate between @rstime and @retime) AND (emsdealer.depid = @depid) ) set @totalpages = ceiling(cast(@total as float)/ @pagesize) if @currentpage < @totalpages begin if @currentpage < 0 begin set @newcpage = 0 end else begin set @newcpage = @currentpage end end else begin set @newcpage = @totalpages - 1 end set @current =@newcpage * @pagesize + 1 --区域编号的临时变量 declare @jxname as nvarchar(50) --存储用游标生成的SQL语句,4000字符是nvarchar类型的极限 declare @tmpsql as nvarchar(4000) set @tmpsql = '' --声明一个游标用来从表中循环读取所有的区域名称 declare c cursor scroll for SELECT emsdealer.jxname FROM emsreimburse LEFT OUTER JOIN emsmperform ON emsreimburse.porno = emsmperform.orno LEFT OUTER JOIN emsdealer RIGHT OUTER JOIN emswldw ON emsdealer.jxid = emswldw.jxid ON emsmperform.wlid = emswldw.wlid WHERE (emsreimburse.reidate between @rstime and @retime) AND (emsdealer.depid = @depid) GROUP BY emsdealer.jxname ORDER BY emsdealer.jxname set @i = 0 open c --读取第一条记录 fetch absolute @current from c into @jxname --如果上一FETCH语句成功运行 while ((@@fetch_status = 0) and (@i < @pagesize)) begin set @tmpsql = @tmpsql + ',SUM(CASE emsdealer.jxname WHEN '''+ @jxname + ''' THEN emsreimburse.money ELSE 0 END) as ''' + @jxname + ''' ' set @i = @i + 1 fetch next from c into @jxname end close c deallocate c set @tmpsql = ' select emsmperform.fyid as 费用ID, emsexpenss.fyname as 费用类型 ' + @tmpsql + ' FROM emsexpenss RIGHT OUTER JOIN emsmperform ON emsexpenss.fyid = emsmperform.fyid RIGHT OUTER JOIN emsreimburse ON emsmperform.orno = emsreimburse.porno LEFT OUTER JOIN emsdealer RIGHT OUTER JOIN emswldw ON emsdealer.jxid = emswldw.jxid ON emsmperform.wlid = emswldw.wlid WHERE (emsreimburse.reidate BETWEEN ''' + cast(@rstime as nvarchar(10)) + ''' AND ''' + cast(@retime as nvarchar(10)) + ''') AND (emsdealer.depid = ''' + @depid + ''') GROUP BY emsmperform.fyid, emsexpenss.fyname ' execute sp_executesql @tmpsql --select @tmpsql as 'test' GO 2006/1/18 C#中二进制运算在权限验证的应用 在我的另一篇文章中[委托在权限验证中的应用](见我的blog: http://blog.csdn.net/curllion/ 或http://spaces.msn.com/members/curllion)提到了一个函数ValidateAuthor,其实我以前见过这样实现的,就是一个权限用一个文本来表示,可能有“修改”、“填写”、“审核”、“删除”等等,这样,对于每个用户,在数据库中,都有一个字段,用来记录这样的权限,某用户权限值可能是“修改|删除|审核”,这样,在查找时,就会比较用户权限值中,有没有包括一个操作的名称。这样验证权限好办,但我弄不明白,权限的赋予与除去是如何实现的。每一个操作的权限值,用一个二进制数来表示,每个权限值,只能是(0),(10),(100),(1000),也就是说,是2的N次方,在MSSQL2000中,最大的
整数是bigint类型的,最大可以表示64种操作。当然,这个是远远不够的。所以可以给权限分组。比如,可分为文员,主管,经理,那么,我们最多可以分64个组,N0到N64组,第个组的权限值(rolevalue)就可以用2的N次方来表示,所以,可以表示64权限组了。这样,不同的用户,可能属于1个或是多个权限组,一个权限组,可能包括多个用户,同理,对于一个操作,可能会属于多个权限组,一个权限组,肯定要包括多个操作。
先说说我的做法的主要思路:
如何记录一个权限呢,比如一个用户,他有一个权限值,最大可表示为2的64次方减1,所以,这个值有64个二进制位,那么,每一位要么是0,要么是1,所以,如果每一位表示一种权限的话,就可以表示64种权限了,因此,只要对指定的一位进行判断是0还是1,就可以验证用户的权限了。对于每一个操作的权限值,可以用一个二进制数来表示,每个权限值,只能是(0),(10),(100),(1000),也就是说,是2的N次方,在MSSQL2000中,最大的整数是bigint类型的,最大可以表示64种操作。当然,这个是远远不够的。所以可以给权限分组。比如,可分为文员,主管,经理,那么,我们最多可以分64个组,N0到N64组,第N个组的权限值(rolevalue)就可以用2的N-1次方来表示,所以,可以表示64个权限组了。这样,不同的用户,可以属于1个或是多个权限组,一个权限组,可能包括多个用户,同理,对于一个操作,可能会属于多个权限组,一个权限组,肯定要包括多个操作。因此,用户与操作是通过权限组联系以一起的。权限组与用户,与操作之间的对应关系,都是一对多的关系 。
现在说说用C#来实现,不是在SQL中实现。
在数据库中的表: 操作组: opname oprolevalue 填写 3 修改 3 删除 11 提交 6 审核 12 权限组:
rolename rolevalue 文员 1 主管 2 经理 4 总监 8 用户信息:
username userrolevalue u1 5 在C#中:
Long userrolevalue ;//用户的权限值,根据他属于的权限组,这个值会不同 Long oprolevalue ;//一个操作的权限值,根据他属于的权限组,这个值会不同 1、权限的赋予(或运算)
userrolevalue = userrolevalue | oprolevalue 假设一个用户u1,他的初始权限值为0(00000000)。如果要指定他有经理的权限,经理的权限值为4(00000100),在第三个二进制位为1。很显然,userrolevalue = 0 | 4 ,值为4,如果u1要同时具有文员、主管、经理的权限呢, userrolevalue = 0 | 1 00000000 | 00000001 = 00000001 userrolevalue = 1 | 2 00000001 | 00000010 = 00000011 userrolevalue = 3 | 4 00000011 | 00000100 = 00000111 这样,第1、2、3位都是1了,用 “或”的好处就是只改变指定位的值,如果用户已经有了该权限,再赋予一次,也不会出错,但是直接简单的用加法来做,这会出错了,如下: userrolevalue = 7 | 4 00000111 | 00000100 = 00000111 2、权限的除去(求补、与运算)
userrolevalue = userrolevalue & (~oprolevalue) 假设一个用户u1,他的初始权限值为7(00000111),说明他能做文员、主管、经理权限组所能作的所有操作。如果不想让他有主管权限组能作的操作呢,那么,就要把他的权限值变为00000101,而主管权限组的权限值是00000010,显然简单的用减法,肯定也是不行的,但是先对00000010作补运算,可以得到11111101,再同00000111作与运算,就得到了00000101,这样就只对第二位作了改变,不会影响到其它位,我们的目的也就达到了。对于一个操作,哪些权限组能操作它,也可以用与运算来做,不让某些权限组有些操作的权限,也可以先求补,再作与运算来解决。 3、权限的验证(与运算)
(userrolevalue & oprolevalue) != 0 对于某用户,有没有某操作的权限,只要判断它们两个是不是属于同一个,或多个权限组就可以了,所以用与运算就最合适不过了。比如u1用户,他的权限值是5(文员组、经理组00000101),对于填写操作的权限值是00000011,说明对于文员组与主管组有填写的权限。因为 00000101 & 00000011 == 1。对于一个文员,他只属于文员组,他的权限值肯定是00000001了,对于提交操作0000110,因为00000101 &00000001 == 0,所以,他也就没有提交操作的权限了。 此的验证方法,还可以用在菜单权限的验证上来。权限值还可以是Binary数据类型的,此类型的位数可以很大,因此可以用作不分权限组的情
况。 2006/1/17 委托在权限验证中的应用 在开发中,我们希望一个窗体(或页面中)尽可能多地完成一些功能。比如,一个页面,可能即要做到能查询一批记录,还要做到用户能对这些记录进行审核、修改、删除。有时,我们会对菜单的权限进行控制,但菜单的权限控制只能限制权限不足的用户不能打开此窗口(WEB页面),而且,对于WEB页面,只控制了菜单,用户还是可以通过往地址栏输入地址来打开此页面的。
如何对于同窗体中的不同功能进行权限控制呢?
首先,我们明确要有多少种功能,假设有订货单的填写,查询,修改,删除,初审,复审,终审
我们先在数据库中建一个表,记录这7个操作。当然,还有其它内容的操作,也可以记录在此表中。 然后建一个模板(窗体或是web页),这里用WEB页面来举例:
/// <summary>
/// 所有要求页面验证的页面的模板,可以执行页面验证及业务操作权限验证(用委托实现) /// </summary> namespace Leoas { /// <summary> /// 使用委托来执行业务操作,同时来验证该操作的合法性 /// </summary> public delegate void ExcProcEvent(); public class FormTemple : System.Web.UI.Page { ...... /// <summary> /// 在验证用户的业务操作权限后,再决定是否执行业务操作 /// </summary> public event ExcProcEvent OnExcProc; protected void Page_Load(object sender, System.EventArgs e) { // 如果没有Session,则出错 if(Session.Count == 0) Response.Redirect("/leoas/Errors.aspx?ErrID=7",true); //删除非本页所产生的临时Session this.SessionMutex(this.ToString()); if(!IsPostBack) { //验证页面权限,仅仅限制菜单是没有用了,用户可以通过往地址栏输入地址来打开此页面的 if(!Leoas.NoSqlMethod.ValidateAuthor( "rolevalue", Convert.ToUInt64(Leoas.NoSqlMethod.GetSelectedText("id",Session["s00001"].ToString(),"roleauthor",(System.Data.DataTable)Application["A00006"])), "pagename", this.ToString(), (System.Data.DataTable)Application["a00005"]) ) { Response.Redirect("/leoas/Errors.aspx?ErrID=6",true); Response.End(); } } } /// <summary> /// 通过委托,来确定业务操作的执行 /// </summary> /// <param name="proid">用户正在执行的操作</param> protected void ExcProc(string proid) { //Leoas.NoSqlMethod.ValidateAuthor函数可以确定用户对某个操作是否有权限,其中session值保存了用户的ID,可以能过ID,由Leoas.NoSqlMethod.GetSelectedText来确定用户对应的权限值 //总体思路是,用户有权限,则执行,没有就报错 if(!Leoas.NoSqlMethod.ValidateAuthor("rolevalue",Convert.ToUInt64(Leoas.NoSqlMethod.GetSelectedText("id",Session["s00001"].ToString(),"roleauthor",(System.Data.DataTable)Application["A00006"])),"proid",proid,(System.Data.DataTable)Application["a00004"])) { string procname = Leoas.NoSqlMethod.GetSelectedText("proid",proid,"process",(System.Data.DataTable)Application["a00004"]); Response.Write("<script>window.alert('您没有\"" + procname + "\"操作的权限!\\n如确实需要,请与管理员联系!')</script>"); } else { //执行委托 this.OnExcProc(); } } ...... } } 最后就可以应用这个模板了,我们假设新建一个WEB页面,就可以从上面这个模板来继承: namespace Leoas.EMS.Mbudget { /// <summary> /// MbudgetAuditByDep 的摘要说明。 /// </summary> public class MbudgetAuditByDep : Leoas.FormTemple { ...... //重写 new private void Page_Load(object sender, System.EventArgs e) { //调用基类的Page_Load,来验证用户对本页的访问权限 base.Page_Load(sender,e); if(!IsPostBack) { this.BindData(); } } /// <summary> /// 取得主网格的数据源 /// </summary> private void BindGrid_hz() {...} /// <summary> /// 取得明细网格数据 /// </summary> private void BindGrid_mx() {...} /// <summary> /// 在汇总表中审核 /// </summary> private void Audit_hz() {...} /// <summary> /// 在明细表中审核 /// </summary> private void Audit_mx() {...} //响应[刷新]按钮click事件的总表的查询,其中,“00008”就是查询总表的操作的ID号,通过委托, //就会调用到Leoas.NoSqlMethod.ValidateAuthor函数,来比较用户对于“00008”这个操作是否有权限 private void BT_refresh_Click(object sender, System.EventArgs e) { //查询之前,要先将表格的当前页设到第一页,因为有可能重新查询后,可能表的总页数会比当前页小 this.DG_hz.CurrentPageIndex = 0; this.OnExcProc+=new ExcProcEvent(this.BindGrid_hz); this.ExcProc("00008"); this.DG_mx.DataBind(); //清空明细表,防止误操作 } //汇总表的翻页 private void DG_hz_SelectedIndexChanged(object sender, System.EventArgs e) { this.OnExcProc+=new ExcProcEvent(this.BindGrid_mx); this.ExcProc("00008"); } //在汇总表中审核,“00009”表示提交操作,“00010”表示审核操作 private void BTAudit_hz_Click(object sender, System.EventArgs e) { if(this.DG_hz.Items.Count > 0) { this.OnExcProc+=new ExcProcEvent(this.Audit_hz); if(this.BTAudit_hz.Text == "提交") this.ExcProc("00009"); else this.ExcProc("00010"); this.BindGrid_hz(); if(this.DG_mx.Items.Count > 0) this.BindGrid_mx(); } } //明细表中的操作同上 ... } } 这样就可实现在WEB页面中对每个细节操作进行权限控制了。其实windows应用程序也是一样的,对于ValidateAuthor函数是如何验证权限的,可以看一下我其它的文章,我的blog是:http://spaces.msn.com/members/curllion |
||||
|
|