@@ -24,6 +24,7 @@ internal class DataverseService
2424 private readonly EntityIconService entityIconService ;
2525 private readonly RecordMappingService recordMappingService ;
2626 private readonly SolutionComponentService solutionComponentService ;
27+ private readonly SolutionComponentExtractor solutionComponentExtractor ;
2728 private readonly WorkflowService workflowService ;
2829 private readonly RelationshipService relationshipService ;
2930
@@ -38,6 +39,7 @@ public DataverseService(
3839 EntityIconService entityIconService ,
3940 RecordMappingService recordMappingService ,
4041 SolutionComponentService solutionComponentService ,
42+ SolutionComponentExtractor solutionComponentExtractor ,
4143 WorkflowService workflowService ,
4244 RelationshipService relationshipService )
4345 {
@@ -49,6 +51,7 @@ public DataverseService(
4951 this . recordMappingService = recordMappingService ;
5052 this . workflowService = workflowService ;
5153 this . relationshipService = relationshipService ;
54+ this . solutionComponentExtractor = solutionComponentExtractor ;
5255
5356 // Register all analyzers with their query functions
5457 analyzerRegistrations = new List < IAnalyzerRegistration >
@@ -69,7 +72,7 @@ public DataverseService(
6972 this . solutionComponentService = solutionComponentService ;
7073 }
7174
72- public async Task < ( IEnumerable < Record > , IEnumerable < SolutionWarning > , Dictionary < string , GlobalOptionSetUsage > ) > GetFilteredMetadata ( )
75+ public async Task < ( IEnumerable < Record > , IEnumerable < SolutionWarning > , IEnumerable < SolutionComponentCollection > , Dictionary < string , GlobalOptionSetUsage > ) > GetFilteredMetadata ( )
7376 {
7477 // used to collect warnings for the insights dashboard
7578 var warnings = new List < SolutionWarning > ( ) ;
@@ -315,8 +318,79 @@ public DataverseService(
315318 } )
316319 . ToList ( ) ;
317320
318- logger . LogInformation ( $ "[{ DateTime . Now : yyyy-MM-dd HH:mm:ss.fff} ] GetFilteredMetadata completed - returning { records . Count } records with { globalOptionSetUsages . Count } global option sets") ;
319- return ( records , warnings , globalOptionSetUsages ) ;
321+ /// SOLUTION COMPONENTS FOR INSIGHTS
322+ List < SolutionComponentCollection > solutionComponentCollections ;
323+ try
324+ {
325+ logger . LogInformation ( $ "[{ DateTime . Now : yyyy-MM-dd HH:mm:ss.fff} ] Extracting solution components for insights view") ;
326+
327+ // Build name lookups from entity metadata for the extractor
328+ var entityNameLookup = entitiesInSolutionMetadata . ToDictionary (
329+ e => e . MetadataId ! . Value ,
330+ e => e . DisplayName . ToLabelString ( ) ?? e . SchemaName ) ;
331+
332+ var attributeNameLookup = entitiesInSolutionMetadata
333+ . SelectMany ( e => e . Attributes . Where ( a => a . MetadataId . HasValue ) )
334+ . ToDictionary (
335+ a => a . MetadataId ! . Value ,
336+ a => a . DisplayName . ToLabelString ( ) ?? a . SchemaName ) ;
337+
338+ var relationshipNameLookup = entitiesInSolutionMetadata
339+ . SelectMany ( e => e . ManyToManyRelationships . Cast < RelationshipMetadataBase > ( )
340+ . Concat ( e . OneToManyRelationships )
341+ . Concat ( e . ManyToOneRelationships ) )
342+ . Where ( r => r . MetadataId . HasValue )
343+ . DistinctBy ( r => r . MetadataId ! . Value )
344+ . ToDictionary (
345+ r => r . MetadataId ! . Value ,
346+ r => r . SchemaName ) ;
347+
348+ // Build entity lookups for attributes, relationships, and keys (maps component ID to parent entity name)
349+ var attributeEntityLookup = entitiesInSolutionMetadata
350+ . SelectMany ( e => e . Attributes . Where ( a => a . MetadataId . HasValue )
351+ . Select ( a => ( AttributeId : a . MetadataId ! . Value , EntityName : e . DisplayName . ToLabelString ( ) ?? e . LogicalName ) ) )
352+ . ToDictionary ( x => x . AttributeId , x => x . EntityName ) ;
353+
354+ var relationshipEntityLookup = entitiesInSolutionMetadata
355+ . SelectMany ( e => e . ManyToManyRelationships . Cast < RelationshipMetadataBase > ( )
356+ . Concat ( e . OneToManyRelationships )
357+ . Concat ( e . ManyToOneRelationships )
358+ . Where ( r => r . MetadataId . HasValue )
359+ . Select ( r => ( RelationshipId : r . MetadataId ! . Value , EntityName : e . DisplayName . ToLabelString ( ) ?? e . LogicalName ) ) )
360+ . DistinctBy ( x => x . RelationshipId )
361+ . ToDictionary ( x => x . RelationshipId , x => x . EntityName ) ;
362+
363+ var keyEntityLookup = entitiesInSolutionMetadata
364+ . SelectMany ( e => ( e . Keys ?? Array . Empty < EntityKeyMetadata > ( ) )
365+ . Where ( k => k . MetadataId . HasValue )
366+ . Select ( k => ( KeyId : k . MetadataId ! . Value , EntityName : e . DisplayName . ToLabelString ( ) ?? e . LogicalName ) ) )
367+ . ToDictionary ( x => x . KeyId , x => x . EntityName ) ;
368+
369+ // Build solution name lookup
370+ var solutionNameLookup = solutionLookup . ToDictionary (
371+ kvp => kvp . Key ,
372+ kvp => kvp . Value . Name ) ;
373+
374+ solutionComponentCollections = await solutionComponentExtractor . ExtractSolutionComponentsAsync (
375+ solutionIds ,
376+ solutionNameLookup ,
377+ entityNameLookup ,
378+ attributeNameLookup ,
379+ relationshipNameLookup ,
380+ attributeEntityLookup ,
381+ relationshipEntityLookup ,
382+ keyEntityLookup ) ;
383+
384+ logger . LogInformation ( $ "[{ DateTime . Now : yyyy-MM-dd HH:mm:ss.fff} ] Extracted components for { solutionComponentCollections . Count } solutions") ;
385+ }
386+ catch ( Exception ex )
387+ {
388+ logger . LogWarning ( ex , $ "[{ DateTime . Now : yyyy-MM-dd HH:mm:ss.fff} ] Failed to extract solution components for insights, continuing without them") ;
389+ solutionComponentCollections = new List < SolutionComponentCollection > ( ) ;
390+ }
391+
392+ logger . LogInformation ( $ "[{ DateTime . Now : yyyy-MM-dd HH:mm:ss.fff} ] GetFilteredMetadata completed") ;
393+ return ( records , warnings , solutionComponentCollections , globalOptionSetUsages ) ;
320394 }
321395 }
322396
0 commit comments