Curllion's profilecurllionPhotosBlogListsMore Tools Help

curllion

MSN SPACES太慢了,我的博客搬家了 http://hi.baidu.com/curllion

Curllion Zhang

Occupation
Location
在我青春年少的时候,
有个女孩愿意为我去死,
她意志坚定地对我说:
你再缠着我,我就死给你看。
在我穷困潦倒的时候,
有个女人愿和我共赴黄泉,
她斩钉截铁地对我说:
你再不还钱,我就和你同归如尽!
My msn id : curllion@hotmail.com
感谢访问!
Please wait...
Sorry, the comment you entered is too long. Please shorten it.
You didn't enter anything. Please try again.
Sorry, we can't add your comment right now. Please try again later.
To add a comment, you need permission from your parent. Ask for permission
Your parent has turned off comments.
Sorry, we can't delete your comment right now. Please try again later.
You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
Complete the security check below to finish leaving your comment.
The characters you type in the security check must match the characters in the picture or audio.
Anikawrote:
Hello,
I´m back. It was so wonderful in america!
Dec. 15
庄梅wrote:
你女儿很漂亮很可爱  我也有个儿子  现在有22个月了
Dec. 8
晶 晶wrote:
谢谢你的支持~呵呵
Oct. 29
12/21/2006

女儿1岁半

我女儿1岁半了,晚上老是哭个不停.
有一次她奶奶问她:你看看,这么晚了,还有谁在哭?
她说:有!
奶奶又问:你说,还有谁?
我女儿说:艾艾.---艾艾就是她自己
 
7/19/2006

一看就明白的线程的例子

在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);
        }
...
4/18/2006

能横向翻页的动态交叉查询存储过程

-----------------------------------按经销售商列出报销汇总交叉表,没有必要按状态来筛-------------------------------------
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
1/18/2006

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数据类型的,此类型的位数可以很大,因此可以用作不分权限组的情
况。
1/17/2006

委托在权限验证中的应用

      在开发中,我们希望一个窗体(或页面中)尽可能多地完成一些功能。比如,一个页面,可能即要做到能查询一批记录,还要做到用户能对这些记录进行审核、修改、删除。有时,我们会对菜单的权限进行控制,但菜单的权限控制只能限制权限不足的用户不能打开此窗口(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
 
Photo 1 of 6