顯示具有 SAP 標籤的文章。 顯示所有文章
顯示具有 SAP 標籤的文章。 顯示所有文章

2011年10月26日 星期三

SAP: Destination configuration already initialized - Part II

這個範例主要用來解決兩件事情:

1. 避免 Destination configuration already initialized 錯誤
2. 展示如何把 IRfcTable 當作呼叫 RFC 的輸入參數

歷經浪費非常多天的 try and error,有需要的請服用。

public IRfcTable TEST_B0001(string VBELN, string POSNR, string CHARG, string LFIMG)
{
        IRfcTable IRetTable = null;
        HLBackendConfig hlConfig = new HLBackendConfig();
        
        bool bRegistered = false;
        
        try
        {
            RfcDestinationManager.RegisterDestinationConfiguration(hlConfig);
            RfcDestination rfcDest = RfcDestinationManager.GetDestination("INIT_SAP");
            RfcRepository rfcRepo = rfcDest.Repository;
            IRfcFunction IReader = rfcRepo.CreateFunction("TEST_B0001");
        
            bRegistered = true;
        
            IRfcTable Itab = IReader.GetTable("IT_INPUT");

            Itab.Append();
        
            Itab[0].SetValue("VBELN", VBELN);
            Itab[0].SetValue("POSNR", POSNR);
            Itab[0].SetValue("CHARG", CHARG);
            Itab[0].SetValue("LFIMG", LFIMG);
        
            IReader.SetValue("IT_INPUT", Itab);
        
            IReader.Invoke(rfcDest);
            
            IRetTable = IReader.GetTable("ET_RETURN");
        }
        catch (Exception ex)
            {
            throw ex;
        }
        finally
        {
            if (bRegistered)
                RfcDestinationManager.UnregisterDestinationConfiguration(hlConfig);
        }
        
        return IRetTable;
}

2011年10月18日 星期二

SAP 與 .NET 整合心得

經過了數個月專案的奮鬥,總結出一些心得列出如下:

1. .NET 與 SAP 函數溝通透過 DataTable <--> IRfcTable 兩者間的互相轉換最方便
2. .NET Web Service 與 SAP 函數溝通 DataTable <--> DataSet <--> IRfcTable 三者間的互轉最方便
3. 設計溝通介面函數時使用以上兩個原則,實作系統的速度很快而且具有快速擴充的彈性
4. SAP Connector 本身的問題很多,每次呼叫 RFC 時,Init / Uninit 要寫在同一段處理函數中
5. SAP Connector 也是透過 .NET 撰寫而成,第一次實機部署安裝之後,要記得重新啟動作業系統
7. 由於第六點原因,RFC 呼叫非常耗資源及時間,可以的話盡量將資料另外儲存在自己系統的資料庫中,資料庫沒資料才考慮從 RFC 呼叫取得
8. 多多利用 SAP Logon 等 GUI 工具,文件上寫的往往都不是真的,看 code 才是王道
9. 一有功能要馬上讓使用者試用,並且立即強迫畫押簽收,以免日後發生糾紛時沒有防禦的系統

2011年8月18日 星期四

SAP: Destination configuration already initialized

本篇文章的內容無法解決此問題,請參考此篇才有解答。


SAP Connector 要使用時要進行初始:

// 1.初始 SAP 連接介面              
RfcDestinationManager.RegisterDestinationConfiguration(new MyBackendConfig());


這個方法在單執行緒中沒有太大的問題,但是搬到 ASP.NET (web)的世界中就麻煩了,
原因是因為這個方法 RegisterDestinationConfiguration 是靜態方法,

在多執行緒的世界中,程式一不小心就會造成執行階段報錯:Destination configuration already initialized
所以要透過 UnregisterDestinationConfiguration 來進行關閉,
但是要呼叫之前一定要確認傳進去的組態參數是同一個,透過此下的程式碼就可以"沒效率"的解決此問題。

為何說沒效率呢?因為每次呼叫RFC 時,都要 Init & Uninit,浪費資源也浪費時間,
但是目前可能這是唯一的解法,若之後有更好的解決方法再另外補充。

public class HuLane
{
RfcDestination m_rfcDest = null;
RfcRepository m_rfcRepo = null;
HLBackendConfig m_Config = null;

public bool InitSAP()
{
bool bRet = false;
try
{
m_Config = new HLBackendConfig();

RfcDestinationManager.RegisterDestinationConfiguration(m_Config);
m_rfcDest = RfcDestinationManager.GetDestination("JTC_000");
m_rfcRepo = m_rfcDest.Repository;

bRet = true;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(
String.Format("InitSAP(): {0}", ex.Message));
}

return bRet;
}

public bool UninitSAP()
{
bool bRet = false;
try
{
RfcDestinationManager.UnregisterDestinationConfiguration(m_Config);
bRet = true;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(
String.Format("UninitSAP(): {0}", ex.Message));
}

return bRet;
}
}



參考資料來源連結

IRfcTable、DataTable、DataSet 資料轉換模組

撰寫 SAP RFC 應用時,一定會遇到資料轉換的問題,
撰寫成公用模組,可以很方便的叫用。



public static DataTable GetDataTableFromRfcTable(IRfcTable rfcTable)
{
DataTable dtRet = new DataTable();

for (int liElement = 0; liElement < rfcTable.ElementCount; liElement++)
{
RfcElementMetadata rfcEMD = rfcTable.GetElementMetadata(liElement);
dtRet.Columns.Add(rfcEMD.Name);
}

foreach (IRfcStructure row in rfcTable)
{
DataRow dr = dtRet.NewRow();

for (int liElement = 0; liElement < rfcTable.ElementCount; liElement++)
{
RfcElementMetadata rfcEMD = rfcTable.GetElementMetadata(liElement);
dr[rfcEMD.Name] = row.GetString(rfcEMD.Name);
}

dtRet.Rows.Add(dr);
}

return dtRet;
}

public static DataTable[] GetDataTablesFromRfcTables(IRfcTable[] rfcTables)
{
DataTable[] dtRetTables = null;

if (rfcTables.Length > 0)
{
dtRetTables = new DataTable[rfcTables.Length];

for (int i = 0; i < rfcTables.Length; i++)
{
dtRetTables[i] = GetDataTableFromRfcTable(rfcTables[i]);
}
}

return dtRetTables;
}

public static DataTable[] GetDataTablesFromDataSet(DataSet ds)
{
DataTable[] dtRetTables = new DataTable[ds.Tables.Count];

for (int i = 0; i < ds.Tables.Count; i++)
dtRetTables[i] = ds.Tables[i];

return dtRetTables;
}

public static DataSet GetDataSetFromDataTables(DataTable[] dtTables)
{
DataSet dsRet = new DataSet();

for (int i = 0; i < dtTables.Length; i++)
{
dsRet.Tables.Add(dtTables[i]);
}

return dsRet;
}

2011年8月15日 星期一

呼叫 SAP RFC - 傳入 IRfcTable

由呼叫 SAP RFC得知,呼叫 RFC 和一般寫程式的概念大同小異,
相關的元素可是一樣都沒有少(輸出入參數、宣告呼叫方法),
接下來講的是如何把 IRfcTable 當作輸入的參數傳入 SAP RFC 中,

要傳入 IRfcTable 到 SAP RFC 中,有分為以下幾個步驟:

1.初始 SAP 連接介面
2.設定 SAP 連接參數
3.初始 SAP RFC 來源
4.建立 IRfcTable 結構
5.設定 IRfcTalbe 參數
6.呼叫 SAP RFC 返回值
7.取得 SAP RFC 返回值

實際上透過程式碼的實作如下:

using SAP.Middleware.Connector;


using System;
using System.Linq;
using System.Text;
using SAP.Middleware.Connector;


namespace csSAP
{
class Program
{
static void Main(string[] args)
{
// 1.初始 SAP 連接介面
RfcDestinationManager.RegisterDestinationConfiguration(new MyBackendConfig());

//2.設定 SAP 連接參數
RfcDestination prd = RfcDestinationManager.GetDestination("PRD_000");
try
{

//3.初始 SAP RFC 來源
RfcRepository repo = prd.Repository;
IRfcFunction IFunciton = repo.CreateFunction("JTC_BR0002");

//4.建立 IRfcTable 結構
IFunciton.Invoke(prd);
IRfcTable Itab = IFunciton.GetTable("IT_JTC"); // 取得 SAP 中定義的表格結構

//5.設定 IRfcTalbe 參數
Itab.Append(); // 加入一筆record 到表格中
Itab[0].SetValue("FIELD1", "1000");
Itab[0].SetValue("FIELD2", "1001");

Itab.Append(); // 再加入一筆record 到表格中
Itab[1].SetValue("FIELD1", "1001");
Itab[1].SetValue("FIELD2", "1002");

//6.呼叫 SAP RFC 返回值
IFunciton.SetValue("IT_JTC", Itab);
IFunciton.Invoke(prd);

//7.取得 SAP RFC 返回值
IRfcTable IRetTab = IFunciton.GetTable("ET_RETURN");

}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}

public class MyBackendConfig : IDestinationConfiguration
{
public RfcConfigParameters GetParameters(String destinationName)
{
if ("JTC_000".Equals(destinationName))
{
RfcConfigParameters parms = new RfcConfigParameters();
parms.Add(RfcConfigParameters.AppServerHost, "192.168.1.1");
parms.Add(RfcConfigParameters.SystemNumber, "00");
parms.Add(RfcConfigParameters.User, "UserName");
parms.Add(RfcConfigParameters.Password, "UserPassword");
parms.Add(RfcConfigParameters.Client, "100");
parms.Add(RfcConfigParameters.PoolSize, "5");
parms.Add(RfcConfigParameters.MaxPoolSize, "10");
parms.Add(RfcConfigParameters.IdleTimeout, "600");
parms.Add(RfcConfigParameters.SystemID, "JTC");
return parms;
}
else return null;
}
// The following two are not used in this example:
public bool ChangeEventsSupported()
{
return false;
}
public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
}
}

2011年8月12日 星期五

呼叫 SAP RFC

要呼叫 SAP RFC 取得值,有分為以下幾個步驟:

1.初始 SAP 連接介面
2.設定 SAP 連接參數
3.初始 SAP RFC 來源
4.呼叫 SAP RFC 函數
5.取得 SAP RFC 返回值

實際上透過程式碼的實作如下:

using SAP.Middleware.Connector;


using System;
using System.Linq;
using System.Text;
using SAP.Middleware.Connector;

namespace csSAP
{
class Program
{
static void Main(string[] args)
{
// 1.初始 SAP 連接介面
RfcDestinationManager.RegisterDestinationConfiguration(new MyBackendConfig());

//2.設定 SAP 連接參數
RfcDestination prd = RfcDestinationManager.GetDestination("JTC_000");
try
{

//3.初始 SAP RFC 來源
RfcRepository repo = prd.Repository;

//4.呼叫 SAP RFC 函數
IRfcFunction stfcReader = repo.CreateFunction("BAPI_COMPANY_GETDETAIL");

// 設定要傳入的參數
stfcReader.SetValue("COMPANYID", "1010");

stfcReader.Invoke(prd);

//5.取得 SAP RFC 返回值
string strRet = stfcReader.GetStructure("COMPANY_DETAIL").GetString("NAME1");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}

public class MyBackendConfig : IDestinationConfiguration
{
public RfcConfigParameters GetParameters(String destinationName)
{
if ("JTC_000".Equals(destinationName))
{
RfcConfigParameters parms = new RfcConfigParameters();
parms.Add(RfcConfigParameters.AppServerHost, "192.168.1.1");
parms.Add(RfcConfigParameters.SystemNumber, "00");
parms.Add(RfcConfigParameters.User, "UserName");
parms.Add(RfcConfigParameters.Password, "UserPassword");
parms.Add(RfcConfigParameters.Client, "100");
parms.Add(RfcConfigParameters.PoolSize, "5");
parms.Add(RfcConfigParameters.MaxPoolSize, "10");
parms.Add(RfcConfigParameters.IdleTimeout, "600");
parms.Add(RfcConfigParameters.SystemID, "JTC");
return parms;
}
else return null;
}
// The following two are not used in this example:
public bool ChangeEventsSupported()
{
return false;
}
public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
}
}

2011年8月7日 星期日

.NET with SAP RFC

SAP 開發者資源


早期的 SAP Connector 2.0 只有支援 .NET Framework 1.1,
需要安裝的是 sapnco30,安裝完畢之後,在 IDE 中手動加入參考,
瀏覽至安裝的資料夾中,引用 sapnco.dll 及 sapnco_utils.dll。

主要的功能都在下面的 namespace 中:
using SAP.Middleware.Connector;