2013年9月24日 星期二

超義科技 Power Process BPM Suite 6.0

最近工作上用到的開發簽核輔助工具,包括流程引擎、流程設計工具、流程管理平臺、使用者申請及簽核的BPM工作區,還有整合其他系統所需的整合管理伺服器等。

該套系統主要的概念是透過組織設定、流程設計抓取組織的設定,然後進行簽核。我沒有使用他所提供的簽核頁面,而是自己開發表單(ASP.Net)透過該公司提供的Web Service、再串流程設計工具,進行開發。

我的流程很簡單,但執行起來不簡單,細節很多:




功能增改申請單簽核流程













期間碰到不少問題,目前終於克服,進入最後細節修正階段。

  1. 開發的API文件相當不齊全,很多都只能用猜測的或問廠商窗口。
  2. 流程設計、管理平台、Web Service提供的模組不一致,導致debug困難。
  3. 流程設計中的程式撰寫,使用Javascript及Java混合體,很難判斷該用什麼語言,容易混淆,而且沒有debug工具,語法檢測沒什麼效用,不會告知程式有沒有錯誤。
  4. Web Service的建置有問題,廠商處理速度很慢,還一直覺得是我引用錯誤的問題。
  5. 呼叫web service錯誤產生的錯誤訊息不夠明確,很難判斷是程式錯還是web service錯。
  6. 子流程的部分,因為我要傳出所有分析人,其中可能有加簽者,不太好解。
  7. 組織沒有設定部門主管,只有用角色層級去做區分,故簽核人員可能無法直接選取,造成比較大的麻煩(這是本公司的問題)。
  8. 整體而言,套用該套工具,要對提供的模組夠熟悉才能節省時間,但是因為教育訓練不夠深入,再加上文件相當不齊全,很多問題都是實作時產生,進入的門檻相對高。

補充:
  1. 20131007,因為是呼叫既定的web service,無法設定tracsaction的起始和結束點,可能我系統已經存檔了,但是流程引擎exception會難以處理。

2013年7月11日 星期四

DataSet建立DataRelation

DataRelation的基本觀念在於,DataTable與DataTable之間,如果要建立關聯性,必須存在於同一個DataSet,再將DataTable加入分為Parent及Child的概念,建立關聯性程式碼如下:
 
private void CreateRelation() 
{
    // 建立Parent的關聯欄位
    DataColumn parentColumn = DataSet1.Tables["Customers"].Columns["CustID"];
    // 建立Chlid的關聯欄位
    DataColumn childColumn = DataSet1.Tables["Orders"].Columns["CustID"];
    // 建立關連性
    DataRelation rel = new DataRelation("CusOrd", parentColumn, childColumn);
    // 將關聯性置於DataSet中
    DataSet1.Relations.Add(rel);
}
 
讀取資料的方是如下:
 
//由Parent角度讀取Child資料
for(DataRow row in DataSet1.Tables["Customers"].Rows)
{
    //多筆->回傳陣列
      DataRow[] dr = row.GetChildRows("CustomersOrders");
      for(int i=0; i< dr.count; i++)
      {
            //process dr (即關聯中的Orders)資料
      }
}
 
//由Child角度讀取Parent資料
foreach(DataRow row in DataSet1.Tables["Orders"].Rows)
{
    //單筆
    DataRow parentRow = row.GetParentRow("CustomersOrders");
}


2013年6月19日 星期三

Crystal Report無法載入檔案或組件System.Web.Extensions

Crystal Report在執行時期出現以下錯誤:

無法載入檔案或組件 'System.Web.Extensions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' 或其相依性的其中之一。 找到的組件資訊清單定義與組件參考不符。 (發生例外狀況於 HRESULT: 0x80131040)

 找了很久以為是參考的dll檔版本錯誤,結果發現dll並沒有直接相關,後來找到原因是web.config的<runtime>(組件繫結)設定要修改,由原本的1.1.0.0改為2.0.0.0,如下:

 <!--組件繫結-->
<runtime>
    <!--組件版本重新導向和組件位置-->
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <!--封裝組件的繫結原則和組件位置-->
        <dependentAssembly>
            <!--包含有關組件的識別資訊-->
            <assemblyIdentity name="System.Web.Extensions" publicKeyToken="31bf3856ad364e35"/>
            <!--將一個組件版本重新導向為另一個版本-->
            <bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="3.5.0.0"/>
        </dependentAssembly>
    </assemblyBinding>
</runtime>

2013年5月24日 星期五

如何讓TextBox的MaxLength對中文字元的長度計算也起作用?

在TextBox元件中設定的MaxLength,測試結果是對字數的限制,但是對應到資料庫的欄位長度就不符合,若輸入中文(double bytes)可能常常造成Oracle吐錯誤訊息「ORA-01401: Inserted value too large for column」,提供參考方向: 自訂控制項。

1. 繼承原有的TextBox,並修改MaxLength的實作AddAttributesToRender

using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
using System;

namespace CONTROL
{
  [ToolboxData("<{0}:TxtCommon runat=\"server\"></{0}:TxtCommon>")]//指定當自訂控制項從工具箱拖曳出來時,為此自訂控制項產生的預設標記。
  public class TxtCommon : TextBox
  {
    protected override void OnInit(System.EventArgs e)
    {
      base.OnInit(e);

      // 開始註冊JS在頁面上, 如果已經註冊便跳過
      Type type = this.GetType();
      string strJsLoc = "";
      string key = "CONTROL.JavaScript.Utility.js";
      if (!Page.ClientScript.IsClientScriptIncludeRegistered(key))
      {
        strJsLoc = this.Page.ClientScript.GetWebResourceUrl(type, key);
        this.Page.ClientScript.RegisterClientScriptInclude(key, strJsLoc);
      }
    }
   
    /// <summary>
    /// 寫入控制項額外屬性
    /// </summary>
    /// <param name="writer"></param>

    protected override void AddAttributesToRender(System.Web.UI.HtmlTextWriter writer)
    {
      base.AddAttributesToRender(writer);
      string strOnBlur = "";
      
      if (this.MaxLength != 0)
      {
          strOnBlur += string.Format("ChkBLen(this,{0});", this.MaxLength.ToString());
      }

      if (strOnBlur != "")
      {
          writer.AddAttribute("onblur", strOnBlur);
      }
    }
  }
}


2. 在註冊的JS中實作ChkBLen:

 //檢核長度
function ChkBLen(obj,intMaxByte)
{
    obj.value=obj.value;
    if(obj.value.Blength()>intMaxByte)
    {
        alert("超過最大長度限制:"+intMaxByte);
        while(obj.value.Blength()>intMaxByte)
        {
            obj.value=obj.value.substr(0,obj.value.length-1)
        }
        obj.focus();
    }
  
}

//傳回字串的byte長度
String.prototype.Blength = function() {
    var arr = this.match(/[^\x00-\xff]/ig);
    return  arr == null ? this.length : this.length + arr.length;



如此一來,在新增 TxtCommon自訂控制項時,若設定MaxLength,則在onblur事件發生時,就會自動呼叫ChkBLen,自動截斷多於的長度。

2013年5月9日 星期四

字串處理,如何得知在substring時,是否拆到中文字?



使用length()取string長度,會遇到中文字長度只有回傳1,所以如果是要算真正的長度,則需要將字串轉為byte陣列,但在做substring時,可能會截到中文字,故可先作byte是否為中文的判斷後,再做位置調整。主要是利用isChineseCharacter類別中所寫中文內碼範圍判斷,範例如下:

public class getByteTest {
    /**
     * 測試中文拆解,isChineseCharacter可判斷是否為中文字
     * @param args
     * @throws Throwable
     */
    public static void main(String[] args) throws Throwable{
        // TODO Auto-generated method stub
        String aaa="ABC小妹妹test123";
        byte[] dataByte = null;
         try {
             dataByte = aaa.getBytes("Big5");
         } catch (Throwable e) {
             System.out.println("傳入字串轉成Big5編碼的byte array 失敗 ");
         }
         String tmp = new String(dataByte, 0, dataByte.length, "Big5");
         boolean isChinese;
         for(int i=0; i<tmp.length();i++){
             isChinese=isChineseCharacter(tmp.charAt(i));
             System.out.println(tmp.charAt(i)+" : "+isChinese);
        }
    }
    public static boolean isChineseCharacter (char c){  
        return (19968<=(int)c)&&((int)c<=171941);
    }
}

2013年5月8日 星期三

利用SQL*Plus執行SQL->匯出成txt檔->FTP上傳

今天接到的需求是定期要把資料庫的資料格式化後,匯出成txt檔,並上傳到指定FTP。

 1.製作讓批次檔呼叫的sql檔
[spool.sql]
set newpage none --換頁空幾行

set space 0 --欄位間空幾個space
set colsep '|' --分隔符號,需搭配space <> 0時使用
set heading off --是否顯示欄位表頭
set pagesize 30 --一頁行數,設定0時是沒有分頁
set trimspool on --刪除行尾空白
set linesize 2000 --每行長度(超過自動斷行)
set feedback off --是否回寫總筆數
set termout off --是否將執行的command顯示在畫面上

spool C:\test.txt  --指定匯出檔名路徑
select 'test' from dual;
spool off
exit


2.製作file.bat批次檔
連線到sqlplus並執行上述sql,接著上傳ftp
[file.bat]
sqlplus ID/PassWord@DB @spool.sql

ftp 位址
id
pw
put 檔名

exit

3.排程file.bat檔,以後就可以自動執行。

ASP.NET FileUpload取不到完整路徑+遇到Postback將元件值清除問題的解法

在頁面中,任何postback都會將FileUpload元件上所選取的欄位清空,可能是資料檢核、下拉選單的change事件等等,造成使用者已經選取檔案路徑,操作其他元件卻又遺失的問題。

IE8 Security
上面關鍵字File Upload Control有說明
我試過firefox、chrome、IE8 above瀏覽器都有問題,無法用網路上說明的解法使用Fileupload.PostedFile.FileName取得檔案路徑。其他解法是說明要加入安全性網站,但我不想要做這種需要特殊設定才能執行的功能。

解決方法:使用javascript及Callback取代Postback

實作ICallbackEventHandler
用戶端回呼實作 (C#) 範例 

1.須繼承System.Web.UI.ICallbackEventHandler

public partial class ClientCallback : System.Web.UI.Page,
     System.Web.UI.ICallbackEventHandler
 
2.實作以下兩個Method

public void RaiseCallbackEvent(String eventArgument)
{
//javascript 呼叫function 
//eventArgument為javascript傳入的參數,如本範例中的sCardNO
 }
public String GetCallbackResult()
{
   //javascript可接收到的值 
 return returnValue;
}
 
3. javascript部份寫法
 
//呼叫RaiseCallbackEvent
function GetBoss1Info()
{
//傳入參數:sCardNO
//回傳接收的function
   <%= Page.ClientScript.GetCallbackEventReference(this, "sCardNO", "ProcessResult1", null) %>;
}
//GetCallbackResult回傳
function ProcessResult1(returnmessage, context)
{
  //returnmessage為回傳資料
}

C#中呼叫command console執行動作


 ProcessStartInfo 類別

ProcessStartInfo cmd = new System.Diagnostics.ProcessStartInfo("cmd.exe");
cmd.RedirectStandardInput = true;
cmd.RedirectStandardOutput = true; 
cmd.RedirectStandardError = true; 
cmd.UseShellExecute = false;


Process 類別

Process console = Process.Start(cmd);
console.StandardInput.WriteLine("D:");
console.StandardInput.WriteLine("exit");
string output = console.StandardOutput.ReadToEnd()
console.Close();