Laden...

Daten aus SAP lesen (RFC_Read_Table)

Erstellt von chris007 vor 15 Jahren Letzter Beitrag vor 15 Jahren 21.120 Views
C
chris007 Themenstarter:in
5 Beiträge seit 2008
vor 15 Jahren
Daten aus SAP lesen (RFC_Read_Table)

Hallo 🙂

bin hier ganz Neu und ich hoff ihr könnt mir helfen!
Hoffentlich steht das Thema nun in der richtigen Kategorie.

Kurze Info,
Habe SAP NetWeaver und möchte mit c# Daten auslesen.
Und um genau zu sagen die User die dort vorhanden sind.
Hab schon gelesen das die User in der Tabelle USR02 stehen.
Die Verbindung mit dem SAP NetWeaver besteht auch und um die USER auslesen zu können, benutzt ich den Funktionsbaustein RFC_Read_Table.
Da kommt dann auch das Problem bei der ganzen Sache, ich hab grad irgendwie nicht so den Plan wie das genau funktioniert =(
Aber ich hab schon ein wenig was geschrieben:


using System;
using System.Data;
using SAP.Connector;
using SAP.Connector.Rfc;
using SAP.Connector.Internal;



namespace ConsoleApplication1
{
	
	class Class1
	{
		public void table()
		{
			string con = "ASHOST=**; SYSNR=**; CLIENT=**; LANG=EN; USER=**; PASSWD=**";
			
			SAPConnection sapconnection = new SAPConnection(con);

			try
			{
				sapconnection.Open();
				Console.WriteLine("SAP Connction was opened..");

				SAPProxy1 proxy = new SAPProxy1(con);
				
				TAB512Table table = new TAB512Table();
				RFC_DB_FLDTable fldtable = new RFC_DB_FLDTable();
				RFC_DB_OPTTable opttable = new RFC_DB_OPTTable();

			

				proxy.Rfc_Read_Table("", "BNAME", "USR02", 10, 10, ref table, ref fldtable, ref opttable );
				
	

				sapconnection.Close();
			}
			catch(SAP.Connector.RfcException rfcEx)
			{
				Console.WriteLine("SAP Connection was failed..");
				Console.WriteLine(rfcEx.Message + rfcEx.StackTrace);
				Console.WriteLine();
			}
			
			
		}


		static void Main(string[] args)
		{
			Class1 test = new Class1();
			test.table();
			
		}
	}
}

Vielleicht habt ihr ja ne Idee?
Danke.

chris007

G
497 Beiträge seit 2006
vor 15 Jahren

RFC_READ_TABLE ist eine interne Funktion und nicht für die Kundenverwendung freigegeben. Zudem hat der Baustein Probleme in Unicode-Systemen. Es gibt dazu einen Hinweis 758278, der einen angepassten, vom Kunden zu implementierenden Baustein enthält (YRFC_READ_TABLE). Muss man bei auftretenden Fehlern einfach wissen, daß SAP diesen Baustein anscheinend selbst nur noch spärlich verwendet und die Funktion nicht garantiert.

Ich selbst habe den Baustein YRFC_READ_TABLE im System implementiert und über den Proxygenerator die Klassendefinitionen für die Parameter erzeugt. Dazu braucht man dann noch den SAP-Klienten (bei mir eine von SAPClient abgeleitete Klasse). Das sieht dann beispielseweise so aus (Klasse BAPITableAccess):


    [Serializable]
    public class BAPITableAccess: SAPClient
    {

        /// <summary>
        /// Exception constant for ABAP-Exception DATA_BUFFER_EXCEEDED
        /// </summary>
        public const string Data_Buffer_Exceeded = "DATA_BUFFER_EXCEEDED";

        /// <summary>
        /// Exception constant for ABAP-Exception FIELD_NOT_VALID
        /// </summary>
        public const string Field_Not_Valid = "FIELD_NOT_VALID";

        /// <summary>
        /// Exception constant for ABAP-Exception NOT_AUTHORIZED
        /// </summary>
        public const string Not_Authorized = "NOT_AUTHORIZED";

        /// <summary>
        /// Exception constant for ABAP-Exception OPTION_NOT_VALID
        /// </summary>
        public const string Option_Not_Valid = "OPTION_NOT_VALID";

        /// <summary>
        /// Exception constant for ABAP-Exception TABLE_NOT_AVAILABLE
        /// </summary>
        public const string Table_Not_Available = "TABLE_NOT_AVAILABLE";

        /// <summary>
        /// Exception constant for ABAP-Exception TABLE_WITHOUT_DATA
        /// </summary>
        public const string Table_Without_Data = "TABLE_WITHOUT_DATA";
   

        [RfcMethod(AbapName = "YRFC_READ_TABLE")]
        public void rfc_read_table(
           [RfcParameter(AbapName = "DELIMITER", Direction = RFCINOUT.IN, RfcType = RFCTYPE.RFCTYPE_CHAR, Length = 1, Length2 = 2, Optional = true)] string delimiter,
           [RfcParameter(AbapName = "DISTINCT", RfcType = RFCTYPE.RFCTYPE_CHAR, Optional = true, Direction = RFCINOUT.IN, Length = 1, Length2 = 2)] string distinct,
           [RfcParameter(AbapName = "FIX", RfcType = RFCTYPE.RFCTYPE_CHAR, Optional = true, Direction = RFCINOUT.IN, Length = 1, Length2 = 2)] string fix,
           [RfcParameter(AbapName = "NO_DATA", Direction = RFCINOUT.IN, RfcType = RFCTYPE.RFCTYPE_CHAR, Length = 1, Length2 = 2, Optional = true)] string nodata,
           [RfcParameter(AbapName = "QUERY_TABLE", Direction = RFCINOUT.IN, RfcType = RFCTYPE.RFCTYPE_CHAR, Length = 30, Length2 = 60, Optional = false)] string tableName,
           [RfcParameter(AbapName = "ROWSKIPS", Direction = RFCINOUT.IN, RfcType = RFCTYPE.RFCTYPE_INT, Optional = true, Length = 4, Length2 = 4)] int rowskips,
           [RfcParameter(AbapName = "ROWCOUNT", Direction = RFCINOUT.IN, RfcType = RFCTYPE.RFCTYPE_INT, Optional = true, Length = 4, Length2 = 4)] int rowcount,
           [RfcParameter(AbapName = "DATA", Direction = RFCINOUT.INOUT, RfcType = RFCTYPE.RFCTYPE_ITAB, Optional = false)] ref BAPIRfcTab_YTAB4000 data,
           [RfcParameter(AbapName = "FIELDS", Direction = RFCINOUT.INOUT, RfcType = RFCTYPE.RFCTYPE_ITAB, Optional = false)] ref BAPIRfcTab_RFC_DB_FLD tabFields,
           [RfcParameter(AbapName = "OPTIONS", Direction = RFCINOUT.INOUT, RfcType = RFCTYPE.RFCTYPE_ITAB, Optional = false)] ref BAPIRfcTab_RFC_DB_OPT tabOptions)
        {
            object[] results = null;
            object[] pars = new object[] { delimiter, distinct, fix, nodata, tableName, rowskips, rowcount, data, tabFields, tabOptions};
            results = this.SAPInvoke("rfc_read_table", pars);

            if (results.Length >= 1)
                data = results[0] as BAPIRfcTab_YTAB4000;
        }



        public BAPIRfcTab_YTAB4000 Read_Table(string tableName, string delimiter, BAPIRfcTab_RFC_DB_OPT options, BAPIRfcTab_RFC_DB_FLD fields)
        {
            BAPIRfcTab_YTAB4000 result = new BAPIRfcTab_YTAB4000();

            rfc_read_table(delimiter, " ", "X", " ", tableName, 0, 0, ref result, ref fields, ref options);

            return result;
        }


        public BAPIRfcTab_YTAB4000 Read_Table(string tableName, string delimiter, BAPIRfcTab_RFC_DB_OPT options, BAPIRfcTab_RFC_DB_FLD fields, int rowSkips, int rowCount)
        {
            BAPIRfcTab_YTAB4000 result = new BAPIRfcTab_YTAB4000();

            rfc_read_table(delimiter, " ", "X", " ", tableName, rowSkips, rowCount, ref result, ref fields, ref options);

            return result;
        }
    

    }


Definition von BAPIRfcTab_YTAB4000


    public class BAPIRfcTab_YTAB4000: SAPTable
    {

        public override Type GetElementType()
        {
            return typeof(BAPIRfcStruct_YTAB4000);
        }

        public override object CreateNewRow()
        {
            return new BAPIRfcStruct_YTAB4000();
        }

        public BAPIRfcStruct_YTAB4000 this[int index]
        {
            get
            {
                return ((BAPIRfcStruct_YTAB4000)(List[index]));
            }
            set
            {
                List[index] = value;
            }
        }

        public int Add(BAPIRfcStruct_YTAB4000 value)
        {
            return List.Add(value);
        }

        public void Insert(int index, BAPIRfcStruct_YTAB4000 value)
        {
            List.Insert(index, value);
        }

        public int IndexOf(BAPIRfcStruct_YTAB4000 value)
        {
            return List.IndexOf(value);
        }

        public bool Contains(BAPIRfcStruct_YTAB4000 value)
        {
            return List.Contains(value);
        }

        public void Remove(BAPIRfcStruct_YTAB4000 value)
        {
            List.Remove(value);
        }

        public void CopyTo(BAPIRfcStruct_YTAB4000[] array, int index)
        {
            List.CopyTo(array, index);
        }

    }

Definition von BAPIRfcStruct_YTAB4000


    [RfcStructure(AbapName = "YTAB4000", Length = 4010, Length2 = 8020)]
    public class BAPIRfcStruct_YTAB4000: SAPStructure
    {

        private string m_WorkArea;

        [RfcField(AbapName = "WA", RfcType = RFCTYPE.RFCTYPE_CHAR, Length = 4010, Length2 = 8020, Offset = 0, Offset2 = 0)]
        [XmlElement("WA", Form = XmlSchemaForm.Unqualified)]
        public string Wa
        {
            get { return m_WorkArea; }
            set { m_WorkArea = value; }
        }

    }

Zu guter letzt hab ich mir eine Funktion geschrieben, die das ganze etwas kapselt und mir die zurückgegebenen Zeilen in einer generischen Liste List<string> zurückgibt. Die darin benutzte Klasse SAPConnector.Main.ConnectionHelper ist nur eine Hilfsklasse, die mir eine Verbindung zu einem SAP-System anhand eines Systemalias erzeugt. Die Proxys für die Tabellen OPTIONS, FIELDS und DATA heissen bei mir nur anders, sollten von der Struktur her aber deinen entsprechen.


        public static List<string> Read_Table(string systemAlias, string tableName, string delimiter, string abapQueryOptions, List<string> fieldNames, int rowSkips, int rowCount)
        {
            List<string> rows = new List<string>();
            BAPIRfcTab_YTAB4000 result = new BAPIRfcTab_YTAB4000();

            // Verbindung zum R/3
            SAP.Connector.SAPConnection connection = SAPConnector.Main.ConnectionHelper.OpenConnection(systemAlias);

            if (connection != null)
            {
                BAPIRfcTab_RFC_DB_OPT tabOpt = new BAPIRfcTab_RFC_DB_OPT();
                if (abapQueryOptions != string.Empty)
                {

                    if (abapQueryOptions.Length > 72)
                    {
                        int counter = 0;
                        while (counter < abapQueryOptions.Length)
                        {
                            int length = abapQueryOptions.Length - counter;
                            if (length > 72)
                                length = 72;
                            BAPIRfcStruct_RFC_DB_OPT rowOpt = tabOpt.CreateNewRow() as BAPIRfcStruct_RFC_DB_OPT;
                            rowOpt.Text = abapQueryOptions.Substring(counter, length);
                            tabOpt.Add(rowOpt);
                            counter += length;
                        }
                    }
                    else
                    {
                        BAPIRfcStruct_RFC_DB_OPT rowOpt = tabOpt.CreateNewRow() as BAPIRfcStruct_RFC_DB_OPT;
                        rowOpt.Text = abapQueryOptions;
                        tabOpt.Add(rowOpt);
                    }
                }

                BAPIRfcTab_RFC_DB_FLD tabFields = new BAPIRfcTab_RFC_DB_FLD();
                foreach (string fieldName in fieldNames)
                {
                    BAPIRfcStruct_RFC_DB_FLD rowFields = tabFields.CreateNewRow() as BAPIRfcStruct_RFC_DB_FLD;
                    rowFields.Fieldname = fieldName;
                    tabFields.Add(rowFields);
                }

                BAPITableAccess bapiClass = new BAPITableAccess();
                bapiClass.Connection = connection;

                result = bapiClass.Read_Table(tableName, delimiter, tabOpt, tabFields, rowSkips, rowCount);

                // Verbindung schliessen
                SAPConnector.Main.ConnectionHelper.CloseConnection(systemAlias);

                foreach (BAPIRfcStruct_YTAB4000 row in result)
                {
                    rows.Add(row.Wa);
                }

            }


            return rows;
        }

du müsstest das für dich etwas verändern, da ich z. B. den Distinct-Parameter nicht von aussen annehme und immer leer an den RFC-Baustein übergebe.

C
chris007 Themenstarter:in
5 Beiträge seit 2008
vor 15 Jahren

Ok also kann ich rfc_read_table nicht benutzen, dass schon gut zu wissen.

YRFC_READ_TABLE << hast du selbst erstellt oder von der RFC_READ_TABLE abgeleitet?
Und wies aussieht besitze ich den Funktionsbausein BAPIRfcStruct_YTAB4000 nicht =/

Jedenfalls danke für die ausführliche Antwort.
Nur ich kenn mich damit jetzt noch nit sooo gut damit aus, deswegen fällt es mir grad ein wenig schwer das alles so richtig zu verstehen =/
Versuch es jetzt noch paar mal zu lesen 😉

Aber es gibt soviele Funktionsbausteine, gibt es da keinen der jetzt einfach die Daten aus der Tablle USR02 auslesen kann? Oder ist das wirklich so ein "komplizierter" Weg?

Danke

G
497 Beiträge seit 2006
vor 15 Jahren

du kannst rfc_read_table schon benutzen, aber musst dir im Klaren sein, daß es da Probleme gibt. yrfc_read_table ist ein Funktionsbaustein, der im SAP Service-Center unter der angegebenen Hinweisnummer 758278 zu finden ist. Kann da runtergeladen und installiert werden.

BAPIRfcStruct_YTAB4000 ist eine Struktur (genauer gesagt: ein Proxy für eine Struktur im SAP-System), kein Funktionsbaustein. Und wie erwähnt unterscheidet sich da wahrscheinlich nur die Benennung der Proxyklassen, die das SAP-Tool im Visual Studio generiert. YTAB4000 ist eine Struktur, die man für YRFC_READ_TABLE im ERP-System anlegen muss, die Struktur im C#-Projekt dient als Gegenstück zu dieser Struktur.

Wenn man minimale Abap-Kenntnisse hat, kann man sich auch einen RFC-fähigen Baustein schreiben, der diese spezielle Aufgabe erledigt. Das muss man dann aber im SAP-System selbst machen. Und es ist äußerst unüblich und daher von der SAP nicht gern gesehen, wenn man direkt Tabellendaten ausliest. In vielen Fällen gibts BAPIs (Business API) für Zugriffe auf SAP-Daten, die beachten dann auch die Businessregeln des ERP-Systems. Obs jetzt was für diesen Fall gibt, kann ich nicht sagen. Das Vorgehen wäre aber sehr ähnlich. Da kann dir wahrscheinlich eher ein SAP-Berater Auskunft geben.

C
chris007 Themenstarter:in
5 Beiträge seit 2008
vor 15 Jahren

Also direkt Funktionsbausteine schreiben wäre ne Idee aber da fehlt mir jetzt auch die Erfahrung das zu machen. Müsste ich dann einmal nachlesen.

Das mit den User auslesen dachte ich mir, dass es vielleicht ein guter Einstieg ist mit Funktionsbausteinen zu arbeiten.
Da es ja ein paar StandardUser in SAP NetWeaver gibt und es bestimmt nit so schwierig sein wird die auszulesen wenn so Funktionsbausteine wie rfc_read_table vorhanden sind.
Oder soll ich vielleicht erstmal andere Daten aus SAP lesen? Wie es vielleicht leichter gehen könnte?

G
497 Beiträge seit 2006
vor 15 Jahren

da kommts gar nicht so sehr auf die Tabellen an sich an, mehr auf das allgemeine Vorgehen beim Ansprechen von RFC-Bausteinen im SAP-System. Das Vorgehen ist immer dasselbe, nur die Parameter und der Baustein ändern sich. Von daher ist dieses Beispiel genauso gut oder schlecht wie jedes andere auch.

Ich hab mir das ganze damals mehr oder weniger aus den Beispielen, die beim SAP .Net-Connector dabei waren, erarbeitet. Die Beispiele sind nicht der Hammer, aber für erste Gehversuche reichts. Wenn man dann einmal verstanden hat, wie man damit arbeitet, kann man relativ problemlos eigene Wege gehen.

Es ist aber immer auch wichtig zu wissen, was im SAP-System vor sich geht. Speziell was die Datentypen im SAP-Dictionary angeht, muss man sich ein bisschen informieren. Sowas wie NUMC (ein numerischer Wert, gespeichert in einem String) gibts im Grunde nicht in der .Net-Welt. Da gibts dann noch QUAN (Mengenfeld, immer abhängig von einer Mengeneinheit, der Verweis auf das dazugehörige Mengenfeld muss im Dictionary angegeben sein), DATS (Datumsfeld, im Grunde aber ein 8 Zeichen langes Characterfeld) und diverse andere. Die meisten sind im Endeffekt nur Characterfelder mit irgendeiner Form von Eingabeprüfung, von daher wirst du fast alle Daten in Form von Zeichenketten aus dem SAP-System bekommen. Dazu gibts dann aber auch Append-Strukturen, hier können ganze Strukturen in eine Tabelle eingehängt werden.

C
chris007 Themenstarter:in
5 Beiträge seit 2008
vor 15 Jahren

Hm oke.
Ja die Beispiele habe ich mir auch schon angeschaut, nur bei meinem SAP System gibts den Funktionsbaustein rfc_customer_get nicht.
Aber wie ich ja nun erfahren hab, kann ich den noch dazu installieren.
Also bei SAP Service Market hab ich es nicht gefunden=/
Jap sowas hatte ich öfters schon gelesen das es da Unterschiede gibt und man dadrauf achten muss.
Ja genau da ist auch das Problem, grad fehlt mir das wissen wie ich genau so ein Funktionsbaustein benutze. Z.b. was ich genau übergeben muss.


TAB512Table table = new TAB512Table();
				RFC_DB_FLDTable fldtable = new RFC_DB_FLDTable();
				RFC_DB_OPTTable opttable = new RFC_DB_OPTTable();
				
				proxy.Rfc_Read_Table("", "","USR02", 20, 20, ref table, ref fldtable, ref opttable);
				
				
				foreach (RFC_DB_FLD row in fldtable)
				{
					Console.WriteLine("User Name: "+ row.Fieldname);
				}
				Console.WriteLine();

Wenn ich das ausführe gibt er mir eine Liste von:
MANDT
BNAME
BCODE
GLTGI

usw..

Wie komm ich jetzt direkt an die Liste von BNAME?