Skip to content
Merged

up #284

Show file tree
Hide file tree
Changes from all commits
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 @@ -8,7 +8,7 @@ namespace DKNet.EfCore.Extensions.Configurations;

/// <summary>
/// Describes a data seeding configuration for an entity type. Implementations may provide a synchronous
/// list of data via <see cref="HasData" />, and/or an asynchronous seeding callback via <see cref="SeedAsync" />.
/// list of data asynchronous seeding callback via <see cref="SeedAsync" />.
/// </summary>
public interface IDataSeedingConfiguration
{
Expand All @@ -29,13 +29,6 @@ public interface IDataSeedingConfiguration
/// </summary>
Func<DbContext, bool, CancellationToken, Task>? SeedAsync { get; }

/// <summary>
/// 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.
/// </summary>
IEnumerable<dynamic> HasData { get; }

/// <summary>
/// The CLR <see cref="Type" /> of the entity that this seeding configuration targets.
/// </summary>
Expand All @@ -46,7 +39,7 @@ public interface IDataSeedingConfiguration

/// <summary>
/// Generic base class for data seeding configurations. Implementers can provide model-managed seed data via
/// <see cref="GetData" /> or an asynchronous seed routine via <see cref="SeedAsync" />.
/// <see cref="GetDataAsync" /> or an asynchronous seed routine via <see cref="SeedAsync" />.
/// </summary>
/// <typeparam name="TEntity">The entity type to seed.</typeparam>
public abstract class DataSeedingConfiguration<TEntity> : IDataSeedingConfiguration where TEntity : class
Expand All @@ -56,15 +49,28 @@ public abstract class DataSeedingConfiguration<TEntity> : IDataSeedingConfigurat
/// <inheritdoc />
public Type EntityType => typeof(TEntity);

/// <inheritdoc />
public IEnumerable<dynamic> HasData => GetData();

/// <inheritdoc />
public virtual int Order => 0;


/// <inheritdoc />
public virtual Func<DbContext, bool, CancellationToken, Task>? SeedAsync => null;
public virtual Func<DbContext, bool, CancellationToken, Task> SeedAsync =>
async (context, isMigration, cancellation) =>
{
var data = await GetDataAsync(cancellation).ConfigureAwait(false);
if (data.Count == 0)
return;

var dbSet = context.Set<TEntity>();
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

Expand All @@ -74,7 +80,7 @@ public abstract class DataSeedingConfiguration<TEntity> : IDataSeedingConfigurat
/// Gets the collection of seed data for the target entity type./>
/// </summary>
/// <returns></returns>
protected abstract ICollection<TEntity> GetData();
protected abstract ValueTask<ICollection<TEntity>> GetDataAsync(CancellationToken cancellation = default);

#endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,33 +35,6 @@
return [.. types];
}

/// <summary>
/// Registers model-managed seed data from discovered <see cref="IDataSeedingConfiguration" /> types.
/// This will call HasData on the model for any configuration that exposes non-empty HasData collections.
/// </summary>
/// <param name="modelBuilder">The model builder to register seed data on.</param>
/// <param name="assemblies">Assemblies to scan for IDataSeedingConfiguration implementations.</param>
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<IDataSeedingConfiguration>()
.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());
}
}

/// <summary>
/// Configure the <see cref="DbContextOptionsBuilder" /> to automatically run data seeding callbacks
/// discovered in the provided assemblies during migrations or startup.
Expand Down Expand Up @@ -92,7 +65,7 @@
// Asynchronous seeding hook
@this.UseAsyncSeeding(async (context, performedStoreOperation, cancellation) =>
{
foreach (var s in seedingInstances)

Check warning on line 68 in src/EfCore/DKNet.EfCore.Extensions/Extensions/EfCoreDataSeedingExtensions.cs

View workflow job for this annotation

GitHub Actions / build-test-coverage (10.x.x)

Loop should be simplified by calling Select(s => s.SeedAsync))
if (s.SeedAsync != null)
await s.SeedAsync.Invoke(context, performedStoreOperation, cancellation).ConfigureAwait(false);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,16 @@
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);

Check warning on line 20 in src/EfCore/DKNet.EfCore.Extensions/Internal/AutoConfigModelCustomizer.cs

View workflow job for this annotation

GitHub Actions / build-test-coverage (10.x.x)

Remove this commented out code.

//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)
Expand All @@ -44,10 +38,7 @@
var register = options.FindExtension<EntityAutoConfigRegister>();
var assemblies = register?.Assemblies ?? [];

if (assemblies.Length <= 0)
{
assemblies = [dbContext.GetType().Assembly];
}
if (assemblies.Length <= 0) assemblies = [dbContext.GetType().Assembly];

return assemblies;
}
Expand Down
27 changes: 14 additions & 13 deletions src/EfCore/EfCore.Extensions.Tests/DataSeedingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@ public class UserSeedingConfiguration : DataSeedingConfiguration<User>
{
#region Methods

protected override ICollection<User> GetData() =>
[
new(
1, "seeded1")
{
FirstName = "Seeded", LastName = "User1"
},
new(2, "seeded2")
{
FirstName = "Seeded",
LastName = "User2"
}
];
protected override ValueTask<ICollection<User>> GetDataAsync(CancellationToken cancellationToken = default) =>
ValueTask.FromResult<ICollection<User>>(
[
new User(
1, "seeded1")
{
FirstName = "Seeded", LastName = "User1"
},
new User(2, "seeded2")
{
FirstName = "Seeded",
LastName = "User2"
}
]);

#endregion
}
Expand Down
Loading