现在的位置: 首页 > 综合 > 正文

VS2005 NET连接SAP系统专题

2013年09月03日 ⁄ 综合 ⁄ 共 23626字 ⁄ 字号 评论关闭
文章目录

原文地址:http://blog.csdn.net/mengxin523/article/details/6710088

 .NET调用RFC几种方式(一)

本 来今天是要写一篇关于NCO3.0的东西,就是关乎.NET调用SAP的RFC的,支持VS2010和.NET 4.0等。现在网上到处都是充斥着NCO1.X和NCO2.0,需要用VS2003来使用,都是一些没什么大用的东西,连.NET 2.0的不能支持,只能算是废材一个。而且相关的资料非常的少,转来转去全部都是那么一个例子,丝毫没有直观的DEMO,更搞笑的是SAP里面的RFC是怎么建立的都没有说明,只有该死的C#代码。同时还有微软发布的DataProvider 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这个东西。

 

SAP中新建可远程调用的RFC(二)

何 谓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#中调用它咯,下篇博文放出!

 

NET连接SAP系统专题:C#调用RFC代码(三)

 

本文就说明在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);

            RfcDestinationprd = RfcDestinationManager.GetDestination("PRD_000");

           RfcDestinationManager.UnregisterDestinationConfiguration(ID);

            nco(prd);

        }

        public voidnco(RfcDestination prd)

        {           

            RfcRepositoryrepo = prd.Repository;

            IRfcFunctioncompanyBapi = 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

        {

            publicRfcConfigParameters 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 boolChangeEventsSupported()

            {

               return false;

            }

            public eventRfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;

        }

        private void Form1_Load(objectsender, EventArgs e)

        {

           comboBox1.Items.Clear();

            nco();

           comboBox1.SelectedIndex = 1;

        }

        //当下拉框索引变化的时候传递品号进去查询出品名出来

        private voidcomboBox1_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下载地址:

http://files.cnblogs.com/mengxin523/SAP_DotNetConnector3.zip

 

获取RFC中自定义的异常(四)

有 的时候我们需要在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);

            RfcDestinationprd = RfcDestinationManager.GetDestination("PRD_000");

           RfcDestinationManager.UnregisterDestinationConfiguration(ID);   //反注册

            nco(prd);

        }

        public voidnco(RfcDestination prd)

        {

            string type =string.Empty;

            RfcRepositoryrepo = prd.Repository;

            IRfcFunctioncompanyBapi = 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 (RfcBaseExceptionex)  //此Exception是总Exception类,可以获取所有的异常,如果有多个Catch,则不可以放第一位!!!!

            {

               MessageBox.Show("其他所有错误", "SAP RFC返回信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);

            }

            prd = null;

            repo = null;

        }

        public class MyBackendConfig: IDestinationConfiguration

        {

            publicRfcConfigParameters 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;

                }

                elsereturn null;

            }

            public boolChangeEventsSupported()

            {

               return false;

            }

            public eventRfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;

             }

        private voidbutton1_Click(object sender, EventArgs e)

        {

            nco();

        }

如此,C#运行结果如下:

以上可以正确得出我们自己抛出的异常的信息出来。程序测试OK!!!

 

C#(NCO3)调用BAPI(五)

周六加班,翻看以前写OA程序的时候无意中发现了当初的一个案子,就是让用户现在OA上申请一个SAP用户,然后提交给相关人员审核,通过之后直接在 SAP中生成这个用户,不必在登陆到SAP中请系统管理员来做了。至于这一步,当然是使用BAPI来实现了,不可能对用户表进行新增数据,这样少了很多的 关联表和一些逻辑判断,会是一场灾难。

    前几篇讲的都是C#去调用RFC的东西,既然BAPI也是属于RFC,如果标准的勾选了那个remote的话,那按道理来说是应该可以实现调用的了。但难 点是BAPI里面很多的参数都是引用结构的,而不是单纯的string和int的类型,这点有点困难。在C#中翻看了NCO3是否有相关的结构类,可惜, 都没有!

    查看了一下相关内容,发现几乎没有任何调用BAPI的例子,看来使用NCO3的人极少。

    只能自力更生了,还好,经过很严密的思考和尝试,终于实现了在C#中调用BAPI去实现录入业务数据了。我尝试的就是用BAPI_USER_CREATE这个BAPI,通过它可以新增一个用户。

    那么,相关内容和代码说明,就在下篇博文放出吧!

 

NCO3调用BAPI的一些说明(六)

 

既然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的代码!

网站访问量地图:

 

NCO3调用BAPI的代码(七)

上面博文可知BAPI_USER_CREATE的一些结构和参数。所以在C#中要调用它就很清楚了要输入哪些参数了。

      1、首先引用dll,然后在程序开头:using SAP.Middleware.Connector;

      2、接下去就是设置登陆参数了,以前相关博文都有说明:

      public class MyBackendConfig :IDestinationConfiguration

        {

            publicRfcConfigParameters 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 eventRfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;

        }

        3、设置一个方法,引用这个登陆参数的类:

        public void nco()

        {

           IDestinationConfiguration ID = new MyBackendConfig();

           RfcDestinationManager.RegisterDestinationConfiguration(ID);

            RfcDestinationprd = RfcDestinationManager.GetDestination("PRD_000");

           RfcDestinationManager.UnregisterDestinationConfiguration(ID);

            nco(prd);

        }

        4、然后就是开始了调用代码,以下代码全部放出,然后一行一行说明:

        public voidnco(RfcDestination prd)

        {

            //选择要调用的BAPI的名称

           RfcFunctionMetadata BAPI_COMPANYCODE_GETDETAIL_MD =prd.Repository.GetFunctionMetadata("BAPI_USER_CREATE");

            //新建调用该BAPI的一个“实例”

            IRfcFunctionfunction = null;

            function =BAPI_COMPANYCODE_GETDETAIL_MD.CreateFunction();

            //因为用户名称是一个字段,所以直接赋予字符串即可

           function.SetValue("USERNAME", USERID.Text.Trim());

            //接下去是密码。因为密码是一个结构,该结构只有一个栏位,所以这里先获取这个密码结构

            IRfcStructurePWD = function.GetStructure("PASSWORD");

            //设置这个结构中的这个栏位的内容

           PWD.SetValue("BAPIPWD", password.Text.Trim());

            //再把这个结构传进去给作为密码参数

           function.SetValue("PASSWORD", PWD);

            //这个是登陆信息,类型也是一个结构,所以这里获取出这个登录信息的结构出来

            IRfcStructurelogo = function.GetStructure("LOGONDATA");

            //以下两个是设置这个登陆结构的一些栏位的值

           logo.SetValue("GLTGV", "20110821");

           logo.SetValue("GLTGB", "99991231");

            //将登陆信息的结构传进去给LOGONDATA

           function.SetValue("LOGONDATA", logo);   //设置参数

            //这个是地址信息,取结构,然后赋值,再将它传进去

            IRfcStructureaddress = function.GetStructure("ADDRESS");

           address.SetValue("FIRSTNAME", firstname.Text.Trim());

           address.SetValue("LASTNAME", lastname.Text.Trim());

           address.SetValue("DEPARTMENT", "资讯室");

           function.SetValue("ADDRESS", address);   //设置参数

            //以下这个DEFAULTS是账户的默认信息,不是必须要的。我们这里利用它设置登入账户的语言为ZH

            IRfcStructureDEFAULTS = function.GetStructure("DEFAULTS");

           DEFAULTS.SetValue("LANGU", "1"); //语言代码,对应简体中文!

           function.SetValue("DEFAULTS", DEFAULTS);

            //以下这个是RETURN,是回传结果的结构。记住,这里跟上面不一样,上面是Structure,这里则是Table!!

            IRfcTablereturnStructure = 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对其新增权限!!方法大同小异,这里不再详述。

 

C#调用BAPI给账户赋予权限(八)

接上篇博文。

    上篇博文讲到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

        {

            publicRfcConfigParameters 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 boolChangeEventsSupported()

            {

               return false;

            }

            public eventRfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;

        }

        3、设置一个方法,引用这个登陆参数的类:

        public void nco()

        {

           IDestinationConfiguration ID = new MyBackendConfig();

           RfcDestinationManager.RegisterDestinationConfiguration(ID);

            RfcDestinationprd = RfcDestinationManager.GetDestination("PRD_000");

           RfcDestinationManager.UnregisterDestinationConfiguration(ID);

            nco(prd);

        }

        4、然后就是开始了调用代码,以下代码全部放出,然后一行一行说明:

        public voidnco(RfcDestination prd)

        {

            //调用BAPI

           RfcFunctionMetadata BAPI_COMPANYCODE_GETDETAIL_MD = prd.Repository.GetFunctionMetadata("BAPI_USER_ACTGROUPS_ASSIGN");

            IRfcFunctionfunction = null;

            function =BAPI_COMPANYCODE_GETDETAIL_MD.CreateFunction();

            //传入我们要赋予权限的用户名

           function.SetValue("USERNAME", USERID.Text.Trim());

            //接下去就是传入角色名。由于ACTIVITYGROUPS 是在Table属性页,所以我们是用Table 而不是Import和Export属性页里面用的Structure

            IRfcTableROFTable = 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);

            //引用回传结果

            IRfcTableRETURNStructure = function.GetTable("RETURN");

            //提交调用          

            function.Invoke(prd);

            //显示调用结果

           MessageBox.Show(RETURNStructure.GetString("MESSAGE").ToString());

            prd = null;

        }

        5、调用结果如下:

        

         

         

         如此则此用户已经赋予相应的权限了,运行所有T-CODE,没问题!

         对了,如果要赋予多个角色的话,那么就是多做ROFTable.Insert();然后再做相应赋值即可。

         

         PS:C#调用RFC系列专题就到此告一段落了,如果读者有建议或者疑问欢迎交流指正~!

sapnco.dll在ASP.NET中载入失败的解决方法(九)

当所有一切代码准备就绪之后,如果是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++200532bit版,然后刷新一切就正常了!

解决方法:安装相应vc++2005运行库即可!(实践证明:VC++2008不行!)

附VC++2005 32位运行库下载地址:

http://files.cnblogs.com/mengxin523/vcredist2005sp1_x86_XiaZaiBa.zip

 

BAPI_TRANSACTION_COMMIT的使用方法(十)

为什么.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、首先引用:usingSAP.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已经可以认出来了!!

C#如何导入内文至SAP(十一)

内文这个东西就像长篇大论的描述,跟在WORD里面一样可以输入无数个字。如果在sap中输入事务码进去可以维护多少个字都可以,但是如果是用.NET调用BAPI传进去内文的话,会将内文超过132个字符的内容全部截掉。要怎么样才能全部导入所有内文呢?

以下以导入请购单的程序来描述:

//表头内文 转换多笔实现超长文本插入

……
PRHEADERTEXT.Insert();                            // 这个是BAPI里面导入内文的参数
if (txtQG07.Text.Trim().Length > 120)         // txtQG07.Text的内容就是所有内文,这里用120个字符判断
{
int len = txtQG07.Text.Trim().Length / 120;  // 这里len取整数
len = len + 1;                                           // 可能有结余,故加一
for (int q = 0; q < len; q++)
{
if (q + 1 == len)                                      // 如果到了最后一行,则去最后一行之后的所有内文
PRHEADERTEXT.CurrentRow.SetValue(”TEXT_LINE”, txtQG07.Text.Trim().Substring(q *120));
else
PRHEADERTEXT.CurrentRow.SetValue(”TEXT_LINE”, txtQG07.Text.Trim().Substring(q *120, 120));
PRHEADERTEXT.Append();                        // 将截取的每一段内容都附加到内文后面。如果不加这一句,则文本会倒置
}
}
else
{
PRHEADERTEXT.CurrentRow.SetValue(”TEXT_LINE”, txtQG07.Text.Trim());
}

……

如此则可以将内文全部导入。但如果是项目中的内文呢?因为会涉及到传入项目序号,所以需要稍作处理:

//项目内文
PRITEMTEXT.Insert();
if (ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Length > 120)
{
int len = ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Length /120;
len = len + 1;
for (int q = 0; q < len; q++)
{
PRITEMTEXT.CurrentRow.SetValue(”PREQ_ITEM”, j.ToString());    // 将额外需要的参数都添加到这里,每循环一次都要SetValue一次
if (q + 1 == len)
PRITEMTEXT.CurrentRow.SetValue(”TEXT_LINE”,ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Substring(q * 120));
else
PRITEMTEXT.CurrentRow.SetValue(”TEXT_LINE”,ds.Tables[0].Rows[i]["QGA11"].ToString().Trim().Substring(q * 120,120));
PRITEMTEXT.Append();
}
}
else
{
PRITEMTEXT.CurrentRow.SetValue(”PREQ_ITEM”, j.ToString());
PRITEMTEXT.CurrentRow.SetValue(”TEXT_LINE”,ds.Tables[0].Rows[i]["QGA11"].ToString());
}

PS:.NET连接SAP系统专题就到此结束了。欢迎各位朋友一起交流指正…

 

抱歉!评论已关闭.