Conversation
Phase 1: Infrastructure Setup Created automation tools and templates to support systematic migration of 120+ legacy .NET Framework 4.7.2 samples to modern .NET 6+ architecture. PowerShell Scripts: - New-CategoryFolder.ps1: Creates category folder structure with standard files - New-ModernSample.ps1: Scaffolds new sample projects within categories - Convert-LegacySample.ps1: Assists with code transformation and modernization - Test-MigratedSamples.ps1: Validates samples build successfully Templates: - Program.cs.template: Standard modern sample structure with Setup/Run/Cleanup - Sample.csproj.template: SDK-style project with modern packages - CategoryREADME.md.template: Category-level documentation template - SampleREADME.md.template: Sample-level documentation template These tools follow the established pattern documented in README-code-design.md and will accelerate migration while ensuring consistency across all samples. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Created Audit category structure and migrated first sample: New Category: - Audit/ folder with standard structure - Category-level appsettings.json and README.md - Audit.slnx solution file Completed Sample - AuditEntityData: - Fully migrated from legacy .NET Framework to modern .NET 6+ - Converted from early-bound to late-bound entities - Modernized to Setup/Run/Cleanup pattern - Demonstrates: * Enabling organization and entity-level auditing * Retrieving record change history * Retrieving attribute change history * Displaying audit details with old/new values - Complete README documentation - Builds successfully (verified with dotnet build) In Progress - AuditUserAccess: - Project scaffolding created - Implementation pending This validates the automation scripts work correctly and establishes the migration pattern for remaining 118+ samples. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Completed pilot category with both samples fully migrated and tested: AuditUserAccess Sample (NEW - COMPLETE): - Fully migrated from legacy .NET Framework to modern .NET 6+ - Converted from early-bound to late-bound entities - Modernized to Setup/Run/Cleanup pattern - Demonstrates: * Enabling organization and user access auditing * Retrieving user access audit records * Filtering audit records by action type and time * Displaying audit information with friendly names - Complete README documentation with sample output - Builds successfully (verified with dotnet build) Category Updates: - Updated Audit/README.md with complete samples table - Both samples link to Microsoft Learn documentation - Solution builds successfully with both projects Testing Results: - ✅ AuditEntityData builds successfully - ✅ AuditUserAccess builds successfully - ✅ Solution (Audit.slnx) builds successfully - ✅ Zero build errors Migration Progress: 2 of 120+ samples complete (Pilot Phase 2) Next: Continue pilot with remaining categories (DuplicateDetection, Queues) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Migrated 3 duplicate detection samples from legacy .NET Framework to modern .NET 6: 1. EnableDuplicateDetection - Enables duplicate detection at org and entity levels, publishes rules with async tracking, and retrieves duplicate records 2. DetectMultipleDuplicateRecords - Creates duplicate detection rule programmatically, uses BulkDetectDuplicatesRequest for async bulk detection, queries duplicaterecord table 3. UseDuplicatedetectionforCRUD - Demonstrates SuppressDuplicateDetection parameter with CreateRequest and UpdateRequest to control duplicate detection behavior All samples: - Follow modern pattern with Setup/Run/Cleanup structure - Use late-bound Entity syntax (no early-bound types) - Include comprehensive README documentation - Build successfully with zero errors - Share appsettings.json configuration Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…ogress) Created Queues category infrastructure with 8 queue management samples. Fully implemented: 1. CreateQueue - Creates queue with configuration options 2. DeleteQueue - Creates and deletes queue 3. AddRecordToQueue - Adds records to queues and moves between queues Scaffolded (build successfully, implementation in progress): 4. ReleaseQueueItems - Release queue items from workers 5. SpecifyQueueItem - Assign queue items to workers 6. CleanHistoryQueue - Remove completed items from queues 7. ShareQueue - Share queue access with teams 8. AddSecurityPrincipalToQueue - Add security principals to queues All samples: - Build successfully with zero errors - Follow modern pattern structure - Share appsettings.json configuration - Part of Phase 2 pilot migration Phase 2 pilot progress: 5 of 12 samples fully complete (Audit: 2, DuplicateDetection: 3, Queues: 3/8 implemented) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implemented remaining 5 Queue samples to complete the category: 1. ReleaseQueueItems - Release queue items from workers using ReleaseToQueueRequest 2. SpecifyQueueItem - Assign queue items to workers using PickFromQueueRequest 3. CleanHistoryQueue - Remove completed items from queues using RemoveFromQueueRequest 4. ShareQueue - Share queue access with teams using GrantAccessRequest 5. AddSecurityPrincipalToQueue - Add teams to queues using AddPrincipalToQueueRequest All Queues samples (8/8): - Build successfully (0 errors) - Follow modern pattern (Setup/Run/Cleanup) - Use late-bound Entity syntax - Share appsettings.json configuration - Demonstrate queue management SDK operations Phase 2 Pilot COMPLETE: - Audit: 2 samples ✓ - DuplicateDetection: 3 samples ✓ - Queues: 8 samples ✓ Total: 13 samples, 100% build success rate Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replaced template content with detailed documentation for each Queue sample: - CreateQueue: Queue creation with configuration options - DeleteQueue: Queue lifecycle management (create/delete) - AddRecordToQueue: Adding and routing records between queues - ReleaseQueueItems: Releasing queue items from worker assignments - SpecifyQueueItem: Assigning queue items to specific workers - CleanHistoryQueue: Removing completed items from queues - ShareQueue: Sharing queue access with teams using GrantAccessRequest - AddSecurityPrincipalToQueue: Adding teams as queue members Each README includes: - Comprehensive description of functionality - Detailed Setup/Run/Cleanup workflow - Key SDK concepts demonstrated - Sample console output - Links to Microsoft Learn documentation Phase 2 Pilot (Queues category) documentation now complete. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Created Query category structure with: - Category README and appsettings.json Migrated 3 samples from legacy C# to modern .NET 6+: 1. RetrieveMultipleQueryByAttribute - Query records using QueryByAttribute 2. RetrieveMultipleByQueryExpression - Query with linked entities and aliases 3. UseQueryExpressionwithPaging - Demonstrate paging with PagingInfo and paging cookies All samples follow modern pattern: - ServiceClient (not CrmServiceClient) - appsettings.json configuration - Setup/Run/Cleanup structure - Late-bound Entity syntax - EntityStore tracking - Comprehensive README documentation All samples build successfully (0 errors). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Migrated 4 samples demonstrating FetchXML capabilities: 1. UseFetchXMLWithPaging - Paging with FetchXML using page and paging-cookie attributes 2. UseAggregationInFetchXML - 17 aggregate query patterns (AVG, COUNT, SUM, MIN, MAX, GROUP BY, date grouping) 3. Convertqueriesfetchqueryexpressions - Converting between FetchXML and QueryExpression 4. ValidateandExecuteSavedQuery - Creating, validating, and executing saved queries (views) All samples: - Use modern ServiceClient with appsettings.json - Follow Setup/Run/Cleanup pattern - Use late-bound Entity syntax - Track entities in entityStore - Build successfully (0 errors) - Include comprehensive README documentation Query category progress: 7 of 14 samples completed. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Migrated remaining 7 Query samples to complete the category: 1. RetrieveRecordsFromIntersectTable - Query many-to-many intersect tables (3 approaches) 2. QueryByReciprocalRole - Query connection roles by reciprocal role 3. QueryByRecord - Query connections for specific records 4. QueryWorkingHours - Query working hours schedules using QueryScheduleRequest 5. QueryHoursMultipleUsers - Query working hours for multiple users 6. QueriesUsingLINQ - 23 LINQ query patterns with OrganizationServiceContext 7. ExportDataUsingFetchXmlToAnnotation - Export query results to CSV annotations All samples: - Use modern ServiceClient with appsettings.json - Follow Setup/Run/Cleanup pattern - Use late-bound Entity syntax (including LINQ sample) - Track entities in entityStore - Build successfully (0 errors) - Include comprehensive README documentation Query category complete: 14 of 14 samples migrated. Phase 3 Batch 1 (Core Operations) progress: - Query: 14/14 samples ✓ COMPLETE - CRUD: 0/7 samples (next) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
Rather than migrate old samples from the We need a The center of gravity shifted already. .NET Framework is the exception to the rule. |
JimDaly
left a comment
There was a problem hiding this comment.
I didn't get to review everything. I got as far as Query, but I think there are some things to discuss.
| /// <param name="entityLogicalName">The logical name of the entity</param> | ||
| /// <param name="flag">True to enable auditing, false to disable</param> | ||
| /// <returns>The previous value of the IsAuditEnabled attribute</returns> | ||
| private static bool EnableEntityAuditing(ServiceClient service, string entityLogicalName, bool flag) |
There was a problem hiding this comment.
private static bool EnableEntityAuditing(ServiceClient service, string entityLogicalName, bool flag)
Unless the method is using one of the methods added by ServiceClient, this parameter should be an IOrganizationService interface.
| /// <param name="entityLogicalName">The logical name of the entity</param> | ||
| /// <param name="flag">True to enable auditing, false to disable</param> | ||
| /// <returns>The previous value of the IsAuditEnabled attribute</returns> | ||
| private static bool EnableEntityAuditing(ServiceClient service, string entityLogicalName, bool flag) |
There was a problem hiding this comment.
The name of this function is confusing. EnableEntityAuditing should do one thing: Enable entity auditing. There should not be any flag value.
The function should check if auditing is enabled and enable it if it isn't enabled.
If it is already enabled, do nothing.
Otherwise, change the name of the function to EnableOrDisableEntityAuditing - but recommend just changing the signature and behavior
This same function is defined in the next AuditUserAccess sample.
Can't we define it in a single place within this Audit folder and reuse it?
| /// </summary> | ||
| /// <param name="service">The service client</param> | ||
| /// <param name="detail">The audit detail to display</param> | ||
| private static void DisplayAuditDetails(ServiceClient service, AuditDetail detail) |
There was a problem hiding this comment.
private static void DisplayAuditDetails(ServiceClient service, AuditDetail detail)
I don't see that the service parameter is used anywhere in this function. Remove it?
| ServiceClient serviceClient = | ||
| new(app.Configuration.GetConnectionString("default")); | ||
|
|
||
| if (!serviceClient.IsReady) |
There was a problem hiding this comment.
This IsReady check isn't necessary. See my email. This is cargo-cult boilerplate
| /// <summary> | ||
| /// Sets up sample data by enabling auditing and creating an account | ||
| /// </summary> | ||
| private static void Setup(ServiceClient service) |
There was a problem hiding this comment.
This is a global comment.
Unless a method uses some ServiceClient.method, the parameter should be IOrganizationService, which ServiceClient implements.
| /// <summary> | ||
| /// Enables duplicate detection for a specific entity | ||
| /// </summary> | ||
| private static void EnableDuplicateDetectionForEntity(ServiceClient service, string entityName) |
There was a problem hiding this comment.
I have some doubts about whether it is necessary to publish the changes to the EntityMetadata. Retrieving metadata using RetrieveAsIfPublished is a documented anti-pattern.
What is more common is using Strong Consistency when retrieving changes. With SDK this is by setting the ServiceClient.ForceServerMetadataCacheConsistency Property
| /// <summary> | ||
| /// Retrieves the organization ID | ||
| /// </summary> | ||
| private static Guid? RetrieveOrganizationId(ServiceClient service) |
There was a problem hiding this comment.
Why not use WhoAmI to get WhoAmIResponse.OrganizationId at the start and cache it?
| /// <summary> | ||
| /// Waits for async jobs to complete | ||
| /// </summary> | ||
| private static void WaitForAsyncJobCompletion(ServiceClient service, IEnumerable<Guid> asyncJobIds) |
There was a problem hiding this comment.
I think having this reusable function defined at a higher level would demonstrate DRY coding.
| } | ||
|
|
||
| attempts++; | ||
| Thread.Sleep(1000); |
There was a problem hiding this comment.
See earlier comment about code smell using Thread.Sleep.
| - Bypass duplicate detection when needed | ||
| - Allow duplicate detection to run when needed | ||
|
|
||
| The SuppressDuplicateDetection parameter provides fine-grained control over when duplicate detection fires during CRUD operations, allowing developers to choose when to enforce or bypass duplicate detection rules. |
There was a problem hiding this comment.
This is one of several 'optional parameters'. We should use this term.
We should have a link to this section Suppress duplicate detection
Upgrade the existing 128 Dataverse code samples that use deprecated APIs and an outdated code design. The end result is an improved customer learning experience and adherence to modern coding standards.