in

SDT Community Server

SDT Forums, Blogs, Photos server.

wego

  • Session 共享

    使用如下 code  原理可实现新开的一个网页窗口不用重复登录已登录的系统

     

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            Dictionary<string, string> ipSession;          
            string ip = Request.ServerVariables["REMOTE_ADDR"];
            bool ipExists = true;
            string useSessionID = Session.SessionID;

            if (Application["IPSession"] == null)
            {               
                ipSession = new Dictionary<string, string>();               
                ipExists = false;               
            }
            else
            {
                ipSession = (Dictionary<string, string> )Application["IPSession"];

                if (!ipSession.ContainsKey(ip))
                {
                    ipExists = false;                               
                }
                else
                {
                    useSessionID = ipSession[ip];
                }
            }

            if (!ipExists)
            {
                ipSession.Add(ip, useSessionID);

                Application.Lock();
                Application["IPSession"] = ipSession;
                Application.UnLock();
            }

            //
            HttpCookie cookie = new HttpCookie("ASP.NET_SessionId", useSessionID);  // 这里 Cookie 值须使用 SessionId 形式, 我试过写 "test" 等不会生效

            cookie.Expires = DateTime.Now.AddMinutes(100);
            cookie.Path = "/";

            Response.Cookies.Remove("ASP.NET_SessionId");
            Response.Cookies.Add(cookie);

        }
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        Session["t"] = string.Concat(Request.ServerVariables["REMOTE_USER"], " -- ", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
    }

    protected void Button2_Click(object sender, EventArgs e)
    {
        Response.Write(Session["t"] ?? "null");
    }

  • 减少算术误差2则

    尽量除改乘, 即使一定要除也要先乘后除


    1)  若 bd>0, 则 a/b < c/d 改成 a*d < c*b (等于两边 * bd )


    2) decimal a = 1;
        decimal b = 3;
        decimal ng = a / b * b;
        decimal ok = a * b / b;

  • decimal 的误差

    decimal 数值类型并不是想象中的那样精确可靠,  下面的 c 结果为0 (这里宁愿它抛出一个数值超出范围的异常也不希望得到一个错误的结果), 做会计等系统时切要提防提防!

    decimal a = 0.00000000000000000000000000065M;
    decimal b = 0.0000000000000000000000000006M;
    decimal c = a - b;

    所以从严格来说, 用 DotNet 所开发的系统涉及运算的部分都有潜在隐患! 


     

     

  • 多核时代, 使用多线程 .Net 4.0 前后的差异

    服务器使用多核逐渐成为趋势,  使用多线程可令到多核同时运转, 优势越发明显. 以下是.Net 4.0 前后使用多线程的差异, 例子是实现一个简单的矩阵相乘 (实际使用时可仿效思路改为业务复杂操作),  矩阵为 3 * 12 方阵,  把其从上到下切分为4部分 (即各 3*3 ) 使用4个线程进行相乘.  令外, 从中可以看到 .Net 4.0 语法非常清晰简明, 有更好的支持!

     

    const int X_LENGTH = 12;
    const int Y_LENGTH = 3;

    void Test()
    {
        var array1 = new int[X_LENGTH, Y_LENGTH];
        var array2 = new int[X_LENGTH, Y_LENGTH];
        var array3 = new int[X_LENGTH, Y_LENGTH];

        var arrays = new List<int[,]> { array1, array2 };
        var random = new Random();

        arrays.ForEach(array =>
        {
            for (int i = 0; i < X_LENGTH; i++)
            {
                for (int j = 0; j < Y_LENGTH; j++)
                {
                    array[i, j] = random.Next(10, 100);
                }
            }
        });

        MatrixMult(4, array1, array2, array3);
    }

     .Net 4.0 前:

    void MatrixMult(int threadCount, int[,] array1, int[,] array2, int[,] array3)
    {
        AutoResetEvent signal = new AutoResetEvent(false);

        int counter = threadCount;

        for (int c = 0; c < threadCount; c++)
        {
            ThreadPool.QueueUserWorkItem(o =>
            {
                int lc = (int)o;
                for (int i = lc * (X_LENGTH / threadCount); i <= (lc + 1) * (X_LENGTH / threadCount) - 1; i++)
                {
                    for (int j = 0; j < Y_LENGTH; j++)
                    {
                        array3[i, j] = array1[i, j] * array2[i, j];
                    }
                }
                if (Interlocked.Decrement(ref counter) == 0)
                {
                    signal.Set();
                }
            }, c);
        }
        signal.WaitOne();

     .Net 4.0:

    void MatrixMult(int threadCount, int[,] array1, int[,] array2, int[,] array3)
    {       
        Parallel.For(0, threadCount, delegate(int c)
        {
            for (int i = c * (X_LENGTH / threadCount); i <= (c + 1) * (X_LENGTH / threadCount) - 1; i++)
            {
                    for (int j = 0; j < Y_LENGTH; j++)
                    {
                        array3[i, j] = array1[i, j] * array2[i, j];
                    }
            }
        });
    }
  • jQuery 选择器空格注意

    今天在使用 jq 选择器 ($('#divSelItems:checkbox:checked').length) 时硬是为零, 原来空格是很重要的, 需改成如下:

    if ($('#divSelItems :checkbox:checked').length == 0) {  
        alert('Please select one item at least');
        return false;
    }

    Posted Apr 02 2010, 01:15 PM by wego with no comments
    Filed under:
  • [转] VS2010最新AD-想做你的code

  • 写个 C# string 转换类型扩展方法

    public static class StringExtension
    {
        public static T ChangeStringToType<T>(this string value)
        {
            if (typeof(T).IsValueType)
            {
                if (!typeof(T).IsEnum)
                {
                    return (T)Convert.ChangeType(value, typeof(T));
                }
                else
                {
                    return (T)Enum.Parse(typeof(T), value);
                }
            }
            else
            {
                if (typeof(T).IsArray)
                {
                    string[] strs = value.Split(new char[] { '\0' });
                    Type elementType = typeof(T).GetElementType();
                    Array array = Array.CreateInstance(elementType, strs.Length);
                    for (int i = 0; i < strs.Length; i++)
                    {
                        array.SetValue(TypeDescriptor.GetConverter(elementType).ConvertFrom(null, null, strs[i]), i);
                    }
                    return (T)Convert.ChangeType(array, typeof(T));
                }
                else
                {
                    throw new InvalidOperationException("Invalid cast from 'System.String' to '" + typeof(T).ToString() + "'.");
                }
            }
        }
    }

     

    public enum Role
    {
        Guest,
        Member,
        Manager,
        Admin

     

    // 调用:

    string a = "1";
    int b = a.ChangeStringToType<int>();  // 也可: var b = a.ChangeStringToType<int>();

     

    string c = "12-18-2009";  // 也可:  string c = "2009-12-18";
    DateTime d = c.ChangeStringToType<DateTime>();

     

    string e = "Member";
    Role f = e.ChangeStringToType<Role>();

     

    string g = "as\0sdk";
    string[] h = g.ChangeStringToType<string[]>();

     

    string i = "Guest\0Manager\0Admin";
    Role[] j = i.ChangeStringToType<Role[]>();

     

    // 上面的也可以直接使用字符串常量, 如:  DateTime d = "12-18-2009".ChangeStringToType<DateTime>();

     

    // 当然! 也可以发挥自己的创意, 定制一套规则协议, 使之能转换成 DataTable 等类型.

  • 使用 jQuery 处理 SimpleGridView 的一个选择行背景色问题

    使用 SimpleGridView 时, 如果需要在某些行添加特定背景样式, 则会出现点击这些行时,  它的背景色没有变成点击获焦行的背景色. 此时, 可使用如下 jQuery 脚本解决. 另外,  jQuery 非常好用, 方便灵活, 脚本代码大量减少, 网页大小也随之而小; 且良好的兼容性, 不用担心到了别的浏览器或其它版本效果就不一样. 所以非常推荐推荐!

     

    public void gvData_RowDataBound(object sender, GridViewRowEventArgs e)
    {
            switch (e.Row.RowType)
            {
                case DataControlRowType.DataRow:
                    if (...)
                    {
                        for (int i = 0; i < e.Row.Cells.Count - 1; i++)
                        {
                            e.Row.Cells[i].Attributes.Add("style", "font-weight:bold;background-color:#CCFFCC;");
                        }
                    }
                    break;
            }
    }

     

     

    protected void Page_Load(object sender, EventArgs e)
    {

                StringBuilder script = new StringBuilder();

     

                // global variable
                script.Append("var lastSelRow;");
                script.Append("var lastSelRowBgColor;");

     

                // reconvert backgroundColor for last selected row

                // :gt(index) :  匹配所有大于给定索引值的元素;  因为这里第1个 tr 是列标题行
                script.Append("$('#{0} tr:gt(0)').mousedown(function() {{ " +

     

                                    "if (lastSelRow) {{ " +
                                        "$(lastSelRow).children('td').each(function() {{ " +
                                            "$(this).css('backgroundColor', lastSelRowBgColor);" +
                                        "}}); " +
                                    "}} " +

     

                                    "lastSelRow = $(this)[0]; " +
                                    "lastSelRowBgColor = $(this).children('td:first').css('backgroundColor'); " +    
                         
                                "}});");

     

                // set backgroundColor for current selected row
                script.Append("$('#{0} tr:gt(0)').click(function() {{ " +                                      
                                    "$(this).children('td').each(function() {{ " +
                                        "$(this).css('backgroundColor', '#e1edf8'); " +
                                    "}}); " +
                                "}});");

                //
                ClientScript.RegisterStartupScript(this.GetType(), "SetSelRowBackgroundColor", string.Format(script.ToString(), gvData.ClientID), true);

    }

  • Oracle 竖变横需注意 ROWNUM & Order By

    WITH t AS
         (
            SELECT 'BU0301' bu
              FROM DUAL
            UNION ALL
            SELECT 'BU0701'
              FROM DUAL
            UNION ALL
            SELECT 'BU0401'
              FROM DUAL
            UNION ALL
            SELECT 'BU0501'
              FROM DUAL
            UNION ALL
            SELECT 'BU0401'
              FROM DUAL
            UNION ALL
            SELECT 'BU0801'
              FROM DUAL
            UNION ALL
            SELECT 'BU0901'
              FROM DUAL
            UNION ALL
            SELECT 'BU0201'
              FROM DUAL
            UNION ALL
            SELECT 'BU0101'
              FROM DUAL
            UNION ALL
            SELECT 'BU0601'
              FROM DUAL)


    SELECT     SUBSTR (MAX (SYS_CONNECT_BY_PATH ('''' || bu || '''', ', ')), 3)
          FROM (SELECT ROWNUM rn, bu
                  FROM (SELECT DISTINCT bu
                                   FROM t))
    START WITH rn = 1
    CONNECT BY rn - 1 = PRIOR rn; 

     

    -- 输出: 'BU0301', 'BU0201', 'BU0301', 'BU0401', 'BU0501', 'BU0601', 'BU0701', 'BU0801', 'BU0901'

    甚奇! 'BU0101' 不见了! 另外, 明明没 BU 重复且已加 DISTINCT, 但结果竟有 'BU0301' 重复!

     

     

    SELECT     SUBSTR (MAX (SYS_CONNECT_BY_PATH ('''' || bu || '''', ', ')), 3)
          FROM (SELECT ROWNUM rn, bu
                  FROM (SELECT DISTINCT bu
                                   FROM t
                               ORDER BY bu))
    START WITH rn = 1
    CONNECT BY rn - 1 = PRIOR rn;

     

    或者:

     

    SELECT     SUBSTR (MAX (SYS_CONNECT_BY_PATH ('''' || bu || '''', ', ')), 3)
          FROM (SELECT DISTINCT DENSE_RANK () OVER (ORDER BY bu) AS rn, bu
                           FROM t)
    START WITH rn = 1
    CONNECT BY rn - 1 = PRIOR rn;

     

    -- 输出: 'BU0101', 'BU0201', 'BU0301', 'BU0401', 'BU0501', 'BU0601', 'BU0701', 'BU0801', 'BU0901'

  • 当心在多线程中使用循环传递 HttpContext 参数

     今天发现在多线程中使用循环传递 HttpContext 参数时, 有时会出现传递失效的情况.  请见如下 Code

     

    delegate DataTable GetSegDataDelegate(HttpContext context, int reportid);
    DataTable dtSeg1, dtSeg2, dtSeg3;   

     

    protected void Page_Load(object sender, EventArgs e)
    {
        Session["company"] = "SHK";
        Session["currency"] = "USD";

        GetSegDataDelegate getSegDataDelegate = new GetSegDataDelegate(GetSegData);       

        for (int i = 7; i <= 9; i++)          // CodeA
        {           
            getSegDataDelegate.BeginInvoke(HttpContext.Current, i, new AsyncCallback(SetSegData), null);
        }

        while (dtSeg1 == null)
        { }
        gvData1.DataSource = dtSeg1;
        gvData1.DataBind();


        while (dtSeg2 == null)
        { }
        gvData2.DataSource = dtSeg2;
        gvData2.DataBind();


        while (dtSeg3 == null)
        { }
        gvData3.DataSource = dtSeg3;
        gvData3.DataBind();
    }

     

    public DataTable GetSegData(HttpContext context, int reportid)
    {
        DataTable dt = new DataTable();

        try
        {
            int i = 0;
            OracleParameter[] p = new OracleParameter[4];
            p[i] = new OracleParameter("p_cursor", OracleType.Cursor);
            p[i].Direction = ParameterDirection.Output;
            i++;
            p[i] = new OracleParameter("p_reportid", OracleType.Number);
            p[i].Value = reportid;
            i++;
            p[i] = new OracleParameter("p_company", OracleType.VarChar, 100);
            p[i].Value = context.Session["company"];                // CodeB
            i++;           
            p[i] = new OracleParameter("p_curr", OracleType.VarChar, 50);
            p[i].Value = context.Session["currency"];                 // CodeC
            i++;

            dt = db.ExecProcedure("ACCP.ACC_SEG_PKG.query_amount", p);
        }
        catch (Exception ex)
        {
            throw ex;
        }

        dt.TableName = "SEG" + reportid;
        return dt;
    }

     

    public void SetSegData(IAsyncResult result)
    {
        AsyncResult async = (AsyncResult)result;
        GetSegDataDelegate DelegateInstance = (GetSegDataDelegate)async.AsyncDelegate;
        DataTable dt = DelegateInstance.EndInvoke(result);
        switch (dt.TableName)
        {
            case "SEG7":
                dtSeg1 = dt;               
                break;

            case "SEG8":
                dtSeg2 = dt;               
                break;

            case "SEG9":
                dtSeg3 = dt;               
                break;
        }
    }
     

     

     当 CodeA 循环到第2或第3次时 (即 i=8 或 i=9 ), CodeB, CodeC 处找不到 Session 值(即 context.Session["company"] 和 context.Session["currency"] 为 null ), 表明传入的  HttpContext 参数失效. 但第1次 (即 i=7) 是完全没有问题的. 非常奇怪?!  可改成如下 code 处理之.

     

    delegate DataTable GetSegDataDelegate(string company, string currency, int reportid);
    DataTable dtSeg1, dtSeg2, dtSeg3;   

     

    protected void Page_Load(object sender, EventArgs e)
    {
        Session["company"] = "SHK";
        Session["currency"] = "HKD";

        GetSegDataDelegate getSegDataDelegate = new GetSegDataDelegate(GetSegData);       

        for (int i = 7; i <= 9; i++)
        {
            getSegDataDelegate.BeginInvoke(Session["company"].ToString(), Session["currency"].ToString(), i, new AsyncCallback(SetSegData), null);
        }

        while (dtSeg1 == null)
        { }
        gvData1.DataSource = dtSeg1;
        gvData1.DataBind();


        while (dtSeg2 == null)
        { }
        gvData2.DataSource = dtSeg2;
        gvData2.DataBind();


        while (dtSeg3 == null)
        { }
        gvData3.DataSource = dtSeg3;
        gvData3.DataBind();
    }

     

    public DataTable GetSegData(string company, string currency, int reportid)
    {
        DataTable dt = new DataTable();

        try
        {
            int i = 0;
            OracleParameter[] p = new OracleParameter[4];
            p[i] = new OracleParameter("p_cursor", OracleType.Cursor);
            p[i].Direction = ParameterDirection.Output;
            i++;
            p[i] = new OracleParameter("p_reportid", OracleType.Number);
            p[i].Value = reportid;
            i++;
            p[i] = new OracleParameter("p_company", OracleType.VarChar, 100);
            p[i].Value = company;
            i++;           
            p[i] = new OracleParameter("p_curr", OracleType.VarChar, 50);
            p[i].Value = currency;
            i++;

            dt = db.ExecProcedure("ACCP.ACC_SEG_PKG1.query_amount", p);
        }
        catch (Exception ex)
        {
            throw ex;
        }

        dt.TableName = "SEG" + reportid;
        return dt;

    }

     

    这里之所以采用多线程, 是为了提高查询速度. 之前运行的情况是 Procedure2 要等待 Procedure1 完成才运行,  Procedure3 要等待 Procedure2 完成才运行. 而现在是3个 Procedure 并列同步运行, 实际运行速度几乎是原来的3倍.

    另外还有一个注意点, 在线程回调过程里不可操作页面上的控件, 如下

     

    public void SetSegData(IAsyncResult result)
    {
        AsyncResult async = (AsyncResult)result;
        GetSegDataDelegate DelegateInstance = (GetSegDataDelegate)async.AsyncDelegate;
        DataTable dt = DelegateInstance.EndInvoke(result);
        switch (dt.TableName)
        {
            case "SEG7":               
                gvData1.DataSource = dt;
                gvData1.DataBind();
                break;

            case "SEG8":              
                gvData2.DataSource = dt;
                gvData2.DataBind();
                break;

            case "SEG9":            
                gvData3.DataSource = dt;
                gvData3.DataBind();
                break;
        }
    }

  • C# Class 访问用户控件 (ascx) 的属性

    C# Class (如在 /App_Code/BasePage.cs 中) 不能像页面文件那样使用如下方式访问用户控件 

     

    test.aspx 

    <%@ Reference Control="~/_SYS/Control/SearchTextBox/SearchTextBox.ascx" %>

     

     

    test.aspx.cs
    protected override void OnLoad(EventArgs e)
    {
              base.OnLoad(e);

     

              _SYS_Control_SearchTextBox_SearchTextBox c = FindControlEx(this, "SearchTextBox1") as _SYS_Control_SearchTextBox_SearchTextBox;

              if (c != null)
              {
                     c.TestProperty = "…";
              }
    }


    但可借助 Control > ClassName 属性解决. 但是有一缺点, 如下面例子, 若 TestUserControl.ascx 中的 TextBox1 控件 ID 后来改变了, 则 App_Code\TestUserControl.cs 中的相应代码也要作改动.

     

    App_Code\CommonFunc.cs

    public class CommonFunc
    {
        public static Control FindControlEx(Control parent, string id)
        {
            foreach (Control control in parent.Controls)
            {
                if (control.ID != null && control.ID.Equals(id))
                {
                    return control;
                }
                else
                {
                    if (control.HasControls())
                    {
                        Control subControl = FindControlEx(control, id);
                        if (subControl != null)
                        {
                            return subControl;
                        }
                    }
                }
            }
            return null;
        }

    }

     

    App_Code\TestUserControl.cs 

    public class TestUserControl : UserControl
    {   
        protected TextBox TextBox1
        {
            get
            {
                return CommonFunc.FindControlEx(this, "TextBox1") as TextBox;
            }
        }

        public string TestProperty
        {
            get
            {
                return TextBox1.Text;
            }
            set
            {
                TextBox1.Text = value;
            }
        }
    }

     

     TestUserControl.ascx

    <%@ Control Language="C#" ClassName="TestUserControl"  Inherits="TestUserControl" %>
    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

     

    App_Code\BasePage.cs  

    public class BasePage : System.Web.UI.Page
    {
             protected override void OnLoad(EventArgs e)
             {
                     base.OnLoad(e);   


                     TestUserControl c = CommonFunc.FindControlEx(this, "TestUserControl1") as TestUserControl;          
                     if (c != null)          
                     {
                            c.TestProperty = "…";           
                     } 
             }

    }

  • ORACLE 自定义异常和输出错误信息及行号

    PROCEDURE p_test
    IS
       my_exception   EXCEPTION;
    BEGIN

       ...
       IF ...
       THEN
          RAISE my_exception;
       END IF;

        ... 


    EXCEPTION
       WHEN my_exception

       THEN
          DBMS_OUTPUT.put_line ('...');
       WHEN OTHERS
       THEN
          DBMS_OUTPUT.put_line ('Message: ' || SQLERRM);
          DBMS_OUTPUT.put_line ('Line No.: '
                                || DBMS_UTILITY.format_error_backtrace
                               );
    END;

  • Oracle 使用'名称定位参数' 调用 Function 或 Procedure

    Oracle 支持使用'名称定位参数' 调用 FUNCTION 或 PROCEDURE, 但有2个注意点. 请看如下 code

     

    FUNCTION F_TEST (p1 IN NUMBER, p2 IN VARCHAR2 := NULL, p3 IN VARCHAR2 := NULL)
       RETURN VARCHAR2
    IS
    BEGIN
       RETURN 'P3:' || p3;
    END;

     

    PROCEDURE P_TEST
    IS
       v_result   VARCHAR2 (100);
    BEGIN
       v_result := f_test (6, p3 => 'test');            --- A

       v_result := f_test (p3 => 'test', p1 => 6);   --- B

       SELECT f_test (6, p3 => 'test')                --- C
         INTO v_result
         FROM DUAL;

       v_result := f_test (p3 => 'test', 6);             ---D

       DBMS_OUTPUT.put_line (v_result);
    END;

     

    A), B) 正常运行

    C) 编译报错: PL/SQL: ORA-00907: 缺失右括号

        注意点1: 不能用在 sql 语句中

    D) 编译报错: PLS-00312: 一个定位相关参数没有说明其相关性

        注意点2: 所有位置参数须写在定位参数前面

     

    C# 4.0 也开始支持类似的 '名称定位参数' 使用了, 不知到时会否也有上面的点2需注意 Smile

     

       

     

  • IE8 VS2005 调试问题

    昨天安装 IE8 后发现 VS2005 的程序按 F5 运行后会进行非调试状态, 所有断点不会触发.后查阅网上资料可按如下方法解决.

    1. 点运行regedit.
    2. 定位到HKEY_LOCALMACHINE -> SOFTWARE -> Microsoft -> Internet Explorer -> Main
    3. 新建一名为TabProcGrowth的dword值, value 为0.

     

  • Oracle sys_refcursor 游标变量不能用作参数传递

    今天在使用 Oracle sys_refcursor 游标变量时, 抛出错误: 'Execution failed: ORA-00604: 递归 SQL 级别1出现错误', 'ORA-01001: 无效的游标'.  原来是sys_refcursor 游标变量不能用作参数传递.  请见如下 code:

      PROCEDURE P1(
          p_cursor     OUT    sys_refcursor,
          p_year        IN       NUMBER
       )

       BEGIN

           ......

           P2(p_cursor, 'test');

       END;

       PROCEDURE P2(
          p_cursor      OUT    sys_refcursor,
          p_company  IN       VARCHAR2
        )

        BEGIN

           ......

        END;

     

    需改成

     TYPE t_cursor IS REF CURSOR;   -- 在 PACKAGE(Spec) 中定义

      PROCEDURE P1(
          p_cursor     OUT    t_cursor,
          p_year        IN       NUMBER
       )

       BEGIN

           ......

           P2(p_cursor, 'test');

       END;

       PROCEDURE P2(
          p_cursor      OUT    t_cursor,
          p_company  IN       VARCHAR2
        )

        BEGIN

           ......

        END;

     

     

  • C# 泛型方法中的 out 参数问题

    C#  方法中的 out 参数中需要在方法内为其赋值, 而当 out 参数定义为泛型数据类型时, 因为其类型需运行时才具体确定, 有时要用到 C# 的一个特殊关键字 default.

     public class CacheHandler
    {
        public enum Keys
        {
            ReportLinks
        }

        protected static Cache CurrentCache
        {
            get { return HttpContext.Current.Cache; }
        }

        public static bool IsNull(Keys key)
        {
            return CurrentCache[key.ToString()] == null;
        }

        public static void Insert<T>(Keys key, T value)
        {
            CurrentCache.Insert(key.ToString(), value);
        }

        public static bool TryGetValue<T>(Keys key, out T value)
        {
            if (IsNull(key))
            {
                value = default(T);    // 这里不能写成 value = null;
                return false;
            }
            else
            {
                try
                {
                    value = (T)CurrentCache[key.ToString()];
                    return true;
                }
                catch (Exception ex)
                {
                    value = default(T);
                    return false;
                }
            }
        }
    }

    // 调用

    Dictionary<int, string> reportLinks;
    if (!CacheHandler.TryGetValue(CacheHandler.Keys.ReportLinks, out reportLinks))
    {
        reportLinks.Add(1, "test");
        // ...
        CacheHandler.Insert(CacheHandler.Keys.ReportLinks, reportLinks);
    }

  • 怪事年年有, 今年特别多之 Oracle ROLLUP

    Oracle 的 GROUP BY ROLLUP 具有分组合计功能, 非常好用! 以前一直用开都没什么问题. 但今天使用时发现一个奇怪现象, 同一条 sql 在数据库中(如在 SQL Navigator 工具) 正常运行, 但在 C# 中执行则抛出 'OCI-22060: argument [2] is an invalid or uninitialized number' 错误 (注意这个错误不是以 ORA 开头), 更奇怪的是我自己创建一个测试表,  改了一下 sql 表名则 C#  就执行正常了.  虽然很明显是数据问题, 但我查过多次数据也没什么特别.

    sql 缩简 如下:

    sqlA = "SELECT   companyid, SUM (f1) f1 " +
                       "FROM acc_a15_data a " +
                   "GROUP BY ROLLUP (companyid) ";

    sqlB = "SELECT   companyid, SUM (f1) f1 " +
                   "FROM  wego_test " +
               "GROUP BY ROLLUP (companyid) ";

    DataTable dtB = BESDb.Db.ExecQuery(sqlB);  -- 正常
     

    DataTable dtA = BESDb.Db.ExecQuery(sqlA);  -- 报错

     

    解决方法可改成如下:

    sqlA = "SELECT   TO_CHAR(companyid), SUM (f1) f1 " +
                       "FROM acc_a15_data a " +
                   "GROUP BY ROLLUP (companyid) ";

     

  • 使用数据库储存的文件内容作邮件附件

    请见如下 Code:

    string sql = string.Format("SELECT file_name, file_content FROM {0} WHERE id = {1}", DbTabeName, RecordID);
    string attachmentPath = null;

    DbDataReader reader = null;
    try
    {
        reader = BESDb.ExecReader(sql);
        reader.Read();

        attachmentPath = String.Format("{0}\\{1}", SessionInfo.TempDir, reader["file_name"].ToString());
        using (FileStream fs = new FileStream(attachmentPath, FileMode.Create, FileAccess.Write, FileShare.Write))
        {                  
            using (StreamWriter streamwriter = new StreamWriter(fs, Response.ContentEncoding))
            {
                byte[] fileContent = (byte[])reader["file_content"];
                streamwriter.BaseStream.Write(fileContent, 0, fileContent.Length);                      
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        if (!reader.IsClosed)
        {
            reader.Close();
        }
    }

    simpleMail.Send(address, null, subject, body.ToString(), null, new string[] { attachmentPath });

     

  • ORACLE正则表达式 - 字符簇方括号匹配问题

    Oracle 正则中的字符簇在方括号匹配上有别于其它普通字符,  需改用 '|'  匹配符或如 '[[:digit:]pw]+' 的方式, 请见如下4项表达式.

    a. SELECT 1 FROM DUAL WHERE REGEXP_LIKE ('wap', '[pwa]+')  -- 1

    b. SELECT 1 FROM DUAL WHERE REGEXP_LIKE ('w2p', '[pw[[:digit:]]]+')  -- null

    c. SELECT 1 FROM DUAL WHERE REGEXP_LIKE ('w2p', '(pw|[[:digit:]])+')  -- 1

    d. SELECT 1 FROM DUAL WHERE REGEXP_LIKE ('w2p', '[[:digit:]pw]+')  -- 1

    Posted Apr 08 2009, 01:51 PM by wego with no comments
    Filed under:
  • UpdatePanel 转用 span 作容器及网页源码的截获和加工

    UpdatePanel 自身使用 div 作为容器, 有时产生的问题是出现不希望的换行 (例如: 希望 UpdatePanel 外两边的控件和其内部的控件位于同一行), 可考虑使用如下方式解决之 (思路: 截获网页客户端源码, 将其有关的 div 转成 span)

    public class UpdatePanel : System.Web.UI.UpdatePanel
    {
        public bool UseSpanAsContainer { get; set; }           

        protected override void Render(HtmlTextWriter writer)
        {
            if (UseSpanAsContainer && !IsInPartialRendering)
            {
                Page.Response.Filter = new HttpResponseFilter(Page.Response.Filter);

                HttpResponseFilter filter = (HttpResponseFilter)Page.Response.Filter;
                filter.OnSourceCodeWrited += sourceCode =>
                {
                    string destinationCode = sourceCode;

                    string pattern = "<" + ClientID + ">\\s*<div";
                    destinationCode = Regex.Replace(destinationCode, pattern, "<span");

                    pattern = "</div>\\s*</" + ClientID + ">";
                    destinationCode = Regex.Replace(destinationCode, pattern, "</span>");

                    return destinationCode;
                };          

                //
                writer.RenderBeginTag(ClientID);
                base.Render(writer);
                writer.RenderEndTag();
            }
            else
            {
                base.Render(writer);
            }
        }
    }   

    public class HttpResponseFilter : Stream
    {
        Stream responseStream;   
        StringBuilder responseHtml;

        public delegate string SourceCodeWrited_EventHandler(string sourceCode);
        public event SourceCodeWrited_EventHandler OnSourceCodeWrited;

        public HttpResponseFilter(Stream inputStream)
        {
            responseStream = inputStream;
            responseHtml = new StringBuilder();
        }

        public override bool CanRead
        {
            get
            {
                return true;
            }
        }

        public override bool CanSeek
        {
            get
            {
                return true;
            }
        }

        public override bool CanWrite
        {
            get
            {
                return true;
            }
        }

        public override void Close()
        {
            responseStream.Close();
        }

        public override void Flush()
        {
            responseStream.Flush();
        }

        public override long Length
        {
            get
            {
                return 0;
            }
        }

        public override long Position { get; set; }   

        public override int Read(byte[] buffer, int offset, int count)
        {
            return responseStream.Read(buffer, offset, count);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return responseStream.Seek(offset, origin);
        }

        public override void SetLength(long length)
        {
            responseStream.SetLength(length);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            string strBuffer = System.Text.UTF8Encoding.UTF8.GetString(buffer, offset, count);

            Regex eof = new Regex("</html>", RegexOptions.IgnoreCase);

            if (!eof.IsMatch(strBuffer))
            {
                responseHtml.Append(strBuffer);
            }
            else
            {
                responseHtml.Append(strBuffer);
                string finalHtml = responseHtml.ToString();


                 /*  这里比较奇怪, 第一次截获会输出如 "<html><head><title>Object moved</title></head><body>
    <h2>Object moved to <a href="%2fITMC%2fHardware%2fAccount%2fComputer.aspx%3f_slbg%3d0%26_slbi%3d0" mce_href="%2fITMC%2fHardware%2fAccount%2fComputer.aspx%3f_slbg%3d0%26_slbi%3d0">here</a>.</h2>
    </body></html>" 的源码, 所以需过滤掉 (这里说的 "第一次" 不是 Page.IsPostBack, 而是系统关闭后再重新运行 ) */

                if (!finalHtml.StartsWith("<html><head><title>Object moved</title>"))
                {
                    OnSourceCodeWrited.DoIfNotNull((obj) => finalHtml = OnSourceCodeWrited(finalHtml));
                }

                byte[] data = System.Text.UTF8Encoding.UTF8.GetBytes(finalHtml);
                responseStream.Write(data, 0, data.Length);
            }
        }
    }

    令外, 由于UpdatePanel 是位于 System.Web.UI 命名空间而非 System.Web.UI.WebControls, 所以不能像 WebControls 一样直接如下解决:

    protected override HtmlTextWriterTag TagKey
    {

        get { return HtmlTextWriterTag.Span; }
    }    

     

  • 结合 UpdatePanel DropDownList 使用的一个 bug

     

    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
            <itmc:Localize runat="server" ID="Localize1" Text="<%$ Resources:Labels,Module %>" />
            <itmc:DropDownList runat="server" ID="ddlNavModule" AutoPostBack="true" OnSelectedIndexChanged="ddlNavModule_SelectedIndexChanged">
            </itmc:DropDownList>
            &nbsp;
            <itmc:Localize runat="server" ID="Localize2" Text="<%$ Resources:Labels,FunctionGroup %>" />
            <itmc:DropDownList runat="server" ID="ddlNavGroup" AutoPostBack="true" OnSelectedIndexChanged="ddlNavGroup_SelectedIndexChanged">
            </itmc:DropDownList>
            &nbsp;
            <itmc:Localize runat="server" ID="Localize4" Text="<%$ Resources:Labels,Function %>" />
            <itmc:DropDownList runat="server" ID="ddlNavMember">
            </itmc:DropDownList>
        </ContentTemplate>
    </asp:UpdatePanel>

    public class DropDownList : SimpleDropDownList

    {

            ...

            protected override void OnPreRender(EventArgs e)
            {
                base.OnPreRender(e);

                ...

                //
                if (ItemUseResource == true)
                {
                    foreach (ListItem item in Items)
                    {
                        if (!SpecialItems.Contains(item.Value))
                        {
                            string itemValue = item.Value;      // 语句 a
                            item.Text = GlobalResource.GetLabString(item.Text);
                            item.Value = itemValue;              // 语句 b
                        }
                    }
                }
            }

    }

    如果页面使用了 UpdatePanel  且 OnPreRender 发生在异步刷新过程中, 必须添加语句a和b, 否则 item.Value 自动等于 item.Text

  • window.onunload 在 IE6, 7 上的差异

    // javascript 

    window.onunload = function() {

        alert('b');

    }

     

    // C#

    protected void Page_Load(object sender, EventArgs e)
    {

        Page.ClientScript.RegisterStartupScript(Page.GetType(), "test", "alert('a'); window.returnValue = '1'; window.close();", true);

    }

     

    //

    假设页面为模式弹出窗体, IE6 不会执行 window.onunload , 即只弹出 "a"; 而 IE7 会执行 window.onunload, 即弹出 "a" 和 "b".

    故在模式窗体最好尽量少用  window.onunload, 因为您系统中用 IE6 或 7 的用户可能都有.

     

     

  • throw 语句在委托中需加花括号

    public static class ObjectExtension
    {

        public static void DoIfNull(this object data, Action action)
            {
                if (data == null)
                {
                    action();
                }
            }
    }

    DataRow dr = ...

    int i =1;

    dr.DoIfNull(() =>  throw new ApplicationException(GlobalResource.GetMsgString("EmailNotExists")) );   // 不能编译通过

    dr.DoIfNull(() =>  i = 100 );   // 可以编译通过, 同样是单条语句

    dr.DoIfNull(() => { throw new ApplicationException(GlobalResource.GetMsgString("EmailNotExists")); });  // 可以编译通过

     

  • Oracle复合数据结构的一个注意点

    CREATE OR REPLACE TYPE t_str_array AS TABLE OF VARCHAR2 (500); 

     

     SELECT b.COLUMN_VALUE  FROM bd_pc_type a, TABLE (t_str_array ('手提', '工作站', '手提机')) b

     WHERE a.code(+) = b.COLUMN_VALUE AND a.code IS NULL

     

    无记录返回, 若改成

     

      SELECT a.code, b.COLUMN_VALUE  FROM bd_pc_type a, TABLE (t_str_array ('手提', '工作站', '手提机')) b

     WHERE a.code(+) = b.COLUMN_VALUE AND a.code IS NULL

     

    有记录返回

  • LINQ 无短路

    昨天在用 LINQ 时, 发现其不像其 if 或其它条件表达式一样会自动产生短路效应. 若各位刚好用到时请加以注意, 避免不必要的运行时错误.

    请见如下 Code:

    public static void SetPageInfo(BasePage page)
    {
        DataTable rights = GetOperationRights();

        var relatedRights = from right in rights.AsEnumerable()

                            where (right.Field<string>("module_code") == null || right.Field<string>("module_code").Equals(page.Module)) &&
                                  (right.Field<string>("function_group_code") == null || right.Field<string>("function_group_code").Equals(page.Group)) &&
                                  (right.Field<string>("function_key") == null || right.Field<string>("function_key").Equals(page.Member))

                            orderby right.Field<string>("module_code") descending,
                                    right.Field<string>("function_group_code") descending,
                                    right.Field<string>("function_key") descending

                            select new
                            {
                                CanQry = right.Field<decimal>("can_qry"),
                                CanNew = right.Field<decimal>("can_new"),
                                CanEdit = right.Field<decimal>("can_edt"),
                                CanDel = right.Field<decimal>("can_del")
                            };

        var relatedRight = relatedRights.FirstOrDefault();

        ...

    }

    当实际某 right.Field<string>("module_code") 为 null 时, 该 Code 会产生运行时错误. 故 where 子句需改成如下:

    where (right.Field<string>("module_code") == null || (right.Field<string>("module_code") ?? String.Empty).Equals(page.Module)) &&
          (right.Field<string>("function_group_code") == null || (right.Field<string>("function_group_code") ?? String.Empty).Equals(page.Group)) &&
          (right.Field<string>("function_key") == null || (right.Field<string>("function_key") ?? String.Empty).Equals(page.Member))

    令外, LINQ 中已把原 DBNull 转成 DotNet 的 null, 所以不用 Convert.IsDBNull(right.Field<string>("module_code")). 

     

  • UpdatePanel & UpdateProgress & Download File 的相关问题

          程序中有时会因要执行业务比较复杂, 运行时间长的一系统操作, 需要显示如 "执行中 .." 等用户友好提示,  可用ASP.NET AJAX 的 UpdatePanel & UpdateProgress 控件完成. 但若执行结果需要下载文件时, 则会出现执行完成但文件却下载不了的情况. 因为 UpdateProgress 是异步进行中.  解决方法可以建立一个 'DownloadFile.aspx' 页面作辅助, 请见如下主要代码片断  

    -------  Data.aspx ----------- 

    <asp:ScriptManager ID="ScriptManager" runat="server" /> 

    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate> 

                   <asp:UpdateProgress ID="UpdateProgress1" runat="server" DisplayAfter="2" DynamicLayout="true">
                                <ProgressTemplate>
                                    <img src='<%=CommonUrl.Images %>wait_clock.gif' />
                                    <span id='spanProgressNote'></span>
                                </ProgressTemplate>
                  </asp:UpdateProgress>

             </ContentTemplate>
    </asp:UpdatePanel>  

     <script language="javascript">

            Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(initializeRequestHandler);

            function initializeRequestHandler(sender, e) {
                var postBackElementId = e.get_postBackElement().id;

                switch (postBackElementId) {
                    case '<%= btnSearch.ClientID %>':
                        spanProgressNote.innerHTML = 'Loading ..';
                        break;

                    case '<%= imgExport.ClientID %>':
                        spanProgressNote.innerHTML = 'Exporting ..';
                        break;
                  }                      

             }       

    </script>

    -------  Data.cs ----------- 

    protected void imgExport_Click(object sender, ImageClickEventArgs e)
     {
            ...

           // SimpleWebUtils.DownloadFile(Response, filePath, Path.GetFileName(filePath));     // 不能直接在当前页下载文件 

            ScriptManager.RegisterStartupScript(this, this.GetType(), "RegisterStartupScript"
                                                    , String.Format("window.navigate('../Control/'DownloadFile.aspx?path={0}');", Server.UrlEncode(filePath)), true);


            //ScriptManager.RegisterStartupScript(this, this.GetType(), "RegisterStartupScript"
            //                                       , String.Format("window.navigate('{0}');", fileUrl), true);      // 用这种方式可以不用 ''DownloadFile.aspx' 页面但下载文件直接打开是用网页方式而不是实际文件方式 (如: Excel)

     }

    -------  DownloadFile.cs -----------

     protected void Page_Load(object sender, EventArgs e)
     {
            string path = Request.QueryString["path"];
            SimpleWebUtils.DownloadFile(Response, path, Path.GetFileName(path));
            ScriptManager.RegisterStartupScript(this, this.GetType(), "RegisterStartupScript", "window.close();", true);                                                
     }

  • 当心 Oracle Procedure 中的 IN 参数也会被你无意中修改了它的值

    在 Oracle Procedure 中使用 IN 参数似乎很好地对该参数起到保护作用, 其实不然! 请看如下 Code

    -- Spec Part 

    PACKAGE TEST
    IS
       g1   NUMBER;

       PROCEDURE proc1;

       PROCEDURE proc2 (p1 IN NUMBER);
    END;        

    -- Body Part

     PACKAGE BODY TEST
    IS
       PROCEDURE proc1
       IS
       BEGIN
          g1 := 1;
          proc2 (g1);
          DBMS_OUTPUT.put_line ('g1=' || g1);
       END;

       PROCEDURE proc2 (p1 IN NUMBER)
       IS
          c1   CONSTANT NUMBER := p1;
       BEGIN
          --p1 := p1 + 1;  这句是编译不通过的, 似乎已对 IN 参数保护了.
          DBMS_OUTPUT.put_line ('p1=' || p1);
          DBMS_OUTPUT.put_line ('c1=' || c1);
          g1 := g1 + 1;  -- 这句间接地改变了 IN 参数值!
          DBMS_OUTPUT.put_line ('after p1=' || p1);   -- 已经被改了!
          DBMS_OUTPUT.put_line ('after c1=' || c1); 
       END;
    END;

    运行 proc1 后输出:

    p1=1
    c1=1
    after p1=2
    after c1=1
    g1=2

    总结:

    1) 若要确保过程内任意地方都能访问到 IN 参数的初始值, 需要使用附加常量

    2) 尽可能少用全局变量 (风险很大)

    3) 其它非 Oracle 数据库也可能出现该情况.

     

    Posted Oct 08 2008, 03:29 PM by wego with no comments
    Filed under:
  • ASP.NET 自定义控件之解析标签内属性内嵌

    实现如下:

    <smic:Note runat=server ID='NoteInfo'>
         <b>Development Tools</b>
         <br />
         C#, JAVA, PHP
    </smic:Note>

    代码片段:

    [ToolboxBitmap(typeof(Label))]
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [ToolboxData("<{0}:Note runat=\"server\"></{0}:Note>"), DefaultProperty("Message"), ParseChildren(false), Designer("System.Web.UI.Design.WebControls.LabelDesigner, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
    [ControlBuilder(typeof(LabelControlBuilder)), ControlValueProperty("Message"), DataBindingHandler("System.Web.UI.Design.TextDataBindingHandler, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
    public class Note : WebControl
    {

            [PersistenceMode(PersistenceMode.InnerDefaultProperty), Bindable(true), Localizable(true)]       
            public string Message
            {
                get { return ViewState["Message"] as string; }
                set { ViewState["Message"] = value; }
            }

            protected override void AddParsedSubObject(object obj)        // 必须 Override WebControl 的 AddParsedSubObject 方法
            {
                if (this.HasControls())
                {
                    base.AddParsedSubObject(obj);
                }
                else if (obj is LiteralControl)
                {
                    this.Message = ((LiteralControl)obj).Text;
                }
                else
                {
                    string message = this.Message;
                    if (message.Length != 0)
                    {
                        this.Message = string.Empty;
                        base.AddParsedSubObject(new LiteralControl(message));
                    }
                    base.AddParsedSubObject(obj);
                }
            }

            protected override void Render(HtmlTextWriter output)
            {
                output.AddAttribute("class", "container");
                output.RenderBeginTag(HtmlTextWriterTag.Div);
                output.Write(this.Message);
                output.RenderEndTag();            
            }

    }

     

     

     

     

  • Lambda 不支持 yield

    今天将 Lambda 与 yield 结合起来使用, 结果编译不通过. (如下)

    public IEnumerable<int> DeletedIDs
    {
        get
        {
           
            var deletedIDs = new int[]{123, 456};

            Array.ForEach(deletedIDs, i => (yield return i));

        }
    }

    要写成如下:

    public IEnumerable<int> DeletedIDs
    {
        get
        {
            
           var deletedIDs = new int[]{123, 456};

           foreach (int deletedID in deletedIDs)
               yield return deletedID;
        }
    }

  • Oracle 处理数据高并发响应

    某些系统可能对系统应付数据高并发的响应要求比较高 (如: 飞行航空订票系统). 本文采用 Oracle 的 lock table 解决之.

    Procedure 中关键 code 如下:

    -------------------------------------------------------------

    lock table tb_book_ticket in share mode;

    ...

    insert into tb_book_ticket (id, scheduled_flight, ticket_no, book_time) values (ID.nextval, v_scheduled_flight, v_ticket_no, SYSDATE);    -- mark1

    ...    -- mark2

    select count(*) into v_book_count from tb_book_ticket where scheduled_flight = v_scheduled_flight;    -- mark3

    if v_book_count <= v_book_max_count then

        v_result := 'book successfully';

        commit;

    else

        v_result := 'book unsuccessfully';

        rollback;

    end if;

    return v_result;

    -------------------------------------------------------------

    以上有2个地方需注意:

    1) 采用 lock table table_name in share mode 方式而非 lock table table_name in exclusive mode 提高并发性处理, 尽量减少不必要执行等待.

    2) 按常规思路, 可能会将 mark1 与 mark3 顺序交换, 但此举不行

    -------------------------------------------------------------

    select count(*) into v_book_count from tb_book_ticket where scheduled_flight = v_scheduled_flight;    -- mark3

    ...    -- mark2

    if v_book_count < v_book_max_count then

        insert into tb_book_ticket (id, scheduled_flight, ticket_no, book_time) values (ID.nextval, v_scheduled_flight, v_ticket_no, SYSDATE);    -- mark1

         -- mark4

        commit;

    end if;

    -------------------------------------------------------------

    试想若 userA 执行到  mark4, 而同时 userB 执行到  mark3, 就可能会出现由于 userA  尚未commit 而导致 userB 订到机票但实际没有座位的情况.

    若确希望使用常规逻辑来实现, 需要用到 Oracle 的表的读锁功能 (精细访问策略), 而此非本文范围, 且其对于SYS用户无效.

     

    Posted Jul 03 2008, 09:00 AM by wego with no comments
    Filed under:
More Posts Next page »
Copyright SDT, 2006-2009. All rights reserved.