Skip to content

Commit

Permalink
Query: Add tests for indexer properties
Browse files Browse the repository at this point in the history
Resolves #19495
Resolves #19458

Changes
- Sort indexer properties along with shadow properties during model diff for column ordering in a table
- Updated documentation of IndexedProperty
- Add IndexedProperty fluent API to owned navigation builder
- Re-attach indexer property correctly after detach
  • Loading branch information
smitpatel committed Feb 6, 2020
1 parent dfd3192 commit 1711290
Show file tree
Hide file tree
Showing 13 changed files with 1,513 additions and 543 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,8 @@ private static IEnumerable<IProperty> GetSortedProperties(IEntityType entityType
foreach (var property in entityType.GetDeclaredProperties())
{
var clrProperty = property.PropertyInfo;
if (clrProperty == null)
if (property.IsShadowProperty()
|| (clrProperty != null && clrProperty.IsIndexerProperty())
{
if (property.IsPrimaryKey())
{
Expand Down
9 changes: 8 additions & 1 deletion src/EFCore/Metadata/Builders/EntityTypeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,11 @@ public virtual PropertyBuilder Property([NotNull] Type propertyType, [NotNull] s
/// Returns an object that can be used to configure a property of the entity type.
/// If no property with the given name exists, then a new property will be added.
/// </para>
/// <para>
/// Indexed properties are stored in the entity using
/// <see href="https://docs.microsoft.com/dotnet/csharp/programming-guide/indexers/">an indexer</see>
/// supplying the provided property name.
/// </para>
/// </summary>
/// <typeparam name="TProperty"> The type of the property to be configured. </typeparam>
/// <param name="propertyName"> The name of the property to be configured. </param>
Expand All @@ -201,7 +206,9 @@ public virtual PropertyBuilder<TProperty> IndexedProperty<TProperty>([NotNull] s
/// If no property with the given name exists, then a new property will be added.
/// </para>
/// <para>
/// Indexed properties are stored in the entity using an indexer supplying the provided property name.
/// Indexed properties are stored in the entity using
/// <see href="https://docs.microsoft.com/dotnet/csharp/programming-guide/indexers/">an indexer</see>
/// supplying the provided property name.
/// </para>
/// </summary>
/// <param name="propertyType"> The type of the property to be configured. </param>
Expand Down
40 changes: 40 additions & 0 deletions src/EFCore/Metadata/Builders/OwnedNavigationBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,46 @@ public virtual PropertyBuilder Property([NotNull] Type propertyType, [NotNull] s
Check.NotNull(propertyType, nameof(propertyType)),
Check.NotEmpty(propertyName, nameof(propertyName)), ConfigurationSource.Explicit).Metadata);

/// <summary>
/// <para>
/// Returns an object that can be used to configure a property of the entity type.
/// If no property with the given name exists, then a new property will be added.
/// </para>
/// <para>
/// Indexed properties are stored in the entity using
/// <see href="https://docs.microsoft.com/dotnet/csharp/programming-guide/indexers/">an indexer</see>
/// supplying the provided property name.
/// </para>
/// </summary>
/// <typeparam name="TProperty"> The type of the property to be configured. </typeparam>
/// <param name="propertyName"> The name of the property to be configured. </param>
/// <returns> An object that can be used to configure the property. </returns>
public virtual PropertyBuilder<TProperty> IndexedProperty<TProperty>([NotNull] string propertyName)
=> new PropertyBuilder<TProperty>(
DependentEntityType.Builder.IndexedProperty(
typeof(TProperty),
Check.NotEmpty(propertyName, nameof(propertyName)), ConfigurationSource.Explicit).Metadata);

/// <summary>
/// <para>
/// Returns an object that can be used to configure a property of the entity type.
/// If no property with the given name exists, then a new property will be added.
/// </para>
/// <para>
/// Indexed properties are stored in the entity using
/// <see href="https://docs.microsoft.com/dotnet/csharp/programming-guide/indexers/">an indexer</see>
/// supplying the provided property name.
/// </para>
/// </summary>
/// <param name="propertyType"> The type of the property to be configured. </param>
/// <param name="propertyName"> The name of the property to be configured. </param>
/// <returns> An object that can be used to configure the property. </returns>
public virtual PropertyBuilder IndexedProperty([NotNull] Type propertyType, [NotNull] string propertyName)
=> new PropertyBuilder(
DependentEntityType.Builder.IndexedProperty(
Check.NotNull(propertyType, nameof(propertyType)),
Check.NotEmpty(propertyName, nameof(propertyName)), ConfigurationSource.Explicit).Metadata);

/// <summary>
/// Excludes the given property from the entity type. This method is typically used to remove properties
/// or navigations from the owned entity type that were added by convention.
Expand Down
56 changes: 35 additions & 21 deletions src/EFCore/Metadata/Internal/EntityType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2616,36 +2616,50 @@ public virtual IEnumerable<IDictionary<string, object>> GetSeedData(bool provide
{
if (!properties.TryGetValue(memberInfo.GetSimpleMemberName(), out var propertyBase))
{
if (memberInfo is PropertyInfo propertyInfo
&& propertyInfo.IsIndexerProperty())
{
foreach (var indexerPropertyBase in this.GetPropertiesAndNavigations().Where(pb => pb.IsIndexerProperty()))
{
ProcessPropertyBase(indexerPropertyBase, indexer: true);
}
}

continue;
}

ValueConverter valueConverter = null;
if (providerValues
&& !valueConverters.TryGetValue(memberInfo.Name, out valueConverter))
ProcessPropertyBase(propertyBase);

void ProcessPropertyBase(IPropertyBase propertyBase, bool indexer = false)
{
if (propertyBase is IProperty property)
ValueConverter valueConverter = null;
if (providerValues
&& !valueConverters.TryGetValue(propertyBase.Name, out valueConverter))
{
valueConverter = property.FindTypeMapping()?.Converter
?? property.GetValueConverter();
if (propertyBase is IProperty property)
{
valueConverter = property.FindTypeMapping()?.Converter
?? property.GetValueConverter();
}

valueConverters[propertyBase.Name] = valueConverter;
}

valueConverters[memberInfo.Name] = valueConverter;
}
object value = null;
switch (memberInfo)
{
case PropertyInfo propertyInfo:
value = propertyInfo.GetValue(rawSeed, indexer ? new[] { propertyBase.Name } : null);
break;
case FieldInfo fieldInfo:
value = fieldInfo.GetValue(rawSeed);
break;
}

object value = null;
switch (memberInfo)
{
case PropertyInfo propertyInfo:
value = propertyInfo.GetValue(rawSeed);
break;
case FieldInfo fieldInfo:
value = fieldInfo.GetValue(rawSeed);
break;
seed[propertyBase.Name] = valueConverter == null
? value
: valueConverter.ConvertToProvider(value);
}

seed[memberInfo.Name] = valueConverter == null
? value
: valueConverter.ConvertToProvider(value);
}
}

Expand Down
11 changes: 8 additions & 3 deletions src/EFCore/Metadata/Internal/InternalPropertyBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -584,10 +584,14 @@ public virtual InternalPropertyBuilder Attach([NotNull] InternalEntityTypeBuilde
}
else
{
newPropertyBuilder = Metadata.GetIdentifyingMemberInfo() == null
var identifyingMemberInfo = Metadata.GetIdentifyingMemberInfo();

newPropertyBuilder = identifyingMemberInfo == null
? entityTypeBuilder.Property(
Metadata.ClrType, Metadata.Name, Metadata.GetTypeConfigurationSource(), configurationSource)
: entityTypeBuilder.Property(Metadata.GetIdentifyingMemberInfo(), configurationSource);
: (identifyingMemberInfo as PropertyInfo)?.IsIndexerProperty() == true
? entityTypeBuilder.IndexedProperty(Metadata.ClrType, Metadata.Name, configurationSource)
: entityTypeBuilder.Property(identifyingMemberInfo, configurationSource);
}

if (newProperty == Metadata)
Expand Down Expand Up @@ -658,7 +662,8 @@ public virtual InternalPropertyBuilder Attach([NotNull] InternalEntityTypeBuilde
/// </summary>
IConventionProperty IConventionPropertyBuilder.Metadata
{
[DebuggerStepThrough] get => Metadata;
[DebuggerStepThrough]
get => Metadata;
}

/// <summary>
Expand Down
Loading

0 comments on commit 1711290

Please sign in to comment.