@@ -257,61 +257,68 @@ public bool TryGetEntityNameFromPath(string entityPathName, [NotNullWhen(true)]
257257 [ JsonConstructor ]
258258 public RuntimeConfig (
259259 string ? Schema ,
260- DataSource DataSource ,
261- RuntimeEntities Entities ,
260+ DataSource ? DataSource ,
261+ RuntimeEntities ? Entities ,
262262 RuntimeAutoentities ? Autoentities = null ,
263263 RuntimeOptions ? Runtime = null ,
264264 DataSourceFiles ? DataSourceFiles = null ,
265265 AzureKeyVaultOptions ? AzureKeyVault = null )
266266 {
267267 this . Schema = Schema ?? DEFAULT_CONFIG_SCHEMA_LINK ;
268- this . DataSource = DataSource ;
268+ this . DataSource = DataSource ! ;
269269 this . Runtime = Runtime ;
270270 this . AzureKeyVault = AzureKeyVault ;
271- this . Entities = Entities ;
271+ this . Entities = Entities ! ;
272272 this . Autoentities = Autoentities ;
273273 this . DefaultDataSourceName = Guid . NewGuid ( ) . ToString ( ) ;
274274
275- if ( this . DataSource is null )
275+ bool hasDataSourceFiles = DataSourceFiles ? . SourceFiles ? . Any ( ) == true ;
276+
277+ if ( this . DataSource is null && ! hasDataSourceFiles )
276278 {
277279 throw new DataApiBuilderException (
278280 message : "data-source is a mandatory property in DAB Config" ,
279281 statusCode : HttpStatusCode . UnprocessableEntity ,
280282 subStatusCode : DataApiBuilderException . SubStatusCodes . ConfigValidationError ) ;
281283 }
282284
283- // we will set them up with default values
284- _dataSourceNameToDataSource = new Dictionary < string , DataSource >
285- {
286- { this . DefaultDataSourceName , this . DataSource }
287- } ;
285+ // Initialize data source dictionary - may be empty if parent relies solely on data-source-files
286+ _dataSourceNameToDataSource = this . DataSource is not null
287+ ? new Dictionary < string , DataSource > { { this . DefaultDataSourceName , this . DataSource } }
288+ : new Dictionary < string , DataSource > ( ) ;
288289
289290 _entityNameToDataSourceName = new Dictionary < string , string > ( ) ;
290- if ( Entities is null )
291+
292+ if ( Entities is null && ! hasDataSourceFiles )
291293 {
292294 throw new DataApiBuilderException (
293295 message : "entities is a mandatory property in DAB Config" ,
294296 statusCode : HttpStatusCode . UnprocessableEntity ,
295297 subStatusCode : DataApiBuilderException . SubStatusCodes . ConfigValidationError ) ;
296298 }
297299
298- foreach ( KeyValuePair < string , Entity > entity in Entities )
300+ if ( Entities is null )
301+ {
302+ this . Entities = new RuntimeEntities ( new Dictionary < string , Entity > ( ) ) ;
303+ }
304+
305+ foreach ( KeyValuePair < string , Entity > entity in this . Entities )
299306 {
300307 _entityNameToDataSourceName . TryAdd ( entity . Key , this . DefaultDataSourceName ) ;
301308 }
302309
303310 // Process data source and entities information for each database in multiple database scenario.
304311 this . DataSourceFiles = DataSourceFiles ;
305312
306- if ( DataSourceFiles is not null && DataSourceFiles . SourceFiles is not null )
313+ if ( hasDataSourceFiles )
307314 {
308- IEnumerable < KeyValuePair < string , Entity > > allEntities = Entities . AsEnumerable ( ) ;
315+ IEnumerable < KeyValuePair < string , Entity > > allEntities = this . Entities . AsEnumerable ( ) ;
309316 // Iterate through all the datasource files and load the config.
310317 IFileSystem fileSystem = new FileSystem ( ) ;
311318 // This loader is not used as a part of hot reload and therefore does not need a handler.
312319 FileSystemRuntimeConfigLoader loader = new ( fileSystem , handler : null ) ;
313320
314- foreach ( string dataSourceFile in DataSourceFiles . SourceFiles )
321+ foreach ( string dataSourceFile in DataSourceFiles ! . SourceFiles ! )
315322 {
316323 // Use default replacement settings for environment variable replacement
317324 DeserializationVariableReplacementSettings replacementSettings = new ( azureKeyVaultOptions : null , doReplaceEnvVar : true , doReplaceAkvVar : true ) ;
@@ -320,6 +327,13 @@ public RuntimeConfig(
320327 {
321328 try
322329 {
330+ // If parent has no DataSource, adopt first child's DataSource as the default.
331+ if ( this . DataSource is null && config . DataSource is not null )
332+ {
333+ this . DataSource = config . DataSource ;
334+ _dataSourceNameToDataSource [ this . DefaultDataSourceName ] = this . DataSource ;
335+ }
336+
323337 _dataSourceNameToDataSource = _dataSourceNameToDataSource . Concat ( config . _dataSourceNameToDataSource ) . ToDictionary ( kvp => kvp . Key , kvp => kvp . Value ) ;
324338 _entityNameToDataSourceName = _entityNameToDataSourceName . Concat ( config . _entityNameToDataSourceName ) . ToDictionary ( kvp => kvp . Key , kvp => kvp . Value ) ;
325339 allEntities = allEntities . Concat ( config . Entities . AsEnumerable ( ) ) ;
@@ -339,6 +353,15 @@ public RuntimeConfig(
339353 this . Entities = new RuntimeEntities ( allEntities . ToDictionary ( x => x . Key , x => x . Value ) ) ;
340354 }
341355
356+ // Final validation: ensure at least one data source exists after loading all children
357+ if ( this . DataSource is null )
358+ {
359+ throw new DataApiBuilderException (
360+ message : "data-source is a mandatory property in DAB Config. When using data-source-files, at least one child config must contain a valid data-source." ,
361+ statusCode : HttpStatusCode . UnprocessableEntity ,
362+ subStatusCode : DataApiBuilderException . SubStatusCodes . ConfigValidationError ) ;
363+ }
364+
342365 SetupDataSourcesUsed ( ) ;
343366
344367 }
0 commit comments