Skip to content

Commit 219a96c

Browse files
committed
Map IPNetwork <-> cidr
Closes #3321
1 parent 222c13a commit 219a96c

File tree

5 files changed

+159
-15
lines changed

5 files changed

+159
-15
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
using System.Net;
2+
using System.Text.Json;
3+
using Microsoft.EntityFrameworkCore.Storage.Json;
4+
5+
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.Mapping;
6+
7+
/// <summary>
8+
/// The type mapping for the PostgreSQL cidr type.
9+
/// </summary>
10+
/// <remarks>
11+
/// See: https://www.postgresql.org/docs/current/static/datatype-net-types.html#DATATYPE-CIDR
12+
/// </remarks>
13+
public class NpgsqlLegacyCidrTypeMapping : NpgsqlTypeMapping
14+
{
15+
/// <summary>
16+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
17+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
18+
/// any release. You should only use it directly in your code with extreme caution and knowing that
19+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
20+
/// </summary>
21+
public static NpgsqlLegacyCidrTypeMapping Default { get; } = new();
22+
23+
/// <summary>
24+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
25+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
26+
/// any release. You should only use it directly in your code with extreme caution and knowing that
27+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
28+
/// </summary>
29+
public NpgsqlLegacyCidrTypeMapping()
30+
: base("cidr", typeof(NpgsqlCidr), NpgsqlDbType.Cidr, JsonCidrLegacyReaderWriter.Instance)
31+
{
32+
}
33+
34+
/// <summary>
35+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
36+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
37+
/// any release. You should only use it directly in your code with extreme caution and knowing that
38+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
39+
/// </summary>
40+
protected NpgsqlLegacyCidrTypeMapping(RelationalTypeMappingParameters parameters)
41+
: base(parameters, NpgsqlDbType.Cidr)
42+
{
43+
}
44+
45+
/// <summary>
46+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
47+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
48+
/// any release. You should only use it directly in your code with extreme caution and knowing that
49+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
50+
/// </summary>
51+
protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters)
52+
=> new NpgsqlLegacyCidrTypeMapping(parameters);
53+
54+
/// <summary>
55+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
56+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
57+
/// any release. You should only use it directly in your code with extreme caution and knowing that
58+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
59+
/// </summary>
60+
protected override string GenerateNonNullSqlLiteral(object value)
61+
{
62+
var cidr = (NpgsqlCidr)value;
63+
return $"CIDR '{cidr.Address}/{cidr.Netmask}'";
64+
}
65+
66+
/// <summary>
67+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
68+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
69+
/// any release. You should only use it directly in your code with extreme caution and knowing that
70+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
71+
/// </summary>
72+
public override Expression GenerateCodeLiteral(object value)
73+
{
74+
var cidr = (NpgsqlCidr)value;
75+
return Expression.New(
76+
NpgsqlCidrConstructor,
77+
Expression.Call(ParseMethod, Expression.Constant(cidr.Address.ToString())),
78+
Expression.Constant(cidr.Netmask));
79+
}
80+
81+
private static readonly MethodInfo ParseMethod = typeof(IPAddress).GetMethod("Parse", [typeof(string)])!;
82+
83+
private static readonly ConstructorInfo NpgsqlCidrConstructor =
84+
typeof(NpgsqlCidr).GetConstructor([typeof(IPAddress), typeof(byte)])!;
85+
86+
/// <summary>
87+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
88+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
89+
/// any release. You should only use it directly in your code with extreme caution and knowing that
90+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
91+
/// </summary>
92+
public sealed class JsonCidrLegacyReaderWriter : JsonValueReaderWriter<NpgsqlCidr>
93+
{
94+
private static readonly PropertyInfo InstanceProperty = typeof(JsonCidrLegacyReaderWriter).GetProperty(nameof(Instance))!;
95+
96+
/// <summary>
97+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
98+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
99+
/// any release. You should only use it directly in your code with extreme caution and knowing that
100+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
101+
/// </summary>
102+
public static JsonCidrLegacyReaderWriter Instance { get; } = new();
103+
104+
/// <summary>
105+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
106+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
107+
/// any release. You should only use it directly in your code with extreme caution and knowing that
108+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
109+
/// </summary>
110+
public override NpgsqlCidr FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
111+
=> new(manager.CurrentReader.GetString()!);
112+
113+
/// <summary>
114+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
115+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
116+
/// any release. You should only use it directly in your code with extreme caution and knowing that
117+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
118+
/// </summary>
119+
public override void ToJsonTyped(Utf8JsonWriter writer, NpgsqlCidr value)
120+
=> writer.WriteStringValue(value.ToString());
121+
122+
/// <inheritdoc />
123+
public override Expression ConstructorExpression => Expression.Property(null, InstanceProperty);
124+
}
125+
}

src/EFCore.PG/Storage/Internal/Mapping/NpgsqlCidrTypeMapping.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class NpgsqlCidrTypeMapping : NpgsqlTypeMapping
2727
/// doing so can result in application failures when updating to a new Entity Framework Core release.
2828
/// </summary>
2929
public NpgsqlCidrTypeMapping()
30-
: base("cidr", typeof(NpgsqlCidr), NpgsqlDbType.Cidr, JsonCidrReaderWriter.Instance)
30+
: base("cidr", typeof(IPNetwork), NpgsqlDbType.Cidr, JsonCidrReaderWriter.Instance)
3131
{
3232
}
3333

@@ -59,8 +59,8 @@ protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters p
5959
/// </summary>
6060
protected override string GenerateNonNullSqlLiteral(object value)
6161
{
62-
var cidr = (NpgsqlCidr)value;
63-
return $"CIDR '{cidr.Address}/{cidr.Netmask}'";
62+
var ipNetwork = (IPNetwork)value;
63+
return $"CIDR '{ipNetwork.BaseAddress}/{ipNetwork.PrefixLength}'";
6464
}
6565

6666
/// <summary>
@@ -71,25 +71,25 @@ protected override string GenerateNonNullSqlLiteral(object value)
7171
/// </summary>
7272
public override Expression GenerateCodeLiteral(object value)
7373
{
74-
var cidr = (NpgsqlCidr)value;
74+
var cidr = (IPNetwork)value;
7575
return Expression.New(
7676
NpgsqlCidrConstructor,
77-
Expression.Call(ParseMethod, Expression.Constant(cidr.Address.ToString())),
78-
Expression.Constant(cidr.Netmask));
77+
Expression.Call(ParseMethod, Expression.Constant(cidr.BaseAddress.ToString())),
78+
Expression.Constant(cidr.PrefixLength));
7979
}
8080

8181
private static readonly MethodInfo ParseMethod = typeof(IPAddress).GetMethod("Parse", [typeof(string)])!;
8282

8383
private static readonly ConstructorInfo NpgsqlCidrConstructor =
84-
typeof(NpgsqlCidr).GetConstructor([typeof(IPAddress), typeof(byte)])!;
84+
typeof(IPNetwork).GetConstructor([typeof(IPAddress), typeof(int)])!;
8585

8686
/// <summary>
8787
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
8888
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
8989
/// any release. You should only use it directly in your code with extreme caution and knowing that
9090
/// doing so can result in application failures when updating to a new Entity Framework Core release.
9191
/// </summary>
92-
public sealed class JsonCidrReaderWriter : JsonValueReaderWriter<NpgsqlCidr>
92+
public sealed class JsonCidrReaderWriter : JsonValueReaderWriter<IPNetwork>
9393
{
9494
private static readonly PropertyInfo InstanceProperty = typeof(JsonCidrReaderWriter).GetProperty(nameof(Instance))!;
9595

@@ -107,17 +107,17 @@ public sealed class JsonCidrReaderWriter : JsonValueReaderWriter<NpgsqlCidr>
107107
/// any release. You should only use it directly in your code with extreme caution and knowing that
108108
/// doing so can result in application failures when updating to a new Entity Framework Core release.
109109
/// </summary>
110-
public override NpgsqlCidr FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
111-
=> new(manager.CurrentReader.GetString()!);
110+
public override IPNetwork FromJsonTyped(ref Utf8JsonReaderManager manager, object? existingObject = null)
111+
=> IPNetwork.Parse(manager.CurrentReader.GetString()!);
112112

113113
/// <summary>
114114
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
115115
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
116116
/// any release. You should only use it directly in your code with extreme caution and knowing that
117117
/// doing so can result in application failures when updating to a new Entity Framework Core release.
118118
/// </summary>
119-
public override void ToJsonTyped(Utf8JsonWriter writer, NpgsqlCidr value)
120-
=> writer.WriteStringValue(value.ToString());
119+
public override void ToJsonTyped(Utf8JsonWriter writer, IPNetwork ipNetwork)
120+
=> writer.WriteStringValue(ipNetwork.ToString());
121121

122122
/// <inheritdoc />
123123
public override Expression ConstructorExpression => Expression.Property(null, InstanceProperty);

src/EFCore.PG/Storage/Internal/NpgsqlTypeMappingSource.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,8 @@ static NpgsqlTypeMappingSource()
114114
private readonly NpgsqlMacaddr8TypeMapping _macaddr8 = NpgsqlMacaddr8TypeMapping.Default;
115115
private readonly NpgsqlInetTypeMapping _inetAsIPAddress = NpgsqlInetTypeMapping.Default;
116116
private readonly NpgsqlInetTypeMapping _inetAsNpgsqlInet = new(typeof(NpgsqlInet));
117-
private readonly NpgsqlCidrTypeMapping _cidr = NpgsqlCidrTypeMapping.Default;
117+
private readonly NpgsqlCidrTypeMapping _cidrAsIPNetwork = NpgsqlCidrTypeMapping.Default;
118+
private readonly NpgsqlLegacyCidrTypeMapping _cidrAsNpgsqlCidr = NpgsqlLegacyCidrTypeMapping.Default;
118119

119120
// Built-in geometric types
120121
private readonly NpgsqlPointTypeMapping _point = NpgsqlPointTypeMapping.Default;
@@ -257,7 +258,7 @@ public NpgsqlTypeMappingSource(
257258
{ "macaddr", [_macaddr] },
258259
{ "macaddr8", [_macaddr8] },
259260
{ "inet", [_inetAsIPAddress, _inetAsNpgsqlInet] },
260-
{ "cidr", [_cidr] },
261+
{ "cidr", [_cidrAsIPNetwork, _cidrAsNpgsqlCidr] },
261262
{ "point", [_point] },
262263
{ "box", [_box] },
263264
{ "line", [_line] },
@@ -320,7 +321,8 @@ public NpgsqlTypeMappingSource(
320321
{ typeof(PhysicalAddress), _macaddr },
321322
{ typeof(IPAddress), _inetAsIPAddress },
322323
{ typeof(NpgsqlInet), _inetAsNpgsqlInet },
323-
{ typeof(NpgsqlCidr), _cidr },
324+
{ typeof(IPNetwork), _cidrAsIPNetwork },
325+
{ typeof(NpgsqlCidr), _cidrAsNpgsqlCidr },
324326
{ typeof(BitArray), _varbit },
325327
{ typeof(ImmutableDictionary<string, string>), _immutableHstore },
326328
{ typeof(Dictionary<string, string>), _hstore },

test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingSourceTest.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Net;
2+
using System.Net.NetworkInformation;
13
using Microsoft.EntityFrameworkCore.Storage.Json;
24
using NetTopologySuite.Geometries;
35
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
@@ -41,6 +43,7 @@ public class NpgsqlTypeMappingSourceTest
4143
[InlineData("geometry(POLYGONM)", typeof(Polygon), null, null, null, false)]
4244
[InlineData("xid", typeof(uint), null, null, null, false)]
4345
[InlineData("xid8", typeof(ulong), null, null, null, false)]
46+
[InlineData("cidr", typeof(IPNetwork), null, null, null, false)]
4447
public void By_StoreType(string typeName, Type type, int? size, int? precision, int? scale, bool fixedLength)
4548
{
4649
var mapping = CreateTypeMappingSource().FindMapping(typeName);
@@ -114,6 +117,10 @@ public void Timestamp_without_time_zone_Array_5()
114117
[InlineData(typeof(List<NpgsqlRange<int>>), "int4multirange")]
115118
[InlineData(typeof(Geometry), "geometry")]
116119
[InlineData(typeof(Point), "geometry")]
120+
[InlineData(typeof(IPAddress), "inet")]
121+
[InlineData(typeof(IPNetwork), "cidr")]
122+
[InlineData(typeof(NpgsqlCidr), "cidr")] // legacy
123+
[InlineData(typeof(PhysicalAddress), "macaddr")]
117124
public void By_ClrType(Type clrType, string expectedStoreType)
118125
{
119126
var mapping = CreateTypeMappingSource().FindMapping(clrType);

test/EFCore.PG.Tests/Storage/NpgsqlTypeMappingTest.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,10 +282,20 @@ public void GenerateCodeLiteral_returns_inet_literal()
282282

283283
[Fact]
284284
public void GenerateSqlLiteral_returns_cidr_literal()
285+
=> Assert.Equal("CIDR '192.168.1.0/24'", GetMapping("cidr").GenerateSqlLiteral(new IPNetwork(IPAddress.Parse("192.168.1.0"), 24)));
286+
287+
[Fact]
288+
public void GenerateSqlLiteral_returns_legacy_cidr_literal()
285289
=> Assert.Equal("CIDR '192.168.1.0/24'", GetMapping("cidr").GenerateSqlLiteral(new NpgsqlCidr(IPAddress.Parse("192.168.1.0"), 24)));
286290

287291
[Fact]
288292
public void GenerateCodeLiteral_returns_cidr_literal()
293+
=> Assert.Equal(
294+
"""new System.Net.IPNetwork(System.Net.IPAddress.Parse("192.168.1.0"), 24)""",
295+
CodeLiteral(new IPNetwork(IPAddress.Parse("192.168.1.0"), 24)));
296+
297+
[Fact]
298+
public void GenerateCodeLiteral_returns_legacy_cidr_literal()
289299
=> Assert.Equal(
290300
"""new NpgsqlTypes.NpgsqlCidr(System.Net.IPAddress.Parse("192.168.1.0"), (byte)24)""",
291301
CodeLiteral(new NpgsqlCidr(IPAddress.Parse("192.168.1.0"), 24)));

0 commit comments

Comments
 (0)