SapNwRfc icon indicating copy to clipboard operation
SapNwRfc copied to clipboard

connection.Connect() not working

Open deus82ex opened this issue 2 years ago • 2 comments

Hi,

I try to setup a .Net6 API controller. The call connection.Connect() does not complete to continue the following code, there is also nor exception thrown, no error in Eventlog. The code is just not reactinfg anymore. Any suggestions, what could be wrong?

`[HttpPost] public IActionResult GetSapBapiMetadata([FromBody] SapConfig sapConfig) { var result = new List<SapMetaData>();

        try
        {
            Log.Info($"GetSapBapiStructure for: {JsonConvert.SerializeObject(sapConfig)}");

            using var connection = new SapConnection(GetSapConnectionParameters(sapConfig));
            connection.Connect();

            var functionMetadata = connection.GetFunctionMetadata(sapConfig.Function);
            var functionName = functionMetadata.GetName();
            var parameterCount = functionMetadata.Parameters.Count;
            foreach (var parameterMetadata in functionMetadata.Parameters)
            {
                var parameterName = parameterMetadata.Name;
                var parameterType = parameterMetadata.Type;
            }
            functionMetadata.Parameters.TryGetValue("PARAMETER_NAME", out var parameterNameMetadata);
        }
        catch (Exception ex)
        {
            Log.Error(ex);
            return BadRequest(ex.Message);
        }

        return Ok(result);
    }

    private static SapConnectionParameters GetSapConnectionParameters(SapConfig sapConfig)
    {
        Log.Info($"GetSapConnectionParameters");
        var parameters = new SapConnectionParameters();

        try
        {
            parameters.Name = sapConfig.Name;
            parameters.AppServerHost = sapConfig.AppServerHost;
            parameters.SystemNumber = sapConfig.SysNr;
            parameters.User = sapConfig.User;
            parameters.Password = sapConfig.Password;
            parameters.Client = sapConfig.Client;
            parameters.Language = sapConfig.Language;
            parameters.PoolSize = "5";
            parameters.IdleTimeout = Convert.ToString(sapConfig.Timeout);
        }
        catch (Exception ex)
        {
            Log.Error(ex);
        }

        Log.Info($"{JsonConvert.SerializeObject(parameters)}");

        return parameters;
    }`

Regards Mario

deus82ex avatar Jul 07 '22 11:07 deus82ex

Connect forwards the call to the underlying C++ DLL from SAP itself, so I would think the host/network is not reachable and it takes a very long time to timeout.

What you could verify is that the bitness (x86 / x64) of the SAP libraries match those of your project by specifying <PlatformTarget>x64</PlatformTarget> in your csproj f.e.

huysentruitw avatar Aug 23 '22 19:08 huysentruitw

Just so I don't forget next time I get this same error, managed to fix this when publishing on an Azure App Service. It crashes on Connect() because it could not found all those ICU related DLLs. Putting them in various places (wwwroot, wwwroot/bin) do not work as expected. You can tell because the SAP trace file that gets generated on the app root looks like this:

Could not open the ICU common library. The following files must be in the path described by the environment variable "PATH": icuuc50.dll, icudt50.dll, icuin50.dll [D:/depot/bas/753_REL/src/flat/nlsui0.c 662] pid = 8324

Don't know exactly what I did to fix it, but I suggest a mixture of this:

  • as referenced here, you can modify your PATH. This is what I added on the /site folder:
<?xml version="1.0"?> 
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform"> 
  <system.webServer> 
    <runtime xdt:Transform="InsertIfMissing">
      <environmentVariables xdt:Transform="InsertIfMissing">
        <add name="PATH" value="%HOME%\site\wwwroot\Bin;D:\home\site\deployments\tools;%PATH%;" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />    
      </environmentVariables>
    </runtime> 
  </system.webServer> 
</configuration>
  • Copy the DLL files to a "bin" folder inside your wwwroot. Also copy them to the /site/deployments/tools (found that folder when checking the PATH in the Kudu enviroment).

  • Don't know this is needed, but as referenced in another ticket, I copied the CurrentDirectoryHelpers.SetCurrentDirectory and called it in my Program.cs:

namespace Backend.Datasync.API.Utils
{
    internal class CurrentDirectoryHelpers
    {
        internal const string AspNetCoreModuleDll = "aspnetcorev2_inprocess.dll";

        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        [System.Runtime.InteropServices.DllImport(AspNetCoreModuleDll)]
        private static extern int http_get_application_properties(ref IISConfigurationData iiConfigData);

        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        private struct IISConfigurationData
        {
            public IntPtr pNativeApplication;
            [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.BStr)]
            public string pwzFullApplicationPath;
            [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.BStr)]
            public string pwzVirtualApplicationPath;
            public bool fWindowsAuthEnabled;
            public bool fBasicAuthEnabled;
            public bool fAnonymousAuthEnable;
        }

        public static bool SetCurrentDirectory()
        {
            try
            {
                // Check if physical path was provided by ANCM
                var sitePhysicalPath = Environment.GetEnvironmentVariable("ASPNETCORE_IIS_PHYSICAL_PATH");
                if (string.IsNullOrEmpty(sitePhysicalPath))
                {
                    // Skip if not running ANCM InProcess
                    if (GetModuleHandle(AspNetCoreModuleDll) == IntPtr.Zero)
                    {
                        return false;
                    }

                    IISConfigurationData configurationData = default(IISConfigurationData);
                    if (http_get_application_properties(ref configurationData) != 0)
                    {
                        return false;
                    }

                    sitePhysicalPath = configurationData.pwzFullApplicationPath;
                }

                Environment.CurrentDirectory = sitePhysicalPath;
                return true;
            }
            catch
            {
                // ignore
                return false;
            }
        }
    }
}

  • Restart your app service.

Right now it's working, so I'll keeping knocking on wood and hope it keeps working.

itoledo avatar Aug 31 '22 15:08 itoledo