Start work on some support for sequence value generation. Still under investigation if possible

7.0-servicing
Christopher Jolly 2 years ago
parent 7cee2d0581
commit c46f980c09

@ -2,6 +2,7 @@
using EntityFrameworkCore.Jet.Metadata;
using EntityFrameworkCore.Jet.Metadata.Internal;
using EntityFrameworkCore.Jet.Utilities;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
@ -10,6 +11,8 @@ namespace Microsoft.EntityFrameworkCore
{
public static class JetModelExtensions
{
public const string DefaultSequenceNameSuffix = "Sequence";
/// <summary>
/// Returns the default identity seed.
/// </summary>
@ -123,5 +126,95 @@ namespace Microsoft.EntityFrameworkCore
/// <returns> The <see cref="ConfigurationSource" /> for the default <see cref="JetValueGenerationStrategy" />. </returns>
public static ConfigurationSource? GetJetValueGenerationStrategyConfigurationSource([NotNull] this IConventionModel model)
=> model.FindAnnotation(JetAnnotationNames.ValueGenerationStrategy)?.GetConfigurationSource();
/// <summary>
/// Returns the suffix to append to the name of automatically created sequences.
/// </summary>
/// <param name="model">The model.</param>
/// <returns>The name to use for the default key value generation sequence.</returns>
public static string GetJetSequenceNameSuffix(this IReadOnlyModel model)
=> (string?)model[JetAnnotationNames.SequenceNameSuffix]
?? DefaultSequenceNameSuffix;
/// <summary>
/// Sets the suffix to append to the name of automatically created sequences.
/// </summary>
/// <param name="model">The model.</param>
/// <param name="name">The value to set.</param>
public static void SetJetSequenceNameSuffix(this IMutableModel model, string? name)
{
Check.NullButNotEmpty(name, nameof(name));
model.SetOrRemoveAnnotation(JetAnnotationNames.SequenceNameSuffix, name);
}
/// <summary>
/// Sets the suffix to append to the name of automatically created sequences.
/// </summary>
/// <param name="model">The model.</param>
/// <param name="name">The value to set.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The configured value.</returns>
public static string? SetJetSequenceNameSuffix(
this IConventionModel model,
string? name,
bool fromDataAnnotation = false)
=> (string?)model.SetOrRemoveAnnotation(
JetAnnotationNames.SequenceNameSuffix,
Check.NullButNotEmpty(name, nameof(name)),
fromDataAnnotation)?.Value;
/// <summary>
/// Returns the <see cref="ConfigurationSource" /> for the default value generation sequence name suffix.
/// </summary>
/// <param name="model">The model.</param>
/// <returns>The <see cref="ConfigurationSource" /> for the default key value generation sequence name.</returns>
public static ConfigurationSource? GetJetSequenceNameSuffixConfigurationSource(this IConventionModel model)
=> model.FindAnnotation(JetAnnotationNames.SequenceNameSuffix)?.GetConfigurationSource();
/// <summary>
/// Returns the schema to use for the default value generation sequence.
/// <see cref="JetPropertyBuilderExtensions.UseSequence" />
/// </summary>
/// <param name="model">The model.</param>
/// <returns>The schema to use for the default key value generation sequence.</returns>
public static string? GetJetSequenceSchema(this IReadOnlyModel model)
=> (string?)model[JetAnnotationNames.SequenceSchema];
/// <summary>
/// Sets the schema to use for the default key value generation sequence.
/// </summary>
/// <param name="model">The model.</param>
/// <param name="value">The value to set.</param>
public static void SetJetSequenceSchema(this IMutableModel model, string? value)
{
Check.NullButNotEmpty(value, nameof(value));
model.SetOrRemoveAnnotation(JetAnnotationNames.SequenceSchema, value);
}
/// <summary>
/// Sets the schema to use for the default key value generation sequence.
/// </summary>
/// <param name="model">The model.</param>
/// <param name="value">The value to set.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The configured value.</returns>
public static string? SetJetSequenceSchema(
this IConventionModel model,
string? value,
bool fromDataAnnotation = false)
=> (string?)model.SetOrRemoveAnnotation(
JetAnnotationNames.SequenceSchema,
Check.NullButNotEmpty(value, nameof(value)),
fromDataAnnotation)?.Value;
/// <summary>
/// Returns the <see cref="ConfigurationSource" /> for the default key value generation sequence schema.
/// </summary>
/// <param name="model">The model.</param>
/// <returns>The <see cref="ConfigurationSource" /> for the default key value generation sequence schema.</returns>
public static ConfigurationSource? GetJetSequenceSchemaConfigurationSource(this IConventionModel model)
=> model.FindAnnotation(JetAnnotationNames.SequenceSchema)?.GetConfigurationSource();
}
}

@ -8,6 +8,7 @@ using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Infrastructure;
using System.Linq;
using EntityFrameworkCore.Jet.Utilities;
// ReSharper disable once CheckNamespace
namespace Microsoft.EntityFrameworkCore
@ -302,5 +303,177 @@ namespace Microsoft.EntityFrameworkCore
?? property.FindTypeMapping()?.Converter)
== null;
}
/// <summary>
/// Returns the name to use for the key value generation sequence.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The name to use for the key value generation sequence.</returns>
public static string? GetJetSequenceName(this IReadOnlyProperty property)
=> (string?)property[JetAnnotationNames.SequenceName];
/// <summary>
/// Returns the name to use for the key value generation sequence.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="storeObject">The identifier of the store object.</param>
/// <returns>The name to use for the key value generation sequence.</returns>
public static string? GetJetSequenceName(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject)
{
var annotation = property.FindAnnotation(JetAnnotationNames.SequenceName);
if (annotation != null)
{
return (string?)annotation.Value;
}
return property.FindSharedStoreObjectRootProperty(storeObject)?.GetJetSequenceName(storeObject);
}
/// <summary>
/// Sets the name to use for the key value generation sequence.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="name">The sequence name to use.</param>
public static void SetJetSequenceName(this IMutableProperty property, string? name)
=> property.SetOrRemoveAnnotation(
JetAnnotationNames.SequenceName,
Check.NullButNotEmpty(name, nameof(name)));
/// <summary>
/// Sets the name to use for the key value generation sequence.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="name">The sequence name to use.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The configured value.</returns>
public static string? SetJetSequenceName(
this IConventionProperty property,
string? name,
bool fromDataAnnotation = false)
=> (string?)property.SetOrRemoveAnnotation(
JetAnnotationNames.SequenceName,
Check.NullButNotEmpty(name, nameof(name)),
fromDataAnnotation)?.Value;
/// <summary>
/// Returns the <see cref="ConfigurationSource" /> for the key value generation sequence name.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The <see cref="ConfigurationSource" /> for the key value generation sequence name.</returns>
public static ConfigurationSource? GetJetSequenceNameConfigurationSource(this IConventionProperty property)
=> property.FindAnnotation(JetAnnotationNames.SequenceName)?.GetConfigurationSource();
/// <summary>
/// Returns the schema to use for the key value generation sequence.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The schema to use for the key value generation sequence.</returns>
public static string? GetJetSequenceSchema(this IReadOnlyProperty property)
=> (string?)property[JetAnnotationNames.SequenceSchema];
/// <summary>
/// Returns the schema to use for the key value generation sequence.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="storeObject">The identifier of the store object.</param>
/// <returns>The schema to use for the key value generation sequence.</returns>
public static string? GetJetSequenceSchema(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject)
{
var annotation = property.FindAnnotation(JetAnnotationNames.SequenceSchema);
if (annotation != null)
{
return (string?)annotation.Value;
}
return property.FindSharedStoreObjectRootProperty(storeObject)?.GetJetSequenceSchema(storeObject);
}
/// <summary>
/// Sets the schema to use for the key value generation sequence.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="schema">The schema to use.</param>
public static void SetJetSequenceSchema(this IMutableProperty property, string? schema)
=> property.SetOrRemoveAnnotation(
JetAnnotationNames.SequenceSchema,
Check.NullButNotEmpty(schema, nameof(schema)));
/// <summary>
/// Sets the schema to use for the key value generation sequence.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="schema">The schema to use.</param>
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
/// <returns>The configured value.</returns>
public static string? SetJetSequenceSchema(
this IConventionProperty property,
string? schema,
bool fromDataAnnotation = false)
=> (string?)property.SetOrRemoveAnnotation(
JetAnnotationNames.SequenceSchema,
Check.NullButNotEmpty(schema, nameof(schema)),
fromDataAnnotation)?.Value;
/// <summary>
/// Returns the <see cref="ConfigurationSource" /> for the key value generation sequence schema.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The <see cref="ConfigurationSource" /> for the key value generation sequence schema.</returns>
public static ConfigurationSource? GetGetSequenceSchemaConfigurationSource(this IConventionProperty property)
=> property.FindAnnotation(JetAnnotationNames.SequenceSchema)?.GetConfigurationSource();
/// <summary>
/// Finds the <see cref="ISequence" /> in the model to use for the key value generation pattern.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The sequence to use, or <see langword="null" /> if no sequence exists in the model.</returns>
public static IReadOnlySequence? FindJetSequence(this IReadOnlyProperty property)
{
var model = property.DeclaringEntityType.Model;
var sequenceName = property.GetJetSequenceName()
?? model.GetJetSequenceNameSuffix();
var sequenceSchema = property.GetJetSequenceSchema()
?? model.GetJetSequenceSchema();
return model.FindSequence(sequenceName, sequenceSchema);
}
/// <summary>
/// Finds the <see cref="ISequence" /> in the model to use for the key value generation pattern.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="storeObject">The identifier of the store object.</param>
/// <returns>The sequence to use, or <see langword="null" /> if no sequence exists in the model.</returns>
public static IReadOnlySequence? FindJetSequence(this IReadOnlyProperty property, in StoreObjectIdentifier storeObject)
{
var model = property.DeclaringEntityType.Model;
var sequenceName = property.GetJetSequenceName(storeObject)
?? model.GetJetSequenceNameSuffix();
var sequenceSchema = property.GetJetSequenceSchema(storeObject)
?? model.GetJetSequenceSchema();
return model.FindSequence(sequenceName, sequenceSchema);
}
/// <summary>
/// Finds the <see cref="ISequence" /> in the model to use for the key value generation pattern.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The sequence to use, or <see langword="null" /> if no sequence exists in the model.</returns>
public static ISequence? FindJetSequence(this IProperty property)
=> (ISequence?)((IReadOnlyProperty)property).FindJetSequence();
/// <summary>
/// Finds the <see cref="ISequence" /> in the model to use for the key value generation pattern.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="storeObject">The identifier of the store object.</param>
/// <returns>The sequence to use, or <see langword="null" /> if no sequence exists in the model.</returns>
public static ISequence? FindJetSequence(this IProperty property, in StoreObjectIdentifier storeObject)
=> (ISequence?)((IReadOnlyProperty)property).FindJetSequence(storeObject);
}
}

@ -1,6 +1,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Linq;
using EntityFrameworkCore.Jet.Metadata;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
@ -24,8 +25,14 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
[NotNull] ProviderConventionSetBuilderDependencies dependencies,
[NotNull] RelationalConventionSetBuilderDependencies relationalDependencies)
{
Dependencies = dependencies;
RelationalDependencies = relationalDependencies;
}
protected virtual ProviderConventionSetBuilderDependencies Dependencies { get; }
protected virtual RelationalConventionSetBuilderDependencies RelationalDependencies { get; }
/// <summary>
/// Called after a model is initialized.
/// </summary>
@ -48,26 +55,24 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
foreach (var property in entityType.GetDeclaredProperties())
{
JetValueGenerationStrategy? strategy = null;
var table = entityType.GetTableName();
if (table != null)
var declaringTable = property.GetMappedStoreObjects(StoreObjectType.Table).FirstOrDefault();
if (declaringTable.Name != null!)
{
var storeObject = StoreObjectIdentifier.Table(table, entityType.GetSchema());
strategy = property.GetValueGenerationStrategy(storeObject);
strategy = property.GetValueGenerationStrategy(declaringTable);
if (strategy == JetValueGenerationStrategy.None
&& !IsStrategyNoneNeeded(property, storeObject))
&& !IsStrategyNoneNeeded(property, declaringTable))
{
strategy = null;
}
}
else
{
var view = entityType.GetViewName();
if (view != null)
var declaringView = property.GetMappedStoreObjects(StoreObjectType.View).FirstOrDefault();
if (declaringView.Name != null!)
{
var storeObject = StoreObjectIdentifier.View(view, entityType.GetViewSchema());
strategy = property.GetValueGenerationStrategy(storeObject);
strategy = property.GetValueGenerationStrategy(declaringView);
if (strategy == JetValueGenerationStrategy.None
&& !IsStrategyNoneNeeded(property, storeObject))
&& !IsStrategyNoneNeeded(property, declaringView))
{
strategy = null;
}
@ -75,9 +80,23 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
}
// Needed for the annotation to show up in the model snapshot
if (strategy != null)
if (strategy != null
&& declaringTable.Name != null)
{
property.Builder.HasValueGenerationStrategy(strategy);
if (strategy == JetValueGenerationStrategy.Sequence)
{
var sequence = modelBuilder.HasSequence(
property.GetJetSequenceName(declaringTable)
?? entityType.GetRootType().ShortName() + modelBuilder.Metadata.GetJetSequenceNameSuffix(),
property.GetJetSequenceSchema(declaringTable)
?? modelBuilder.Metadata.GetJetSequenceSchema()).Metadata;
property.Builder.HasDefaultValueSql(
RelationalDependencies.UpdateSqlGenerator.GenerateObtainNextSequenceValueOperation(
sequence.Name, sequence.Schema));
}
}
}
}

@ -57,5 +57,23 @@ namespace EntityFrameworkCore.Jet.Metadata.Internal
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public const string IdentityIncrement = Prefix + "IdentityIncrement";
public const string SequenceNameSuffix = Prefix + "SequenceNameSuffix";
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public const string SequenceName = Prefix + "SequenceName";
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public const string SequenceSchema = Prefix + "SequenceSchema";
}
}

@ -5,6 +5,7 @@ namespace EntityFrameworkCore.Jet.Metadata
public enum JetValueGenerationStrategy
{
None,
IdentityColumn
IdentityColumn,
Sequence
}
}

Loading…
Cancel
Save