Repository: npgsql/npgsql

Issue #2314: Range API improvement
Created: 2019-02-05T21:39:52Z
Originally proposed by @shoooe in npgsql/efcore.pg#785

Motivation
As @shoooe already pointed out, the current API isn't friendly. This proposal is trying to solve it and investigate more natural way to create ranges.
Proposal
public readonly struct NpgsqlRange<T> : IEquatable<NpgsqlRange<T>>
    where T : IEquatable<T>, IComparable<T>
{
    // Creates a range with an exclusive upper bound
    public NpgsqlRange(T lowerBound, T upperBound);
    // Inclusivity is specified by bounds
    public NpgsqlRange(NpgsqlRangeBound<T> lowerBound, NpgsqlRangeBound<T> upperBound);

    public NpgsqlRangeBound<T> LowerBound { get; }
    public NpgsqlRangeBound<T> UpperBound { get; }

    public bool IsEmpty { get; }

    public bool IsCanonical { get; }
    public NpgsqlRange<T> Canonicalize();

    public bool Equals(object other);
    public bool Equals(NpgsqlRange<T> other);
    public int GetHashCode();
}

public readonly struct NpgsqlRangeBound<T> : IEquatable<NpgsqlRangeBound<T>>, IComparable<NpgsqlRangeBound<T>>
    where T : IEquatable<T>, IComparable<T>
{
    public NpgsqlRangeBound(T value);
    public NpgsqlRangeBound(T value, bool isInclusive);

    public bool IsInclusive { get; }
    public bool IsInfinite { get; }
    public T Value { get; }

    public bool Equals(object other);
    public bool Equals(NpgsqlRangeBound<T> other);
    public int CompareTo(NpgsqlRangeBound<T> other);
    public int GetHashCode();

    public static bool operator ==(NpgsqlRangeBound<T> left, NpgsqlRangeBound<T> right);
    public static bool operator !=(NpgsqlRangeBound<T> left, NpgsqlRangeBound<T> right);

    public static bool operator <(NpgsqlRangeBound<T> left, NpgsqlRangeBound<T> right);
    public static bool operator >(NpgsqlRangeBound<T> left, NpgsqlRangeBound<T> right);

    public static implicit operator T?(NpgsqlRangeBound<T value);
}

public static class NpgsqlRangeBound
{
    public static NpgsqlRangeBound<T> Exclusive(T value)
        where T : IEquatable<T>, IComparable<T>;

    public static NpgsqlRangeBound<T> Inclusive(T value)
        where T : IEquatable<T>, IComparable<T>;

    public static NpgsqlRangeBound<T> Infinite()
        where T : IEquatable<T>, IComparable<T>;
}

Duplicate issue #3598: NpgsqlRange<T> and null bounds
Created: 2021-03-14T15:18:09Z
What is the story begind null/infinity range bounds?
I'm currently working on better support for Range type in linq2db and one of issues I'm stumbled upon is how to create range value with null (infinity) bounds.
I see that bound parameters are marked by [AllowNull] attribute https://github.com/npgsql/npgsql/blob/main/src/Npgsql/NpgsqlTypes/NpgsqlRange.cs#L142, but it doesn't make any value for most of types, supported by range as they are value types.
And using something like NpgsqlRange<int?> doesn't really help as it fails on parameter value validation as it tries to convert value to non-nullable range, e.g.  NpgsqlRange<int>:
  Exception: System.InvalidCastException
    Message  : Specified cast is not valid.
       at Npgsql.TypeHandlers.RangeHandler`1.ValidateObjectAndGetLength(Object value, NpgsqlLengthCache& lengthCache, NpgsqlParameter parameter)
       at Npgsql.NpgsqlParameter.ValidateAndGetLength()
       at Npgsql.NpgsqlCommand.ValidateParameters(ConnectorTypeMapper typeMapper)

Closing comment (2021-03-14T15:21:32Z): duplicate of #2314