This file provides guidance to Claude Code when working with the Generator project.
The Generator is a C# .NET 8 console application that extracts metadata from Microsoft Dataverse and generates TypeScript definitions for the Website project. It analyzes entities, attributes, relationships, security roles, plugins, flows, and web resources.
- .NET 8.0 with C# 13
- Microsoft.PowerPlatform.Dataverse.Client v1.2.2 - Dataverse SDK
- Azure.Identity v1.13.1 - Authentication via DefaultAzureCredential
- System.Linq.Dynamic.Core v1.6.7 - Dynamic LINQ queries
- Microsoft.Extensions.Configuration - Configuration management
- Microsoft.Extensions.Logging - Console logging
Simple flow:
- Load configuration from environment variables and
appsettings.local.json - Create DataverseService with logger
- Call
GetFilteredMetadata()to extract data - Pass to WebsiteBuilder to generate TypeScript output
Responsibility: Connect to Dataverse and extract all metadata
Key Methods:
GetFilteredMetadata()- Main orchestrator, filters by solutionsGetEntities()- Query all EntityMetadataMapEntity()- Convert Dataverse entity to DTOMapAttribute()- Convert attributes to polymorphic type-safe DTOsGetRelationships()- Extract N:1, 1:N, N:N relationships
Authentication: Uses DefaultAzureCredential which tries in order:
- Environment variables (for pipelines)
- Azure CLI credentials (for local dev)
- Managed Identity (for Azure deployment)
Filtering: Only includes entities from specified solutions in DataverseSolutionNames config
Responsibility: Generate TypeScript file from metadata DTOs
Output: Website/generated/Data.ts containing:
export const EntityGroups: Map<string, EntityType[]>
export const Warnings: SolutionWarningsList[]
export const SolutionComponents: SolutionComponentList[]
export const timestamp: string
export const logo: string | nullKey Methods:
AddData()- Orchestrates file generation- Groups entities by category (from entity descriptions or config)
- Converts C# DTOs to TypeScript type syntax
Responsibility: Detect customizations to standard fields
Key Logic:
StandardFieldHasChanged()- Detects if OOB fields were modified- Compares display names and descriptions against defaults (English, Danish)
- Checks if StatusCode/StateCode options have custom (unmanaged) values
- Returns true if any customization detected
Use Case: Website can hide/show standard vs. customized fields
Generator/
├── DTO/ # Data Transfer Objects
│ ├── Attributes/ # Attribute type DTOs (polymorphic)
│ ├── Entity.cs # Entity DTO
│ ├── Relationship.cs # Relationship DTO
│ ├── SecurityRole.cs # Security role DTO
│ └── Solution.cs # Solution component DTOs
│
├── Services/ # Analyzers for special components
│ ├── PluginAnalyzer.cs # Extracts plugin steps and dependencies
│ ├── FlowAnalyzer.cs # Parses Power Automate flows
│ └── WebResourceAnalyzer.cs # Analyzes JavaScript web resources
│
├── Queries/ # FetchXML and query helpers
│ └── (Query definition files)
│
├── Program.cs # Entry point
├── DataverseService.cs # Main Dataverse interaction
├── WebsiteBuilder.cs # TypeScript generator
├── MetadataExtensions.cs # Customization detection
├── ClientExtensions.cs # ServiceClient helpers
├── UtilityExtensions.cs # General utilities
└── Generator.csproj # Project file
{
"DataverseUrl": "https://org.crm4.dynamics.com",
"DataverseSolutionNames": "SolutionUniqueName1,SolutionUniqueName2",
"OutputFolder": "../Website/generated",
"Verbosity": "Information"
}Configuration Keys:
DataverseUrl- Full URL to Dataverse environment (required)DataverseSolutionNames- Comma-separated list of solution unique names (required)OutputFolder- Path to output directory (default:../Website/generated)Verbosity- Logging level:Trace,Debug,Information,Warning,Error(default:Information)TableGroups- Optional semi-colon separated group definitions (format:Group Name: table1, table2; Other Group: table3)
DataverseUrl- Same as configDataverseSolutionNames- Same as configOutputFolder- Same as configAZURE_CLIENT_ID- Azure App Registration Client IDAZURE_CLIENT_SECRET- Azure App Registration SecretAZURE_TENANT_ID- Azure Tenant ID
cd Generator
dotnet restoredotnet build
dotnet build --configuration Release# Default output to ../Website/generated
dotnet run
# Custom output folder
dotnet run --OutputFolder /path/to/output
# With specific configuration
dotnet run --DataverseUrl https://org.crm4.dynamics.com --OutputFolder ../Website/generateddotnet format
dotnet format --verify-no-changes # Check only, no modificationsdotnet clean
dotnet buildThe Generator uses DefaultAzureCredential which requires Azure CLI authentication locally:
# Login with Azure CLI
az login
# Select correct subscription if needed
az account list
az account set --subscription "Subscription Name or ID"
# Verify access
az account showRequired Permissions: The authenticated user must have at least Basic read access to the Dataverse environment.
public class Entity
{
public string DisplayName { get; set; }
public string SchemaName { get; set; }
public string? Group { get; set; }
public string Ownership { get; set; }
public bool IsActivity { get; set; }
public bool IsCustom { get; set; }
public List<Attribute> Attributes { get; set; }
public List<Relationship> Relationships { get; set; }
public List<SecurityRole> SecurityRoles { get; set; }
public List<Key> Keys { get; set; }
}Base: Attribute (abstract)
Concrete Types:
ChoiceAttribute- Picklist with optionsLookupAttribute- Foreign key (Targets list)StringAttribute- Text (MaxLength, Format)DateTimeAttribute- Date/Time (Behavior, Format)IntegerAttribute- Whole numberDecimalAttribute- Decimal/MoneyBooleanAttribute- Two-option (TrueLabel, FalseLabel)StatusAttribute- Status with linked StateFileAttribute- File/Image (MaxSize)GenericAttribute- Fallback for unsupported types
Pattern: Each attribute type has properties specific to its Dataverse counterpart.
public class Relationship
{
public string Name { get; set; }
public string RelationshipSchema { get; set; }
public string TableSchema { get; set; }
public bool IsManyToMany { get; set; }
public CascadeConfiguration CascadeConfiguration { get; set; }
}-
Create DTO: Add new class in
DTO/Attributes/inheriting fromAttributepublic class NewAttribute : Attribute { public string SpecialProperty { get; set; } }
-
Update Mapper: Add case in
DataverseService.cs:MapAttribute()case NewAttributeMetadata newAttr: return new NewAttribute { // Map properties SpecialProperty = newAttr.SpecialProperty };
-
Update Website: Add corresponding TypeScript type in
Website/lib/Types.ts
- Create Service: Add new analyzer in
Services/directory - Extract Data: Query relevant Dataverse records
- Add to Output: Update
WebsiteBuilder.csto include new data - Update Website: Create UI to display the new data
- Find Query: Locate in
Queries/or inDataverseService.csmethods - Update FetchXML/QueryExpression: Modify query structure
- Test Locally: Run generator and verify output
- Validate Output: Check
Website/generated/Data.tsis valid TypeScript
Problem: Cannot connect to Dataverse
Solutions:
- Verify
DataverseUrlis correct in config - Check Azure CLI is logged in:
az account show - Verify user has Dataverse access
- Check network connectivity
- Try different authentication methods (environment variables vs. CLI)
Enable Detailed Logging:
{
"Verbosity": "Trace"
}Problem: Expected entities not in output
Check:
- Verify entity exists in specified solutions
- Check
DataverseSolutionNamesincludes correct solutions - Review console warnings for excluded entities
- Verify solution publisher prefix matches
The generated Data.ts file structure:
// Entity groups mapped by category
export const EntityGroups = new Map<string, EntityType[]>([
["Core", [/* entities */]],
["Custom", [/* entities */]],
// ...
]);
// Validation warnings for solutions
export const Warnings: SolutionWarningsList[] = [
{
SolutionName: "MySolution",
Warnings: [/* warning objects */]
}
];
// Solution component breakdown
export const SolutionComponents: SolutionComponentList[] = [
{
SolutionName: "MySolution",
Components: [/* component objects */]
}
];
// Generation timestamp
export const timestamp = "2025-11-10T15:30:00Z";
// Optional base64 encoded logo
export const logo = "data:image/png;base64,...";Critical: Website depends on this output file. Always regenerate after Generator changes.
Workflow:
- Modify Generator code or configuration
- Run
dotnet runto regenerateData.ts - Restart Website dev server to pick up changes
- Test Website functionality
Type Safety: Keep C# DTOs in sync with TypeScript types in Website/lib/Types.ts
Large Environments:
- Fetching 500+ entities can take 2-5 minutes
- Relationship queries are expensive
- Plugin/Flow analysis adds overhead
Optimization:
- Filter by specific solutions only
- Reduce verbosity for production runs
- Consider caching metadata for incremental updates
- Run against test environment
- Check console output for errors/warnings
- Verify generated TypeScript file:
- Valid syntax (no compile errors)
- Expected entities present
- Attributes have correct types
- Relationships are bidirectional
- Generator runs without exceptions
- Output file created at correct location
- No TypeScript compilation errors in output
- Entity count matches expectation
- Custom entities identified correctly
- Relationships include both directions
- Security roles populated
- Warning list shows known issues
- Run
az loginand authenticate - Verify subscription has Dataverse access
- Check if using correct Azure tenant
- Verify solution unique names (not display names)
- Check solutions exist in target environment
- Ensure solutions have same publisher prefix
- Filter by more specific solutions
- Consider splitting into multiple generation runs
- Website may have performance issues with 1000+ entities
- Check if filtered by solution components
- Verify attribute is not system-only
- Ensure relationship is managed or in solutions
- Always run Generator before starting Website development session
- Keep
appsettings.local.jsonout of source control (already in .gitignore) - DefaultAzureCredential requires Azure CLI for local dev
- Output folder must exist before running (Generator will not create it)
- TypeScript output must be valid - Generator will not validate syntax
- Generator runs synchronously - expect 2-5 minute execution time for large environments