| | | 1 | | namespace ZhEaIsNsAaBn.DbSeeder.Design; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Reads the seed rows of a table from the dev database (§8.3): <c>AsNoTracking()</c> + <c>IgnoreQueryFilters()</c> (so |
| | | 5 | | /// too), every column through <c>EF.Property</c> (shadow properties included), values turned into <b>provider values</b |
| | | 6 | | /// converters. Only the data columns the runtime accepts are read (<see cref="SeedEntityShape"/>), plus the soft-delete |
| | | 7 | | /// </summary> |
| | | 8 | | internal static class SeedExporter |
| | | 9 | | { |
| | 3 | 10 | | private static readonly System.Reflection.MethodInfo ExportCoreMethod = typeof( SeedExporter ).GetMethod( nameof( Ex |
| | | 11 | | |
| | | 12 | | |
| | | 13 | | public static Task<ExportedTable> ExportAsync( DbContext dbContext, SeedEntityShape shape, CancellationToken cancell |
| | 630 | 14 | | => (Task<ExportedTable>) ExportCoreMethod.MakeGenericMethod( shape.EntityType.ClrType ).Invoke( null, [ dbContex |
| | | 15 | | |
| | | 16 | | |
| | | 17 | | private static async Task<ExportedTable> ExportCoreAsync< TEntity >( DbContext dbContext, SeedEntityShape shape, Can |
| | | 18 | | where TEntity : class |
| | | 19 | | { |
| | 630 | 20 | | var flag = shape.Table.SoftDeleteFlag; |
| | 630 | 21 | | var properties = shape.DataColumns.Select( c => c.Property ).Concat( flag is null ? [] : [ flag.Property ] ).ToL |
| | 630 | 22 | | var entity = Expression.Parameter( typeof( TEntity ), "e" ); |
| | 630 | 23 | | var values = properties.Select( p => (Expression) Expression.Convert( Expression.Call( typeof( EF ), nameof( |
| | 630 | 24 | | typeof( object ) ) ); |
| | 630 | 25 | | var projection = Expression.Lambda<Func<TEntity, object[]>>( Expression.NewArrayInit( typeof( object ), values ) |
| | | 26 | | |
| | 630 | 27 | | var raw = await dbContext.Set<TEntity>().AsNoTracking().IgnoreQueryFilters().Select( projection ).ToListAsync( c |
| | | 28 | | |
| | | 29 | | |
| | 630 | 30 | | var match = shape.Table.MatchColumns.Select( m => shape.DataColumns.Select( c => c.ColumnName ).ToList().I |
| | 630 | 31 | | var rows = new List<DesignRow>( raw.Count ); |
| | 630 | 32 | | var softDeleted = new HashSet<string>( StringComparer.Ordinal ); |
| | | 33 | | |
| | 4530 | 34 | | foreach ( var record in raw ) |
| | | 35 | | { |
| | 1635 | 36 | | var json = shape.DataColumns.Select( ( c, i ) => SeedJsonValue.Format( ToProvider( c.Property, record[ i ] ) |
| | 1635 | 37 | | var key = SeedKeyComparer.Key( match.Select( i => json[ i ] ) ); |
| | | 38 | | |
| | 1635 | 39 | | rows.Add( new DesignRow( key, shape.DataColumns.Select( ( c, i ) => (c.PropertyName, json[ i ]) ).ToList() ) |
| | | 40 | | |
| | 1791 | 41 | | if ( flag is not null && record[ ^1 ] is true ) { softDeleted.Add( key ); } |
| | | 42 | | } |
| | | 43 | | |
| | | 44 | | |
| | 630 | 45 | | return new ExportedTable { Shape = shape, Rows = rows.OrderBy( r => r.Key, SeedKeyComparer.Instance ).ToList(), |
| | 630 | 46 | | } |
| | | 47 | | |
| | | 48 | | private static object? ToProvider( IProperty property, object? value ) |
| | 5427 | 49 | | => value is null ? null : property.GetTypeMapping().Converter?.ConvertToProvider( value ) ?? value; |
| | | 50 | | } |