|
19 | 19 |
|
20 | 20 | namespace Pomelo.EntityFrameworkCore.MySql.Storage.Internal |
21 | 21 | { |
22 | | - /// <summary> |
23 | | - /// Type mapping for complex JSON types (used when JSON is mapped to complex .NET types). |
24 | | - /// Always converts string to MemoryStream since EF Core expects MemoryStream for complex JSON. |
25 | | - /// </summary> |
26 | | - public class MySqlComplexJsonTypeMapping : MySqlJsonTypeMapping<string> |
27 | | - { |
28 | | - public MySqlComplexJsonTypeMapping( |
29 | | - [NotNull] string storeType, |
30 | | - [CanBeNull] ValueConverter valueConverter, |
31 | | - [CanBeNull] ValueComparer valueComparer, |
32 | | - bool noBackslashEscapes, |
33 | | - bool replaceLineBreaksWithCharFunction) |
34 | | - : base(storeType, valueConverter, valueComparer, noBackslashEscapes, replaceLineBreaksWithCharFunction) |
35 | | - { |
36 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping constructor called - StoreType: {storeType}, Converter: {valueConverter?.GetType().Name ?? "null"}"); |
37 | | - } |
38 | | - |
39 | | - protected MySqlComplexJsonTypeMapping( |
40 | | - RelationalTypeMappingParameters parameters, |
41 | | - MySqlDbType mySqlDbType, |
42 | | - bool noBackslashEscapes, |
43 | | - bool replaceLineBreaksWithCharFunction) |
44 | | - : base(parameters, mySqlDbType, noBackslashEscapes, replaceLineBreaksWithCharFunction) |
45 | | - { |
46 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping protected constructor called - ClrType: {ClrType?.Name ?? "null"}, StoreType: {StoreType}"); |
47 | | - } |
48 | | - |
49 | | - protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters) |
50 | | - { |
51 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping.Clone(parameters) called - ClrType: {parameters.CoreParameters.ClrType?.Name ?? "null"}"); |
52 | | - return new MySqlComplexJsonTypeMapping(parameters, MySqlDbType, NoBackslashEscapes, ReplaceLineBreaksWithCharFunction); |
53 | | - } |
54 | | - |
55 | | - protected override RelationalTypeMapping Clone(bool? noBackslashEscapes = null, bool? replaceLineBreaksWithCharFunction = null) |
56 | | - { |
57 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping.Clone(bool) called - ClrType: {ClrType?.Name ?? "null"}, StoreType: {StoreType}"); |
58 | | - return new MySqlComplexJsonTypeMapping( |
59 | | - Parameters, |
60 | | - MySqlDbType, |
61 | | - noBackslashEscapes ?? NoBackslashEscapes, |
62 | | - replaceLineBreaksWithCharFunction ?? ReplaceLineBreaksWithCharFunction); |
63 | | - } |
64 | | - |
65 | | - /// <summary> |
66 | | - /// Returns the method to be used for reading JSON values from the database. |
67 | | - /// MySQL stores JSON as strings, so we use GetString. |
68 | | - /// </summary> |
69 | | - public override MethodInfo GetDataReaderMethod() |
70 | | - { |
71 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping.GetDataReaderMethod() called - ClrType: {ClrType?.Name ?? "null"}, returning: {_getString?.Name ?? "null"}"); |
72 | | - return _getString; |
73 | | - } |
74 | | - |
75 | | - /// <summary> |
76 | | - /// For complex JSON, we ALWAYS convert string to MemoryStream. |
77 | | - /// </summary> |
78 | | - public override Expression CustomizeDataReaderExpression(Expression expression) |
79 | | - { |
80 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping.CustomizeDataReaderExpression() called - ExpressionType: {expression.Type?.Name ?? "null"}, ClrType: {ClrType?.Name ?? "null"}"); |
81 | | - |
82 | | - if (expression.Type == typeof(string)) |
83 | | - { |
84 | | - // Validate that reflection lookups succeeded (using cached members from base class) |
85 | | - if (_utf8Property == null || _getBytesMethod == null || _memoryStreamCtor == null) |
86 | | - { |
87 | | - throw new InvalidOperationException( |
88 | | - "Failed to find required reflection members for JSON type mapping."); |
89 | | - } |
90 | | - |
91 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping: Converting string expression to MemoryStream"); |
92 | | - |
93 | | - // Convert string to MemoryStream: new MemoryStream(Encoding.UTF8.GetBytes(stringValue)) |
94 | | - return Expression.New( |
95 | | - _memoryStreamCtor, |
96 | | - Expression.Call( |
97 | | - Expression.Property(null, _utf8Property), |
98 | | - _getBytesMethod, |
99 | | - expression)); |
100 | | - } |
101 | | - |
102 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping: No conversion, calling base.CustomizeDataReaderExpression"); |
103 | | - return base.CustomizeDataReaderExpression(expression); |
104 | | - } |
105 | | - |
106 | | - public override string GenerateSqlLiteral(object value) |
107 | | - { |
108 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping.GenerateSqlLiteral called - value type: {value?.GetType()?.Name ?? "null"}"); |
109 | | - var result = base.GenerateSqlLiteral(value); |
110 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping.GenerateSqlLiteral result: {result}"); |
111 | | - return result; |
112 | | - } |
113 | | - |
114 | | - public override string GenerateProviderValueSqlLiteral(object value) |
115 | | - { |
116 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping.GenerateProviderValueSqlLiteral called - value type: {value?.GetType()?.Name ?? "null"}"); |
117 | | - var result = base.GenerateProviderValueSqlLiteral(value); |
118 | | - Console.WriteLine($"[DEBUG] MySqlComplexJsonTypeMapping.GenerateProviderValueSqlLiteral result: {result}"); |
119 | | - return result; |
120 | | - } |
121 | | - } |
122 | | - |
123 | 22 | public class MySqlJsonTypeMapping<T> : MySqlJsonTypeMapping |
124 | 23 | { |
125 | 24 | public static new MySqlJsonTypeMapping<T> Default { get; } = new("json", null, null, false, true); |
@@ -234,6 +133,44 @@ protected override void ConfigureParameter(DbParameter parameter) |
234 | 133 | } |
235 | 134 | } |
236 | 135 |
|
| 136 | + /// <summary> |
| 137 | + /// Returns the method to be used for reading JSON values from the database. |
| 138 | + /// MySQL stores JSON as strings, so we use GetString instead of the default GetFieldValue<T>. |
| 139 | + /// </summary> |
| 140 | + public override MethodInfo GetDataReaderMethod() |
| 141 | + => _getString; |
| 142 | + |
| 143 | + /// <summary> |
| 144 | + /// Customizes the data reader expression for JSON types. |
| 145 | + /// MySQL stores JSON as strings, but EF Core expects MemoryStream for complex JSON types. |
| 146 | + /// We only convert for non-string CLR types (complex JSON types), not for regular JSON columns mapped to string. |
| 147 | + /// </summary> |
| 148 | + public override Expression CustomizeDataReaderExpression(Expression expression) |
| 149 | + { |
| 150 | + // Only convert for complex JSON types (where ClrType is NOT string) |
| 151 | + // For regular JSON columns mapped to string, don't convert |
| 152 | + if (expression.Type == typeof(string) && ClrType != typeof(string)) |
| 153 | + { |
| 154 | + // Validate that reflection lookups succeeded |
| 155 | + if (_utf8Property == null || _getBytesMethod == null || _memoryStreamCtor == null) |
| 156 | + { |
| 157 | + throw new InvalidOperationException( |
| 158 | + "Failed to find required reflection members for JSON type mapping. " + |
| 159 | + "This may indicate an incompatible version of the .NET runtime."); |
| 160 | + } |
| 161 | + |
| 162 | + // Convert string to MemoryStream: new MemoryStream(Encoding.UTF8.GetBytes(stringValue)) |
| 163 | + return Expression.New( |
| 164 | + _memoryStreamCtor, |
| 165 | + Expression.Call( |
| 166 | + Expression.Property(null, _utf8Property), |
| 167 | + _getBytesMethod, |
| 168 | + expression)); |
| 169 | + } |
| 170 | + |
| 171 | + return base.CustomizeDataReaderExpression(expression); |
| 172 | + } |
| 173 | + |
237 | 174 | void IMySqlCSharpRuntimeAnnotationTypeMappingCodeGenerator.Create( |
238 | 175 | CSharpRuntimeAnnotationCodeGeneratorParameters codeGeneratorParameters, |
239 | 176 | CSharpRuntimeAnnotationCodeGeneratorDependencies codeGeneratorDependencies) |
|
0 commit comments