diff --git a/src/EfCore/DKNet.EfCore.Extensions/Configurations/IDataSeedingConfiguration.cs b/src/EfCore/DKNet.EfCore.Extensions/Configurations/IDataSeedingConfiguration.cs index bab3bf5a..9ad30e94 100644 --- a/src/EfCore/DKNet.EfCore.Extensions/Configurations/IDataSeedingConfiguration.cs +++ b/src/EfCore/DKNet.EfCore.Extensions/Configurations/IDataSeedingConfiguration.cs @@ -8,7 +8,7 @@ namespace DKNet.EfCore.Extensions.Configurations; /// /// Describes a data seeding configuration for an entity type. Implementations may provide a synchronous -/// list of data via , and/or an asynchronous seeding callback via . +/// list of data asynchronous seeding callback via . /// public interface IDataSeedingConfiguration { @@ -29,13 +29,6 @@ public interface IDataSeedingConfiguration /// Func? SeedAsync { get; } - /// - /// Model-managed seed data (collection of anonymous/dynamic objects) that will be used by EF Core's model seeding - /// support. - /// Implementations may expose strongly typed collections via the generic base class and map them to this property. - /// - IEnumerable HasData { get; } - /// /// The CLR of the entity that this seeding configuration targets. /// @@ -46,7 +39,7 @@ public interface IDataSeedingConfiguration /// /// Generic base class for data seeding configurations. Implementers can provide model-managed seed data via -/// or an asynchronous seed routine via . +/// or an asynchronous seed routine via . /// /// The entity type to seed. public abstract class DataSeedingConfiguration : IDataSeedingConfiguration where TEntity : class @@ -56,15 +49,28 @@ public abstract class DataSeedingConfiguration : IDataSeedingConfigurat /// public Type EntityType => typeof(TEntity); - /// - public IEnumerable HasData => GetData(); - /// public virtual int Order => 0; - /// - public virtual Func? SeedAsync => null; + public virtual Func SeedAsync => + async (context, isMigration, cancellation) => + { + var data = await GetDataAsync(cancellation).ConfigureAwait(false); + if (data.Count == 0) + return; + + var dbSet = context.Set(); + foreach (var item in data) + { + // Check if the item already exists in the database to avoid duplicates + var exists = await dbSet.AnyAsync(e => e.Equals(item), cancellation).ConfigureAwait(false); + if (!exists) + await dbSet.AddAsync(item, cancellation).ConfigureAwait(false); + } + + await context.SaveChangesAsync(cancellation).ConfigureAwait(false); + }; #endregion @@ -74,7 +80,7 @@ public abstract class DataSeedingConfiguration : IDataSeedingConfigurat /// Gets the collection of seed data for the target entity type./> /// /// - protected abstract ICollection GetData(); + protected abstract ValueTask> GetDataAsync(CancellationToken cancellation = default); #endregion } \ No newline at end of file diff --git a/src/EfCore/DKNet.EfCore.Extensions/Extensions/EfCoreDataSeedingExtensions.cs b/src/EfCore/DKNet.EfCore.Extensions/Extensions/EfCoreDataSeedingExtensions.cs index 197a0344..4063469b 100644 --- a/src/EfCore/DKNet.EfCore.Extensions/Extensions/EfCoreDataSeedingExtensions.cs +++ b/src/EfCore/DKNet.EfCore.Extensions/Extensions/EfCoreDataSeedingExtensions.cs @@ -35,33 +35,6 @@ private static Type[] GetDataSeedingTypes(this ICollection? assemblies return [.. types]; } - /// - /// Registers model-managed seed data from discovered types. - /// This will call HasData on the model for any configuration that exposes non-empty HasData collections. - /// - /// The model builder to register seed data on. - /// Assemblies to scan for IDataSeedingConfiguration implementations. - internal static void RegisterDataSeeding(this ModelBuilder modelBuilder, params Assembly[] assemblies) - { - ArgumentNullException.ThrowIfNull(modelBuilder); - - var seedingTypes = assemblies.GetDataSeedingTypes(); - var instances = seedingTypes - .Select(t => Activator.CreateInstance(t) as IDataSeedingConfiguration) - .OfType() - .OrderBy(s => s.Order); - - foreach (var item in instances) - { - var data = item.HasData?.ToList() ?? []; - if (data.Count == 0) continue; - - var entityType = item.EntityType; - // ModelBuilder.Entity(Type).HasData accepts params object[] - modelBuilder.Entity(entityType).HasData(data.ToArray()); - } - } - /// /// Configure the to automatically run data seeding callbacks /// discovered in the provided assemblies during migrations or startup. diff --git a/src/EfCore/DKNet.EfCore.Extensions/Internal/AutoConfigModelCustomizer.cs b/src/EfCore/DKNet.EfCore.Extensions/Internal/AutoConfigModelCustomizer.cs index 600f756b..e922b3a2 100644 --- a/src/EfCore/DKNet.EfCore.Extensions/Internal/AutoConfigModelCustomizer.cs +++ b/src/EfCore/DKNet.EfCore.Extensions/Internal/AutoConfigModelCustomizer.cs @@ -14,22 +14,16 @@ private static void ConfigModelCreating(DbContext dbContext, ModelBuilder modelB var assemblies = GetAssemblies(dbContext); //Register Entities - foreach (var assembly in assemblies) - { - modelBuilder.ApplyConfigurationsFromAssembly(assembly); - } + foreach (var assembly in assemblies) modelBuilder.ApplyConfigurationsFromAssembly(assembly); //Register StaticData Of - modelBuilder.RegisterDataSeeding(assemblies); + //modelBuilder.RegisterDataSeeding(assemblies); //Register Global Filter modelBuilder.RegisterGlobalModelBuilders(assemblies, dbContext); //Register Sequence - if (dbContext.IsSqlServer()) - { - modelBuilder.RegisterSequences(assemblies); - } + if (dbContext.IsSqlServer()) modelBuilder.RegisterSequences(assemblies); } public void Customize(ModelBuilder modelBuilder, DbContext context) @@ -44,10 +38,7 @@ private static Assembly[] GetAssemblies(DbContext dbContext) var register = options.FindExtension(); var assemblies = register?.Assemblies ?? []; - if (assemblies.Length <= 0) - { - assemblies = [dbContext.GetType().Assembly]; - } + if (assemblies.Length <= 0) assemblies = [dbContext.GetType().Assembly]; return assemblies; } diff --git a/src/EfCore/EfCore.Extensions.Tests/DataSeedingTests.cs b/src/EfCore/EfCore.Extensions.Tests/DataSeedingTests.cs index fe519e59..91f2c7ac 100644 --- a/src/EfCore/EfCore.Extensions.Tests/DataSeedingTests.cs +++ b/src/EfCore/EfCore.Extensions.Tests/DataSeedingTests.cs @@ -8,19 +8,20 @@ public class UserSeedingConfiguration : DataSeedingConfiguration { #region Methods - protected override ICollection GetData() => - [ - new( - 1, "seeded1") - { - FirstName = "Seeded", LastName = "User1" - }, - new(2, "seeded2") - { - FirstName = "Seeded", - LastName = "User2" - } - ]; + protected override ValueTask> GetDataAsync(CancellationToken cancellationToken = default) => + ValueTask.FromResult>( + [ + new User( + 1, "seeded1") + { + FirstName = "Seeded", LastName = "User1" + }, + new User(2, "seeded2") + { + FirstName = "Seeded", + LastName = "User2" + } + ]); #endregion }