Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Long and ULong properties marked as rowversion automatically convert to binary on SQL Server #29961

Merged
merged 1 commit into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,15 @@ public class SqlServerLongTypeMapping : LongTypeMapping
/// </summary>
public SqlServerLongTypeMapping(
string storeType,
ValueConverter? converter = null,
ValueComparer? comparer = null,
ValueComparer? providerValueComparer = null,
DbType? dbType = System.Data.DbType.Int64)
: base(storeType, dbType)
: this(
new RelationalTypeMappingParameters(
new CoreTypeMappingParameters(typeof(long), converter, comparer, providerValueComparer),
storeType,
dbType: dbType))
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@ private readonly SqlServerByteArrayTypeMapping _rowversion
v => v.ToArray()),
storeTypePostfix: StoreTypePostfix.None);

private readonly SqlServerLongTypeMapping _longRowversion
= new(
"rowversion",
converter: new NumberToBytesConverter<long>(),
providerValueComparer: new ValueComparer<byte[]>(
(v1, v2) => StructuralComparisons.StructuralEqualityComparer.Equals(v1, v2),
v => StructuralComparisons.StructuralEqualityComparer.GetHashCode(v),
v => v.ToArray()),
dbType: DbType.Binary);

private readonly SqlServerLongTypeMapping _ulongRowversion
= new(
"rowversion",
converter: new NumberToBytesConverter<ulong>(),
providerValueComparer: new ValueComparer<byte[]>(
(v1, v2) => StructuralComparisons.StructuralEqualityComparer.Equals(v1, v2),
v => StructuralComparisons.StructuralEqualityComparer.GetHashCode(v),
v => v.ToArray()),
dbType: DbType.Binary);

private readonly IntTypeMapping _int
= new("int");

Expand Down Expand Up @@ -293,6 +313,16 @@ public SqlServerTypeMappingSource(
return mapping;
}

if (clrType == typeof(ulong) && mappingInfo.IsRowVersion == true)
{
return _ulongRowversion;
}

if (clrType == typeof(long) && mappingInfo.IsRowVersion == true)
{
return _longRowversion;
}

if (clrType == typeof(string))
{
var isAnsi = mappingInfo.IsUnicode == false;
Expand Down
6 changes: 3 additions & 3 deletions test/EFCore.Specification.Tests/DataAnnotationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -926,16 +926,16 @@ public class GeneratedEntityNonInteger
public virtual IModel Timestamp_takes_precedence_over_MaxLength()
{
var modelBuilder = CreateModelBuilder();
modelBuilder.Entity<TimestampAndMaxlen>().Ignore(x => x.NonMaxTimestamp);
modelBuilder.Entity<TimestampAndMaxlength>().Ignore(x => x.NonMaxTimestamp);

var model = Validate(modelBuilder);

Assert.Null(GetProperty<TimestampAndMaxlen>(model, "MaxTimestamp").GetMaxLength());
Assert.Null(GetProperty<TimestampAndMaxlength>(model, "MaxTimestamp").GetMaxLength());

return model;
}

protected class TimestampAndMaxlen
protected class TimestampAndMaxlength
{
public int Id { get; set; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ public override IModel Timestamp_takes_precedence_over_MaxLength()
{
var model = base.Timestamp_takes_precedence_over_MaxLength();

var property = GetProperty<TimestampAndMaxlen>(model, "MaxTimestamp");
var property = GetProperty<TimestampAndMaxlength>(model, "MaxTimestamp");

var storeType = property.GetRelationalTypeMapping().StoreType;

Expand Down
13 changes: 6 additions & 7 deletions test/EFCore.SqlServer.FunctionalTests/F1SqlServerFixture.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore.TestModels.ConcurrencyModel;

namespace Microsoft.EntityFrameworkCore;
Expand All @@ -22,14 +23,10 @@ protected override void BuildModelExternal(ModelBuilder modelBuilder)
.OwnsOne(
s => s.Details, eb =>
{
eb.Property<ulong>("Version").IsRowVersion().HasConversion<byte[]>();
eb.Property<ulong>("Version").IsRowVersion();
});

modelBuilder
.Entity<OptimisticOptionalChild>()
.Property(x => x.Version)
.IsRowVersion()
.HasConversion<byte[]>();
modelBuilder.Entity<OptimisticOptionalChild>();

modelBuilder
.Entity<OptimisticParent>()
Expand Down Expand Up @@ -78,7 +75,9 @@ public class OptimisticOptionalChild
{
public Guid Id { get; set; }
public ICollection<OptimisticParent> Parents { get; set; }
public ulong Version { get; set; }

[Timestamp]
public long Version { get; set; }
}

public class OptimisticParent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public override IModel Timestamp_takes_precedence_over_MaxLength()
{
var model = base.Timestamp_takes_precedence_over_MaxLength();

var property = GetProperty<TimestampAndMaxlen>(model, "MaxTimestamp");
var property = GetProperty<TimestampAndMaxlength>(model, "MaxTimestamp");

var storeType = property.GetRelationalTypeMapping().StoreType;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -471,12 +471,16 @@ public void StringLengthAttribute_on_field_sets_max_length_with_conventional_bui

#region TimestampAttribute

[ConditionalFact]
public void TimestampAttribute_overrides_configuration_from_convention_source()
[ConditionalTheory]
[InlineData("Timestamp")]
[InlineData("LongTimestamp")]
[InlineData("ULongTimestamp")]
public void TimestampAttribute_overrides_configuration_from_convention_source(string propertyName)
{
var entityTypeBuilder = CreateInternalEntityTypeBuilder<A>();

var propertyBuilder = entityTypeBuilder.Property(typeof(byte[]), "Timestamp", ConfigurationSource.Explicit);
var propertyBuilder = entityTypeBuilder.Property(
typeof(A).GetProperty(propertyName)!.PropertyType, propertyName, ConfigurationSource.Explicit)!;

propertyBuilder.ValueGenerated(ValueGenerated.Never, ConfigurationSource.Convention);
propertyBuilder.IsConcurrencyToken(false, ConfigurationSource.Convention);
Expand All @@ -487,12 +491,16 @@ public void TimestampAttribute_overrides_configuration_from_convention_source()
Assert.True(propertyBuilder.Metadata.IsConcurrencyToken);
}

[ConditionalFact]
public void TimestampAttribute_does_not_override_configuration_from_explicit_source()
[ConditionalTheory]
[InlineData("Timestamp")]
[InlineData("LongTimestamp")]
[InlineData("ULongTimestamp")]
public void TimestampAttribute_does_not_override_configuration_from_explicit_source(string propertyName)
{
var entityTypeBuilder = CreateInternalEntityTypeBuilder<A>();

var propertyBuilder = entityTypeBuilder.Property(typeof(byte[]), "Timestamp", ConfigurationSource.Explicit);
var propertyBuilder = entityTypeBuilder.Property(
typeof(A).GetProperty(propertyName)!.PropertyType, propertyName, ConfigurationSource.Explicit)!;

propertyBuilder.ValueGenerated(ValueGenerated.Never, ConfigurationSource.Explicit);
propertyBuilder.IsConcurrencyToken(false, ConfigurationSource.Explicit);
Expand Down Expand Up @@ -782,6 +790,12 @@ private class A
[Timestamp]
public byte[] Timestamp { get; set; }

[Timestamp]
public long LongTimestamp { get; set; }

[Timestamp]
public ulong ULongTimestamp { get; set; }

[Required]
private int? PrivateProperty { get; set; }

Expand Down Expand Up @@ -854,7 +868,13 @@ public class F
public string StringLengthProperty;

[Timestamp]
public byte[] Timestamp;
public byte[] Timestamp { get; set; }

[Timestamp]
public long LongTimestamp { get; set; }

[Timestamp]
public ulong ULongTimestamp { get; set; }
}

private class BaseEntity
Expand Down