Skip to content

Commit 93da2f0

Browse files
committed
Add model building support for collections of owned types.
Add HasKey() to ReferenceOwnershipBuilder Add missing string overloads of OwnsOne() and HasOne() Add hiding overloads of HasData() Sort methods and fix some comments Move owned type primary key configuration to KeyDiscoveryConvention Part of #8172
1 parent ea9b3d8 commit 93da2f0

File tree

46 files changed

+4463
-856
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+4463
-856
lines changed

EFCore.Runtime.sln.DotSettings

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=Microsoft_002EEntityFrameworkCore_002EAnnotations/@EntryIndexedValue">True</s:Boolean>
23
<s:String x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=D44E9AA0167CE64B866A64486B319AB0/RelativePath/@EntryValue">..\EFCore.sln.DotSettings</s:String>
34
<s:Boolean x:Key="/Default/Environment/InjectedLayers/FileInjectedLayer/=D44E9AA0167CE64B866A64486B319AB0/@KeyIndexDefined">True</s:Boolean>
45
<s:Boolean x:Key="/Default/Environment/InjectedLayers/InjectedLayerCustomization/=FileD44E9AA0167CE64B866A64486B319AB0/@KeyIndexDefined">True</s:Boolean>

src/EFCore.Design/Migrations/Design/CSharpSnapshotGenerator.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,14 @@ protected virtual void GenerateEntityType(
137137
Check.NotNull(entityType, nameof(entityType));
138138
Check.NotNull(stringBuilder, nameof(stringBuilder));
139139

140-
var ownerNavigation = entityType.FindOwnership()?.PrincipalToDependent.Name;
140+
var ownership = entityType.FindOwnership();
141+
var ownerNavigation = ownership?.PrincipalToDependent.Name;
141142

142143
stringBuilder
143144
.Append(builderName)
144145
.Append(
145146
ownerNavigation != null
146-
? ".OwnsOne("
147+
? ownership.IsUnique ? ".OwnsOne(" : ".OwnsMany("
147148
: ".Entity(")
148149
.Append(Code.Literal(entityType.Name));
149150

@@ -186,10 +187,7 @@ protected virtual void GenerateEntityType(
186187

187188
GenerateProperties(builderName, entityType.GetDeclaredProperties(), stringBuilder);
188189

189-
if (ownerNavigation == null)
190-
{
191-
GenerateKeys(builderName, entityType.GetDeclaredKeys(), entityType.FindDeclaredPrimaryKey(), stringBuilder);
192-
}
190+
GenerateKeys(builderName, entityType.GetDeclaredKeys(), entityType.FindDeclaredPrimaryKey(), stringBuilder);
193191

194192
GenerateIndexes(builderName, entityType.GetDeclaredIndexes(), stringBuilder);
195193

src/EFCore.Design/Migrations/Internal/SnapshotModelProcessor.cs

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Linq;
67
using System.Reflection;
@@ -43,41 +44,67 @@ public SnapshotModelProcessor([NotNull] IOperationReporter operationReporter)
4344
/// </summary>
4445
public virtual IModel Process(IModel model)
4546
{
46-
if (model != null
47-
&& model.GetProductVersion()?.StartsWith("1.") == true)
47+
if (model == null)
4848
{
49-
ProcessElement(model);
49+
return null;
50+
}
5051

51-
foreach (var entityType in model.GetEntityTypes())
52-
{
53-
ProcessElement(entityType);
54-
ProcessCollection(entityType.GetProperties());
55-
ProcessCollection(entityType.GetKeys());
56-
ProcessCollection(entityType.GetIndexes());
52+
var version = model.GetProductVersion();
53+
if (version == null)
54+
{
55+
return model;
56+
}
5757

58-
foreach (var element in entityType.GetForeignKeys())
59-
{
60-
ProcessElement(element);
61-
ProcessElement(element.DependentToPrincipal);
62-
ProcessElement(element.PrincipalToDependent);
63-
}
58+
ProcessElement(model, version);
59+
60+
foreach (var entityType in model.GetEntityTypes())
61+
{
62+
ProcessElement(entityType, version);
63+
ProcessCollection(entityType.GetProperties(), version);
64+
ProcessCollection(entityType.GetKeys(), version);
65+
ProcessCollection(entityType.GetIndexes(), version);
66+
67+
foreach (var element in entityType.GetForeignKeys())
68+
{
69+
ProcessElement(element, version);
70+
ProcessElement(element.DependentToPrincipal, version);
71+
ProcessElement(element.PrincipalToDependent, version);
6472
}
6573
}
6674

6775
return model;
6876
}
6977

70-
private void ProcessCollection(IEnumerable<IAnnotatable> metadata)
78+
private void ProcessCollection(IEnumerable<IAnnotatable> metadata, string version)
7179
{
7280
foreach (var element in metadata)
7381
{
74-
ProcessElement(element);
82+
ProcessElement(element, version);
83+
}
84+
}
85+
86+
private void ProcessElement(IEntityType entityType, string version)
87+
{
88+
ProcessElement((IAnnotatable)entityType, version);
89+
90+
if ((version.StartsWith("2.0", StringComparison.Ordinal)
91+
|| version.StartsWith("2.1", StringComparison.Ordinal))
92+
&& entityType is IMutableEntityType mutableEntityType
93+
&& entityType.FindPrimaryKey() == null)
94+
{
95+
var ownership = mutableEntityType.FindOwnership();
96+
if (ownership is IMutableForeignKey mutableOwnership
97+
&& ownership.IsUnique)
98+
{
99+
mutableEntityType.SetPrimaryKey(mutableOwnership.Properties);
100+
}
75101
}
76102
}
77103

78-
private void ProcessElement(IAnnotatable metadata)
104+
private void ProcessElement(IAnnotatable metadata, string version)
79105
{
80-
if (metadata is IMutableAnnotatable mutableMetadata)
106+
if (version.StartsWith("1.", StringComparison.Ordinal)
107+
&& metadata is IMutableAnnotatable mutableMetadata)
81108
{
82109
foreach (var annotation in mutableMetadata.GetAnnotations().ToList())
83110
{
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using JetBrains.Annotations;
5+
using Microsoft.EntityFrameworkCore.Infrastructure;
6+
using Microsoft.EntityFrameworkCore.Metadata.Builders;
7+
using Microsoft.EntityFrameworkCore.Metadata.Internal;
8+
using Microsoft.EntityFrameworkCore.Utilities;
9+
10+
// ReSharper disable once CheckNamespace
11+
namespace Microsoft.EntityFrameworkCore
12+
{
13+
/// <summary>
14+
/// Relational database specific extension methods for <see cref="CollectionOwnershipBuilder" />.
15+
/// </summary>
16+
public static class RelationalCollectionOwnershipBuilderExtensions
17+
{
18+
/// <summary>
19+
/// Configures the view or table that the entity maps to when targeting a relational database.
20+
/// </summary>
21+
/// <param name="collectionOwnershipBuilder"> The builder for the entity type being configured. </param>
22+
/// <param name="name"> The name of the view or table. </param>
23+
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
24+
public static CollectionOwnershipBuilder ToTable(
25+
[NotNull] this CollectionOwnershipBuilder collectionOwnershipBuilder,
26+
[CanBeNull] string name)
27+
{
28+
Check.NotNull(collectionOwnershipBuilder, nameof(collectionOwnershipBuilder));
29+
Check.NullButNotEmpty(name, nameof(name));
30+
31+
collectionOwnershipBuilder.GetInfrastructure<InternalEntityTypeBuilder>()
32+
.Relational(ConfigurationSource.Explicit)
33+
.ToTable(name);
34+
35+
return collectionOwnershipBuilder;
36+
}
37+
38+
/// <summary>
39+
/// Configures the view or table that the entity maps to when targeting a relational database.
40+
/// </summary>
41+
/// <typeparam name="TEntity"> The entity type being configured. </typeparam>
42+
/// <typeparam name="TDependentEntity"> The entity type that this relationship targets. </typeparam>
43+
/// <param name="collectionOwnershipBuilder"> The builder for the entity type being configured. </param>
44+
/// <param name="name"> The name of the view or table. </param>
45+
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
46+
public static CollectionOwnershipBuilder<TEntity, TDependentEntity> ToTable<TEntity, TDependentEntity>(
47+
[NotNull] this CollectionOwnershipBuilder<TEntity, TDependentEntity> collectionOwnershipBuilder,
48+
[CanBeNull] string name)
49+
where TEntity : class
50+
where TDependentEntity : class
51+
=> (CollectionOwnershipBuilder<TEntity, TDependentEntity>)ToTable((CollectionOwnershipBuilder)collectionOwnershipBuilder, name);
52+
53+
/// <summary>
54+
/// Configures the view or table that the entity maps to when targeting a relational database.
55+
/// </summary>
56+
/// <param name="collectionOwnershipBuilder"> The builder for the entity type being configured. </param>
57+
/// <param name="name"> The name of the view or table. </param>
58+
/// <param name="schema"> The schema of the view or table. </param>
59+
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
60+
public static CollectionOwnershipBuilder ToTable(
61+
[NotNull] this CollectionOwnershipBuilder collectionOwnershipBuilder,
62+
[CanBeNull] string name,
63+
[CanBeNull] string schema)
64+
{
65+
Check.NotNull(collectionOwnershipBuilder, nameof(collectionOwnershipBuilder));
66+
Check.NullButNotEmpty(name, nameof(name));
67+
Check.NullButNotEmpty(schema, nameof(schema));
68+
69+
collectionOwnershipBuilder.GetInfrastructure<InternalEntityTypeBuilder>()
70+
.Relational(ConfigurationSource.Explicit)
71+
.ToTable(name, schema);
72+
73+
return collectionOwnershipBuilder;
74+
}
75+
76+
/// <summary>
77+
/// Configures the view or table that the entity maps to when targeting a relational database.
78+
/// </summary>
79+
/// <typeparam name="TEntity"> The entity type being configured. </typeparam>
80+
/// <typeparam name="TDependentEntity"> The entity type that this relationship targets. </typeparam>
81+
/// <param name="collectionOwnershipBuilder"> The builder for the entity type being configured. </param>
82+
/// <param name="name"> The name of the view or table. </param>
83+
/// <param name="schema"> The schema of the view or table. </param>
84+
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
85+
public static CollectionOwnershipBuilder<TEntity, TDependentEntity> ToTable<TEntity, TDependentEntity>(
86+
[NotNull] this CollectionOwnershipBuilder<TEntity, TDependentEntity> collectionOwnershipBuilder,
87+
[CanBeNull] string name,
88+
[CanBeNull] string schema)
89+
where TEntity : class
90+
where TDependentEntity : class
91+
=> (CollectionOwnershipBuilder<TEntity, TDependentEntity>)ToTable((CollectionOwnershipBuilder)collectionOwnershipBuilder, name, schema);
92+
93+
/// <summary>
94+
/// Configures the foreign key constraint name for this relationship when targeting a relational database.
95+
/// </summary>
96+
/// <param name="referenceReferenceBuilder"> The builder being used to configure the relationship. </param>
97+
/// <param name="name"> The name of the foreign key constraint. </param>
98+
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
99+
public static CollectionOwnershipBuilder HasConstraintName(
100+
[NotNull] this CollectionOwnershipBuilder referenceReferenceBuilder,
101+
[CanBeNull] string name)
102+
{
103+
Check.NotNull(referenceReferenceBuilder, nameof(referenceReferenceBuilder));
104+
Check.NullButNotEmpty(name, nameof(name));
105+
106+
referenceReferenceBuilder.GetInfrastructure<InternalRelationshipBuilder>()
107+
.Relational(ConfigurationSource.Explicit)
108+
.HasConstraintName(name);
109+
110+
return referenceReferenceBuilder;
111+
}
112+
113+
/// <summary>
114+
/// Configures the foreign key constraint name for this relationship when targeting a relational database.
115+
/// </summary>
116+
/// <param name="referenceReferenceBuilder"> The builder being used to configure the relationship. </param>
117+
/// <param name="name"> The name of the foreign key constraint. </param>
118+
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
119+
/// <typeparam name="TEntity"> The entity type on one end of the relationship. </typeparam>
120+
/// <typeparam name="TDependentEntity"> The entity type on the other end of the relationship. </typeparam>
121+
public static CollectionOwnershipBuilder<TEntity, TDependentEntity> HasConstraintName<TEntity, TDependentEntity>(
122+
[NotNull] this CollectionOwnershipBuilder<TEntity, TDependentEntity> referenceReferenceBuilder,
123+
[CanBeNull] string name)
124+
where TEntity : class
125+
where TDependentEntity : class
126+
=> (CollectionOwnershipBuilder<TEntity, TDependentEntity>)HasConstraintName(
127+
(CollectionOwnershipBuilder)referenceReferenceBuilder, name);
128+
}
129+
}

src/EFCore.Relational/Storage/Internal/RelationalCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ protected virtual DbCommand CreateCommand(
352352
command.CommandText = AdjustCommandText(CommandText);
353353

354354
ConfigureCommand(command);
355-
355+
356356
if (connection.CurrentTransaction != null)
357357
{
358358
command.Transaction = connection.CurrentTransaction.GetDbTransaction();
@@ -388,7 +388,7 @@ protected virtual DbCommand CreateCommand(
388388
protected virtual void ConfigureCommand(DbCommand command)
389389
{
390390
}
391-
391+
392392
/// <summary>
393393
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
394394
/// directly from your code. This API may change or be removed in future releases.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using JetBrains.Annotations;
5+
using Microsoft.EntityFrameworkCore.Metadata.Builders;
6+
using Microsoft.EntityFrameworkCore.Utilities;
7+
8+
// ReSharper disable once CheckNamespace
9+
namespace Microsoft.EntityFrameworkCore
10+
{
11+
/// <summary>
12+
/// SQL Server specific extension methods for <see cref="CollectionOwnershipBuilder" />.
13+
/// </summary>
14+
public static class SqlServerCollectionOwnershipBuilderExtensions
15+
{
16+
/// <summary>
17+
/// Configures the table that the entity maps to when targeting SQL Server as memory-optimized.
18+
/// </summary>
19+
/// <param name="collectionOwnershipBuilder"> The builder for the entity type being configured. </param>
20+
/// <param name="memoryOptimized"> A value indicating whether the table is memory-optimized. </param>
21+
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
22+
public static CollectionOwnershipBuilder ForSqlServerIsMemoryOptimized(
23+
[NotNull] this CollectionOwnershipBuilder collectionOwnershipBuilder, bool memoryOptimized = true)
24+
{
25+
Check.NotNull(collectionOwnershipBuilder, nameof(collectionOwnershipBuilder));
26+
27+
collectionOwnershipBuilder.OwnedEntityType.SqlServer().IsMemoryOptimized = memoryOptimized;
28+
29+
return collectionOwnershipBuilder;
30+
}
31+
32+
/// <summary>
33+
/// Configures the table that the entity maps to when targeting SQL Server as memory-optimized.
34+
/// </summary>
35+
/// <typeparam name="TEntity"> The entity type being configured. </typeparam>
36+
/// <typeparam name="TRelatedEntity"> The entity type that this relationship targets. </typeparam>
37+
/// <param name="collectionOwnershipBuilder"> The builder for the entity type being configured. </param>
38+
/// <param name="memoryOptimized"> A value indicating whether the table is memory-optimized. </param>
39+
/// <returns> The same builder instance so that multiple calls can be chained. </returns>
40+
public static CollectionOwnershipBuilder<TEntity, TRelatedEntity> ForSqlServerIsMemoryOptimized<TEntity, TRelatedEntity>(
41+
[NotNull] this CollectionOwnershipBuilder<TEntity, TRelatedEntity> collectionOwnershipBuilder, bool memoryOptimized = true)
42+
where TEntity : class
43+
where TRelatedEntity : class
44+
=> (CollectionOwnershipBuilder<TEntity, TRelatedEntity>)ForSqlServerIsMemoryOptimized((CollectionOwnershipBuilder)collectionOwnershipBuilder, memoryOptimized);
45+
}
46+
}

src/EFCore/Metadata/Builders/CollectionNavigationBuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,13 @@ private InternalRelationshipBuilder WithOneBuilder(PropertyIdentity reference)
155155
}
156156

157157
return referenceName != null
158-
&& RelatedEntityType != foreignKey.DeclaringEntityType
158+
&& RelatedEntityType != foreignKey.DeclaringEntityType
159159
? reference.Property == null && CollectionProperty == null
160160
? Builder.Navigations(reference.Name, CollectionName, DeclaringEntityType, RelatedEntityType, ConfigurationSource.Explicit)
161161
: Builder.Navigations(reference.Property, CollectionProperty, DeclaringEntityType, RelatedEntityType, ConfigurationSource.Explicit)
162162
: reference.Property == null
163-
? Builder.DependentToPrincipal(reference.Name, ConfigurationSource.Explicit)
164-
: Builder.DependentToPrincipal(reference.Property, ConfigurationSource.Explicit);
163+
? Builder.DependentToPrincipal(reference.Name, ConfigurationSource.Explicit)
164+
: Builder.DependentToPrincipal(reference.Property, ConfigurationSource.Explicit);
165165
}
166166

167167
#region Hidden System.Object members

0 commit comments

Comments
 (0)