{"id":15019115,"url":"https://github.com/metalsimyaci/sapco2","last_synced_at":"2025-10-24T05:30:21.742Z","repository":{"id":48199359,"uuid":"283211234","full_name":"metalsimyaci/SapCo2","owner":"metalsimyaci","description":"SAP Connector for .Net5","archived":false,"fork":false,"pushed_at":"2021-09-21T12:46:29.000Z","size":1060,"stargazers_count":31,"open_issues_count":7,"forks_count":10,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-30T22:51:31.847Z","etag":null,"topics":["abap","net5","nwrfc","sap","sap-nwrfc-sdk","sap-rfc","sapco","sapco2","sapnwrfc"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/metalsimyaci.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-07-28T12:51:07.000Z","updated_at":"2025-01-01T06:43:36.000Z","dependencies_parsed_at":"2022-09-15T22:51:16.211Z","dependency_job_id":null,"html_url":"https://github.com/metalsimyaci/SapCo2","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metalsimyaci%2FSapCo2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metalsimyaci%2FSapCo2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metalsimyaci%2FSapCo2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metalsimyaci%2FSapCo2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/metalsimyaci","download_url":"https://codeload.github.com/metalsimyaci/SapCo2/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":237915423,"owners_count":19386724,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abap","net5","nwrfc","sap","sap-nwrfc-sdk","sap-rfc","sapco","sapco2","sapnwrfc"],"created_at":"2024-09-24T19:53:00.627Z","updated_at":"2025-10-24T05:30:20.748Z","avatar_url":"https://github.com/metalsimyaci.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![license](https://img.shields.io/badge/license-MIT-blue?)](https://opensource.org/licenses/MIT)\n[![Nuget](https://img.shields.io/nuget/v/sapco2?label=Nuget\u0026logo=Nuget\u0026logoColor=blue)](https://www.nuget.org/packages/SapCo2/)\n[![Nuget-download](https://img.shields.io/nuget/dt/sapco2?logo=nuget)](https://www.nuget.org/packages/SapCo2/)\n\n[![Multi Build And Test ( Net5.0, Core3.1, Core2.1 )](https://github.com/metalsimyaci/SapCo2/workflows/Multi%20Build%20And%20Test%20(%20Net5.0,%20Core3.1,%20Core2.1%20)/badge.svg?branch=master)](https://github.com/metalsimyaci/SapCo2/actions?query=workflow%3A%22Multi+Build+And+Test+%28+Net5.0%2C+Core3.1%2C+Core2.1+%29%22)\n\n[Türkçe Dökümantasyon](https://github.com/metalsimyaci/SapCo2/blob/master/README_TR.md)\n\n# SapCo2 (SAP Connector Core)\n\nWe tried to design SAPCO2 as a library for data processing over SAP in the simplest and fastest way. We focused on quick use, simplifying it as much as possible.\n\nReferenced by [SapNwRfc](https://github.com/huysentruitw/SapNwRfc) and [NwRfcNet](https://github.com/nunomaia/NwRfcNet) projects that were previously developed during the development phase. I recommend that you take a look at these two projects before examining the project. Especially the Wrapper parts have emerged by combining the common aspects of the two projects.\n\nsupportted .Net Core, Net 5 ve .Net Frameworks.\n\nI would like to thank Mahmut KOLTUK for his contributions at many points in the new architectural structure.\n\n## Requiretment\n\nOur library needs .dll files developed with C ++ in SAP NetWeaver RFC Library 7.50 SDK.\nFor the installation of the relevant SDK package and more: You can refer to the official page of: sparkles: [SAP](https://support.sap.com/en/product/connectors/nwrfcsdk.html).\n\nAfter obtaining the relevant SKD (requires an SAP licensed account from you);\n\n- Extract your zip file to a directory of your own and show the **lib** directory in the files you extract to the environment variable ``PATH`` (Windows), ``LD_LIBRARY_PATH`` (Linux), ``DYLD_LIBRARY_PATH`` (MacOS) according to your operating system.\n- or directly copy the contents of the **lib** folder to your output directory.\n\n To use the SDK package in the Windows operating system, you must install the 64-bit version of [Visual C ++ 2013 redistributable package](https://www.microsoft.com/en-us/download/details.aspx?id=40784).\n\n## Instalization\n\nPackageManager\n\n```shell\nInstall-Package SapCo2\n```\n\nOr DotNet\n\n```shell\ndotnet add package SapCo2\n```\nor Package Referance\n```xml\n\u003cPackageReference Include=\"SapCo2\" Version=\"1.2.0.1\" /\u003e\n```\n\n**Note:** Has dependencies [Microsoft.Extensions.DependencyInjection](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection/) and [Microsoft.Extensions.Options](https://www.nuget.org/packages/Microsoft.Extensions.Options).\n\n## Usage\n\nYou can access all the examples described in the document with the projects under the [Samples](https://github.com/metalsimyaci/SapCo2/tree/master/src/SapCo2/Samples) directory.\n\nTo use it, we add it with `.AddSapCo2` on `Dependency injection`. It needs connection information in the add process.\n\n\n### Connection \n\nTo assign the connection information as a string\n\n```csharp\nvar connectionString = \"Name=ALIAS1;AppServerHost=HOST_NAME; SystemNumber=00; User=USER; Password=PASSWORD; Client=CLIENT_CODE; Language=EN; PoolSize=100; Trace=0;\";\n```\n\nor multiple in **Appsettings.json**\n\n```json\n{\n  \"SapCo2\": {\n    \"DefaultServer\": \"ALIAS1\",\n    \"Connections\": [\n      {\n        \"Alias\": \"ALIAS1\",\n        \"ConnectionPooling\": {\n          \"Enabled\": false,\n          \"PoolSize\": 8,\n          \"IdleTimeout\": \"00:00:30\",\n          \"IdleDetectionInterval\": \"00:00:01\"\n        },\n        \"ConnectionString\": \"Name=ALIAS1;User=USER;Password=PASSWORD;Client=CLIENT_CODE;SystemId:xxx;Language=EN;AppServerHost=HOST_NAME;SystemNumber=00;MaxPoolSize:100;PoolSize=50;IdleTimeout:600;Trace=0;\",\n        \"ConnectionOptions\": {}\n      },\n      {\n        \"Alias\": \"ALIAS2\",\n        \"ConnectionPooling\": {\n          \"Enabled\": false,\n          \"PoolSize\": 8,\n          \"IdleTimeout\": \"00:00:30\",\n          \"IdleDetectionInterval\": \"00:00:01\"\n        },\n        \"ConnectionString\": \"\",\n        \"ConnectionOptions\": {\n          \"Name\": \"ALIAS2\",\n          \"User\": \"USER\",\n          \"Password\": \"PASSWORD\",\n          \"Client\": \"CLIENT_CODE\",\n          \"SystemId\": \"xxx\",\n          \"Language\": \"TR\",\n          \"AppServerHost\": \"HOST_NAME\",\n          \"SystemNumber\": \"00\",\n          \"MaxPoolSize\": \"100\",\n          \"PoolSize\": \"50\",\n          \"IdleTimeout\": \"600\",\n          \"Trace\": \"0\"\n        }\n      }\n    ]\n  }\n}\n```\n\nin **appsetting.json**;\n\n- You can assign connection information with `ConnectionString` or `ConnectionOptions`.\n- You can define multiple connections as ALIAS1 or ALIAS2.\n- Set a Default connection, it is sufficient to specify the `Alias` in the connection definitions in the `DefaultServer` part.\n\n### Dependency Injection\n\nStartup.cs veya Program.cs veya vb.\n\n```csharp\nIConfiguration configuration = new ConfigurationBuilder()\n    .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)\n    .AddJsonFile(\"appsettings.json\", optional: true)\n    .AddEnvironmentVariables()\n    .Build();\n```\n\nDependencyinjection ile IServceProvider içerisinde `.AddSapCo2` fonksiyonuna IConfiguration  bilgisi atayarak kullanabiliriz.\n\n#### IConfiguration with Appsettings\n\n```csharp\nvar serviceCollection = new ServiceCollection();\nserviceCollection.AddOptions\u003cIConfiguration\u003e();\nserviceCollection.Configure\u003cIOptions\u003cIConfiguration\u003e\u003e(configuration);\nserviceCollection.AddSapCo2(s=\u003es.ReadFromConfiguration(configuration));\nvar serviceProvider = serviceCollection.BuildServiceProvider();\n```\n\n#### Custom Configuration\n\n```csharp\nvar serviceCollection = new ServiceCollection()\n    .AddSapCo2(s =\u003e\n            {\n                s.DefaultServer = \"LIVE\";\n                s.RfcServers = new List\u003cRfcServer\u003e()\n                {\n                    new RfcServer()\n                    {\n                        Alias = \"LIVE\",\n                        ConnectionString =\n                            \"Name=LIVE;User=USER;Password=PASSWORD;Client=CLIENT_CODE;SystemId:xxx;Language=EN;AppServerHost=HOST_NAME;SystemNumber=00;MaxPoolSize:100;PoolSize=50;IdleTimeout:600;Trace=0;\",\n                        ConnectionPooling = new RfcConnectionPoolingOption() {Enabled = true, PoolSize = 10}\n                    }\n                };\n            });\nvar serviceProvider = serviceCollection.BuildServiceProvider();\n```\n\n### IRFCClient Implementation \n\nSAPCO2 can be used in 3 ways. [**Table**](####Table), [**Bapi**](####Bapi) and [**RFC**](####RFC).\nIn addition to these, a feature has been added where we will receive [Meta Data](####Meta-Data)  for RFC and BAPI.\n\n#### RFC\n\nThe most basic of these is RFC (Remote Functon Call). In other stages, it is actually a customized and simplified form of it. You can perform all operations using only RFC.\n\nIt will be sufficient if you create your **input** and **output** objects before the function call and pass them as parameters.\nAs an example, I used the recipe function **`CS_BOM_EXPL_MAT_V2_RFC`** for RFC. associated with it\nI create the **BomInputParameter** class for the parameters I want to give as a condition, and the **BomOutputParameter** class for the values ​​I want to get back.\nI create these classes by selecting the fields I want to use from import and export, Table objects related to SAP `SE37`.\n\nBy marking my classes that I created with the `RfcEntityPropertAttribute` attribute, we specify the corresponding name on the **SAP** side. If we want, we can also provide an explanation of what happened.\n\nI exclude properties that are not on the SAP side but are in my class with the `RfcEntityIgnorePropertyAttribute` attribute.\n\n\u003cdetails\u003e\n\u003csummary\u003eBomInputParameter Class\u003c/summary\u003e\n\n```csharp\npublic sealed class BomInputParameter: IRfcInput\n{\n    [RfcEntityProperty(\"AUMGB\")]\n    public string Aumgb { get; set; }\n\n    [RfcEntityProperty(\"CAPID\")]\n    public string Capid { get; set; }\n\n    [RfcEntityProperty(\"DATUV\")]\n    public DateTime Datuv { get; set; }\n\n    [RfcEntityProperty(\"EMENG\")]\n    public string Emeng { get; set; }\n\n    [RfcEntityProperty(\"MKTLS\")]\n    public string Mktls { get; set; }\n\n    [RfcEntityProperty(\"MEHRS\")]\n    public string Mehrs { get; set; }\n\n    [RfcEntityProperty(\"STPST\")]\n    public string Stpst { get; set; }\n\n    [RfcEntityProperty(\"SVWVO\")]\n    public string Svwvo { get; set; }\n\n    [RfcEntityProperty(\"WERKS\")]\n    public string Werks { get; set; }\n\n    [RfcEntityProperty(\"VRSVO\")]\n    public string Vrsvo { get; set; }\n\n    [RfcEntityProperty(\"STLAN\")]\n    public string Stlan { get; set; }\n\n    [RfcEntityProperty(\"STLAL\")]\n    public string Stlal { get; set; }\n\n    [RfcEntityProperty(\"MTNRV\")]\n    public string Mtnrv { get; set; }\n}\n```\n\n\u003c/details\u003e\n\nAs a condition, I define the production site code with `plantcode` and the material for which I want to get a recipe as a variable with `materialCode`.\n\n```csharp\nvar inputParameter = new BomInputParameter {\n    Aumgb = \"0\",\n    Capid = \"PP01\",\n    Datuv = DateTime.Now,\n    Emeng = \"1\",\n    Mktls = \"x\",\n    Mehrs = \"x\",\n    Stpst = \"0\",\n    Svwvo = \"x\",\n    Werks = plantCode,\n    Vrsvo = \"x\",\n    Stlal = \"1\",\n    Stlan = \"1\",\n    Mtnrv = materialCode\n};\n```\n\nFor the return values, we reference the structor and the table in our **BomOutputParameter** class. We show the tables as `[]` array.\n\n``` csharp\npublic sealed class BomOutputParameter: IRfcOutput\n{\n    [RfcEntityProperty(\"STB\")]\n    public Stb[] StbData { get; set; }\n\n    [RfcEntityProperty(\"TOPMAT\")]\n    public Topmat Topmat { get; set; }\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eTable Stb Class\u003c/summary\u003e\n\n``` csharp\npublic sealed class Stb\n{\n    [RfcEntityProperty(\"STUFE\", Description = \"Seviye\")]\n    public int Level { get; set; }\n\n    [RfcEntityProperty(\"OJTXB\", Description = \"Nesne kısa metni (bileşen grubu)\")]\n    public string Name { get; set; }\n\n    [RfcEntityProperty(\"OJTXP\", Description = \"Nesne kısa metni (kalem)\")]\n    public string SubItemName { get; set; }\n\n    [RfcEntityProperty(\"MMEIN\", Description = \"Temel ölçü birimi\")]\n    public string Unit { get; set; }\n\n    [RfcEntityProperty(\"MNGLG\", Description = \"Temel ölçü biriminde hesaplanan bileşen miktarı\")]\n    public decimal UnitAmount { get; set; }\n\n    [RfcEntityProperty(\"MNGKO\", Description = \"Bileşen ölçü biriminde hesaplanan bileşen miktarı\")]\n    public decimal Amount { get; set; }\n\n    [RfcEntityProperty(\"IDNRK\", Description = \"Ürün ağacı bileşenleri\")]\n    public string SubItemCode { get; set; }\n\n    [RfcEntityProperty(\"MENGE\", Description = \"Bileşen miktarı\")]\n    public decimal ComponentAmount { get; set; }\n\n    [RfcEntityProperty(\"STLTY\", Description = \"Ürün ağacı tipi\")]\n    public string TreeType { get; set; }\n\n    [RfcEntityProperty(\"STLKN\", Description = \"Ürün ağacı kalemi düğüm numarası\")]\n    public decimal TreeNodeNumber { get; set; }\n\n    [RfcEntityProperty(\"STPOZ\", Description = \"Dahili Sayaç\")]\n    public decimal InternalCounter { get; set; }\n\n    [RfcEntityProperty(\"STLNR\", Description = \"Üst Seviye\")]\n    public int UpperTreeId { get; set; }\n\n    [RfcEntityProperty(\"XTLNR\", Description = \"Alt Seviye\")]\n    public int SubTreeId { get; set; }\n\n    [RfcEntityProperty(\"MATMK\", Description = \"Mal Grubu\")]\n    public string MaterialGroup { get; set; }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eStruct Topmat Class\u003c/summary\u003e\n\n```csharp\npublic sealed class Topmat\n{\n    [RfcEntityProperty(\"MATNR\", Description = \"Malzeme Tanımı\")]\n    public string Code { get; set; }\n\n    [RfcEntityProperty(\"MAKTX\", Description = \"Tanım\")]\n    public string Definition { get; set; }\n}\n```\n\u003c/details\u003e\n\nWe take one `IClient` object from dependency injection and call the `ExecuteRfcAsync` function.\n\n```csharp\nusing IRfcClient client = _serviceProvider.GetRequiredService\u003cIRfcClient\u003e();\nBomOutputParameter bomResult = await client.ExecuteRfcAsync\u003cBomInputParameter, BomOutputParameter\u003e(\"CS_BOM_EXPL_MAT_V2_RFC\", inputParameter);\n```\n\nAs a result, we can see the desired output from the `BomOutputParameter` type in the `bomResult`.\n\nHere, the connection is made via the DefaultAlias we created in ``AppSettings``. To do this via a different alias, we need to call the ``UseServer`` function before the ``Execute`` function.\n\n```csharp\nusing IRfcClient client = _serviceProvider.GetRequiredService\u003cIRfcClient\u003e();\nclient.UseServer(\"ALIAS_NAME\");\nBomOutputParameter bomResult = await client.ExecuteRfcAsync\u003cBomInputParameter, BomOutputParameter\u003e(\"CS_BOM_EXPL_MAT_V2_RFC\", inputParameter);\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eThe entire the code\u003c/summary\u003e\n\n```csharp\npublic BomOutputParameter GetBillOfMaterial(string materialCode, string plantCode)\n{\n    var inputParameter = new BomInputParameter\n    {\n        Aumgb = \"0\",\n        Capid = \"PP01\",\n        Datuv = DateTime.Now,\n        Emeng = \"1\",\n        Mktls = \"x\",\n        Mehrs = \"x\",\n        Stpst = \"0\",\n        Svwvo = \"x\",\n        Werks = plantCode,\n        Vrsvo = \"x\",\n        Stlal = \"1\",\n        Stlan = \"1\",\n        Mtnrv = materialCode\n    };\n    using IRfcClient client = _serviceProvider.GetRequiredService\u003cIRfcClient\u003e();\n    BomOutputParameter bomResult = await client.ExecuteRfcAsync\u003cBomInputParameter, BomOutputParameter\u003e(\"CS_BOM_EXPL_MAT_V2_RFC\", inputParameter);\n    return bomResult;\n}\n\n```\n\n\u003c/details\u003e\n\n#### Bapi\n\n**BAPI** is a special type of RFC that is created by the system and is used for operations such as updating, deleting, creating, and contains a return value named `RETURN` for each operation result.\n\nTo make your bapi calls easier, you should derive your output objects from the `IBapiOutput` interface or from the `BapiOutputBase` abstrak class. This will automatically add the `RETURN` object in your output. After the operation, the MessageType value of this object is **Abort** or **Error**\nerror is thrown. The rest of the operations are the same as for the RFC call.\n\n```csharp\npublic sealed class RfcBapiOutputParameter\n{\n    [RfcEntityProperty(\"CODE\")]\n    public string Code { get; set; }\n\n    [RfcEntityProperty(\"TYPE\")]\n    public string MessageType { get; set; }\n\n    [RfcEntityProperty(\"MESSAGE\")]\n    public string Message { get; set; }\n\n    [RfcEntityProperty(\"LOG_NO\")]\n    public string LogNo { get; set; }\n\n    [RfcEntityProperty(\"LOG_MSG_NO\")]\n    public string LogMessageNumber { get; set; }\n\n    [RfcEntityProperty(\"MESSAGE_V1\")]\n    public string MessageV1 { get; set; }\n\n    [RfcEntityProperty(\"MESSAGE_V2\")]\n    public string MessageV2 { get; set; }\n\n    [RfcEntityProperty(\"MESSAGE_V3\")]\n    public string MessageV3 { get; set; }\n\n    [RfcEntityProperty(\"MESSAGE_V4\")]\n    public string MessageV4 { get; set; }\n}\n```\n\nI am using `BBP_VENDOR_GETLIST` bapi for example. This bapi returns the code and name of the vendors in the system according to the Company code it contains.\n\nInput\n\n```csharp\npublic class VendorBapiInputParameter: IBapiInput\n{\n    [RfcEntityProperty(\"COMP_CODE\")]\n    public string CompanyCode { get; set; }\n}\n```\n\nOutput\n\n```csharp\npublic class VendorBapiOutputParameter:IBapiOutput\n{\n    [RfcEntityProperty(\"RETURN\")]\n    public RfcBapiOutputParameter BapiReturn { get; set; }\n\n    [RfcEntityProperty(\"VENDOR\")]\n    public Vendor[] Vendors { get; set; }\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eTable Vendor Class\u003c/summary\u003e\n\n```csharp\npublic class Vendor\n    {\n        [RfcEntityProperty(\"VENDOR_NO\")]\n        public string VendorNo { get; set; }\n\n        [RfcEntityProperty(\"NAME\")]\n        public string Name { get; set; }\n    }\n```\n\u003c/details\u003e\n\nNote that it derives from the `IRfcBapiOutput` interface and has the attribute `[RfcEntityProperty(\"RETURN\")]`.\n\nIt happens instantly with the post RFC process. The `IRfcClient` object is superimposed and executed by calling the `ExecuteBapiAsync` function.\n\n```csharp\n\n    using IRfcClient sapClient = _serviceProvider.GetRequiredService\u003cIRfcClient\u003e();\n    return await sapClient.ExecuteBapiAsync\u003cVendorBapiInputParameter, VendorBapiOutputParameter\u003e(\"BBP_VENDOR_GETLIST\", inputParameter);\n```\n\n`companyCode` values set `200`\n\n\u003cdetails\u003e\n\u003csummary\u003eThe entire the code\u003c/summary\u003e\n\n```csharp\npublic VendorBapiOutputParameter GetVerdorsByCompanyCode(string companyCode)\n{\n    var inputParameter = new VendorBapiInputParameter\n    {\n        CompanyCode = companyCode\n    };\n\n    using IRfcClient sapClient = _serviceProvider.GetRequiredService\u003cIRfcClient\u003e();\n    return await sapClient.ExecuteBapiAsync\u003cVendorBapiInputParameter, VendorBapiOutputParameter\u003e(\"BBP_VENDOR_GETLIST\", inputParameter);\n    return result;\n}\n```\n\u003c/details\u003e\n\n#### Table\n\nSince we do not have direct access to the table objects, the system presented to us\n Using the `RFC_READ_TABLE` function, we perform the pull from the table.\n\n `RFC_READ_TABLE` has certain standard rules. We perform data extraction from the table by assigning the relevant data to those fields. We save you from all this trouble and produce our table function from the `ISapTable` interface for a simpler use.\n\n After the basic RFC process here, the `WA` struct in the `DATA` table, which is output by RFC_READ_TABLE, is split according to the shredding character you specify and assigned to the relevant class properties.\n\n Since the transformation operations during this process are handled differently from the RFC Map operation, you must mark the object you will use as a table with `RfcEntity` properties and `RfcEntityProperty` attributes. Points to the table with the `RrfcTable` name field. Again, it has the `unsafe` feature to be found as information. This generally indicates that the RFC is not up to SAP's standards and is specifically overridden. For our systems, it defines the enhancements referred to as '**Z**'. Likewise, it has `TablePropertySapType` and `Length` properties for transforming properties. In addition, **unsafe** is used to indicate whether this feature is an enhancement, in the same way as the table. `SubTypePropertyName` shows linked tables that will not be pulled in the first place. For now, we fill them by writing the `SetOption` method ourselves. We define these fields as virtual so that they are not taken into account in our normal data.\n\n Instead of the input parameter, the `TEXT` property of the `OPTIONS` table of `RFC_READ_TABLE` is written as text with `ABAP` of the condition statements. You can create this either by creating a text list in the form of `MATNR EQ '12344555'` in plain text, or with `Abap Query`, which we can chain by analogy to Linq. I will add details about `Abap Quey` in [Project Wiki](https://github.com/metalsimyaci/SapCo2/wiki). `FIELDS` fields are automatically assigned according to the attributes in the output class you created. Features such as `ROWCOUNT`, `ROWSKIPS`, `ROWDATA` are **optional** and can be crushed when necessary.\n\n Without further ado, let's look at the example.\n For the example, I will pull the `MARA` table and its sub-details (Material definition `MAKT` and Material class `ZMM24030`) from the tables.\n \n NOTE: If the Material classes table in the sub-details is not included in your SAP system because it is a development table, please replace `new MaterialQueryOptions { IncludeAll = true }` with `new MaterialQueryOptions { IncludeDefinition = true }`. In this way, you only pull the `MAKT` table. \n\n ```csharp\n[RfcEntity(\"MARA\", Description = \"Material General Table\")]\npublic class Material : ISapTable\n{\n    [RfcEntityProperty(\"MATNR\", Description = \"Material Code\", SapDataType = RfcDataTypes.CHAR, Length = 18)]\n    public string Code { get; set; }\n\n    [RfcEntityProperty(\"MATNR\", Description = \"Material Definition\", SapDataType = RfcDataTypes.CHAR, Length = 18, SubTypePropertyName = \"Code\")]\n    public virtual MaterialDefinition Definition { get; set; }\n\n    [RfcEntityProperty(\"ZZEXTWG\", Description = \"Material Category Code\", SapDataType = RfcDataTypes.CHAR, Length = 25, Unsafe = true)]\n    public string MaterialCategoryCode { get; set; }\n\n    [RfcEntityProperty(\"ZZEXTWG\", Description = \"Ürün Sınıfı\", SapDataType = RfcDataTypes.CHAR, Length = 25, SubTypePropertyName = \"ZZEXTWG\", Unsafe = true)]\n    public virtual MaterialCategoryDefinition MaterialCategory { get; set; }\n}\n ```\n\n Afterwards, we create the conditions for which we will draw our painting with `AbapQuey`. We set the condition as `MATNR` material definition starting with `materialCodePrefix` and not marked as deleted.\n\n ```csharp\n List\u003cstring\u003e whereClause = new AbapQuery().Set(QueryOperator.Equal(\"MATNR\", materialCode))\n                .And(QueryOperator.NotEqual(\"LVORM\", true, RfcDataTypes.BOOLEAN_X)).GetQuery();\n ```\n\nWe create our `IRfcClient` object and with the `GetTableDataAsync` method we specify whether we want the condition we want to retrieve, the number of records and the unsafe (included with Development) fields.\n\n ```csharp\nusing IRfcClient sapClient = _serviceProvider.GetRequiredService\u003cIRfcClient\u003e();\nList\u003cMaterial\u003e materials = await sapClient.GetTableDataAsync\u003cMaterial\u003e(whereClause, rowCount: recordCount);\nreturn await SetOptionsAsync(materials, new MaterialQueryOptions { IncludeAll = true });\n ```\n\u003cdetails\u003e\n \u003csummary\u003eThe entire the code\u003c/summary\u003e\n\n ```csharp\n public async Task\u003cList\u003cMaterial\u003e\u003e GetMaterialsByPrefixAsync(string materialCodePrefix,\n            MaterialQueryOptions options = null, bool getUnsafeFields = true, int rowCount = 0)\n{\n    options ??= new MaterialQueryOptions();\n    List\u003cstring\u003e whereClause = new AbapQuery().Set(QueryOperator.StartsWith(\"MATNR\", materialCodePrefix))\n        .And(QueryOperator.NotEqual(\"LVORM\", true, RfcDataTypes.BOOLEAN_X)).GetQuery();\n\n    using IRfcClient sapClient = _serviceProvider.GetRequiredService\u003cIRfcClient\u003e();\n    List\u003cMaterial\u003e result = await sapClient.GetTableDataAsync\u003cMaterial\u003e(whereClause, rowCount: recordCount);\n\n    return await SetOptionsAsync(result, options, getUnsafeFields).ConfigureAwait(false);\n}\n ```\n \u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eSub Table Operations\u003c/summary\u003e\n\nThat's it based on the usage of your table reading event. to fetch child tables\nWe create a function called `setoptions` and connect the sub-tables we get with it to our main `MARA` table. This process was created as an example of using fields such as `unsafe`, `SubProperty`.\n\n\u003cdetails\u003e\n\u003csummary\u003eSub Table Class\u003c/summary\u003e\n\nMaterial Definition (MAKT) Table Class\n\n ```csharp\n[RfcEntity(\"MAKT\", Description = \"Material Definition Table\")]\npublic class MaterialDefinition : ISapTable\n{\n    [RfcEntityProperty(\"MATNR\", Description = \"Material Code\", SapDataType = RfcDataTypes.CHAR, Length = 18)]\n    public string Code { get; set; }\n\n    [RfcEntityProperty(\"MAKTX\", Description = \"Material Short Definition\", SapDataType = RfcDataTypes.CHAR, Length = 40)]\n    public string Definition { get; set; }\n}\n ```\n\nMaterial Category (ZMM24030) Table Class\n\n ```csharp\n[RfcEntity(\"ZMM24030\", Description = \"Material Category Definition Table\", Unsafe = true)]\npublic class MaterialCategoryDefinition : ISapTable\n{\n    #region Properties\n\n    [RfcEntityProperty(\"ZZEXTWG\", Description = \"Material Category Code\", SapDataType = RfcDataTypes.CHAR, Length = 25)]\n    public string Code { get; set; }\n\n    [RfcEntityProperty(\"TANIM\", Description = \"Material Category Definition\", SapDataType = RfcDataTypes.CHAR, Length = 50)]\n    public string Definition { get; set; }\n\n    [RfcEntityProperty(\"ZZPARCASAYI\", Description = \"Material Category Unit Part Count\", SapDataType = RfcDataTypes.INTEGER, Length = 10)]\n    public int PartCount { get; set; }\n\n    #endregion\n\n    #region Methods\n\n    public override string ToString()\n    {\n        return Definition;\n    }\n\n    #endregion\n}\n ```\n\n\u003c/details\u003e\n\nSub Table Operations\n\n```csharp\nprivate async Task\u003cList\u003cMaterial\u003e\u003e SetOptionsAsync(List\u003cMaterial\u003e materialList,\n           MaterialQueryOptions queryOptions, bool getUnsafeFields = true)\n{\n    if (!materialList.Any())\n        return materialList;\n\n    var taskList = new List\u003cTask\u003e();\n    var definitionList = new ConcurrentQueue\u003cMaterialDefinition\u003e();\n    var materialCategoryDefinitionList = new ConcurrentQueue\u003cMaterialCategoryDefinition\u003e();\n\n    List\u003cTask\u003e materialDefinitionTaskList = SetMaterialDefinitionOptionAsync(queryOptions, materialList, definitionList);\n    if (materialDefinitionTaskList.Any())\n        taskList.AddRange(materialDefinitionTaskList);\n\n    List\u003cTask\u003e materialCategoryTaskList =\n        SetMaterialCategoryOptionAsync(queryOptions, materialList, materialCategoryDefinitionList);\n    if (materialCategoryTaskList.Any())\n        taskList.AddRange(materialCategoryTaskList);\n\n    if (!taskList.Any())\n        return materialList;\n\n    await Task.WhenAll(taskList).ConfigureAwait(false);\n\n    ILookup\u003cstring, MaterialDefinition\u003e materialDefinitionLookup = definitionList.ToLookup(x =\u003e x.Code);\n    ILookup\u003cstring, MaterialCategoryDefinition\u003e materialCategoryLookup =\n        materialCategoryDefinitionList.ToLookup(x =\u003e x.Code);\n\n    foreach (Material material in materialList)\n    {\n        material.Definition = materialDefinitionLookup.Contains(material.Code)\n            ? materialDefinitionLookup[material.Code].FirstOrDefault()\n            : null;\n        material.MaterialCategory = materialCategoryLookup.Contains(material.MaterialCategoryCode)\n            ? materialCategoryLookup[material.MaterialCategoryCode].FirstOrDefault()\n            : null;\n    }\n\n    return materialList;\n}\n```\n\nMaterial Defitinion Operations\n\n```csharp\nprivate List\u003cTask\u003e SetMaterialDefinitionOptionAsync(MaterialQueryOptions queryOptions, List\u003cMaterial\u003e materialList, ConcurrentQueue\u003cMaterialDefinition\u003e definitionList)\n{\n    var taskList = new List\u003cTask\u003e();\n    if (!queryOptions.IncludeDefinition)\n        return taskList;\n\n    var list = materialList.Where(x =\u003e x.Code != null).Select(x =\u003e (object)x.Code).ToList();\n    List\u003cobject\u003e[] parts = list.Partition(PartitionCount);\n\n    foreach (List\u003cobject\u003e part in parts)\n    {\n        List\u003cstring\u003e query = new AbapQuery()\n            .Set(QueryOperator.In(\"MATNR\", part))\n            .GetQuery();\n\n        taskList.Add(Task.Run(async () =\u003e\n        {\n            using IRfcClient client = _serviceProvider.GetRequiredService\u003cIRfcClient\u003e();\n            List\u003cMaterialDefinition\u003e definitions = await client.GetTableDataAsync\u003cMaterialDefinition\u003e(query);\n            foreach (MaterialDefinition definition in definitions)\n                definitionList.Enqueue(definition);\n        }));\n    }\n\n    return taskList;\n}\n```\n\nMaterial Category Operations\n\n```csharp\nprivate List\u003cTask\u003e SetMaterialCategoryOptionAsync(MaterialQueryOptions queryOptions, List\u003cMaterial\u003e materialList, ConcurrentQueue\u003cMaterialCategoryDefinition\u003e materialCategoryDefinitionList)\n{\n    var taskList = new List\u003cTask\u003e();\n    if (!queryOptions.IncludeMaterialCategory)\n        return taskList;\n\n    var materialCategoryList =\n        materialList.Where(x =\u003e x.MaterialCategoryCode != null)\n            .Select(x =\u003e (object)x.MaterialCategoryCode)\n            .Distinct()\n            .ToList();\n\n    List\u003cobject\u003e[] materialCategoryParts = materialCategoryList.Partition(PartitionCount);\n\n    foreach (List\u003cobject\u003e part in materialCategoryParts)\n    {\n        List\u003cstring\u003e queryList = new AbapQuery()\n            .Set(QueryOperator.In(\"ZZEXTWG\", part))\n            .GetQuery();\n\n        taskList.Add(Task.Run(async () =\u003e\n        {\n            using IRfcClient client = _serviceProvider.GetRequiredService\u003cIRfcClient\u003e();\n            List\u003cMaterialCategoryDefinition\u003e definitions = await client.GetTableDataAsync\u003cMaterialCategoryDefinition\u003e(queryList);\n            foreach (MaterialCategoryDefinition definition in definitions)\n                materialCategoryDefinitionList.Enqueue(definition);\n        }));\n    }\n\n    return taskList;\n}\n```\n\n\u003c/details\u003e\n\n#### Meta Data\n\nIn order to get the metadata information of the function we are used to from SapNco, I prepared a method that will combine a Field and Parameter information in SapNetwareRFC. Method only works for BAPI and RFC. Unfortunately, we cannot access its metadata from here, as we get the tables via RFC_READ_TABLE.\n\nIn the future, I will have a study to get this metadata from tables where table definitions such as DL003 are kept.\n\nSimilar to other uses, we call the ``ReadFunctionMetaData`` function after generating the ``IRfcClient`` object. This method returns us a list of type ``ParameterMetaData``. This list holds objects in ``Import``,``Export``,``Tables`` that we list with \u003cb\u003eSE37\u003c/b\u003e. We take the elements of these objects under the ``Field`` list.\n\n```csharp\npublic class ParameterMetaData\n{\n    public string Name { get; set; }\n    public string Type { get; set; }\n    public string Direction { get; set; }\n    public int NumericLength { get; set; }\n    public int UcLength { get; set; }\n    public int Decimals { get; set; }\n    public string Description { get; set; }\n    public string DefaultValue { get; set; }\n    public bool Optional { get; set; }\n\n    public List\u003cFieldMetaData\u003e Fields { get; set; }\n\n}\n```\n```csharp\npublic class FieldMetaData\n{\n    public string Name { get; set; }\n    public string Type { get; set; }\n    public int NucLength { get; set; }\n    public int NucOffset { get; set; }\n    public int UcLength { get; set; }\n    public int UcOffset { get; set; }\n    public int Decimals { get; set; }\n}\n```\n\nJust give the name of the RFC or BAPI whose metadata you want to access.\n\n```csharp\nusing IRfcClient client = _serviceProvider.GetRequiredService\u003cIRfcClient\u003e();\nList\u003cParameterMetaData\u003e result = client.ReadFunctionMetaData(functionName);\n```\n\nThat's all for now.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetalsimyaci%2Fsapco2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmetalsimyaci%2Fsapco2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetalsimyaci%2Fsapco2/lists"}