Skip to content

Query: Add tests for indexer properties #19502

New issue

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

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

Already on GitHub? # to your account

Merged
merged 1 commit into from
Feb 6, 2020
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 @@ -673,8 +673,8 @@ private static IEnumerable<IProperty> GetSortedProperties(TableMapping target)

private static IEnumerable<IProperty> GetSortedProperties(IEntityType entityType)
{
var shadowProperties = new List<IProperty>();
var shadowPrimaryKeyProperties = new List<IProperty>();
var leastPriorityProperties = new List<IProperty>();
var leastPriorityPrimaryKeyProperties = new List<IProperty>();
var primaryKeyPropertyGroups = new Dictionary<PropertyInfo, IProperty>();
var groups = new Dictionary<PropertyInfo, List<IProperty>>();
var unorderedGroups = new Dictionary<PropertyInfo, SortedDictionary<int, IProperty>>();
Expand All @@ -683,11 +683,12 @@ private static IEnumerable<IProperty> GetSortedProperties(IEntityType entityType
foreach (var property in entityType.GetDeclaredProperties())
{
var clrProperty = property.PropertyInfo;
if (clrProperty == null)
if (clrProperty == null
|| clrProperty.IsIndexerProperty())
{
if (property.IsPrimaryKey())
{
shadowPrimaryKeyProperties.Add(property);
leastPriorityPrimaryKeyProperties.Add(property);

continue;
}
Expand All @@ -696,7 +697,7 @@ private static IEnumerable<IProperty> GetSortedProperties(IEntityType entityType
.FirstOrDefault(fk => fk.DependentToPrincipal?.PropertyInfo != null);
if (foreignKey == null)
{
shadowProperties.Add(property);
leastPriorityProperties.Add(property);

continue;
}
Expand Down Expand Up @@ -746,7 +747,7 @@ private static IEnumerable<IProperty> GetSortedProperties(IEntityType entityType
var properties = GetSortedProperties(definingForeignKey.DeclaringEntityType).ToList();
if (clrProperty == null)
{
shadowProperties.AddRange(properties);
leastPriorityProperties.AddRange(properties);

continue;
}
Expand Down Expand Up @@ -792,9 +793,9 @@ private static IEnumerable<IProperty> GetSortedProperties(IEntityType entityType
return sortedPropertyInfos
.Select(pi => primaryKeyPropertyGroups.ContainsKey(pi) ? primaryKeyPropertyGroups[pi] : null)
.Where(e => e != null)
.Concat(shadowPrimaryKeyProperties)
.Concat(leastPriorityPrimaryKeyProperties)
.Concat(sortedPropertyInfos.Where(pi => !primaryKeyPropertyGroups.ContainsKey(pi)).SelectMany(p => groups[p]))
.Concat(shadowProperties)
.Concat(leastPriorityProperties)
.Concat(entityType.GetDirectlyDerivedTypes().SelectMany(GetSortedProperties));
}

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
117 changes: 85 additions & 32 deletions src/EFCore/Metadata/Internal/EntityType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2612,40 +2612,86 @@ public virtual IEnumerable<IDictionary<string, object>> GetSeedData(bool provide
var seed = new Dictionary<string, object>(StringComparer.Ordinal);
data.Add(seed);
var type = rawSeed.GetType();
foreach (var memberInfo in type.GetMembersInHierarchy())

if (type == ClrType)
{
if (!properties.TryGetValue(memberInfo.GetSimpleMemberName(), out var propertyBase))
// non-anonymous type
foreach (var propertyBase in properties.Values)
{
continue;
}
ValueConverter valueConverter = null;
if (providerValues
&& !valueConverters.TryGetValue(propertyBase.Name, out valueConverter))
{
if (propertyBase is IProperty property)
{
valueConverter = property.FindTypeMapping()?.Converter
?? property.GetValueConverter();
}

ValueConverter valueConverter = null;
if (providerValues
&& !valueConverters.TryGetValue(memberInfo.Name, out valueConverter))
{
if (propertyBase is IProperty property)
valueConverters[propertyBase.Name] = valueConverter;
}

object value = null;
switch (propertyBase.GetIdentifyingMemberInfo())
{
valueConverter = property.FindTypeMapping()?.Converter
?? property.GetValueConverter();
case PropertyInfo propertyInfo:
if (propertyBase.IsIndexerProperty())
{
try
{
value = propertyInfo.GetValue(rawSeed, new[] { propertyBase.Name });
}
catch (Exception)
{
// Swallow if the property value is not set on the seed data
}
}
else
{
value = propertyInfo.GetValue(rawSeed);
}

break;
case FieldInfo fieldInfo:
value = fieldInfo.GetValue(rawSeed);
break;
}

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

object value = null;
switch (memberInfo)
}
else
{
// anonymous type
foreach (var memberInfo in type.GetMembersInHierarchy())
{
case PropertyInfo propertyInfo:
value = propertyInfo.GetValue(rawSeed);
break;
case FieldInfo fieldInfo:
value = fieldInfo.GetValue(rawSeed);
break;
}
if (!properties.TryGetValue(memberInfo.GetSimpleMemberName(), out var propertyBase))
{
continue;
}

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

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

// All memberInfos are PropertyInfo in anonymous type
var value = ((PropertyInfo)memberInfo).GetValue(rawSeed);

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

Expand Down Expand Up @@ -2865,7 +2911,8 @@ IConventionEntityTypeBuilder IConventionEntityType.Builder
/// </summary>
IModel ITypeBase.Model
{
[DebuggerStepThrough] get => Model;
[DebuggerStepThrough]
get => Model;
}

/// <summary>
Expand All @@ -2876,7 +2923,8 @@ IModel ITypeBase.Model
/// </summary>
IMutableModel IMutableTypeBase.Model
{
[DebuggerStepThrough] get => Model;
[DebuggerStepThrough]
get => Model;
}

/// <summary>
Expand All @@ -2887,7 +2935,8 @@ IMutableModel IMutableTypeBase.Model
/// </summary>
IMutableModel IMutableEntityType.Model
{
[DebuggerStepThrough] get => Model;
[DebuggerStepThrough]
get => Model;
}

/// <summary>
Expand All @@ -2910,7 +2959,8 @@ IConventionModel IConventionEntityType.Model
/// </summary>
IEntityType IEntityType.BaseType
{
[DebuggerStepThrough] get => _baseType;
[DebuggerStepThrough]
get => _baseType;
}

/// <summary>
Expand Down Expand Up @@ -2945,7 +2995,8 @@ IConventionEntityType IConventionEntityType.BaseType
/// </summary>
IEntityType IEntityType.DefiningEntityType
{
[DebuggerStepThrough] get => DefiningEntityType;
[DebuggerStepThrough]
get => DefiningEntityType;
}

/// <summary>
Expand All @@ -2956,7 +3007,8 @@ IEntityType IEntityType.DefiningEntityType
/// </summary>
IMutableEntityType IMutableEntityType.DefiningEntityType
{
[DebuggerStepThrough] get => DefiningEntityType;
[DebuggerStepThrough]
get => DefiningEntityType;
}

/// <summary>
Expand All @@ -2967,7 +3019,8 @@ IMutableEntityType IMutableEntityType.DefiningEntityType
/// </summary>
IConventionEntityType IConventionEntityType.DefiningEntityType
{
[DebuggerStepThrough] get => DefiningEntityType;
[DebuggerStepThrough]
get => DefiningEntityType;
}

/// <summary>
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