| | | 1 | | #region Usings |
| | | 2 | | using ZhEaIsNsAaBn.DbSeeder.Builders; |
| | | 3 | | #endregion |
| | | 4 | | |
| | | 5 | | namespace ZhEaIsNsAaBn.DbSeeder; |
| | | 6 | | |
| | | 7 | | /// <summary>Settings of one context's seed migrations, filled in <c>AddDbSeeder<TDbContext>().Configure( options |
| | | 8 | | public sealed class SeedOptions |
| | | 9 | | { |
| | 710 | 10 | | private readonly Dictionary<Type, SeedEntitySettings> _entities = []; |
| | | 11 | | |
| | | 12 | | |
| | | 13 | | /// <summary>The clock for audit fields and history rows (local time). Default: <see cref="TimeProvider.System"/>.</ |
| | | 14 | | public TimeProvider TimeProvider { get; set; } = TimeProvider.System; |
| | | 15 | | |
| | | 16 | | /// <summary>How long a standalone run waits for the database seed lock. Default: 60 seconds.</summary> |
| | 710 | 17 | | public TimeSpan LockTimeout { get; set; } = TimeSpan.FromSeconds( 60 ); |
| | | 18 | | |
| | | 19 | | /// <summary> |
| | | 20 | | /// The folder of the migrations inside the consumer project, as embedded (<c>DbSeederMigrationsFolder</c> in MSBuil |
| | | 21 | | /// Default: <c>SeedMigrations</c>. |
| | | 22 | | /// </summary> |
| | | 23 | | public string MigrationsFolder { get; set; } = "SeedMigrations"; |
| | | 24 | | |
| | | 25 | | |
| | | 26 | | /// <summary> |
| | | 27 | | /// Databases the design tools never force-delete in (<c>down --force --execute</c> refuses), compared case-insensit |
| | | 28 | | /// </summary> |
| | 710 | 29 | | public ISet<string> ProtectedDatabases { get; } = new HashSet<string>( StringComparer.OrdinalIgnoreCase ); |
| | | 30 | | |
| | | 31 | | |
| | | 32 | | internal object? SystemUserId { get; private set; } |
| | | 33 | | |
| | | 34 | | internal string? SystemUserName { get; private set; } |
| | | 35 | | |
| | 2798 | 36 | | internal IReadOnlyDictionary<Type, SeedEntitySettings> Entities => _entities; |
| | | 37 | | |
| | | 38 | | |
| | | 39 | | /// <summary>The user written to the created-by, updated-by and (on soft delete) deleted-by fields of seed rows.</su |
| | | 40 | | /// <typeparam name="TId">The user key type; must match the entities' <c>*ById</c> properties.</typeparam> |
| | | 41 | | /// <param name="id">The user key.</param> |
| | | 42 | | /// <param name="name">The user name.</param> |
| | | 43 | | /// <returns>These options.</returns> |
| | | 44 | | /// <exception cref="ArgumentException"><paramref name="name"/> is <see langword="null"/> or white space.</exception |
| | | 45 | | public SeedOptions SystemUser< TId >( TId id, string name ) |
| | | 46 | | where TId : struct, IEquatable<TId> |
| | | 47 | | { |
| | 584 | 48 | | ArgumentException.ThrowIfNullOrWhiteSpace( name ); |
| | | 49 | | |
| | 584 | 50 | | SystemUserId = id; |
| | 584 | 51 | | SystemUserName = name; |
| | | 52 | | |
| | 584 | 53 | | return this; |
| | | 54 | | } |
| | | 55 | | |
| | | 56 | | /// <summary>Configures one entity type inline.</summary> |
| | | 57 | | /// <typeparam name="TEntity">The entity type.</typeparam> |
| | | 58 | | /// <returns>The entity's seed builder.</returns> |
| | | 59 | | public SeedEntityBuilder<TEntity> Entity< TEntity >() |
| | | 60 | | where TEntity : class |
| | 43 | 61 | | => new( Settings( typeof( TEntity ) ) ); |
| | | 62 | | |
| | | 63 | | /// <summary>Applies one configuration class.</summary> |
| | | 64 | | /// <typeparam name="TEntity">The entity type.</typeparam> |
| | | 65 | | /// <param name="configuration">The configuration.</param> |
| | | 66 | | /// <returns>These options.</returns> |
| | | 67 | | /// <exception cref="ArgumentNullException"><paramref name="configuration"/> is <see langword="null"/>.</exception> |
| | | 68 | | public SeedOptions ApplyConfiguration< TEntity >( ISeedEntityConfiguration<TEntity> configuration ) |
| | | 69 | | where TEntity : class |
| | | 70 | | { |
| | 6 | 71 | | ArgumentNullException.ThrowIfNull( configuration ); |
| | | 72 | | |
| | 6 | 73 | | configuration.Configure( Entity<TEntity>() ); |
| | | 74 | | |
| | 6 | 75 | | return this; |
| | | 76 | | } |
| | | 77 | | |
| | | 78 | | /// <summary> |
| | | 79 | | /// Applies every non-abstract <see cref="ISeedEntityConfiguration{TEntity}"/> in <paramref name="assembly"/> that h |
| | | 80 | | /// parameterless constructor, in type-name order. Only the assembly given here is scanned. |
| | | 81 | | /// </summary> |
| | | 82 | | /// <param name="assembly">The assembly with the configuration classes.</param> |
| | | 83 | | /// <returns>These options.</returns> |
| | | 84 | | /// <exception cref="ArgumentNullException"><paramref name="assembly"/> is <see langword="null"/>.</exception> |
| | | 85 | | public SeedOptions ApplyConfigurationsFromAssembly( Assembly assembly ) |
| | | 86 | | { |
| | 6 | 87 | | ArgumentNullException.ThrowIfNull( assembly ); |
| | | 88 | | |
| | 6 | 89 | | var apply = typeof( SeedOptions ).GetMethod( nameof( ApplyConfiguration ) )!; |
| | | 90 | | |
| | | 91 | | |
| | 498 | 92 | | foreach ( var type in assembly.GetTypes() |
| | 6 | 93 | | .Where( t => t is { IsAbstract: false, IsGenericTypeDefinition: false } && t.GetCo |
| | 6 | 94 | | .OrderBy( t => t.FullName, StringComparer.Ordinal ) ) |
| | | 95 | | { |
| | 498 | 96 | | foreach ( var contract in type.GetInterfaces().Where( i => i.IsGenericType && i.GetGenericTypeDefinition() = |
| | | 97 | | { |
| | 6 | 98 | | apply.MakeGenericMethod( contract.GenericTypeArguments[ 0 ] ).Invoke( this, [ Activator.CreateInstance( |
| | | 99 | | } |
| | | 100 | | } |
| | | 101 | | |
| | | 102 | | |
| | 6 | 103 | | return this; |
| | | 104 | | } |
| | | 105 | | |
| | | 106 | | |
| | | 107 | | private SeedEntitySettings Settings( Type type ) |
| | | 108 | | { |
| | 43 | 109 | | if ( !_entities.TryGetValue( type, out var settings ) ) |
| | | 110 | | { |
| | 34 | 111 | | settings = new SeedEntitySettings( type ); |
| | 34 | 112 | | _entities[ type ] = settings; |
| | | 113 | | } |
| | | 114 | | |
| | 43 | 115 | | return settings; |
| | | 116 | | } |
| | | 117 | | } |