Skip to content

Commit 68a71b0

Browse files
authored
Merge pull request #172 from DataObjects-NET/6.0-datetimeoffset-fieldaccessor
Adds FieldAccessor for DateTimeOffset
2 parents f46758f + 3dd120e commit 68a71b0

File tree

5 files changed

+282
-40
lines changed

5 files changed

+282
-40
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (C) 2021 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Reflection;
9+
using System.Text;
10+
using NUnit.Framework;
11+
using Xtensive.Tuples.Packed;
12+
using Xtensive.Orm.Tests;
13+
14+
namespace Xtensive.Tuples
15+
{
16+
[TestFixture]
17+
public sealed class DateTimeOffsetTupleTest
18+
{
19+
[Test]
20+
public void OnlyDateTimeOffsetFieldsTest()
21+
{
22+
var minLength = 30;
23+
var maxLength = 200;
24+
var iterationCount = 1000;
25+
var rnd = RandomManager.CreateRandom();
26+
var generator = InstanceGeneratorProvider.Default.GetInstanceGenerator<DateTimeOffset>();
27+
for (var i = 0; i < iterationCount; i++) {
28+
var count = rnd.Next(maxLength - minLength);
29+
30+
var fields = new Type[count];
31+
var data = new DateTimeOffset[count];
32+
for (var j = 0; j < count; j++) {
33+
data[j] = generator.GetInstance(rnd);
34+
fields[j] = typeof(DateTimeOffset);
35+
}
36+
37+
// Testing writes (untyped)
38+
var tuple1 = Tuple.Create(fields);
39+
Assert.That(tuple1, Is.InstanceOf<PackedTuple>());
40+
for (var j = 0; j < count; j++)
41+
tuple1.SetValue(j, (object) data[j]);
42+
// Testing reads (untyped)
43+
for (var j = 0; j < count; j++)
44+
Assert.AreEqual(data[j], tuple1.GetValue(j));
45+
46+
// Testing writes (untyped)
47+
var tuple2 = Tuple.Create(fields);
48+
Assert.That(tuple2, Is.InstanceOf<PackedTuple>());
49+
for (var j = 0; j < count; j++)
50+
tuple2.SetValue(j, data[j]);
51+
for (var j = 0; j < count; j++)
52+
Assert.AreEqual(data[j], tuple2.GetValue<DateTimeOffset>(j));
53+
}
54+
}
55+
}
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (C) 2021 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
7+
namespace Xtensive.Orm.Tests
8+
{
9+
[Serializable]
10+
internal class DateTimeOffsetInstanceGenerator : InstanceGeneratorBase<DateTimeOffset>
11+
{
12+
private readonly IInstanceGenerator<DateTime> dateTimeInstanceGenerator;
13+
14+
public override DateTimeOffset GetInstance(Random random)
15+
{
16+
var randomDateTime = dateTimeInstanceGenerator.GetInstance(random);
17+
var randomTimeSpan = new TimeSpan(random.Next(0, 10), random.Next(0, 60), 0);
18+
19+
var signBase = random.Next(-100, 100);
20+
if (signBase != 0 && (signBase / Math.Abs(signBase)) < 0)
21+
randomTimeSpan = randomTimeSpan.Negate();
22+
23+
return new DateTimeOffset(randomDateTime, randomTimeSpan);
24+
}
25+
26+
public DateTimeOffsetInstanceGenerator(IInstanceGeneratorProvider provider)
27+
: base(provider)
28+
{
29+
dateTimeInstanceGenerator = provider.GetInstanceGenerator<DateTime>();
30+
}
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright (C) 2021 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
4+
5+
using System;
6+
using System.Linq;
7+
using System.Threading.Tasks;
8+
using NUnit.Framework;
9+
using Xtensive.Orm.Configuration;
10+
using Xtensive.Orm.Tests.Issues.IssueGithub0171_ReadDateTimeOffsetFromPackedTupleModel;
11+
12+
namespace Xtensive.Orm.Tests.Issues.IssueGithub0171_ReadDateTimeOffsetFromPackedTupleModel
13+
{
14+
[HierarchyRoot]
15+
public class Cargo : Entity
16+
{
17+
[Field, Key]
18+
public int Id { get; private set; }
19+
20+
[Field]
21+
public DateTimeOffset DateTimeOffsetField { get; set; }
22+
23+
[Field]
24+
public DateTime DateTimeField { get; set; }
25+
26+
[Field, Association(PairTo = nameof(CargoLoad.Cargo))]
27+
public EntitySet<CargoLoad> Loads { get; private set; }
28+
29+
public Cargo(Session session)
30+
: base(session)
31+
{
32+
}
33+
}
34+
35+
[HierarchyRoot]
36+
public class CargoLoad: Entity
37+
{
38+
[Field, Key]
39+
public int Id { get; private set; }
40+
41+
[Field]
42+
public Cargo Cargo { get; private set; }
43+
44+
public CargoLoad(Session session, Cargo cargo)
45+
: base(session)
46+
{
47+
Cargo = cargo;
48+
}
49+
}
50+
}
51+
52+
namespace Xtensive.Orm.Tests.Issues
53+
{
54+
public class IssueGithub0171_ReadDateTimeOffsetFromPackedTuple : AutoBuildTest
55+
{
56+
protected override DomainConfiguration BuildConfiguration()
57+
{
58+
var config = base.BuildConfiguration();
59+
config.Types.Register(typeof(Cargo));
60+
config.Types.Register(typeof(CargoLoad));
61+
config.UpgradeMode = DomainUpgradeMode.Recreate;
62+
return config;
63+
}
64+
65+
protected override void CheckRequirements()
66+
{
67+
Require.ProviderIs(StorageProvider.SqlServer | StorageProvider.Oracle);
68+
}
69+
70+
[Test]
71+
public void DateTimeOffsetCase()
72+
{
73+
using (var session = Domain.OpenSession())
74+
using (var tx = session.OpenTransaction()) {
75+
var expectedDateTimeOffset = new DateTimeOffset(2021, 9, 13, 0, 0, 1, new TimeSpan(5, 0, 0));
76+
var cargo = new Cargo(session) { DateTimeOffsetField = expectedDateTimeOffset};
77+
var loadNoCargo = new CargoLoad(session, null);
78+
var loadWithCargo = new CargoLoad(session, cargo);
79+
80+
var query = session.Query.All<CargoLoad>()
81+
.LeftJoin(session.Query.All<Cargo>(),
82+
cl => cl.Cargo,
83+
c => c,
84+
(cl, c) => new { CargoLoad = cl, Cargo = c })
85+
.Select(t => t.Cargo.DateTimeOffsetField)
86+
.ToArray();
87+
88+
Assert.That(query.Length, Is.EqualTo(2));
89+
Assert.That(query.Any(d => d == DateTimeOffset.MinValue), Is.True);
90+
Assert.That(query.Any(d => d == expectedDateTimeOffset), Is.True);
91+
}
92+
}
93+
94+
[Test]
95+
public void DateTimeCase()
96+
{
97+
using (var session = Domain.OpenSession())
98+
using (var tx = session.OpenTransaction()) {
99+
var expectedDateTime = new DateTime(2021, 9, 13, 0, 0, 1);
100+
var cargo = new Cargo(session) { DateTimeField = expectedDateTime };
101+
var loadNoCargo = new CargoLoad(session, null);
102+
var loadWithCargo = new CargoLoad(session, cargo);
103+
104+
var query = session.Query.All<CargoLoad>()
105+
.LeftJoin(session.Query.All<Cargo>(),
106+
cl => cl.Cargo,
107+
c => c,
108+
(cl, c) => new { CargoLoad = cl, Cargo = c })
109+
.Select(t => t.Cargo.DateTimeField)
110+
.ToArray();
111+
Assert.That(query.Length, Is.EqualTo(2));
112+
Assert.That(query.Any(d => d == DateTime.MinValue), Is.True);
113+
Assert.That(query.Any(d => d == expectedDateTime), Is.True);
114+
}
115+
}
116+
}
117+
}

Orm/Xtensive.Orm/Tuples/Packed/PackedFieldAccessor.cs

+31-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (C) 2003-2012 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2013-2021 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
44
// Created by: Denis Krjuchkov
55
// Created: 2013.01.22
66

@@ -557,4 +557,32 @@ public DecimalFieldAccessor()
557557
{
558558
}
559559
}
560+
561+
internal sealed class DateTimeOffsetFieldAccessor : ValueFieldAccessor<DateTimeOffset>
562+
{
563+
protected override DateTimeOffset Decode(long[] values, int offset)
564+
{
565+
unsafe {
566+
fixed (long* valuePtr = &values[offset])
567+
return *(DateTimeOffset*) valuePtr;
568+
}
569+
}
570+
571+
protected override void Encode(DateTimeOffset value, long[] values, int offset)
572+
{
573+
unsafe {
574+
fixed (long* valuePtr = &values[offset])
575+
*(DateTimeOffset*) valuePtr = value;
576+
}
577+
}
578+
579+
private static unsafe int GetSize()
580+
{
581+
return sizeof(DateTimeOffset);
582+
}
583+
584+
public DateTimeOffsetFieldAccessor()
585+
: base(GetSize() * 8)
586+
{ }
587+
}
560588
}

Orm/Xtensive.Orm/Tuples/Packed/TupleLayout.cs

+46-37
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
// Copyright (C) 2003-2012 Xtensive LLC.
2-
// All rights reserved.
3-
// For conditions of distribution and use, see license.
1+
// Copyright (C) 2012-2021 Xtensive LLC.
2+
// This code is distributed under MIT license terms.
3+
// See the License.txt file in the project root for more information.
44
// Created by: Denis Krjuchkov
55
// Created: 2012.12.29
66

@@ -63,6 +63,8 @@ private static class ValueFieldAccessorResolver
6363
private static readonly Type NullableDecimalType = typeof(decimal?);
6464
private static readonly Type GuidType = typeof(Guid);
6565
private static readonly Type NullableGuidType = typeof(Guid?);
66+
private static readonly Type DateTimeOffsetType = typeof(DateTimeOffset);
67+
private static readonly Type NullableDateTimeOffsetType = typeof(DateTimeOffset?);
6668

6769
private static readonly ValueFieldAccessor BoolAccessor = new BooleanFieldAccessor();
6870
private static readonly ValueFieldAccessor ByteAccessor = new ByteFieldAccessor();
@@ -79,48 +81,55 @@ private static class ValueFieldAccessorResolver
7981
private static readonly ValueFieldAccessor TimeSpanAccessor = new TimeSpanFieldAccessor();
8082
private static readonly ValueFieldAccessor DecimalAccessor = new DecimalFieldAccessor();
8183
private static readonly ValueFieldAccessor GuidAccessor = new GuidFieldAccessor();
84+
private static readonly ValueFieldAccessor DateTimeOffsetAccessor = new DateTimeOffsetFieldAccessor();
8285

8386
private static readonly int NullableTypeMetadataToken = typeof(Nullable<>).MetadataToken;
8487

8588
public static ValueFieldAccessor GetValue(Type probeType)
8689
{
87-
ValueFieldAccessor ResolveByType(Type type) =>
88-
ReferenceEquals(type, BoolType) ? BoolAccessor :
89-
ReferenceEquals(type, ByteType) ? ByteAccessor :
90-
ReferenceEquals(type, SByteType) ? SByteAccessor :
91-
ReferenceEquals(type, Int16Type) ? Int16Accessor :
92-
ReferenceEquals(type, UInt16Type) ? UInt16Accessor :
93-
ReferenceEquals(type, Int32Type) ? Int32Accessor :
94-
ReferenceEquals(type, UInt32Type) ? UInt32Accessor :
95-
ReferenceEquals(type, Int64Type) ? Int64Accessor :
96-
ReferenceEquals(type, UInt64Type) ? UInt64Accessor :
97-
ReferenceEquals(type, SingleType) ? SingleAccessor :
98-
ReferenceEquals(type, DoubleType) ? DoubleAccessor :
99-
ReferenceEquals(type, DateTimeType) ? DateTimeAccessor :
100-
ReferenceEquals(type, TimeSpanType) ? TimeSpanAccessor :
101-
ReferenceEquals(type, DecimalType) ? DecimalAccessor :
102-
ReferenceEquals(type, GuidType) ? GuidAccessor : null;
103-
104-
ValueFieldAccessor ResolveByNullableType(Type type) =>
105-
ReferenceEquals(type, NullableBoolType) ? BoolAccessor :
106-
ReferenceEquals(type, NullableByteType) ? ByteAccessor :
107-
ReferenceEquals(type, NullableSByteType) ? SByteAccessor :
108-
ReferenceEquals(type, NullableInt16Type) ? Int16Accessor :
109-
ReferenceEquals(type, NullableUInt16Type) ? UInt16Accessor :
110-
ReferenceEquals(type, NullableInt32Type) ? Int32Accessor :
111-
ReferenceEquals(type, NullableUInt32Type) ? UInt32Accessor :
112-
ReferenceEquals(type, NullableInt64Type) ? Int64Accessor :
113-
ReferenceEquals(type, NullableUInt64Type) ? UInt64Accessor :
114-
ReferenceEquals(type, NullableSingleType) ? SingleAccessor :
115-
ReferenceEquals(type, NullableDoubleType) ? DoubleAccessor :
116-
ReferenceEquals(type, NullableDateTimeType) ? DateTimeAccessor :
117-
ReferenceEquals(type, NullableTimeSpanType) ? TimeSpanAccessor :
118-
ReferenceEquals(type, NullableDecimalType) ? DecimalAccessor :
119-
ReferenceEquals(type, NullableGuidType) ? GuidAccessor : null;
120-
12190
return (probeType.MetadataToken ^ NullableTypeMetadataToken) == 0
12291
? ResolveByNullableType(probeType)
12392
: ResolveByType(probeType);
93+
94+
ValueFieldAccessor ResolveByType(Type type)
95+
{
96+
return ReferenceEquals(type, BoolType) ? BoolAccessor :
97+
ReferenceEquals(type, ByteType) ? ByteAccessor :
98+
ReferenceEquals(type, SByteType) ? SByteAccessor :
99+
ReferenceEquals(type, Int16Type) ? Int16Accessor :
100+
ReferenceEquals(type, UInt16Type) ? UInt16Accessor :
101+
ReferenceEquals(type, Int32Type) ? Int32Accessor :
102+
ReferenceEquals(type, UInt32Type) ? UInt32Accessor :
103+
ReferenceEquals(type, Int64Type) ? Int64Accessor :
104+
ReferenceEquals(type, UInt64Type) ? UInt64Accessor :
105+
ReferenceEquals(type, SingleType) ? SingleAccessor :
106+
ReferenceEquals(type, DoubleType) ? DoubleAccessor :
107+
ReferenceEquals(type, DateTimeType) ? DateTimeAccessor :
108+
ReferenceEquals(type, TimeSpanType) ? TimeSpanAccessor :
109+
ReferenceEquals(type, DecimalType) ? DecimalAccessor :
110+
ReferenceEquals(type, GuidType) ? GuidAccessor :
111+
ReferenceEquals(type, DateTimeOffsetType) ? DateTimeOffsetAccessor : null;
112+
}
113+
114+
ValueFieldAccessor ResolveByNullableType(Type type)
115+
{
116+
return ReferenceEquals(type, NullableBoolType) ? BoolAccessor :
117+
ReferenceEquals(type, NullableByteType) ? ByteAccessor :
118+
ReferenceEquals(type, NullableSByteType) ? SByteAccessor :
119+
ReferenceEquals(type, NullableInt16Type) ? Int16Accessor :
120+
ReferenceEquals(type, NullableUInt16Type) ? UInt16Accessor :
121+
ReferenceEquals(type, NullableInt32Type) ? Int32Accessor :
122+
ReferenceEquals(type, NullableUInt32Type) ? UInt32Accessor :
123+
ReferenceEquals(type, NullableInt64Type) ? Int64Accessor :
124+
ReferenceEquals(type, NullableUInt64Type) ? UInt64Accessor :
125+
ReferenceEquals(type, NullableSingleType) ? SingleAccessor :
126+
ReferenceEquals(type, NullableDoubleType) ? DoubleAccessor :
127+
ReferenceEquals(type, NullableDateTimeType) ? DateTimeAccessor :
128+
ReferenceEquals(type, NullableTimeSpanType) ? TimeSpanAccessor :
129+
ReferenceEquals(type, NullableDecimalType) ? DecimalAccessor :
130+
ReferenceEquals(type, NullableGuidType) ? GuidAccessor :
131+
ReferenceEquals(type, NullableDateTimeOffsetType) ? DateTimeOffsetAccessor : null;
132+
}
124133
}
125134
}
126135

0 commit comments

Comments
 (0)