Skip to content
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
2 changes: 1 addition & 1 deletion src/EFCore.Design/Design/Internal/DbContextOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ private DbContext CreateContext(string? contextType, KeyValuePair<Type, Func<DbC

throw new OperationException(
DesignStrings.CannotCreateContextInstance(
contextType ?? contextPair.Key.GetType().ShortDisplayName(), ex.Message), ex);
contextType ?? contextPair.Key.ShortDisplayName(), ex.Message), ex);
}
}

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -1677,6 +1677,7 @@ void CheckForNullComplexProperties()
foreach (var complexProperty in entityType.GetFlattenedComplexProperties())
{
if (!complexProperty.IsNullable
&& !complexProperty.IsCollection
&& this[complexProperty] == null
&& complexProperty.ComplexType.GetProperties().Any(p => !p.IsNullable))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public virtual bool Generate(InternalEntityEntry entry, bool includePrimaryKey =
var hasNonStableValues = false;
IProperty? propertyWithNoGenerator = null;

//TODO: Handle complex properties
//TODO: Handle complex properties, #31633
foreach (var property in entry.EntityType.GetValueGeneratingProperties())
{
if (!TryFindValueGenerator(
Expand Down
6 changes: 3 additions & 3 deletions src/EFCore/ChangeTracking/LocalView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,15 @@ private void ObservableCollectionChanged(object? _, NotifyCollectionChangedEvent
}

/// <summary>
/// Returns an <see cref="IEnumerator{T}" /> for all tracked entities of type TEntity
/// Returns an <see cref="IEnumerator{T}" /> for all tracked entities of type <typeparamref name="TEntity"/>
/// that are not marked as deleted.
/// </summary>
/// <returns>An enumerator for the collection.</returns>
public virtual IEnumerator<TEntity> GetEnumerator()
=> _context.GetDependencies().StateManager.GetNonDeletedEntities<TEntity>().GetEnumerator();

/// <summary>
/// Returns an <see cref="IEnumerator{T}" /> for all tracked entities of type TEntity
/// Returns an <see cref="IEnumerator{T}" /> for all tracked entities of type <typeparamref name="TEntity"/>
/// that are not marked as deleted.
/// </summary>
/// <returns>An enumerator for the collection.</returns>
Expand Down Expand Up @@ -237,7 +237,7 @@ public virtual void Add(TEntity item)
}

/// <summary>
/// Marks all entities of type TEntity being tracked by the <see cref="DbContext" />
/// Marks all entities of type <typeparamref name="TEntity"/> being tracked by the <see cref="DbContext" />
/// as <see cref="EntityState.Deleted" />.
/// </summary>
/// <remarks>
Expand Down
6 changes: 0 additions & 6 deletions src/EFCore/Infrastructure/ModelValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,12 +321,6 @@ protected virtual void ValidatePropertyMapping(IConventionComplexProperty comple
throw new InvalidOperationException(
CoreStrings.EmptyComplexType(complexProperty.ComplexType.DisplayName()));
}

if (complexProperty.IsCollection)
{
throw new InvalidOperationException(
CoreStrings.ComplexPropertyCollection(typeBase.DisplayName(), complexProperty.Name));
}
}

/// <summary>
Expand Down
887 changes: 887 additions & 0 deletions src/EFCore/Metadata/Builders/ComplexCollectionBuilder.cs

Large diffs are not rendered by default.

591 changes: 591 additions & 0 deletions src/EFCore/Metadata/Builders/ComplexCollectionBuilder`.cs

Large diffs are not rendered by default.

259 changes: 256 additions & 3 deletions src/EFCore/Metadata/Builders/ComplexPropertyBuilder.cs

Large diffs are not rendered by default.

201 changes: 199 additions & 2 deletions src/EFCore/Metadata/Builders/ComplexPropertyBuilder`.cs

Large diffs are not rendered by default.

259 changes: 255 additions & 4 deletions src/EFCore/Metadata/Builders/EntityTypeBuilder.cs

Large diffs are not rendered by default.

199 changes: 197 additions & 2 deletions src/EFCore/Metadata/Builders/EntityTypeBuilder`.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ public virtual PrimitiveCollectionBuilder<TProperty> PrimitiveCollection<TProper
public new virtual EntityTypeBuilder<TEntity> ComplexProperty<TProperty>(
string propertyName,
Action<ComplexPropertyBuilder<TProperty>> buildAction)
where TProperty : notnull
=> (EntityTypeBuilder<TEntity>)base.ComplexProperty(propertyName, buildAction);

/// <summary>
Expand All @@ -223,10 +224,11 @@ public virtual PrimitiveCollectionBuilder<TProperty> PrimitiveCollection<TProper
string propertyName,
string complexTypeName,
Action<ComplexPropertyBuilder<TProperty>> buildAction)
where TProperty : notnull
=> (EntityTypeBuilder<TEntity>)base.ComplexProperty(propertyName, complexTypeName, buildAction);

/// <summary>
/// Returns an object that can be used to configure a complex property of the complex type.
/// Configures a complex property of the entity type.
/// If no property with the given name exists, then a new property will be added.
/// </summary>
/// <remarks>
Expand All @@ -247,7 +249,7 @@ public virtual PrimitiveCollectionBuilder<TProperty> PrimitiveCollection<TProper
=> (EntityTypeBuilder<TEntity>)base.ComplexProperty(propertyType, propertyName, buildAction);

/// <summary>
/// Returns an object that can be used to configure a complex property of the complex type.
/// Configures a complex property of the entity type.
/// If no property with the given name exists, then a new property will be added.
/// </summary>
/// <remarks>
Expand Down Expand Up @@ -280,6 +282,7 @@ public virtual PrimitiveCollectionBuilder<TProperty> PrimitiveCollection<TProper
/// <returns>An object that can be used to configure the complex property.</returns>
public virtual ComplexPropertyBuilder<TProperty> ComplexProperty<TProperty>(
Expression<Func<TEntity, TProperty?>> propertyExpression)
where TProperty : notnull
=> new(
Builder.ComplexProperty(
Check.NotNull(propertyExpression, nameof(propertyExpression)).GetMemberAccess(),
Expand All @@ -301,6 +304,7 @@ public virtual ComplexPropertyBuilder<TProperty> ComplexProperty<TProperty>(
public virtual ComplexPropertyBuilder<TProperty> ComplexProperty<TProperty>(
Expression<Func<TEntity, TProperty?>> propertyExpression,
string complexTypeName)
where TProperty : notnull
=> new(
Builder.ComplexProperty(
Check.NotNull(propertyExpression, nameof(propertyExpression)).GetMemberAccess(),
Expand All @@ -322,6 +326,7 @@ public virtual ComplexPropertyBuilder<TProperty> ComplexProperty<TProperty>(
public virtual EntityTypeBuilder<TEntity> ComplexProperty<TProperty>(
Expression<Func<TEntity, TProperty?>> propertyExpression,
Action<ComplexPropertyBuilder<TProperty>> buildAction)
where TProperty : notnull
{
Check.NotNull(buildAction, nameof(buildAction));

Expand All @@ -345,6 +350,7 @@ public virtual EntityTypeBuilder<TEntity> ComplexProperty<TProperty>(
Expression<Func<TEntity, TProperty?>> propertyExpression,
string complexTypeName,
Action<ComplexPropertyBuilder<TProperty>> buildAction)
where TProperty : notnull
{
Check.NotNull(buildAction, nameof(buildAction));

Expand All @@ -353,6 +359,195 @@ public virtual EntityTypeBuilder<TEntity> ComplexProperty<TProperty>(
return this;
}

/// <summary>
/// Configures a complex collection of the entity type.
/// If no property with the given name exists, then a new property will be added.
/// </summary>
/// <remarks>
/// When adding a new property with this overload the property name must match the
/// name of a CLR property or field on the complex type. This overload cannot be used to
/// add a new shadow state complex property.
/// </remarks>
/// <param name="propertyName">The name of the property to be configured.</param>
/// <param name="buildAction">An action that performs configuration of the property.</param>
/// <returns>An object that can be used to configure the property.</returns>
public new virtual EntityTypeBuilder<TEntity> ComplexCollection(string propertyName, Action<ComplexCollectionBuilder> buildAction)
=> (EntityTypeBuilder<TEntity>)base.ComplexCollection(propertyName, buildAction);

/// <summary>
/// Configures a complex collection of the entity type.
/// If no property with the given name exists, then a new property will be added.
/// </summary>
/// <remarks>
/// When adding a new property, if a property with the same name exists in the complex class
/// then it will be added to the model. If no property exists in the complex class, then
/// a new shadow state complex property will be added. A shadow state property is one that does not have a
/// corresponding property in the complex class. The current value for the property is stored in
/// the <see cref="ChangeTracker" /> rather than being stored in instances of the complex class.
/// </remarks>
/// <typeparam name="TProperty">The type of the property to be configured.</typeparam>
/// <typeparam name="TElement">The element type.</typeparam>
/// <param name="propertyName">The name of the property to be configured.</param>
/// <param name="buildAction">An action that performs configuration of the property.</param>
/// <returns>An object that can be used to configure the property.</returns>
public new virtual EntityTypeBuilder<TEntity> ComplexCollection<TProperty, TElement>(string propertyName, Action<ComplexCollectionBuilder<TElement>> buildAction)
where TProperty : IEnumerable<TElement>
where TElement : notnull
=> (EntityTypeBuilder<TEntity>)base.ComplexCollection<TProperty, TElement>(propertyName, buildAction);

/// <summary>
/// Configures a complex collection of the entity type.
/// If no property with the given name exists, then a new property will be added.
/// </summary>
/// <remarks>
/// When adding a new property, if a property with the same name exists in the complex class
/// then it will be added to the model. If no property exists in the complex class, then
/// a new shadow state complex property will be added. A shadow state property is one that does not have a
/// corresponding property in the complex class. The current value for the property is stored in
/// the <see cref="ChangeTracker" /> rather than being stored in instances of the complex class.
/// </remarks>
/// <typeparam name="TProperty">The type of the property to be configured.</typeparam>
/// <typeparam name="TElement">The element type.</typeparam>
/// <param name="propertyName">The name of the property to be configured.</param>
/// <param name="complexTypeName">The name of the complex type.</param>
/// <param name="buildAction">An action that performs configuration of the property.</param>
/// <returns>An object that can be used to configure the property.</returns>
public new virtual EntityTypeBuilder<TEntity> ComplexCollection<TProperty, TElement>(
string propertyName,
string complexTypeName,
Action<ComplexCollectionBuilder<TElement>> buildAction)
where TProperty : IEnumerable<TElement>
where TElement : notnull
=> (EntityTypeBuilder<TEntity>)base.ComplexCollection<TProperty, TElement>(propertyName, complexTypeName, buildAction);

/// <summary>
/// Configures a complex collection of the entity type.
/// If no property with the given name exists, then a new property will be added.
/// </summary>
/// <remarks>
/// When adding a new complex property, if a property with the same name exists in the complex class
/// then it will be added to the model. If no property exists in the complex class, then
/// a new shadow state complex property will be added. A shadow state property is one that does not have a
/// corresponding property in the complex class. The current value for the property is stored in
/// the <see cref="ChangeTracker" /> rather than being stored in instances of the complex class.
/// </remarks>
/// <param name="propertyType">The type of the property to be configured.</param>
/// <param name="propertyName">The name of the property to be configured.</param>
/// <param name="buildAction">An action that performs configuration of the property.</param>
/// <returns>An object that can be used to configure the property.</returns>
public new virtual EntityTypeBuilder<TEntity> ComplexCollection(Type propertyType, string propertyName, Action<ComplexCollectionBuilder> buildAction)
=> (EntityTypeBuilder<TEntity>)base.ComplexCollection(propertyType, propertyName, buildAction);

/// <summary>
/// Configures a complex collection of the entity type.
/// If no property with the given name exists, then a new property will be added.
/// </summary>
/// <remarks>
/// When adding a new complex property, if a property with the same name exists in the complex class
/// then it will be added to the model. If no property exists in the complex class, then
/// a new shadow state complex property will be added. A shadow state property is one that does not have a
/// corresponding property in the complex class. The current value for the property is stored in
/// the <see cref="ChangeTracker" /> rather than being stored in instances of the complex class.
/// </remarks>
/// <param name="propertyType">The type of the property to be configured.</param>
/// <param name="propertyName">The name of the property to be configured.</param>
/// <param name="complexTypeName">The name of the complex type.</param>
/// <param name="buildAction">An action that performs configuration of the property.</param>
/// <returns>An object that can be used to configure the property.</returns>
public new virtual EntityTypeBuilder<TEntity> ComplexCollection(
Type propertyType, string propertyName, string complexTypeName, Action<ComplexCollectionBuilder> buildAction)
=> (EntityTypeBuilder<TEntity>)base.ComplexCollection(propertyType, propertyName, complexTypeName, buildAction);

/// <summary>
/// Returns an object that can be used to configure a complex collection property of the entity type.
/// If the specified property is not already part of the model, it will be added.
/// </summary>
/// <typeparam name="TElement">The element type.</typeparam>
/// <param name="propertyExpression">
/// A lambda expression representing the property to be configured (
/// <c>blog => blog.Url</c>).
/// </param>
/// <returns>An object that can be used to configure the complex collection property.</returns>
public virtual ComplexCollectionBuilder<TElement> ComplexCollection<TElement>(
Expression<Func<TEntity, IEnumerable<TElement>?>> propertyExpression)
where TElement : notnull
=> new(
Builder.ComplexProperty(
Check.NotNull(propertyExpression, nameof(propertyExpression)).GetMemberAccess(),
complexTypeName: null,
collection: true,
ConfigurationSource.Explicit)!.Metadata);

/// <summary>
/// Returns an object that can be used to configure a complex collection property of the entity type.
/// If the specified property is not already part of the model, it will be added.
/// </summary>
/// <typeparam name="TElement">The element type.</typeparam>
/// <param name="propertyExpression">
/// A lambda expression representing the property to be configured (
/// <c>blog => blog.Url</c>).
/// </param>
/// <param name="complexTypeName">The name of the complex type.</param>
/// <returns>An object that can be used to configure the complex collection property.</returns>
public virtual ComplexCollectionBuilder<TElement> ComplexCollection<TElement>(
Expression<Func<TEntity, IEnumerable<TElement>?>> propertyExpression,
string complexTypeName)
where TElement : notnull
=> new(
Builder.ComplexProperty(
Check.NotNull(propertyExpression, nameof(propertyExpression)).GetMemberAccess(),
Check.NotEmpty(complexTypeName, nameof(complexTypeName)),
collection: true,
ConfigurationSource.Explicit)!.Metadata);

/// <summary>
/// Configures a complex collection property of the entity type.
/// If the specified property is not already part of the model, it will be added.
/// </summary>
/// <typeparam name="TElement">The element type.</typeparam>
/// <param name="propertyExpression">
/// A lambda expression representing the property to be configured (
/// <c>blog => blog.Url</c>).
/// </param>
/// <param name="buildAction">An action that performs configuration of the property.</param>
/// <returns>An object that can be used to configure the complex collection property.</returns>
public virtual EntityTypeBuilder<TEntity> ComplexCollection<TElement>(
Expression<Func<TEntity, IEnumerable<TElement>?>> propertyExpression,
Action<ComplexCollectionBuilder<TElement>> buildAction)
where TElement : notnull
{
Check.NotNull(buildAction, nameof(buildAction));

buildAction(ComplexCollection(propertyExpression));

return this;
}

/// <summary>
/// Configures a complex collection property of the entity type.
/// If the specified property is not already part of the model, it will be added.
/// </summary>
/// <param name="propertyExpression">
/// A lambda expression representing the property to be configured (
/// <c>blog => blog.Url</c>).
/// </param>
/// <typeparam name="TElement">The element type.</typeparam>
/// <param name="complexTypeName">The name of the complex type.</param>
/// <param name="buildAction">An action that performs configuration of the property.</param>
/// <returns>An object that can be used to configure the complex collection property.</returns>
public virtual EntityTypeBuilder<TEntity> ComplexCollection<TElement>(
Expression<Func<TEntity, IEnumerable<TElement>?>> propertyExpression,
string complexTypeName,
Action<ComplexCollectionBuilder<TElement>> buildAction)
where TElement : notnull
{
Check.NotNull(buildAction, nameof(buildAction));

buildAction(ComplexCollection(propertyExpression, complexTypeName));

return this;
}

/// <summary>
/// Returns an object that can be used to configure an existing navigation property of the entity type.
/// It is an error for the navigation property not to exist.
Expand Down
Loading