NET连接SAP系统专题:.NET调用RFC几种方式(一)
本来今天是要写一篇关于NCO3.0的东西,就是关乎.NET调用SAP的RFC的,支持VS2010和.NET 4.0等。现在网上到处都是充斥着NCO1.X和NCO2.0,需要用VS2003来使用,都是一些没什么大用的东西,连.NET 2.0的不能支持,只能算是废材一个。而且相关的资料非常的少,转来转去全部都是那么一个例子,丝毫没有直观的DEMO,更搞笑的是SAP里面的RFC是怎么建立的都没有说明,只有该死的C#代码。同时还有微软发布的Data Provider for mySAP Business Suite这个,开始用的时候觉得还挺有趣,后来试了一下很失望,抓出来的字段居然是断码,不知道是我不懂还是怎么的。同时更让人无语的是用它的话还必须要导入一个Request NO,加入一个自定义的Function才可以使用Data Provider for mySAP Business Suite,很脑残也很不靠谱。
后来发现NCO3.0也发布了,和以前的版本大不相同。后面试用了一下NCO3.0,效果很好。
公司有几只程序,是在台湾SAP端运行程序,自动下载品号等信息至厦门的SQL Server数据库供OA使用的,每次要用到的品号都需要去SAP系统运行那个程序。虽然Basis设置了4个小时执行一次,但是还是有些过慢。我就想着如果能在OA这边用NCO3.0,然后调用SAP的RFC,就自动下载所需要的品号那就非常方便了。我在030也试用了一下,很OK!
既然.NET能够和SAP互相通信,在RFC里就可以任意发挥了,想做什么就做什么。这样可以免去SAP系统的账户成本压力。
呵呵,下篇博文开始介绍一下NCO3.0这个东西。
分类: 2011-08-22 22:34 2001人阅读 (0)
何谓RFC,就是一个Function,可以被非SAP系统调用,比如VB,C#,Java等。如果我们在RFC中INCLUDE了相关的业务逻辑,那么我们就可以完全操控SAP中的业务数据了。就像在TTE里,有一只程序,前端是在OA开发,设计了相关的客户提领库存,然后还要到SAP系统中去执行程序扣减相应的库存,这样是挺费劲的,如果能够在OA中放一个按钮,点击这个按钮就自动执行了这个程序,方便省事。而这一切,可以利用C#调用RFC来实现。
要实现整个过程,则必须要现在SAP中建立好相应的RFC函数,然后用VS建立好相应的程序,写代码调用就可以了。两者关联就是使用NCO3.0这个东西了。
OK,本节主要讲在SAP中建立我们想要的RFC出来。
那么,我们假定要实现这样的一个功能:运行在SAP系统外的一个程序窗体,上面有一个下拉框和文本框。程序运行之后自动载入SAP中某个Client的品号至该下拉框,用户只要点击了这个下拉框,内容一改变,则旁边的文本框就现实该品号的品名出来。够简单吧!
首先登陆SAP,比如我们要操作的Client为888(我自己建立的,图个吉利)。运行SE37:
然后:
建立函数组,输入相关内容:
完了点击保存。
之后回到SE37,输入我们要调用的RFC函数名,比如:ZRFC_MARA_INFO 然后点击新建(右一按钮):
点击保存之后会弹出如下对话框:
点击打勾,忽略之。
主画面如下:
画面切换至 属性 页,设置该RFC为可远程调用。
因为我们到时候会传递一个品号进来读取品名,SO,在Import页面设置一个参数用来传递进来品号:
第一个复选框代表此参数可选可不选传递,第二个代表参考。在RFC里如果不打勾,保存会提示:
因为我们会要抓取品名,需要有一个栏位抛出品名出来。SO,Export页面如下:
因为我们程序一开始需要抛出所有的品号表格出来,SO,此时我们需要有一个内表专门用于抛出品号信息。Table属性页如下:
至此,所有参数设置完毕。接下去就要去代码页编写相应的代码了。
代码如下:
SELECT * INTO CORRESPONDING FIELDS OF TABLE IT_MARA FROM MARA.
SELECT SINGLE MAKTX INTO MAKTX FROM MAKT WHERE MATNR = MATNR AND SPRAS = '1'.
最后点击激活!这里的激活不是单单激活这个RFC这么简单,还有其他的项目,记得要勾选上:
至此,RFC建立完毕。
接下去就是在C#中调用它咯,下篇博文放出!
分类: 2011-08-22 22:34 2259人阅读 (0)
本文就说明在C#中如何编写代码来调用SAP中的RFC函数获取数据。(Winform32)
首先需要引用两个NCO3.0的DLL,下载地址在文后。
然后在程序代码页面引用:
using SAP.Middleware.Connector;
然后所有的代码如下:
namespace SAP_RFC
{
public partial class Form1 : Form
{
string MATNR = string.Empty;
public Form1()
{
InitializeComponent();
}
public void nco()
{
IDestinationConfiguration ID = new MyBackendConfig();
RfcDestinationManager.RegisterDestinationConfiguration(ID);
RfcDestination prd = RfcDestinationManager.GetDestination("PRD_000");
RfcDestinationManager.UnregisterDestinationConfiguration(ID);
nco(prd);
}
public void nco(RfcDestination prd)
{
RfcRepository repo = prd.Repository;
IRfcFunction companyBapi = repo.CreateFunction("ZRFC_MARA_INFO"); //调用函数名
companyBapi.SetValue("MATNR", MATNR); //设置Import的参数
companyBapi.Invoke(prd); //执行函数
IRfcTable table = companyBapi.GetTable("IT_MARA"); //获取相应的品号内表
string MAKTX = companyBapi.GetValue("MAKTX").ToString(); //获取品名
DataTable dt = new DataTable(); //新建表格
dt.Columns.Add("品号"); //表格添加一列
for (int i = 0; i < table.RowCount; i++)
{
table.CurrentIndex = i; //当前内表的索引行
DataRow dr = dt.NewRow();
dr[0] = table.GetString("MATNR"); //获取表格的某行某列的值
dt.Rows.Add(dr); //填充该表格的值
}
if (MATNR == "")
{
for (int i = 0; i < dt.Rows.Count; i++)
{
this.comboBox1.Items.Add(dt.Rows[i][0].ToString()); //填充下拉框
}
}
this.label1.Text = MAKTX; //显示品名
prd = null;
repo = null;
}
//登陆SAP前的准备工作
public class MyBackendConfig : IDestinationConfiguration
{
public RfcConfigParameters GetParameters(String destinationName)
{
if ("PRD_000".Equals(destinationName))
{
RfcConfigParameters parms = new RfcConfigParameters();
parms.Add(RfcConfigParameters.AppServerHost, "192.168.1.3"); //SAP主机IP
parms.Add(RfcConfigParameters.SystemNumber, "00"); //SAP实例
parms.Add(RfcConfigParameters.User, "MENGXIN"); //用户名
parms.Add(RfcConfigParameters.Password, "5239898"); //密码
parms.Add(RfcConfigParameters.Client, "888"); // Client
parms.Add(RfcConfigParameters.Language, "ZH"); //登陆语言
parms.Add(RfcConfigParameters.PoolSize, "5");
parms.Add(RfcConfigParameters.MaxPoolSize, "10");
parms.Add(RfcConfigParameters.IdleTimeout, "60");
return parms;
}
else return null;
}
public bool ChangeEventsSupported()
{
return false;
}
public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
}
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.Items.Clear();
nco();
comboBox1.SelectedIndex = 1;
}
//当下拉框索引变化的时候传递品号进去查询出品名出来
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
MATNR = comboBox1.Text.ToString();
nco();
}
}
}
我想这个C#代码很简单,我就不多做详细说明了。结果如下:
SAP中品号信息如下:
由此可见数据完全OK,调用成功。
程序在第一次载入的时候有点慢,在链接SAP和登陆。后续在下拉框变化的时候就立马显示出品名出来了,丝毫没有任何停顿。第二次链接SAP的时候大概是不必在登陆了,SAP系统中已有登陆信息,运行T-CODE:SM04
红色框中这两个即是我们的RFC调用所留下的登录会话。一旦我们的C#程序退出之后,这两个RFC也就退出了。
如果我们的C#程序是ASP.NET的话,页面关闭之后这个RFC登录信息都还在的。除非IIS关闭,否则只有等到SAP系统超时退出这两个登陆会话了。
DLL下载地址:
分类: 2011-08-22 22:35 1850人阅读 (0)
有的时候我们需要在RFC中抛出一些自定义的异常,比如输入一个不存在的品号,我们需要抛出一个异常,告知用户品号不存在。有一个笨笨的做法就是通过返回值来判断,但是这样不灵活,同时也会因为品号不存在而继续之行RFC。如果我们能自己抛出异常,程序自动停止执行RFC,那将是两全其美!
现在设定一个功能:输入两个数,第一个数除以第二个数。2个异常:被除数不能为0和除数不能为100。
首先,在SE37中建立一个RFC,就用上篇博文的RFC吧。
在IMPORT页设置如下:
Export页面设置如下:
Changing和Table页面放空
Exceptions页面设置如下:
然后就是源码咯:
代码中,RAISE专门用于抛出异常,程序自动停止执行,所有的返回值将被清空!RAISE 后面的内容就是在Exceptions中定义的内容了。
然后程序记得激活!!如此SAP端设置就结束了。
回到VS2010中,新建一个程序,界面如下:
于是,开始写程序:
首先是引用两个dll(参考上篇),并在程序开头输入:
using SAP.Middleware.Connector;
public void nco()
{
IDestinationConfiguration ID = new MyBackendConfig();
RfcDestinationManager.RegisterDestinationConfiguration(ID);
RfcDestination prd = RfcDestinationManager.GetDestination("PRD_000");
RfcDestinationManager.UnregisterDestinationConfiguration(ID); //反注册
nco(prd);
}
public void nco(RfcDestination prd)
{
string type = string.Empty;
RfcRepository repo = prd.Repository;
IRfcFunction companyBapi = repo.CreateFunction("ZRFC_MARA_INFO"); //指定RFC名称
try
{
companyBapi.SetValue("NUM1", textBox1.Text.Trim()); //输入参数复制
companyBapi.SetValue("NUM2", textBox2.Text.Trim()); //输入参数复制
companyBapi.Invoke(prd); //开始调用执行
textBox3.Text = companyBapi.GetValue("NUM3").ToString(); //获取返回结果
}
catch (RfcAbapException ex) //此Exception专门用于获取用户自定义的异常信息!!!!
{
// companyBapi.Metadata.GetAbapException(ex.Key).Documentation 获取对应的异常的说明文字
MessageBox.Show(companyBapi.Metadata.GetAbapException(ex.Key).Documentation, "SAP RFC返回信息", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (RfcTypeConversionException ex) //此Exception专门用于获取变量类型转换的异常!!!!
{
MessageBox.Show("您输入的不是数值", "SAP RFC返回信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
catch (RfcAbapRuntimeException ex) //此Exception专门用于获取RFC执行过程中的运行时异常!!!!
{
MessageBox.Show(companyBapi.Metadata.GetAbapException(ex.Key).Documentation, "SAP RFC返回信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
catch (RfcBaseException ex) //此Exception是总Exception类,可以获取所有的异常,如果有多个Catch,则不可以放第一位!!!!
{
MessageBox.Show("其他所有错误", "SAP RFC返回信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
prd = null;
repo = null;
}
public class MyBackendConfig : IDestinationConfiguration
{
public RfcConfigParameters GetParameters(String destinationName)
{
if ("PRD_000".Equals(destinationName))
{
RfcConfigParameters parms = new RfcConfigParameters();
parms.Add(RfcConfigParameters.AppServerHost, "192.168.1.3");
parms.Add(RfcConfigParameters.SystemNumber, "00");
parms.Add(RfcConfigParameters.User, "MENGXIN");
parms.Add(RfcConfigParameters.Password, "5239898");
parms.Add(RfcConfigParameters.Client, "888");
parms.Add(RfcConfigParameters.Language, "ZH");
parms.Add(RfcConfigParameters.PoolSize, "5");
parms.Add(RfcConfigParameters.MaxPoolSize, "10");
parms.Add(RfcConfigParameters.IdleTimeout, "60");
return parms;
}
else return null;
}
public bool ChangeEventsSupported()
{
return false;
}
public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
}
private void button1_Click(object sender, EventArgs e)
{
nco();
}
如此,C#运行结果如下:
以上可以正确得出我们自己抛出的异常的信息出来。程序测试OK!!!
分类: 2011-08-22 22:35 1636人阅读 (0)
周六加班,翻看以前写OA程序的时候无意中发现了当初的一个案子,就是让用户现在OA上申请一个SAP用户,然后提交给相关人员审核,通过之后直接在SAP中生成这个用户,不必在登陆到SAP中请系统管理员来做了。至于这一步,当然是使用BAPI来实现了,不可能对用户表进行新增数据,这样少了很多的关联表和一些逻辑判断,会是一场灾难。
前几篇讲的都是C#去调用RFC的东西,既然BAPI也是属于RFC,如果标准的勾选了那个remote的话,那按道理来说是应该可以实现调用的了。但难点是BAPI里面很多的参数都是引用结构的,而不是单纯的string和int的类型,这点有点困难。在C#中翻看了NCO3是否有相关的结构类,可惜,都没有!
查看了一下相关内容,发现几乎没有任何调用BAPI的例子,看来使用NCO3的人极少。
只能自力更生了,还好,经过很严密的思考和尝试,终于实现了在C#中调用BAPI去实现录入业务数据了。我尝试的就是用BAPI_USER_CREATE这个BAPI,通过它可以新增一个用户。
那么,相关内容和代码说明,就在下篇博文放出吧!
分类: 2011-08-24 22:46 1647人阅读 (0)
既然BAPI是一些特殊的RFC,封装了业务逻辑,使得将业务都变成一个一个对象,使用者只需要传入传出参数就可以了。
NCO3连接BAPI之前先来看看BAPI的一些参数规则。我们以 BAPI_USER_CREATE 为例子。此BAPI调用了可以生成一个用户,它当然不是仅仅对USR02进行写入那么简单。生成之后该账户什么权限也没有。
输入T-CODE:BAPI,然后在第二个页签下找到该BAPI:
上图中的红色框框里就是我们要调用的BAPI的名称。双击它,进入到该BAPI的一些说明里:
看到了,在属性页里它勾选了Remote... 说明它是可以远程操控的!
上图里,注意到红色框框都是不可选的,也就是后面的复选框没有选中,它告诉我们,在调用BAPI的时候这些参数是必须要有值传进来的!
这个就是返回的参数。当然,这个Return也是必须要传出来的值。
细心的人从上面几个图片可以看出,“参考打印”栏位里都是BAPI开头的结构,或者是结构里面的一个字段。双击这些结构进去就可以看到相关的栏位组成。
现在回到Import页签里,双击USERNAME的“参考打印”——BAPIBNAME-BAPIBNAME。“-”前部分是结构,后面是这个结构里的一个栏位。进入到里面:
这个BAPINAME结构只有一个栏位,而且是CHAR型,12码,SO,在传 用户名 进来的时候就是写入字符串即可。
我们看看PASSWORD,对应的结构是:BAPIPWD 双击之,如下图:
发现这里面只有一个栏位,CHAR型,40码,传密码之用。
再看看ADDRESS栏位对应的结构BAPIADDR3,双击之:
这个结构栏位就比较多了。虽然这里没有规定那些是必须要有值的,但确实有些是需要赋值,有些则可以放空。
我们在SU01里面新建用户的时候系统会提示要求输入姓和名,那么这里的FIRSTNAME和LASTNAME是必须的了。
BAPI里传入的参数LOGONDATA是放登陆数据的,具体的结构不再赘述。而返回的信息RETURN里有一个MESSAGE是放调用结果的信息,我们在NCO3里调用要用到它来返回结果信息。
知道了这些原理之后就很清楚知道在C#中要怎么写了,那些参数是必须的就一目了然了。SO,下篇博文放出NCO3调用BAPI的代码!
分类: 2011-08-25 22:43 1598人阅读 (0)
上面博文可知BAPI_USER_CREATE的一些结构和参数。所以在C#中要调用它就很清楚了要输入哪些参数了。
1、首先引用dll,然后在程序开头:using SAP.Middleware.Connector;
2、接下去就是设置登陆参数了,以前相关博文都有说明:
public class MyBackendConfig : IDestinationConfiguration
{
public RfcConfigParameters GetParameters(String destinationName)
{
if ("PRD_000".Equals(destinationName))
{
RfcConfigParameters parms = new RfcConfigParameters();
parms.Add(RfcConfigParameters.AppServerHost, "192.168.1.3");
parms.Add(RfcConfigParameters.SystemNumber, "00");
parms.Add(RfcConfigParameters.User, "MENGXIN");
parms.Add(RfcConfigParameters.Password, "5239898");
parms.Add(RfcConfigParameters.Client, "888");
parms.Add(RfcConfigParameters.Language, "ZH");
parms.Add(RfcConfigParameters.PoolSize, "5");
parms.Add(RfcConfigParameters.MaxPoolSize, "10");
parms.Add(RfcConfigParameters.IdleTimeout, "60");
return parms;
}
else return null;
}
public bool ChangeEventsSupported()
{
return false;
}
public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
}
3、设置一个方法,引用这个登陆参数的类:
public void nco()
{
IDestinationConfiguration ID = new MyBackendConfig();
RfcDestinationManager.RegisterDestinationConfiguration(ID);
RfcDestination prd = RfcDestinationManager.GetDestination("PRD_000");
RfcDestinationManager.UnregisterDestinationConfiguration(ID);
nco(prd);
}
4、然后就是开始了调用代码,以下代码全部放出,然后一行一行说明:
public void nco(RfcDestination prd)
{
//选择要调用的BAPI的名称
RfcFunctionMetadata BAPI_COMPANYCODE_GETDETAIL_MD = prd.Repository.GetFunctionMetadata("BAPI_USER_CREATE");
//新建调用该BAPI的一个“实例”
IRfcFunction function = null;
function = BAPI_COMPANYCODE_GETDETAIL_MD.CreateFunction();
//因为用户名称是一个字段,所以直接赋予字符串即可
function.SetValue("USERNAME", USERID.Text.Trim());
//接下去是密码。因为密码是一个结构,该结构只有一个栏位,所以这里先获取这个密码结构
IRfcStructure PWD = function.GetStructure("PASSWORD");
//设置这个结构中的这个栏位的内容
PWD.SetValue("BAPIPWD", password.Text.Trim());
//再把这个结构传进去给作为密码参数
function.SetValue("PASSWORD", PWD);
//这个是登陆信息,类型也是一个结构,所以这里获取出这个登录信息的结构出来
IRfcStructure logo = function.GetStructure("LOGONDATA");
//以下两个是设置这个登陆结构的一些栏位的值
logo.SetValue("GLTGV", "20110821");
logo.SetValue("GLTGB", "99991231");
//将登陆信息的结构传进去给LOGONDATA
function.SetValue("LOGONDATA", logo); //设置参数
//这个是地址信息,取结构,然后赋值,再将它传进去
IRfcStructure address = function.GetStructure("ADDRESS");
address.SetValue("FIRSTNAME", firstname.Text.Trim());
address.SetValue("LASTNAME", lastname.Text.Trim());
address.SetValue("DEPARTMENT", "资讯室");
function.SetValue("ADDRESS", address); //设置参数
//以下这个DEFAULTS是账户的默认信息,不是必须要的。我们这里利用它设置登入账户的语言为ZH
IRfcStructure DEFAULTS = function.GetStructure("DEFAULTS");
DEFAULTS.SetValue("LANGU", "1"); //语言代码,对应简体中文!
function.SetValue("DEFAULTS", DEFAULTS);
//以下这个是RETURN,是回传结果的结构。记住,这里跟上面不一样,上面是Structure,这里则是Table!!
IRfcTable returnStructure = function.GetTable("RETURN");
function.Invoke(prd);//提交调用BAPI
//弹出对话框显示调用结果
MessageBox.Show(returnStructure.GetString("MESSAGE").ToString());
prd = null;
}
5、程序运行结果如下:
6、登陆到SAP,运行SU01,输入IT_XM01,看看结果:
当然,如果该用户已经存在了你还去调用这个BAPI生成用户,系统会很知趣的提示:
使用IT_XM01登陆系统,一切OK,系统状态如下:
新建的账户一点权限也没有的,此时可以调用其他BAPI对其新增权限!!方法大同小异,这里不再详述。
分类: 2011-08-28 11:23 1678人阅读 (0)
接上篇博文。
上篇博文讲到C#调用BAPI BAPI_USER_CREATE 来生成SAP账户,但是新建的账户一点权限也没有,现在我们就再次利用BAPI给账户授予权限。
首先,我们在BAPI画面里查找出相关的BAPI出来,利用角色去给用户添加权限:
双击此BAPI,进入到BAPI预览画面:
Table属性页:
第一个ACTIVITYGROUPS是指我们要传进去的表格内容,不再是一个栏位和结构内容了。第二个的RETURN则是返回调用结果。
双击BAPIAGR的结构,进入结构详情画面:
以上AGR_NAME是角色名,应该是必须要的。
那么,接下去就是在C#中编写相应的代码来调用它了。
1、首先引用dll,然后在程序开头:using SAP.Middleware.Connector;
2、接下去就是设置登陆参数了,以前相关博文都有说明:
public class MyBackendConfig : IDestinationConfiguration
{
public RfcConfigParameters GetParameters(String destinationName)
{
if ("PRD_000".Equals(destinationName))
{
RfcConfigParameters parms = new RfcConfigParameters();
parms.Add(RfcConfigParameters.AppServerHost, "192.168.1.3");
parms.Add(RfcConfigParameters.SystemNumber, "00");
parms.Add(RfcConfigParameters.User, "MENGXIN");
parms.Add(RfcConfigParameters.Password, "5239898");
parms.Add(RfcConfigParameters.Client, "888");
parms.Add(RfcConfigParameters.Language, "ZH");
parms.Add(RfcConfigParameters.PoolSize, "5");
parms.Add(RfcConfigParameters.MaxPoolSize, "10");
parms.Add(RfcConfigParameters.IdleTimeout, "60");
return parms;
}
else return null;
}
public bool ChangeEventsSupported()
{
return false;
}
public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
}
3、设置一个方法,引用这个登陆参数的类:
public void nco()
{
IDestinationConfiguration ID = new MyBackendConfig();
RfcDestinationManager.RegisterDestinationConfiguration(ID);
RfcDestination prd = RfcDestinationManager.GetDestination("PRD_000");
RfcDestinationManager.UnregisterDestinationConfiguration(ID);
nco(prd);
}
4、然后就是开始了调用代码,以下代码全部放出,然后一行一行说明:
public void nco(RfcDestination prd)
{
//调用BAPI
RfcFunctionMetadata BAPI_COMPANYCODE_GETDETAIL_MD = prd.Repository.GetFunctionMetadata("BAPI_USER_ACTGROUPS_ASSIGN");
IRfcFunction function = null;
function = BAPI_COMPANYCODE_GETDETAIL_MD.CreateFunction();
//传入我们要赋予权限的用户名
function.SetValue("USERNAME", USERID.Text.Trim());
//接下去就是传入角色名。由于ACTIVITYGROUPS 是在Table属性页,所以我们是用Table 而不是Import和Export属性页里面用的Structure
IRfcTable ROFTable = function.GetTable("ACTIVITYGROUPS");
//上面语句产生的ROFTable 其实是一张空表,里面除了有那几个栏位之外是没有任何记录的,所以在对表格赋值之前需要先进性新增一行
ROFTable .Insert();
//接下去就是对表格当前行的相关栏位赋值。如下的角色名“MIS”是预选在SAP中建立好的
ROFTable .CurrentRow.SetValue("AGR_NAME", "MIS");
ROFTable .CurrentRow.SetValue("FROM_DAT", "20110825");
ROFTable .CurrentRow.SetValue("TO_DAT", "99991231");
//传入该赋值后的表格
function.SetValue("ACTIVITYGROUPS", ROFTable);
//引用回传结果
IRfcTable RETURNStructure = function.GetTable("RETURN");
//提交调用
function.Invoke(prd);
//显示调用结果
MessageBox.Show(RETURNStructure.GetString("MESSAGE").ToString());
prd = null;
}
5、调用结果如下:
如此则此用户已经赋予相应的权限了,运行所有T-CODE,没问题!
对了,如果要赋予多个角色的话,那么就是多做ROFTable.Insert();然后再做相应赋值即可。
PS:C#调用RFC系列专题就到此告一段落了,如果读者有建议或者疑问欢迎交流指正~!
分类: 2011-11-21 16:51 1646人阅读 (0)
当所有一切代码准备就绪之后,如果是ASP.NET那就是要发布网站到服务器了。如果服务器上的系统是WIN2003,那很不幸,系统会提示这样的“红脸”过来:
意思是说sapnco_utils.dll和sapnco.dll这两个文件不能载入。
Could not load file or assembly "sapnco_utils,Version=3.0.0.42,...
网上查找了方法也不尽然,各说纷纭。但是在WIN2008下的IIS7跟WinXP下的IIS5.1都可以完全正常,但是这个WIN2003就不行。
后来在对这两个DLL进行分析的时候发现它们是用VC++2005开发的,想到WIN2003系统可能没有必要的运行库。于是在工作站测试的时候安装了VC++2005 32bit版,然后刷新一切就正常了!
解决方法:安装相应vc++2005运行库即可!(实践证明:VC++2008不行!)
附VC++2005 32位运行库下载地址:
分类: 2011-11-21 16:52 1518人阅读 (1)
为什么.net调用SAP的BAPI接口需要调用BAPI_TRANSACTION_COMMIT呢?首先得明白BAPI_TRANSACTION_COMMIT这个BAPI的作用。它功劳很大,在SAP里面很多的BAPI直接调用是不会有结果的,因为需要COMMIT一下才能生效,比如生成资产编号的BAPI:BAPI_FIXEDASSET_CREATE1,如果对他直接在SE37中调用运行或者使用SE38调用它,虽然可以得到一个资产编号,但是在AS03里面查询,系统会很白痴得提示你:该资产编号不存在于XX公司。更搞的是当你在AS01中新建资产编号时,新建的资产编号会跳过之前用BAPI生成“失败”的号码。
那么,这就需要COMMIT一下,在调用这个BAPI之后再紧接调用BAPI_TRANSACTION_COMMIT这个。但是,在SE38中是可以这样做,而在.net中就没那么简单了,直接在调用完BAPI_FIXEDASSET_CREATE1之后再紧接调用BAPI_TRANSACTION_COMMIT是不可以的,虽然还是生成了资产编号,但仍旧是个废号。跟在SE37中调用无异。
怎么在.net中解决这个问题呢,这就需要用到RfcSessionManager.BeginContext和RfcSessionManager.EndContext这两个方法了。只有在这两个方法之间调用BAPI才能方保万无一失!
代码如下:
1、首先引用:using SAP.Middleware.Connector;
2、调用代码:
public void nco(DataSet ds)
{ IDestinationConfiguration ID = new RfcConfig();RfcDestinationManager.RegisterDestinationConfiguration(ID);RfcDestination prd = RfcDestinationManager.GetDestination("PRD_000");RfcDestinationManager.UnregisterDestinationConfiguration(ID);nco(prd, ds);}public void nco(RfcDestination prd, DataSet ds){ bool asset = false;//选择要调用的BAPI的名称RfcFunctionMetadata BAPI_COMPANYCODE_GETDETAIL_MD = prd.Repository.GetFunctionMetadata("BAPI_REQUISITION_CREATE");//新建调用该BAPI的一个“实例”IRfcFunction function = null;function = BAPI_COMPANYCODE_GETDETAIL_MD.CreateFunction();IRfcTable ITEMS = function.GetTable("REQUISITION_ITEMS");IRfcTable ACCOUNT = function.GetTable("REQUISITION_ACCOUNT_ASSIGNMENT");IRfcTable RETURN = function.GetTable("RETURN");int j = 0;RfcSessionManager.BeginContext(prd); //因期间需要同一个时间调用BAPI,而且各个BAPI之间有顺序关联,所以最好用这个包围起来for (int i = 0; i < ds.Tables[0].Rows.Count; i++){ ITEMS.Insert();j = j + 1;j = j * 10;ITEMS.CurrentRow.SetValue("PREQ_ITEM", j.ToString());ITEMS.CurrentRow.SetValue("PREQ_NAME", ds.Tables[0].Rows[i]["QGA27"].ToString());ITEMS.CurrentRow.SetValue("CREATED_BY", ds.Tables[0].Rows[i]["QGA27"].ToString());ITEMS.CurrentRow.SetValue("PREQ_DATE", Convert.ToDateTime(ds.Tables[0].Rows[i]["QGA15"].ToString()).Date);ITEMS.CurrentRow.SetValue("MATERIAL", ds.Tables[0].Rows[i]["QGA04"].ToString());ITEMS.CurrentRow.SetValue("SHORT_TEXT", ds.Tables[0].Rows[i]["QGA05"].ToString());ITEMS.CurrentRow.SetValue("PLANT", "1201");ITEMS.CurrentRow.SetValue("QUANTITY", Convert.ToDecimal(ds.Tables[0].Rows[i]["QGA07"].ToString()));ITEMS.CurrentRow.SetValue("DELIV_DATE", Convert.ToDateTime(ds.Tables[0].Rows[i]["QGA09"].ToString()).Date);ITEMS.CurrentRow.SetValue("C_AMT_BAPI", Convert.ToDecimal(ds.Tables[0].Rows[i]["QGA14"].ToString()));ITEMS.CurrentRow.SetValue("ACCTASSCAT", ds.Tables[0].Rows[i]["QGA12"].ToString());ITEMS.CurrentRow.SetValue("DOC_TYPE", ds.Tables[0].Rows[i]["QGA28"].ToString());ITEMS.CurrentRow.SetValue("UNIT", ds.Tables[0].Rows[i]["QGA06"].ToString());ACCOUNT.Insert();
ACCOUNT.CurrentRow.SetValue("PREQ_ITEM", j.ToString());ACCOUNT.CurrentRow.SetValue("COST_CTR", ds.Tables[0].Rows[i]["QGA31"].ToString());ACCOUNT.CurrentRow.SetValue("ORDER_NO", ds.Tables[0].Rows[i]["QGA10"].ToString());if (ds.Tables[0].Rows[i]["QGA12"].ToString().Trim() == "A") //如果类别是A,即资产,则需要资产编号
{ ACCOUNT.CurrentRow.SetValue("ASSET_NO", GetASSET(prd, i, ds)); //设置新建的资产编号ACCOUNT.CurrentRow.SetValue("CO_AREA", "1000");ACCOUNT.CurrentRow.SetValue("SUB_NUMBER", "0000");}}function.SetValue("REQUISITION_ITEMS", ITEMS);function.SetValue("REQUISITION_ACCOUNT_ASSIGNMENT", ACCOUNT);function.Invoke(prd);//提交调用BAPI RfcSessionManager.EndContext(prd);if (RETURN.GetString("TYPE").ToString().Trim() == "I"){ Suess.Text = RETURN.GetString("MESSAGE").ToString();BANFN.Text = "返回的请购单号:" + function.GetString("NUMBER").Trim();}else if (RETURN.GetString("TYPE").ToString().Trim() == "E"){ Error.Text = RETURN.GetString("MESSAGE").ToString();}prd = null;}/// <summary>/// 取得资产编号/// </summary>/// <param name="prd"></param>/// <returns></returns>public string GetASSET(RfcDestination prd, int i, DataSet ds){ RfcFunctionMetadata BAPI_COMPANYCODE_GETDETAIL_MD = prd.Repository.GetFunctionMetadata("BAPI_FIXEDASSET_CREATE1");IRfcFunction function = null;function = BAPI_COMPANYCODE_GETDETAIL_MD.CreateFunction();IRfcStructure KEY = function.GetStructure("KEY");KEY.SetValue("COMPANYCODE", "2012");IRfcStructure GENERALDATA = function.GetStructure("GENERALDATA");GENERALDATA.SetValue("ASSETCLASS", "00005990");GENERALDATA.SetValue("DESCRIPT", ds.Tables[0].Rows[i]["QGA05"].ToString());IRfcStructure GENERALDATAX = function.GetStructure("GENERALDATAX");GENERALDATAX.SetValue("ASSETCLASS", "X");GENERALDATAX.SetValue("DESCRIPT", "X");function.SetValue("KEY", KEY);function.SetValue("GENERALDATA", GENERALDATA);function.SetValue("GENERALDATAX", GENERALDATAX);prd.Repository.ClearFunctionMetadata(); //貌似这句可以省略...RfcFunctionMetadata BAPI_COMPANYCODE_GETDETAIL_MD1 = prd.Repository.GetFunctionMetadata("BAPI_TRANSACTION_COMMIT");IRfcFunction function1 = null;function1 = BAPI_COMPANYCODE_GETDETAIL_MD1.CreateFunction();function1.SetValue("WAIT", "X");RfcSessionManager.BeginContext(prd);function.Invoke(prd); //提交调用BAPI_FIXEDASSET_CREATE1 生成资产编号function1.Invoke(prd); //提交调用BAPI_TRANSACTION_COMMIT 进行COMMIT一下RfcSessionManager.EndContext(prd);twMsgbox.AjaxAlert(function.GetValue("ASSET").ToString().Trim()); return function.GetValue("ASSET").ToString().Trim();}新建立之后的请购单一切OK,同时,建立的资产编号在AS03已经可以认出来了!!