本篇文章中我主要讲的是.NET如何通过RFC从SAP中读取数据。为了功能的可复用性,我将调用RFC的代码从业务层中分离出来单独建立在一个namespace中。
当然除了需要我们自己编写代码以外,还需要引用SAP提供的程序集文件(sapnco.dll、sapnco_utils.dll),在代码文件需要引用相应的命名空间(using SAP.Middleware.Connector;)。
我在这个namespace中建立了三个类来实现这个功能,一个配置类(RfcDestinationConfig)、一个参数类(RfcParam)、一个主体功能类(RfcManager)。
- RfcDestinationConfig
我们需要一个类来实现SAP的连接配置工作,就如同为数据连接层建立一个数据库配置类一样重要。
1 public class RfcDestinationConfig : IDestinationConfiguration 2 { 3 #region 事件 4 /// <summary> 5 /// 配置变更事件 6 /// </summary> 7 public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged; 8 /// <summary> 9 /// 默认接收器名称10 /// </summary>11 public static readonly string DefaultDesName = "destination";12 #endregion13 14 #region 方法15 /// <summary>16 /// 配置变更事件触发时,暂时无用17 /// </summary>18 /// <param name="destinationName"></param>19 /// <param name="args"></param>20 public void OnConfigurationChanged(string destinationName, RfcConfigurationEventArgs args)21 {22 if (ConfigurationChanged != null)23 {24 ConfigurationChanged(destinationName, args);25 }26 }27 28 /// <summary>29 /// 获取SAP配置参数30 /// </summary>31 /// <param name="destinationName"></param>32 /// <returns></returns>33 public RfcConfigParameters GetParameters(string destinationName)34 {35 if (destinationName == DefaultDesName)36 {37 RfcConfigParameters parms = new RfcConfigParameters();38 parms.Add(RfcConfigParameters.AppServerHost,ConfigManager.GetAppSettings("SAPApplicationServer").Trim()); //SAP主机IP39 parms.Add(RfcConfigParameters.SystemNumber, ConfigManager.GetAppSettings("SAPSystemNumber").Trim()); //SAP实例40 parms.Add(RfcConfigParameters.User, ConfigManager.GetAppSettings("SAPUser").Trim()); //用户名41 parms.Add(RfcConfigParameters.Password,ConfigManager.GetAppSettings("SAPPwd").Trim()); //密码42 parms.Add(RfcConfigParameters.Client, ConfigManager.GetAppSettings("SAPClient").Trim()); // Client43 parms.Add(RfcConfigParameters.Language,ConfigManager.GetAppSettings("SAPLanguage").Trim()); //登陆语言44 return parms;45 }46 else47 {48 return null;49 }50 }51 52 /// <summary>53 /// 变更事件方法,暂时无用54 /// </summary>55 /// <returns>true</returns>56 public bool ChangeEventsSupported()57 {58 return true;59 }60 #endregion61 }
- RfcParam
想要从SAP中读取数据,就必须将查询条件作为参数传递给RFC。另外为了返回的结果具有通用性,我使用DataTable作为返回结果的类型,然后考虑到不同条件下列是不同的,我又将列也参数化,最终我将输入参数和输出参数都封装在一个参数类之中。
1 public class RfcParam 2 { 3 /// <summary> 4 /// 初始化 5 /// </summary> 6 public RfcParam() 7 { 8 CoulmnNames = new List<string>(); 9 Param = new Dictionary<string, object>();10 }11 /// <summary>12 /// RFC方法名称13 /// </summary>14 public string RfcName { get; set; }15 /// <summary>16 /// RFC表名17 /// </summary>18 public string TableName { get; set; }19 /// <summary>20 /// 数据表各列的列名21 /// </summary>22 public List<string> CoulmnNames { get; set; }23 /// <summary>24 /// RFC执行参数25 /// </summary>26 public Dictionary<string, object> Param { get; set; }27 }
- RfcManager
该主角登场了,读取数据的功能正是业务层真正想要的东西。
方法ExecRfc首先将输出参数转换成一个真正可用的新的DataTable,然后将输入参数传递给SAP执行相关的RFC功能并返回IRfcTable(SAP定义的一种接口),最后再将IRfcTable转换成我们自定义的DataTable。
1 public class RfcManager 2 { 3 #region 属性字段 4 /// <summary> 5 /// 接收器 6 /// </summary> 7 public RfcDestination Prd { get; set; } 8 /// <summary> 9 /// 数据仓库10 /// </summary>11 public RfcRepository Repo { get; set; }12 #endregion13 14 #region 构造函数15 /// <summary>16 /// 初始化17 /// </summary>18 public RfcManager()19 {20 //初始化RFC接收器21 //配置接收器22 IDestinationConfiguration IDC = new RfcDestinationConfig();23 //注册24 RfcDestinationManager.RegisterDestinationConfiguration(IDC);25 //获取RFC接收器26 this.Prd = RfcDestinationManager.GetDestination(RfcDestinationConfig.DefaultDesName);27 this.Repo = this.Prd.Repository;28 //注销29 RfcDestinationManager.UnregisterDestinationConfiguration(IDC); 30 }31 #endregion32 33 #region 方法34 /// <summary>35 /// 执行RFC获取数据表36 /// </summary>37 /// <param name="rfcname">rfc方法名称</param>38 /// <param name="tablename">rfc表名</param>39 /// <param name="columnnames">数据表列名列表</param>40 /// <param name="param">rfc执行参数</param>41 /// <returns>数据表</returns>42 public DataTable ExecRfc(string rfcname, string tablename, List<string> columnnames, Dictionary<string, object> param)43 {44 DataTable dt = new DataTable();45 46 if (columnnames != null && columnnames.Count > 0)47 {48 //配置datatable49 dt.Columns.Clear();50 foreach (string cname in columnnames)51 {52 dt.Columns.Add(cname, typeof(string));53 }54 dt.AcceptChanges();55 56 //从SAP那获取数据表57 if (!string.IsNullOrEmpty(rfcname) && param != null && param.Count > 0)58 {59 IRfcFunction rfc = this.Repo.CreateFunction(rfcname);60 foreach (KeyValuePair<string, object> kv in param)61 {62 rfc.SetValue(kv.Key, kv.Value);63 }64 rfc.Invoke(this.Prd);65 IRfcTable iTable = rfc.GetTable(tablename);66 if (iTable.Count > 0)67 {68 for (int i = 0; i < iTable.RowCount; i++)69 {70 iTable.CurrentIndex = i;71 DataRow oNewRow = dt.NewRow();72 foreach (string cname in columnnames)73 {74 oNewRow[cname] = iTable.GetString(cname).ToString();75 }76 dt.Rows.Add(oNewRow);77 }78 }79 }80 }81 82 return dt;83 }84 #endregion85 }
- 2楼HHSYHLL
- 我一开始看别人用VB写的连接类,看着不习惯,就想用C#写,结果到这句就不行了,MyConnection.CodePage = quot;8300quot;,c#的SDK没有这个属性,不用这个属性,拉下来的数据就乱码,服务器数据是繁体#183;#183;#183;
- 1楼阿水
- 顶一记,以前做过类似接口用C#不行用VB行,好像是涉及到强类型的问题。
- Re: 浪花一朵朵
- @阿水,早期版本确实如此,现在用C#可以了