Skip to content

Commit 6068740

Browse files
committed
Moved IPrimaryKeyPropertiesProvider to their own files and moved the singletons to IPrimaryKeyPropertiesProvider for better IntelliSense support.
PrimaryKeyPropertiesProviders is not obsolete.
1 parent 6d7537f commit 6068740

File tree

15 files changed

+219
-158
lines changed

15 files changed

+219
-158
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using Microsoft.EntityFrameworkCore.Metadata;
2+
using Thinktecture.EntityFrameworkCore.Data;
3+
4+
namespace Thinktecture.EntityFrameworkCore.TempTables;
5+
6+
internal sealed class AdaptiveEntityTypeConfigurationPrimaryKeyPropertiesProvider : IPrimaryKeyPropertiesProvider
7+
{
8+
public IReadOnlyCollection<PropertyWithNavigations> GetPrimaryKeyProperties(IEntityType entityType, IReadOnlyCollection<PropertyWithNavigations> tempTableProperties)
9+
{
10+
if (tempTableProperties.Count == 0)
11+
return Array.Empty<PropertyWithNavigations>();
12+
13+
var pk = entityType.FindPrimaryKey()?.Properties;
14+
15+
if (pk is null or { Count: 0 })
16+
return Array.Empty<PropertyWithNavigations>();
17+
18+
return pk.Select(p => new PropertyWithNavigations(p, Array.Empty<INavigation>()))
19+
.Intersect(tempTableProperties)
20+
.ToList();
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using Microsoft.EntityFrameworkCore.Metadata;
2+
using Thinktecture.EntityFrameworkCore.Data;
3+
4+
namespace Thinktecture.EntityFrameworkCore.TempTables;
5+
6+
internal sealed class AdaptiveForcedPrimaryKeyPropertiesProvider : IPrimaryKeyPropertiesProvider
7+
{
8+
public IReadOnlyCollection<PropertyWithNavigations> GetPrimaryKeyProperties(IEntityType entityType, IReadOnlyCollection<PropertyWithNavigations> tempTableProperties)
9+
{
10+
if (tempTableProperties.Count == 0)
11+
return Array.Empty<PropertyWithNavigations>();
12+
13+
var pk = entityType.FindPrimaryKey()?.Properties;
14+
15+
if (pk is null or { Count: 0 })
16+
return tempTableProperties;
17+
18+
return pk.Select(p => new PropertyWithNavigations(p, Array.Empty<INavigation>()))
19+
.Intersect(tempTableProperties).ToList();
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Microsoft.EntityFrameworkCore.Metadata;
2+
using Thinktecture.EntityFrameworkCore.Data;
3+
4+
namespace Thinktecture.EntityFrameworkCore.TempTables;
5+
6+
internal sealed class ConfiguredPrimaryKeyPropertiesProvider : IPrimaryKeyPropertiesProvider
7+
{
8+
public IReadOnlyCollection<PropertyWithNavigations> GetPrimaryKeyProperties(IEntityType entityType, IReadOnlyCollection<PropertyWithNavigations> tempTableProperties)
9+
{
10+
ArgumentNullException.ThrowIfNull(entityType);
11+
ArgumentNullException.ThrowIfNull(tempTableProperties);
12+
13+
var pk = entityType.FindPrimaryKey()?.Properties;
14+
15+
if (pk is null or { Count: 0 })
16+
return Array.Empty<PropertyWithNavigations>();
17+
18+
var keyProperties = pk.Select(p => new PropertyWithNavigations(p, Array.Empty<INavigation>())).ToList();
19+
var missingColumns = keyProperties.Except(tempTableProperties);
20+
21+
if (missingColumns.Any())
22+
{
23+
throw new ArgumentException(@$"Cannot create PRIMARY KEY because not all key columns are part of the temp table.
24+
You may use other key properties providers like '{nameof(PrimaryKeyPropertiesProviders)}.{nameof(IPrimaryKeyPropertiesProvider.AdaptiveEntityTypeConfiguration)}' instead of '{nameof(PrimaryKeyPropertiesProviders)}.{nameof(IPrimaryKeyPropertiesProvider.EntityTypeConfiguration)}' to get different behaviors.
25+
Missing columns: {String.Join(", ", missingColumns.Select(p => p.Property.GetColumnBaseName()))}.");
26+
}
27+
28+
return keyProperties;
29+
}
30+
}

src/Thinktecture.EntityFrameworkCore.BulkOperations/EntityFrameworkCore/TempTables/IPrimaryKeyPropertiesProvider.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Linq.Expressions;
12
using Microsoft.EntityFrameworkCore.Metadata;
23
using Thinktecture.EntityFrameworkCore.Data;
34

@@ -8,6 +9,53 @@ namespace Thinktecture.EntityFrameworkCore.TempTables;
89
/// </summary>
910
public interface IPrimaryKeyPropertiesProvider
1011
{
12+
/// <summary>
13+
/// Provides no properties, i.e. no primary key will be created.
14+
/// </summary>
15+
public static readonly IPrimaryKeyPropertiesProvider None = new NoPrimaryKeyPropertiesProvider();
16+
17+
/// <summary>
18+
/// Provides the primary key properties configured for the corresponding <see cref="IEntityType"/>.
19+
/// If the entity is keyless then no primary key is created.
20+
/// </summary>
21+
/// <exception cref="ArgumentException">Is thrown when not all key properties are part of the current temp table.</exception>
22+
public static readonly IPrimaryKeyPropertiesProvider EntityTypeConfiguration = new ConfiguredPrimaryKeyPropertiesProvider();
23+
24+
/// <summary>
25+
/// Provides the primary key properties configured for the corresponding <see cref="IEntityType"/>.
26+
/// If the entity is keyless then no primary key is created.
27+
/// Columns which are not part of the actual temp table are skipped.
28+
/// </summary>
29+
public static readonly IPrimaryKeyPropertiesProvider AdaptiveEntityTypeConfiguration = new AdaptiveEntityTypeConfigurationPrimaryKeyPropertiesProvider();
30+
31+
/// <summary>
32+
/// Provides the primary key properties configured for the corresponding <see cref="IEntityType"/>.
33+
/// If the entity is keyless then all its properties are used for creation of the primary key.
34+
/// Properties which are not part of the actual temp table are skipped.
35+
/// </summary>
36+
public static readonly IPrimaryKeyPropertiesProvider AdaptiveForced = new AdaptiveForcedPrimaryKeyPropertiesProvider();
37+
38+
/// <summary>
39+
/// Extracts members from the provided <paramref name="projection"/>.
40+
/// </summary>
41+
/// <param name="projection">Projection to extract the members from.</param>
42+
/// <typeparam name="T">Type of the entity.</typeparam>
43+
/// <returns>An instance of <see cref="IPrimaryKeyPropertiesProvider"/> containing members extracted from <paramref name="projection"/>.</returns>
44+
/// <exception cref="ArgumentNullException"><paramref name="projection"/> is <c>null</c>.</exception>
45+
/// <exception cref="ArgumentException">No members couldn't be extracted.</exception>
46+
/// <exception cref="NotSupportedException">The <paramref name="projection"/> contains unsupported expressions.</exception>
47+
public static IPrimaryKeyPropertiesProvider From<T>(Expression<Func<T, object?>> projection)
48+
{
49+
ArgumentNullException.ThrowIfNull(projection);
50+
51+
var members = projection.ExtractMembers();
52+
53+
if (members.Count == 0)
54+
throw new ArgumentException("The provided projection contains no properties.");
55+
56+
return new KeyPropertiesProvider(members);
57+
}
58+
1159
/// <summary>
1260
/// Gets the primary key properties.
1361
/// </summary>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System.Reflection;
2+
using Microsoft.EntityFrameworkCore.Metadata;
3+
using Thinktecture.EntityFrameworkCore.Data;
4+
5+
namespace Thinktecture.EntityFrameworkCore.TempTables;
6+
7+
internal class KeyPropertiesProvider : IPrimaryKeyPropertiesProvider
8+
{
9+
private readonly IReadOnlyList<MemberInfo> _members;
10+
11+
public KeyPropertiesProvider(IReadOnlyList<MemberInfo> members)
12+
{
13+
_members = members;
14+
}
15+
16+
public IReadOnlyCollection<PropertyWithNavigations> GetPrimaryKeyProperties(IEntityType entityType, IReadOnlyCollection<PropertyWithNavigations> tempTableProperties)
17+
{
18+
var keyProperties = _members.ConvertToEntityProperties(entityType, true, NoFilter);
19+
var missingColumns = keyProperties.Except(tempTableProperties);
20+
21+
if (missingColumns.Any())
22+
{
23+
throw new ArgumentException(@$"Not all key columns are part of the table.
24+
Missing columns: {String.Join(", ", missingColumns.Select(p => p.Property.GetColumnBaseName()))}.");
25+
}
26+
27+
return keyProperties;
28+
}
29+
30+
private static bool NoFilter(IProperty property, IReadOnlyList<INavigation> navigations)
31+
{
32+
return true;
33+
}
34+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.EntityFrameworkCore.Metadata;
2+
using Thinktecture.EntityFrameworkCore.Data;
3+
4+
namespace Thinktecture.EntityFrameworkCore.TempTables;
5+
6+
internal sealed class NoPrimaryKeyPropertiesProvider : IPrimaryKeyPropertiesProvider
7+
{
8+
public IReadOnlyCollection<PropertyWithNavigations> GetPrimaryKeyProperties(IEntityType entityType, IReadOnlyCollection<PropertyWithNavigations> tempTableProperties)
9+
{
10+
ArgumentNullException.ThrowIfNull(entityType);
11+
ArgumentNullException.ThrowIfNull(tempTableProperties);
12+
13+
return Array.Empty<PropertyWithNavigations>();
14+
}
15+
}
Lines changed: 6 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,39 @@
11
using System.Linq.Expressions;
2-
using System.Reflection;
32
using Microsoft.EntityFrameworkCore.Metadata;
4-
using Thinktecture.EntityFrameworkCore.Data;
53

64
namespace Thinktecture.EntityFrameworkCore.TempTables;
75

86
/// <summary>
97
/// Contains some predefined implementations of <see cref="IPrimaryKeyPropertiesProvider"/>.
108
/// </summary>
9+
[Obsolete($"This class is obsolete. Please use fields and factory method defined on '{nameof(IPrimaryKeyPropertiesProvider)}' instead.", DiagnosticId = "TTEF1000")]
1110
public static class PrimaryKeyPropertiesProviders
1211
{
1312
/// <summary>
1413
/// Provides no properties, i.e. no primary key will be created.
1514
/// </summary>
16-
public static readonly IPrimaryKeyPropertiesProvider None = new NoPrimaryKeyPropertiesProvider();
15+
public static readonly IPrimaryKeyPropertiesProvider None = IPrimaryKeyPropertiesProvider.None;
1716

1817
/// <summary>
1918
/// Provides the primary key properties configured for the corresponding <see cref="IEntityType"/>.
2019
/// If the entity is keyless then no primary key is created.
2120
/// </summary>
2221
/// <exception cref="ArgumentException">Is thrown when not all key properties are part of the current temp table.</exception>
23-
public static readonly IPrimaryKeyPropertiesProvider EntityTypeConfiguration = new ConfiguredPrimaryKeyPropertiesProvider();
22+
public static readonly IPrimaryKeyPropertiesProvider EntityTypeConfiguration = IPrimaryKeyPropertiesProvider.EntityTypeConfiguration;
2423

2524
/// <summary>
2625
/// Provides the primary key properties configured for the corresponding <see cref="IEntityType"/>.
2726
/// If the entity is keyless then no primary key is created.
2827
/// Columns which are not part of the actual temp table are skipped.
2928
/// </summary>
30-
public static readonly IPrimaryKeyPropertiesProvider AdaptiveEntityTypeConfiguration = new AdaptiveEntityTypeConfigurationPrimaryKeyPropertiesProvider();
29+
public static readonly IPrimaryKeyPropertiesProvider AdaptiveEntityTypeConfiguration = IPrimaryKeyPropertiesProvider.AdaptiveEntityTypeConfiguration;
3130

3231
/// <summary>
3332
/// Provides the primary key properties configured for the corresponding <see cref="IEntityType"/>.
3433
/// If the entity is keyless then all its properties are used for creation of the primary key.
3534
/// Properties which are not part of the actual temp table are skipped.
3635
/// </summary>
37-
public static readonly IPrimaryKeyPropertiesProvider AdaptiveForced = new AdaptiveForcedPrimaryKeyPropertiesProvider();
36+
public static readonly IPrimaryKeyPropertiesProvider AdaptiveForced = IPrimaryKeyPropertiesProvider.AdaptiveForced;
3837

3938
/// <summary>
4039
/// Extracts members from the provided <paramref name="projection"/>.
@@ -47,114 +46,6 @@ public static class PrimaryKeyPropertiesProviders
4746
/// <exception cref="NotSupportedException">The <paramref name="projection"/> contains unsupported expressions.</exception>
4847
public static IPrimaryKeyPropertiesProvider From<T>(Expression<Func<T, object?>> projection)
4948
{
50-
ArgumentNullException.ThrowIfNull(projection);
51-
52-
var members = projection.ExtractMembers();
53-
54-
if (members.Count == 0)
55-
throw new ArgumentException("The provided projection contains no properties.");
56-
57-
return new KeyPropertiesProvider(members);
58-
}
59-
60-
private sealed class NoPrimaryKeyPropertiesProvider : IPrimaryKeyPropertiesProvider
61-
{
62-
public IReadOnlyCollection<PropertyWithNavigations> GetPrimaryKeyProperties(IEntityType entityType, IReadOnlyCollection<PropertyWithNavigations> tempTableProperties)
63-
{
64-
ArgumentNullException.ThrowIfNull(entityType);
65-
ArgumentNullException.ThrowIfNull(tempTableProperties);
66-
67-
return Array.Empty<PropertyWithNavigations>();
68-
}
69-
}
70-
71-
private sealed class ConfiguredPrimaryKeyPropertiesProvider : IPrimaryKeyPropertiesProvider
72-
{
73-
public IReadOnlyCollection<PropertyWithNavigations> GetPrimaryKeyProperties(IEntityType entityType, IReadOnlyCollection<PropertyWithNavigations> tempTableProperties)
74-
{
75-
ArgumentNullException.ThrowIfNull(entityType);
76-
ArgumentNullException.ThrowIfNull(tempTableProperties);
77-
78-
var pk = entityType.FindPrimaryKey()?.Properties;
79-
80-
if (pk is null or { Count: 0 })
81-
return Array.Empty<PropertyWithNavigations>();
82-
83-
var keyProperties = pk.Select(p => new PropertyWithNavigations(p, Array.Empty<INavigation>())).ToList();
84-
var missingColumns = keyProperties.Except(tempTableProperties);
85-
86-
if (missingColumns.Any())
87-
{
88-
throw new ArgumentException(@$"Cannot create PRIMARY KEY because not all key columns are part of the temp table.
89-
You may use other key properties providers like '{nameof(PrimaryKeyPropertiesProviders)}.{nameof(AdaptiveEntityTypeConfiguration)}' instead of '{nameof(PrimaryKeyPropertiesProviders)}.{nameof(EntityTypeConfiguration)}' to get different behaviors.
90-
Missing columns: {String.Join(", ", missingColumns.Select(p => p.Property.GetColumnBaseName()))}.");
91-
}
92-
93-
return keyProperties;
94-
}
95-
}
96-
97-
private sealed class AdaptiveEntityTypeConfigurationPrimaryKeyPropertiesProvider : IPrimaryKeyPropertiesProvider
98-
{
99-
public IReadOnlyCollection<PropertyWithNavigations> GetPrimaryKeyProperties(IEntityType entityType, IReadOnlyCollection<PropertyWithNavigations> tempTableProperties)
100-
{
101-
if (tempTableProperties.Count == 0)
102-
return Array.Empty<PropertyWithNavigations>();
103-
104-
var pk = entityType.FindPrimaryKey()?.Properties;
105-
106-
if (pk is null or { Count: 0 })
107-
return Array.Empty<PropertyWithNavigations>();
108-
109-
return pk.Select(p => new PropertyWithNavigations(p, Array.Empty<INavigation>()))
110-
.Intersect(tempTableProperties)
111-
.ToList();
112-
}
113-
}
114-
115-
private sealed class AdaptiveForcedPrimaryKeyPropertiesProvider : IPrimaryKeyPropertiesProvider
116-
{
117-
public IReadOnlyCollection<PropertyWithNavigations> GetPrimaryKeyProperties(IEntityType entityType, IReadOnlyCollection<PropertyWithNavigations> tempTableProperties)
118-
{
119-
if (tempTableProperties.Count == 0)
120-
return Array.Empty<PropertyWithNavigations>();
121-
122-
var pk = entityType.FindPrimaryKey()?.Properties;
123-
124-
if (pk is null or { Count: 0 })
125-
return tempTableProperties;
126-
127-
return pk.Select(p => new PropertyWithNavigations(p, Array.Empty<INavigation>()))
128-
.Intersect(tempTableProperties).ToList();
129-
}
130-
}
131-
132-
private class KeyPropertiesProvider : IPrimaryKeyPropertiesProvider
133-
{
134-
private readonly IReadOnlyList<MemberInfo> _members;
135-
136-
public KeyPropertiesProvider(IReadOnlyList<MemberInfo> members)
137-
{
138-
_members = members;
139-
}
140-
141-
public IReadOnlyCollection<PropertyWithNavigations> GetPrimaryKeyProperties(IEntityType entityType, IReadOnlyCollection<PropertyWithNavigations> tempTableProperties)
142-
{
143-
var keyProperties = _members.ConvertToEntityProperties(entityType, true, NoFilter);
144-
var missingColumns = keyProperties.Except(tempTableProperties);
145-
146-
if (missingColumns.Any())
147-
{
148-
throw new ArgumentException(@$"Not all key columns are part of the table.
149-
Missing columns: {String.Join(", ", missingColumns.Select(p => p.Property.GetColumnBaseName()))}.");
150-
}
151-
152-
return keyProperties;
153-
}
154-
155-
private static bool NoFilter(IProperty property, IReadOnlyList<INavigation> navigations)
156-
{
157-
return true;
158-
}
49+
return IPrimaryKeyPropertiesProvider.From(projection);
15950
}
16051
}

src/Thinktecture.EntityFrameworkCore.SqlServer/EntityFrameworkCore/BulkOperations/SqlServerBulkOperationExecutor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ private async Task<ITempTableQuery<T>> BulkInsertIntoTempTableAsync<T, TEntity>(
304304
var primaryKeyCreation = tempTableCreationOptions.PrimaryKeyCreation; // keep this one in a local variable because we may change it in the next line
305305

306306
if (options.MomentOfPrimaryKeyCreation == MomentOfSqlServerPrimaryKeyCreation.AfterBulkInsert)
307-
tempTableCreationOptions.PrimaryKeyCreation = PrimaryKeyPropertiesProviders.None;
307+
tempTableCreationOptions.PrimaryKeyCreation = IPrimaryKeyPropertiesProvider.None;
308308

309309
var tempTableCreator = _ctx.GetService<ISqlServerTempTableCreator>();
310310
var tempTableReference = await tempTableCreator.CreateTempTableAsync(entityType, tempTableCreationOptions, cancellationToken).ConfigureAwait(false);

src/Thinktecture.EntityFrameworkCore.SqlServer/EntityFrameworkCore/BulkOperations/SqlServerBulkOperationTempTableOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ internal SqlServerBulkOperationTempTableOptions(SqlServerBulkOperationTempTableO
1313
{
1414
if (options is null)
1515
{
16-
PrimaryKeyCreation = PrimaryKeyPropertiesProviders.None;
16+
PrimaryKeyCreation = IPrimaryKeyPropertiesProvider.None;
1717
MomentOfPrimaryKeyCreation = MomentOfSqlServerPrimaryKeyCreation.AfterBulkInsert;
1818
DropTableOnDispose = true;
1919
EnableStreaming = true;

0 commit comments

Comments
 (0)