未验证 提交 7886196b 编写于 作者: S Shay Rojansky 提交者: GitHub

Map uint rowversion properties to xmin (#2544)

Closes #2543
上级 3a26ceb3
......@@ -340,6 +340,7 @@ The .NET Foundation licenses this file to you under the MIT license.
<s:Boolean x:Key="/Default/UserDictionary/Words/=Uniquifier/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Uniquify/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=varbit/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Xmin/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Xunit/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=annotatable/@EntryIndexedValue">True</s:Boolean>
......
......@@ -67,7 +67,7 @@ public override ConventionSet CreateConventionSet()
conventionSet.PropertyAnnotationChangedConventions, (RelationalValueGenerationConvention)valueGenerationConvention);
conventionSet.ModelFinalizingConventions.Add(valueGenerationStrategyConvention);
conventionSet.ModelFinalizingConventions.Add(new NpgsqlPostgresExtensionDiscoveryConvention(_typeMappingSource));
conventionSet.ModelFinalizingConventions.Add(new NpgsqlPostgresModelFinalizingConvention(_typeMappingSource));
ReplaceConvention(conventionSet.ModelFinalizingConventions, storeGenerationConvention);
ReplaceConvention(
conventionSet.ModelFinalizingConventions,
......
......@@ -9,15 +9,15 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Conventions;
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-conventions">Model building conventions</see>.
/// </remarks>
public class NpgsqlPostgresExtensionDiscoveryConvention : IModelFinalizingConvention
public class NpgsqlPostgresModelFinalizingConvention : IModelFinalizingConvention
{
private readonly IRelationalTypeMappingSource _typeMappingSource;
/// <summary>
/// Creates a new instance of <see cref="NpgsqlPostgresExtensionDiscoveryConvention" />.
/// Creates a new instance of <see cref="NpgsqlPostgresModelFinalizingConvention" />.
/// </summary>
/// <param name="typeMappingSource">The type mapping source to use.</param>
public NpgsqlPostgresExtensionDiscoveryConvention(IRelationalTypeMappingSource typeMappingSource)
public NpgsqlPostgresModelFinalizingConvention(IRelationalTypeMappingSource typeMappingSource)
{
_typeMappingSource = typeMappingSource;
}
......@@ -29,29 +29,53 @@ public virtual void ProcessModelFinalizing(IConventionModelBuilder modelBuilder,
{
foreach (var property in entityType.GetDeclaredProperties())
{
var typeMapping = (RelationalTypeMapping?)property.FindTypeMapping()
?? _typeMappingSource.FindMapping((IProperty)property);
DiscoverPostgresExtensions(property, modelBuilder);
ProcessRowVersionProperty(property);
}
}
}
if (typeMapping is null)
{
continue;
}
/// <summary>
/// Discovers certain common PostgreSQL extensions based on property store types (e.g. hstore).
/// </summary>
/// <param name="property"></param>
/// <param name="modelBuilder"></param>
protected virtual void DiscoverPostgresExtensions(IConventionProperty property, IConventionModelBuilder modelBuilder)
{
var typeMapping = (RelationalTypeMapping?)property.FindTypeMapping()
?? _typeMappingSource.FindMapping((IProperty)property);
switch (typeMapping.StoreType)
{
case "hstore":
modelBuilder.HasPostgresExtension("hstore");
continue;
case "citext":
modelBuilder.HasPostgresExtension("citext");
continue;
case "ltree":
case "lquery":
case "ltxtquery":
modelBuilder.HasPostgresExtension("ltree");
continue;
}
if (typeMapping is not null)
{
switch (typeMapping.StoreType)
{
case "hstore":
modelBuilder.HasPostgresExtension("hstore");
break;
case "citext":
modelBuilder.HasPostgresExtension("citext");
break;
case "ltree":
case "lquery":
case "ltxtquery":
modelBuilder.HasPostgresExtension("ltree");
break;
}
}
}
/// <summary>
/// Detects properties which are uint, OnAddOrUpdate and configured as concurrency tokens, and maps these to the PostgreSQL
/// internal "xmin" column, which changes every time the row is modified.
/// </summary>
protected virtual void ProcessRowVersionProperty(IConventionProperty property)
{
if (property.ValueGenerated == ValueGenerated.OnAddOrUpdate
&& property.IsConcurrencyToken
&& property.ClrType == typeof(uint)
&& property.GetValueConverter() is null)
{
property.Builder.HasColumnName("xmin")?.HasColumnType("xid");
}
}
}
// 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 Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Metadata.Conventions;
public class NpgsqlPostgresModelFinalizingConventionTest
{
[Fact]
public void RowVersion_properties_get_mapped_to_xmin()
{
var modelBuilder = NpgsqlTestHelpers.Instance.CreateConventionBuilder();
modelBuilder.Entity<Blog>().Property(b => b.RowVersion).IsRowVersion();
var model = modelBuilder.FinalizeModel();
var entityType = model.FindEntityType(typeof(Blog))!;
var property = entityType.FindProperty(nameof(Blog.RowVersion))!;
Assert.Equal("xmin", property.GetColumnName());
Assert.Equal("xid", property.GetColumnType());
}
class Blog
{
public int Id { get; set; }
[Timestamp]
public uint RowVersion { get; set; }
}
}
......@@ -14,7 +14,6 @@ public void Annotations_are_added_when_conventional_model_builder_is_used()
var annotations = model.GetAnnotations().OrderBy(a => a.Name).ToList();
Assert.Equal(3, annotations.Count);
// TODO for PG9.6 testing: make this conditional
Assert.Equal(NpgsqlAnnotationNames.ValueGenerationStrategy, annotations.First().Name);
Assert.Equal(NpgsqlValueGenerationStrategy.IdentityByDefaultColumn, annotations.First().Value);
}
......@@ -44,4 +43,4 @@ public void Annotations_are_added_when_conventional_model_builder_is_used_with_s
annotations[3].Name);
Assert.NotNull(annotations[3].Value);
}
}
\ No newline at end of file
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册