Skip to content
Merged
Changes from 1 commit
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 @@ -7,41 +7,39 @@ namespace PhenX.EntityFrameworkCore.BulkInsert.Metadata;

internal sealed class MetadataProvider
{
private Dictionary<Type, Dictionary<Type, TableMetadata>> _tablesPerContext = new();
private readonly Dictionary<Type, Dictionary<Type, TableMetadata>> _tablesPerContext = new();

public TableMetadata GetTableInfo<T>(DbContext context)
{
var tables = GetTables(context);

if (!tables.TryGetValue(typeof(T), out var table))
{
throw new InvalidOperationException($"Cannot find metadata for type '{typeof(T)}'.");
}

return table;
}

private Dictionary<Type, TableMetadata> GetTables(DbContext context)
{
lock (_tablesPerContext)
{
var type = context.GetType();
if (_tablesPerContext.TryGetValue(context.GetType(), out var tables))

if (!_tablesPerContext.TryGetValue(type, out var tables))
{
return tables;
tables = new Dictionary<Type, TableMetadata>();
_tablesPerContext[type] = tables;
}

var provider = context.GetService<IBulkInsertProvider>();
var modelType = typeof(T);

if (tables.TryGetValue(modelType, out var table))
{
return table;
}

tables = context.Model.GetEntityTypes()
.GroupBy(x => x.ClrType)
.ToDictionary(
x => x.Key,
x => new TableMetadata(x.First(), provider.SqlDialect));
var entityType = context.Model.FindEntityType(modelType);
if (entityType == null)
{
throw new InvalidOperationException($"The type '{modelType.FullName}' is not part of the model for the current context.");
}

var provider = context.GetService<IBulkInsertProvider>();
Copy link

Copilot AI Oct 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The IBulkInsertProvider service is retrieved inside the lock on every cache miss. Consider moving this retrieval outside the lock (after checking for cache miss but before the lock, or after the model type check) to reduce lock contention, as GetService<T>() is typically thread-safe.

Copilot uses AI. Check for mistakes.

_tablesPerContext[type] = tables;
var tableMetadata = new TableMetadata(entityType, provider.SqlDialect);
tables[modelType] = tableMetadata;

return tables;
return tableMetadata;
}
}
}
Loading