elastic/elasticsearch-net

ElasticsearchClientProductRegistration ArgumentNullException

Opened this issue · 6 comments

Elastic.Clients.Elasticsearch version:9.0.0

Elasticsearch version:9.0.2

.NET runtime version:7.0.101

Operating system version:10.0.19045

Description of the problem:
I am trying to connect to the elasticsearch .net client in my asp.net mvc nopcommerce project.
this is my code:
namespace Nop.Services.Elasticsearch
{
public sealed class ElasticSeachConfiguration
{
private static readonly Lazy LazyClient = new Lazy(CreateClient);

    public static ElasticsearchClient ESContext => LazyClient.Value;

    private const string Username = "user";
    private const string Password = "pass";
    // private const string Fingerprint = "<YOUR_CA_FINGERPRINT>"; // optional

    private static ElasticsearchClient CreateClient()
    {
        var connectionString = LoadConnection();

        if (string.IsNullOrWhiteSpace(connectionString))
            throw new InvalidOperationException("Elasticsearch connection string is missing or invalid in App_Data/ElasticSettings.txt");

        Uri nodeUri;
        try
        {
            nodeUri = new Uri(connectionString);
        }
        catch (UriFormatException ex)
        {
            throw new InvalidOperationException("Invalid Elasticsearch URI format in ElasticSettings.txt", ex);
        }

        var pool = new SingleNodePool(nodeUri);

        var settings = new ElasticsearchClientSettings(pool)
            .Authentication(new BasicAuthentication(Username, Password))
            .CertificateFingerprint("<YOUR_CA_FINGERPRINT>")
            .EnableDebugMode();

        return new ElasticsearchClient(settings);
    }

    static void ConfigureOptions(JsonSerializerOptions o)
    {
        o.PropertyNamingPolicy = null;
    }

    private static string LoadConnection()
    {
        var filePath = Path.Combine(MapPath("~/App_Data/"), "ElasticSettings.txt");

        if (!File.Exists(filePath))
            throw new FileNotFoundException("Elasticsearch connection config not found at: " + filePath);

        var text = File.ReadAllText(filePath).Trim();
        if (string.IsNullOrWhiteSpace(text))
            throw new InvalidOperationException("ElasticSettings.txt is empty or contains only whitespace.");

        return text;
    }

    private static string MapPath(string path)
    {
        if (HostingEnvironment.IsHosted)
            return HostingEnvironment.MapPath(path);

        var baseDir = AppDomain.CurrentDomain.BaseDirectory;
        path = path.Replace("~/", "").TrimStart('/').Replace('/', '\\');
        return Path.Combine(baseDir, path);
    }

    private ElasticSeachConfiguration() { }
}

}

but i get the following error:
System.TypeInitializationException
HResult=0x80131534
Message=The type initializer for 'Elastic.Clients.Elasticsearch.ElasticsearchClientProductRegistration' threw an exception.
Source=Elastic.Clients.Elasticsearch
StackTrace:
at Elastic.Clients.Elasticsearch.ElasticsearchClientSettingsBase1..ctor(NodePool nodePool, IRequestInvoker requestInvoker, SourceSerializerFactory sourceSerializerFactory, IPropertyMappingProvider propertyMappingProvider) at Elastic.Clients.Elasticsearch.ElasticsearchClientSettings..ctor(NodePool nodePool) at Nop.Services.Elasticsearch.ElasticSeachConfiguration.CreateClient() in D:\Projects\IrsaProjects\Abar1\Libraries\Nop.Services\ElasticsearchService\ElasticSeachConfiguration.cs:line 43 at System.Lazy1.CreateValue()
--- End of stack trace from previous location ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Lazy1.get_Value() at Nop.Services.Elasticsearch.ElasticSeachConfiguration.get_ESContext() in D:\Projects\IrsaProjects\Abar1\Libraries\Nop.Services\ElasticsearchService\ElasticSeachConfiguration.cs:line 18 at Nop.Services.ElasticsearchService.ConnectToElasticService.EsClient() in D:\Projects\IrsaProjects\Abar1\Libraries\Nop.Services\ElasticsearchService\ConnectToElasticService.cs:line 16 at Nop.Web.Controllers.CatalogController.<CategoriesIconMode>b__89_0() in D:\Projects\IrsaProjects\Abar1\Presentation\Nop.Web\Controllers\CatalogController.cs:line 2308 at Nop.Core.Caching.CacheExtensions.Get[T](ICacheManager cacheManager, String key, Int32 cacheTime, Func1 acquire) in D:\Projects\IrsaProjects\Abar1\Libraries\Nop.Core\Caching\Extensions.cs:line 39
at Nop.Core.Caching.CacheExtensions.Get[T](ICacheManager cacheManager, String key, Func1 acquire) in D:\Projects\IrsaProjects\Abar1\Libraries\Nop.Core\Caching\Extensions.cs:line 20 at Nop.Web.Controllers.CatalogController.CategoriesIconMode() in D:\Projects\IrsaProjects\Abar1\Presentation\Nop.Web\Controllers\CatalogController.cs:line 2306 at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters)
at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters) at System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult2.CallEndDelegate(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.b__3d()
at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass46.b__3f()

Inner Exception 1:
ArgumentNullException: Value cannot be null.
Parameter name: input

In the previous verison(v8.x) of the client I never experienced such an issue.

Hi @Mahdi-Abbasi-2001 , is there a callstack for the inner exception?

I think this might be related to static initialization order. Btw.: Is it intended that your ElasticSeachConfiguration class is not static itself? Do you instantiate it somewhere?

This is the call stack for inner exception:

at System.Text.RegularExpressions.Regex.Match(String input)
at Elastic.Transport.Extensions.SemVersion.TryParse(String input, SemVersion& version)
at Elastic.Transport.RuntimeVersionInfo.GetRuntimeVersion()
at Elastic.Transport.MetaDataHeader..ctor(VersionInfo version, String serviceIdentifier, Boolean isAsync)
at Elastic.Transport.DefaultMetaHeaderProducer..ctor(VersionInfo versionInfo, String serviceIdentifier)
at Elastic.Transport.DefaultMetaHeaderProvider..ctor(VersionInfo versionInfo, String serviceIdentifier)
at Elastic.Transport.Products.Elasticsearch.ElasticsearchProductRegistration..ctor(Type markerType)
at Elastic.Clients.Elasticsearch.ElasticsearchClientProductRegistration..cctor()

Also I am using the ElasticSeachConfiguration class like this:

using Elastic.Clients.Elasticsearch;
using Nop.Services.Elasticsearch;
using System.Net;

namespace Nop.Services.ElasticsearchService
{
    public class ConnectToElasticService : IConnectToElasticService
    {
        public ElasticsearchClient EsClient()
        {
            ServicePointManager.Expect100Continue = true;
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            return ElasticSeachConfiguration.ESContext;
        }
    }
}

Thank you @flobernd for your time and effort and I'll be looking forward to any updates on this issue. I have developed this project based on nopCommerce 3.70. To test this issue, I tried it in several other non-nopCommerce projects as well, and there was no problem in those cases.

Hit the same issue when trying to use the .NET Standard 2.0 version of Elastic.Transport on a .NET Framework runtime (required for our scenario, because Elastic.Transport for net462 only allows using HttpWebRequest which is not acceptable for some cases).

I believe that GetRuntimeVersion and/or SemVersion.TryParse could handle the inability to detect version more gracefully.

@onyxmaster Thank you for adding this comment. So far I was never able to reproduce the issue, but your scenario with using netstandard2.0 in a net462 project is an important hint.

I'll give this another look.