Skip to content

Commit 41495cb

Browse files
committed
.Net 10
1 parent ad2f0be commit 41495cb

12 files changed

Lines changed: 327 additions & 11 deletions

File tree

.github/workflows/.net-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
- name: Setup .NET Core
2525
uses: actions/setup-dotnet@v1
2626
with:
27-
dotnet-version: 8.0.100
27+
dotnet-version: 10.0.100
2828

2929
- name: Tests
3030
run: dotnet test EntityFrameworkCore.SqlServer.SimpleBulks.Tests/EntityFrameworkCore.SqlServer.SimpleBulks.Tests.csproj /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura

.github/workflows/.net-test-connection-extensions-discriminator-enabled.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
- name: Setup .NET Core
2525
uses: actions/setup-dotnet@v1
2626
with:
27-
dotnet-version: 8.0.100
27+
dotnet-version: 10.0.100
2828

2929
- name: Tests
3030
run: dotnet test EntityFrameworkCore.SqlServer.SimpleBulks.Tests/EntityFrameworkCore.SqlServer.SimpleBulks.Tests.csproj /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura

.github/workflows/.net-test-connection-extensions.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
- name: Setup .NET Core
2525
uses: actions/setup-dotnet@v1
2626
with:
27-
dotnet-version: 8.0.100
27+
dotnet-version: 10.0.100
2828

2929
- name: Tests
3030
run: dotnet test EntityFrameworkCore.SqlServer.SimpleBulks.Tests/EntityFrameworkCore.SqlServer.SimpleBulks.Tests.csproj /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura

.github/workflows/.net-test-dbcontext-extensions-discriminator-enabled.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
- name: Setup .NET Core
2525
uses: actions/setup-dotnet@v1
2626
with:
27-
dotnet-version: 8.0.100
27+
dotnet-version: 10.0.100
2828

2929
- name: Tests
3030
run: dotnet test EntityFrameworkCore.SqlServer.SimpleBulks.Tests/EntityFrameworkCore.SqlServer.SimpleBulks.Tests.csproj /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura

.github/workflows/.net-test-dbcontext-extensions.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
- name: Setup .NET Core
2525
uses: actions/setup-dotnet@v1
2626
with:
27-
dotnet-version: 8.0.100
27+
dotnet-version: 10.0.100
2828

2929
- name: Tests
3030
run: dotnet test EntityFrameworkCore.SqlServer.SimpleBulks.Tests/EntityFrameworkCore.SqlServer.SimpleBulks.Tests.csproj /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura

src/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project>
22
<PropertyGroup>
3-
<CurrentTargetFramework>net8.0</CurrentTargetFramework>
3+
<CurrentTargetFramework>net10.0</CurrentTargetFramework>
44
<CurrentNet8Version>8.22.0</CurrentNet8Version>
55
<CurrentNet9Version>9.0.0-preview.1</CurrentNet9Version>
66
<CurrentNet10Version>10.0.0-preview.1</CurrentNet10Version>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace EntityFrameworkCore.SqlServer.SimpleBulks.Tests.Database;
2+
3+
public class JsonComplexTypeOrder
4+
{
5+
public int Id { get; set; }
6+
7+
public ComplexTypeAddress ShippingAddress { get; set; }
8+
}

src/EntityFrameworkCore.SqlServer.SimpleBulks.Tests/Database/TestDbContext.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ public class TestDbContext : DbContext
2121

2222
public DbSet<ComplexOwnedTypeOrder> ComplexOwnedTypeOrders { get; set; }
2323

24+
public DbSet<JsonComplexTypeOrder> JsonComplexTypeOrders { get; set; }
25+
2426
public DbSet<Blog> Blogs { get; set; }
2527

2628
public DbSet<RssBlog> RssBlogs { get; set; }
@@ -41,7 +43,7 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
4143
protected override void OnModelCreating(ModelBuilder modelBuilder)
4244
{
4345

44-
if(!string.IsNullOrEmpty(_schema))
46+
if (!string.IsNullOrEmpty(_schema))
4547
{
4648
modelBuilder.HasDefaultSchema(_schema);
4749
}
@@ -59,6 +61,16 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
5961

6062
modelBuilder.Entity<Contact>().Property(x => x.Id).HasDefaultValueSql("newsequentialid()");
6163

64+
modelBuilder.Entity<JsonComplexTypeOrder>().ComplexProperty(x => x.ShippingAddress, x =>
65+
{
66+
x.ToJson();
67+
//x.ToJson("xxx").HasColumnType("json");
68+
x.ComplexProperty(y => y.Location, y =>
69+
{
70+
y.HasJsonPropertyName("xxx");
71+
});
72+
});
73+
6274
base.OnModelCreating(modelBuilder);
6375
}
6476
}

src/EntityFrameworkCore.SqlServer.SimpleBulks.Tests/DbContextExtensions/GetPropertiesTests.cs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using EntityFrameworkCore.SqlServer.SimpleBulks.Extensions;
22
using EntityFrameworkCore.SqlServer.SimpleBulks.Tests.Database;
33
using Microsoft.EntityFrameworkCore.Metadata;
4+
using System.Text;
5+
using System.Text.Json;
46

57
namespace EntityFrameworkCore.SqlServer.SimpleBulks.Tests.DbContextExtensions;
68

@@ -251,4 +253,136 @@ public void GetProperties_ComplexOwnedType_ReturnsCorrectColumnInformation()
251253
Assert.False(property.IsPrimaryKey);
252254
Assert.False(property.IsRowVersion);
253255
}
256+
257+
[Fact]
258+
public void GetProperties_JsonComplexType_ReturnsCorrectColumnInformation()
259+
{
260+
// Arrange
261+
var dbContext = new TestDbContext("", "");
262+
263+
// Act
264+
var properties = dbContext.GetProperties(typeof(JsonComplexTypeOrder));
265+
266+
// Assert
267+
Assert.Equal(2, properties.Count);
268+
269+
var property = properties.First(p => p.PropertyName == "Id");
270+
Assert.Equal(typeof(int), property.PropertyType);
271+
Assert.Equal("Id", property.ColumnName);
272+
Assert.Equal("int", property.ColumnType);
273+
Assert.Equal(ValueGenerated.OnAdd, property.ValueGenerated);
274+
Assert.Null(property.DefaultValueSql);
275+
Assert.True(property.IsPrimaryKey);
276+
Assert.False(property.IsRowVersion);
277+
278+
property = properties.First(p => p.PropertyName == "ShippingAddress");
279+
Assert.Equal(typeof(ComplexTypeAddress), property.PropertyType);
280+
Assert.Equal("ShippingAddress", property.ColumnName);
281+
Assert.Equal("nvarchar(max)", property.ColumnType);
282+
Assert.Equal(ValueGenerated.Never, property.ValueGenerated);
283+
Assert.Null(property.DefaultValueSql);
284+
Assert.False(property.IsPrimaryKey);
285+
Assert.False(property.IsRowVersion);
286+
Assert.True(property.IsJson);
287+
Assert.NotNull(property.JsonPropertyWriters);
288+
289+
// Verify that the JsonPropertyWriters have correct structure
290+
var writers = property.JsonPropertyWriters;
291+
var flattenedWriters = property.GetFlattenedJsonPropertyWriters();
292+
Assert.Equal(2, writers.Count); // Street and Location
293+
294+
var streetWriter = writers.FirstOrDefault(w => w.ClrPropertyName == "Street");
295+
Assert.NotNull(streetWriter);
296+
Assert.Equal("Street", streetWriter.JsonPropertyName);
297+
Assert.Equal("Street", streetWriter.FullJsonPath);
298+
Assert.Equal("Street", streetWriter.FullClrPropertyName);
299+
Assert.Equal(typeof(string), streetWriter.PropertyType);
300+
Assert.False(streetWriter.IsNestedComplexType);
301+
302+
var locationWriter = writers.FirstOrDefault(w => w.ClrPropertyName == "Location");
303+
Assert.NotNull(locationWriter);
304+
Assert.Equal("xxx", locationWriter.JsonPropertyName); // Mapped to "xxx" in TestDbContext
305+
Assert.Equal("xxx", locationWriter.FullJsonPath);
306+
Assert.Equal("Location", locationWriter.FullClrPropertyName);
307+
Assert.Equal(typeof(ComplexTypeLocation), locationWriter.PropertyType);
308+
Assert.True(locationWriter.IsNestedComplexType);
309+
Assert.NotNull(locationWriter.NestedProperties);
310+
Assert.Equal(2, locationWriter.NestedProperties.Count); // Lat and Lng
311+
312+
var latWriter = locationWriter.NestedProperties.FirstOrDefault(w => w.ClrPropertyName == "Lat");
313+
Assert.NotNull(latWriter);
314+
Assert.Equal("xxx.Lat", latWriter.FullJsonPath);
315+
Assert.Equal("Location.Lat", latWriter.FullClrPropertyName);
316+
Assert.Equal(typeof(double), latWriter.PropertyType);
317+
318+
var lngWriter = locationWriter.NestedProperties.FirstOrDefault(w => w.ClrPropertyName == "Lng");
319+
Assert.NotNull(lngWriter);
320+
Assert.Equal("xxx.Lng", lngWriter.FullJsonPath);
321+
Assert.Equal("Location.Lng", lngWriter.FullClrPropertyName);
322+
Assert.Equal(typeof(double), lngWriter.PropertyType);
323+
324+
// Verify serialization using JsonPropertyWriters
325+
var testAddress = new ComplexTypeAddress
326+
{
327+
Street = "123 Main St",
328+
Location = new ComplexTypeLocation
329+
{
330+
Lat = 40.7128,
331+
Lng = -74.0060
332+
}
333+
};
334+
335+
var json = SerializeWithJsonPropertyWriters(testAddress, writers);
336+
Assert.NotNull(json);
337+
Assert.Contains("Street", json);
338+
Assert.Contains("123 Main St", json);
339+
Assert.Contains("xxx", json); // Location is mapped to "xxx"
340+
Assert.Contains("Lat", json);
341+
Assert.Contains("Lng", json);
342+
}
343+
344+
private static string SerializeWithJsonPropertyWriters(object obj, IReadOnlyList<JsonPropertyWriter> writers)
345+
{
346+
using var stream = new MemoryStream();
347+
using var writer = new Utf8JsonWriter(stream);
348+
349+
WriteObject(writer, obj, writers);
350+
351+
writer.Flush();
352+
return Encoding.UTF8.GetString(stream.ToArray());
353+
}
354+
355+
private static void WriteObject(Utf8JsonWriter writer, object obj, IReadOnlyList<JsonPropertyWriter> propertyWriters)
356+
{
357+
writer.WriteStartObject();
358+
359+
var type = obj.GetType();
360+
foreach (var propWriter in propertyWriters)
361+
{
362+
var propInfo = type.GetProperty(propWriter.ClrPropertyName);
363+
if (propInfo == null) continue;
364+
365+
var value = propInfo.GetValue(obj);
366+
367+
writer.WritePropertyName(propWriter.JsonPropertyName);
368+
369+
if (propWriter.IsNestedComplexType)
370+
{
371+
if (value == null)
372+
{
373+
writer.WriteNullValue();
374+
}
375+
else
376+
{
377+
WriteObject(writer, value, propWriter.NestedProperties);
378+
}
379+
}
380+
else
381+
{
382+
propWriter.WriteValue(writer, value);
383+
}
384+
}
385+
386+
writer.WriteEndObject();
387+
}
254388
}

src/EntityFrameworkCore.SqlServer.SimpleBulks/ColumnInfor.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using Microsoft.EntityFrameworkCore.Metadata;
2+
using Microsoft.EntityFrameworkCore.Storage.Json;
23
using System;
4+
using System.Collections.Generic;
5+
using System.Text.Json;
36

47
namespace EntityFrameworkCore.SqlServer.SimpleBulks;
58

@@ -22,4 +25,71 @@ public class ColumnInfor
2225
public bool IsRowVersion { get; init; }
2326

2427
public ValueConverter? ValueConverter { get; set; }
28+
29+
public bool IsJson { get; set; }
30+
31+
public IReadOnlyList<JsonPropertyWriter>? JsonPropertyWriters { get; init; }
32+
33+
public IReadOnlyDictionary<string, JsonPropertyWriter> GetFlattenedJsonPropertyWriters()
34+
{
35+
if (JsonPropertyWriters == null || JsonPropertyWriters.Count == 0)
36+
{
37+
return new Dictionary<string, JsonPropertyWriter>();
38+
}
39+
40+
var result = new Dictionary<string, JsonPropertyWriter>();
41+
FlattenJsonPropertyWriters(JsonPropertyWriters, result);
42+
return result;
43+
}
44+
45+
private static void FlattenJsonPropertyWriters(IReadOnlyList<JsonPropertyWriter> writers, Dictionary<string, JsonPropertyWriter> result)
46+
{
47+
foreach (var writer in writers)
48+
{
49+
result[writer.FullClrPropertyName] = writer;
50+
51+
if (writer.IsNestedComplexType)
52+
{
53+
FlattenJsonPropertyWriters(writer.NestedProperties, result);
54+
}
55+
}
56+
}
57+
}
58+
59+
public class JsonPropertyWriter
60+
{
61+
public string JsonPropertyName { get; init; }
62+
63+
public string ClrPropertyName { get; init; }
64+
65+
public string FullJsonPath { get; init; }
66+
67+
public string FullClrPropertyName { get; init; }
68+
69+
public Type PropertyType { get; init; }
70+
71+
public JsonValueReaderWriter? ReaderWriter { get; init; }
72+
73+
public IReadOnlyList<JsonPropertyWriter>? NestedProperties { get; init; }
74+
75+
public bool IsNestedComplexType => NestedProperties != null && NestedProperties.Count > 0;
76+
77+
public void WriteValue(Utf8JsonWriter writer, object? value)
78+
{
79+
if (value == null)
80+
{
81+
writer.WriteNullValue();
82+
return;
83+
}
84+
85+
if (ReaderWriter != null)
86+
{
87+
ReaderWriter.ToJson(writer, value);
88+
}
89+
else
90+
{
91+
// Fallback to System.Text.Json for types without a specific JsonValueReaderWriter
92+
JsonSerializer.Serialize(writer, value, PropertyType);
93+
}
94+
}
2595
}

0 commit comments

Comments
 (0)