Skip to content

Commit 3e376ff

Browse files
committed
Fix byte array stuff
1 parent ccef0d4 commit 3e376ff

4 files changed

Lines changed: 258 additions & 144 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Reflection;
7+
using Microsoft.EntityFrameworkCore;
8+
using Microsoft.EntityFrameworkCore.Diagnostics;
9+
using Microsoft.EntityFrameworkCore.Query;
10+
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
11+
12+
namespace EntityFrameworkCore.Jet.Query.ExpressionTranslators.Internal;
13+
14+
/// <summary>
15+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
16+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
17+
/// any release. You should only use it directly in your code with extreme caution and knowing that
18+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
19+
/// </summary>
20+
public class JetByteArrayMethodTranslator : IMethodCallTranslator
21+
{
22+
private readonly ISqlExpressionFactory _sqlExpressionFactory;
23+
24+
/// <summary>
25+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
26+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
27+
/// any release. You should only use it directly in your code with extreme caution and knowing that
28+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
29+
/// </summary>
30+
public JetByteArrayMethodTranslator(ISqlExpressionFactory sqlExpressionFactory)
31+
{
32+
_sqlExpressionFactory = sqlExpressionFactory;
33+
}
34+
35+
/// <summary>
36+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
37+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
38+
/// any release. You should only use it directly in your code with extreme caution and knowing that
39+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
40+
/// </summary>
41+
public virtual SqlExpression? Translate(
42+
SqlExpression? instance,
43+
MethodInfo method,
44+
IReadOnlyList<SqlExpression> arguments,
45+
IDiagnosticsLogger<DbLoggerCategory.Query> logger)
46+
{
47+
if (method is { IsGenericMethod: true, Name: nameof(Enumerable.Contains) }
48+
&& arguments[0].Type == typeof(byte[]))
49+
{
50+
var source = arguments[0];
51+
var sourceTypeMapping = source.TypeMapping;
52+
53+
var value = arguments[1] is SqlConstantExpression constantValue
54+
? (SqlExpression)_sqlExpressionFactory.Constant(new[] { (byte)constantValue.Value! }, sourceTypeMapping)
55+
: _sqlExpressionFactory.Convert(arguments[1], typeof(byte[]), sourceTypeMapping);
56+
57+
return _sqlExpressionFactory.GreaterThan(
58+
_sqlExpressionFactory.Function(
59+
"INSTR",
60+
new[] { _sqlExpressionFactory.Constant(1), source, value, _sqlExpressionFactory.Constant(1) },
61+
nullable: true,
62+
argumentsPropagateNullability: new[] { true, true },
63+
typeof(int)),
64+
_sqlExpressionFactory.Constant(0));
65+
}
66+
67+
if (method is { IsGenericMethod: true, Name: nameof(Enumerable.First) } && method.GetParameters().Length == 1
68+
&& arguments[0].Type == typeof(byte[]))
69+
{
70+
return _sqlExpressionFactory.Convert(
71+
_sqlExpressionFactory.Function(
72+
"MID",
73+
new[] { arguments[0], _sqlExpressionFactory.Constant(1), _sqlExpressionFactory.Constant(1) },
74+
nullable: true,
75+
argumentsPropagateNullability: new[] { true, true, true },
76+
typeof(byte[])),
77+
method.ReturnType);
78+
}
79+
80+
return null;
81+
}
82+
}

src/EFCore.Jet/Query/ExpressionTranslators/Internal/JetMethodCallTranslatorProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ public JetMethodCallTranslatorProvider(
1919
[NotNull] RelationalMethodCallTranslatorProviderDependencies dependencies)
2020
: base(dependencies)
2121
{
22-
var sqlExpressionFactory = (JetSqlExpressionFactory) dependencies.SqlExpressionFactory;
22+
var sqlExpressionFactory = (JetSqlExpressionFactory)dependencies.SqlExpressionFactory;
2323

2424
// ReSharper disable once VirtualMemberCallInConstructor
2525
AddTranslators(
2626
new IMethodCallTranslator[]
2727
{
28+
new JetByteArrayMethodTranslator(sqlExpressionFactory),
2829
new JetConvertTranslator(sqlExpressionFactory),
2930
new JetDateDiffFunctionsTranslator(sqlExpressionFactory),
3031
new JetDateTimeMethodTranslator(sqlExpressionFactory),

src/EFCore.Jet/Storage/Internal/JetTypeMappingSource.cs

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

33
using System;
4+
using System.Collections;
45
using System.Collections.Generic;
56
using System.Data;
67
using System.Linq;
78
using EntityFrameworkCore.Jet.Infrastructure.Internal;
89
using EntityFrameworkCore.Jet.Internal;
910
using JetBrains.Annotations;
11+
using Microsoft.EntityFrameworkCore.ChangeTracking;
1012
using Microsoft.EntityFrameworkCore.Metadata;
1113
using Microsoft.EntityFrameworkCore.Storage;
1214

@@ -29,7 +31,7 @@ public class JetTypeMappingSource : RelationalTypeMappingSource
2931
// We just map counter etc. to integer. Whether an integer property/column is actually a counter
3032
// is determined by the value generation type.
3133
private readonly IntTypeMapping _counter = new JetIntTypeMapping("integer");
32-
34+
3335
private readonly ByteTypeMapping _byte = new ByteTypeMapping("byte", DbType.Byte); // unsigned, there is no signed byte in Jet
3436
private readonly ShortTypeMapping _smallint = new ShortTypeMapping("smallint", DbType.Int16);
3537
private readonly IntTypeMapping _integer = new JetIntTypeMapping("integer");
@@ -52,7 +54,11 @@ public class JetTypeMappingSource : RelationalTypeMappingSource
5254
private readonly JetStringTypeMapping _unboundedUnicodeString = new JetStringTypeMapping("longchar", unicode: true, storeTypePostfix: StoreTypePostfix.None);
5355

5456
private readonly GuidTypeMapping _guid = new GuidTypeMapping("uniqueidentifier", DbType.Guid);
55-
private readonly JetByteArrayTypeMapping _rowversion = new JetByteArrayTypeMapping("varbinary", size: 8);
57+
private readonly JetByteArrayTypeMapping _rowversion = new JetByteArrayTypeMapping("varbinary", size: 8,
58+
comparer: new ValueComparer<byte[]>(
59+
(v1, v2) => StructuralComparisons.StructuralEqualityComparer.Equals(v1, v2),
60+
v => StructuralComparisons.StructuralEqualityComparer.GetHashCode(v),
61+
v => v.ToArray()));
5662

5763
private readonly Dictionary<string, RelationalTypeMapping> _storeTypeMappings;
5864
private readonly Dictionary<Type, RelationalTypeMapping> _clrTypeMappings;
@@ -76,7 +82,7 @@ public JetTypeMappingSource(
7682

7783
// TODO: Check the types and their mappings against
7884
// https://docs.microsoft.com/en-us/previous-versions/office/developer/office2000/aa140015(v=office.10)
79-
85+
8086
_datetime = new JetDateTimeTypeMapping("datetime", options, dbType: DbType.DateTime);
8187
_datetimeoffset = new JetDateTimeOffsetTypeMapping("datetime", options);
8288
_date = new JetDateTimeTypeMapping("datetime", options, dbType: DbType.Date);
@@ -102,11 +108,11 @@ public JetTypeMappingSource(
102108
{"logical", _bit},
103109
{"logical1", _bit},
104110
{"yesno", _bit},
105-
111+
106112
{"counter", _counter},
107113
{"identity", _counter},
108114
{"autoincrement", _counter},
109-
115+
110116
{"byte", _byte},
111117
{"tinyint", _byte},
112118
{"integer1", _byte},
@@ -119,7 +125,7 @@ public JetTypeMappingSource(
119125
{"long", _bigint},
120126
{"int", _integer},
121127
{"integer4", _integer},
122-
128+
123129
{"single", _single},
124130
{"real", _single},
125131
{"float4", _single},
@@ -304,10 +310,10 @@ protected override void ValidateMapping(CoreTypeMapping? mapping, IProperty? pro
304310
const int maxCharColumnSize = 255;
305311
const int maxIndexedCharColumnSize = 255;
306312

307-
var size = mappingInfo.Size ?? (mappingInfo.IsKeyOrIndex ? (int?) maxIndexedCharColumnSize : null);
313+
var size = mappingInfo.Size ?? (mappingInfo.IsKeyOrIndex ? (int?)maxIndexedCharColumnSize : null);
308314
if (size > maxCharColumnSize)
309315
{
310-
size = isFixedLength ? maxCharColumnSize : (int?) null;
316+
size = isFixedLength ? maxCharColumnSize : (int?)null;
311317
}
312318

313319
return size == null
@@ -331,10 +337,10 @@ protected override void ValidateMapping(CoreTypeMapping? mapping, IProperty? pro
331337

332338
const int maxBinaryColumnSize = 510;
333339

334-
var size = mappingInfo.Size ?? (mappingInfo.IsKeyOrIndex ? (int?) maxBinaryColumnSize : null);
340+
var size = mappingInfo.Size ?? (mappingInfo.IsKeyOrIndex ? (int?)maxBinaryColumnSize : null);
335341
if (size > maxBinaryColumnSize)
336342
{
337-
size = isFixedLength ? maxBinaryColumnSize : (int?) null;
343+
size = isFixedLength ? maxBinaryColumnSize : (int?)null;
338344
}
339345

340346
return size == null

0 commit comments

Comments
 (0)