using AutoMapper;
using Bit.Infrastructure.EntityFramework.Repositories;
using LinqToDB.Data;
using LinqToDB.EntityFrameworkCore;
using EfCollection = Bit.Infrastructure.EntityFramework.Models.Collection;
using EfCollectionGroup = Bit.Infrastructure.EntityFramework.Models.CollectionGroup;
using EfCollectionUser = Bit.Infrastructure.EntityFramework.Models.CollectionUser;
using EfFolder = Bit.Infrastructure.EntityFramework.Vault.Models.Folder;
using EfGroup = Bit.Infrastructure.EntityFramework.Models.Group;
using EfGroupUser = Bit.Infrastructure.EntityFramework.Models.GroupUser;
using EfOrganization = Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization;
using EfOrganizationUser = Bit.Infrastructure.EntityFramework.Models.OrganizationUser;
using EfUser = Bit.Infrastructure.EntityFramework.Models.User;
namespace Bit.Seeder.Pipeline;
///
/// Flushes accumulated entities from to the database via BulkCopy.
///
///
/// Entities are committed in foreign-key-safe order (Organizations → Users → OrgUsers → … → Folders → Ciphers).
/// Most Core entities require AutoMapper conversion to their EF counterparts before insert;
/// a few (Cipher, CollectionCipher) share the same type across layers and copy directly.
/// Each list is cleared after insert so the context is ready for the next pipeline run.
///
/// CollectionUser and CollectionGroup require an explicit table name in BulkCopyOptions because
/// they lack both IEntityTypeConfiguration and .ToTable() mappings in DatabaseContext, so LinqToDB
/// cannot resolve their table names automatically.
///
///
///
internal sealed class BulkCommitter(DatabaseContext db, IMapper mapper)
{
internal void Commit(SeederContext context)
{
MapCopyAndClear(context.Organizations);
MapCopyAndClear(context.Users);
MapCopyAndClear(context.OrganizationUsers);
MapCopyAndClear(context.Groups);
MapCopyAndClear(context.GroupUsers);
MapCopyAndClear(context.Collections);
MapCopyAndClear(context.CollectionUsers, nameof(Core.Entities.CollectionUser));
MapCopyAndClear(context.CollectionGroups, nameof(Core.Entities.CollectionGroup));
MapCopyAndClear(context.Folders);
CopyAndClear(context.Ciphers);
CopyAndClear(context.CollectionCiphers);
}
private void MapCopyAndClear(List entities, string? tableName = null) where TEf : class
{
if (entities.Count is 0)
{
return;
}
var mapped = entities.Select(e => mapper.Map(e));
if (tableName is not null)
{
db.BulkCopy(new BulkCopyOptions { TableName = tableName }, mapped);
}
else
{
db.BulkCopy(mapped);
}
entities.Clear();
}
private void CopyAndClear(List entities) where T : class
{
if (entities.Count is 0)
{
return;
}
db.BulkCopy(entities);
entities.Clear();
}
}