Repository: dotnet/efcore
File: src/EFCore.Relational/Query/SqlNullabilityProcessor.cs (C#)

Code before:
            && collection is SqlParameterExpression collectionParameter)
        {
            // We're looking at a parameter beyond its simple nullability, so we can't use the SQL cache for this query.
            var parameters = ParametersDecorator.GetAndDisableCaching();
            if (parameters[collectionParameter.Name] is not IEnumerable enumerable)
            {
                throw new UnreachableException($"Parameter '{collectionParameter.Name}' is not an IEnumerable.");
            }

            var values = enumerable.Cast<object?>().ToList();
            IList? processedValues = null;

            for (var i = 0; i < values.Count; i++)
            {
                var value = values[i];

                if (value is null)
                {
                    if (processedValues is null)
                    {
                        // We found the first null value - we need to start copying values to a new list which will be used for the rewritten parameter.
                        // The type of the new list must match that of the original enumerable parameter, as there may be value converters involved which
                        // rely on the precise element type (see #37605). We therefore get the type of the element from the original list if it implements
                        // IEnumerable<T>, or default to object.
                        var genericEnumerable = enumerable.GetType().GetGenericTypeImplementations(typeof(IEnumerable<>)).SingleOrDefault();
                        var elementClrType = genericEnumerable?.GenericTypeArguments.Single() ?? typeof(object);
                        processedValues = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elementClrType), values.Count)!;
                        for (var j = 0; j < i; j++)
                        {
                            processedValues.Add(values[j]!);
                        }
                    }

                    // Skip the value
                    continue;
                }

                processedValues?.Add(value);
            }

            if (processedValues is null)
            {
                // No null was found in the parameter's elements - the select expression is already non-nullable.
                // TODO: We should change the project column to be non-nullable, but it's too closed down for that.
                rewrittenSelectExpression = selectExpression;
                foundNull = false;
                return true;
            }

            foundNull = true;


Review comment (suggestion):
`GetGenericTypeImplementations(...).SingleOrDefault()` will throw if the runtime parameter type happens to implement multiple `IEnumerable<T>` interfaces. Since the intent here is to fall back to `object` when the element type can’t be uniquely determined, consider using `TryGetElementType(typeof(IEnumerable<>))` (or otherwise handling the multiple-implementation case) instead of `SingleOrDefault()`.
```suggestion
                        var elementClrType = enumerable.GetType().TryGetElementType(typeof(IEnumerable<>)) ?? typeof(object);
```

Code after:
            && collection is SqlParameterExpression collectionParameter)
        {
            // We're looking at a parameter beyond its simple nullability, so we can't use the SQL cache for this query.
            var parameters = ParametersDecorator.GetAndDisableCaching();
            if (parameters[collectionParameter.Name] is not IEnumerable enumerable)
            {
                throw new UnreachableException($"Parameter '{collectionParameter.Name}' is not an IEnumerable.");
            }

            var values = enumerable.Cast<object?>().ToList();
            IList? processedValues = null;

            for (var i = 0; i < values.Count; i++)
            {
                var value = values[i];

                if (value is null)
                {
                    if (processedValues is null)
                    {
                        // We found the first null value - we need to start copying values to a new list which will be used for the rewritten parameter.
                        // The type of the new list must match that of the original enumerable parameter, as there may be value converters involved which
                        // rely on the precise element type (see #37605). We therefore get the type of the element from the original list if it implements
                        // IEnumerable<T>, or default to object.
                        var elementClrType = enumerable.GetType().TryGetElementType(typeof(IEnumerable<>)) ?? typeof(object);
                        processedValues = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elementClrType), values.Count)!;
                        for (var j = 0; j < i; j++)
                        {
                            processedValues.Add(values[j]!);
                        }
                    }

                    // Skip the value
                    continue;
                }

                processedValues?.Add(value);
            }

            if (processedValues is null)
            {
                // No null was found in the parameter's elements - the select expression is already non-nullable.
                // TODO: We should change the project column to be non-nullable, but it's too closed down for that.
                rewrittenSelectExpression = selectExpression;
                foundNull = false;
                return true;
            }

            foundNull = true;

            var rewrittenParameter = new SqlParameterExpression(