Google Anlatics

Monday, February 3, 2025

Virtual Tables in Microsoft Dataverse - CRM Online

Connect External Data in Real-Time Without Duplication


Introduction


Imagine needing to view customer orders stored in an ERP system directly within Microsoft Dataverse—without importing gigabytes of data. Or pulling live inventory numbers from a SQL database into your CRM to empower sales teams with real-time insights. This isn’t a futuristic dream; it’s exactly what Virtual Tables (formerly known as Virtual Entities) enable today.

Virtual Tables act as a dynamic bridge between Microsoft Dataverse and external systems, allowing you to interact with external data as if it were natively stored in your CRM. No duplication, no manual syncing, and no storage costs—just seamless, real-time access.

In this guide, we’ll explore how Virtual Tables work, when to use them, and how to implement them step-by-step in your Dataverse environment.



What Are Virtual Tables?


A Virtual Table is a special type of table in Microsoft Dataverse that connects to an external data source—like an ERP system, SQL database, or REST API. Unlike standard tables, Virtual Tables don’t store data locally. Instead, they fetch and display data on-demand from the external system, ensuring you always see the latest information.


Key Benefits of Virtual Tables

  • Eliminate Data RedundancyAccess external data without duplicating it in Dataverse.
  • Real-Time InsightsView live data from ERP, SQL, or web services instantly.
  • Cost EfficiencyReduce storage costs by avoiding data imports.
  • Seamless User ExperienceUse Virtual Tables in model-driven apps, Canvas apps, and Power Automate workflows just like native tables.
  • Flexible IntegrationSupport for OData v4 APIs, SQL databases, and custom data providers.
  • When Should You Use Virtual Tables?

Virtual Tables shine in scenarios where:

  1. Data Resides Externally: Your ERP, legacy system, or third-party API holds critical data that needs to be accessed within Dataverse.
  2. Real-Time Access Is Essential: Stale data isn’t an option (e.g., live inventory, pricing, or order status).
  3. Large Datasets Are Involved: Importing massive data into Dataverse is impractical or costly.
  4. Security or Compliance Constraints Exist: Data must remain in its source system due to governance policies.

Example Use Case:

A manufacturing company uses Virtual Tables to connect Dataverse to their on-premises SAP ERP system. Sales teams now view real-time production schedules and inventory levels in CRM, enabling accurate delivery promises without data duplication.



How Do Virtual Tables Work?


Virtual Tables rely on a data provider to communicate with the external system. Here’s the workflow:

  1. Query: A user or app requests data from the Virtual Table (e.g., “Show all open orders”).
  2. Fetch: Dataverse sends the query to the external data provider (e.g., an OData API or SQL connector).
  3. Return: The provider retrieves the data and sends it back to Dataverse.
  4. Display: The data appears in Dataverse as if it were a native table.

By default, Virtual Tables are read-only. However, you can enable create, update, and delete (CRUD) operations if the external system supports them.



Implementing Virtual Tables: A Step-by-Step Guide


Step 1: Prepare Your External Data Source

  • OData v4 APIs: Ensure your API endpoint is accessible and returns data in the correct format.

  • SQL Databases: Whitelist Dataverse IPs or set up secure connectivity (e.g., Azure Private Link).

    • Custom Providers: Develop a custom connector if your source isn’t OData or SQL.


Pro Tip: Use tools like Postman to validate your API endpoints before connecting them to Dataverse.


Step 2: Register the Data Source in Dataverse

  1. Navigate to Power Apps > Dataverse > Tables.
  2. Under Settings (⚙), select Advanced Settings > Virtual Entity Data Sources.
  3. Click New and choose your provider (e.g., OData v4).
  4. Enter connection details (URL, authentication) and test the connection.

Step 3: Create and Map the Virtual Table

  1. In Dataverse, click + New Table and toggle Virtual Table.
  2. Select your registered data source.
  3. Define the schema:
    1. Map the external system’s primary key to Dataverse.
    2. Add fields (e.g., “Order Number,” “Status”) and match them to the source’s columns.
  4. Publish the table.
  • Step 4: Test and Deploy
  • Open the Virtual Table in Dataverse to verify data appears.
  • Embed it in a model-driven app or use it in a Power Automate flow.
  • Test CRUD operations if enabled.

Common Challenges and Solutions

  1. No Data Displayed?
    1. Confirm the data source URL is correct.
    2. Check for authentication errors (e.g., expired tokens).
    3. Verify field mappings (e.g., primary key matches).
  2. Read-Only Limitations
    1. Most providers default to read-only. Enable writes by configuring the external system to handle POST/PATCH requests.
  3. Performance Lag
    1. Optimize API/database queries (e.g., add filters or indexing).
    2. Use pagination for large datasets.
  4. Authentication Issues
    1. Use Azure Active Directory (AAD) for OAuth-secured APIs.
    2. For SQL, leverage Azure SQL’s firewall rules or managed identities.
References : 

Friday, January 24, 2025

How to Integrate Customer Insights with Microsoft Dynamics 365 CRM Online

 In today’s customer-centric world, integrating Customer Insights with Microsoft Dynamics 365 CRM (Customer Relationship Management) Online enables organizations to unlock the power of data-driven decisions. By unifying disparate data sources, businesses can deliver personalized experiences and build stronger customer relationships. Here's a step-by-step guide to integrating Customer Insights with Dynamics 365 CRM Online and leveraging its capabilities.

What is Dynamics 365 Customer Insights?

Microsoft Dynamics 365 Customer Insights is a customer data platform (CDP) that consolidates customer data from various sources into a single unified profile. This helps organizations:

  1. Gain a 360-degree view of customers: By aggregating data from marketing, sales, service, and other channels.

  2. Deliver personalized experiences: Leverage AI and machine learning to gain actionable insights.

  3. Optimize marketing and sales efforts: By predicting customer needs and preferences.

Steps to Integrate Customer Insights with Dynamics 365 CRM Online

1. Set Up Dynamics 365 Customer Insights

  • Provision the Customer Insights environment: Log into the Microsoft Power Platform Admin Center and provision a new Customer Insights instance.

  • Ingest data sources: Import data from multiple sources like Dynamics 365, Excel files, SQL databases, or third-party apps (e.g., Salesforce, Shopify).

  • Map and unify data: Use the unification feature to create a single customer profile by mapping data fields and resolving duplicates.

2. Enable Integration with Dynamics 365 CRM

  • Install Dynamics 365 connectors: Ensure that the Dynamics 365 Customer Insights connector is available in your environment.

  • Set up Power Automate flows: Use Power Automate to create workflows that sync Customer Insights profiles and insights into Dynamics 365 CRM entities such as Contacts or Leads.

3. Configure Customer Insights Cards in Dynamics 365 CRM

  • Enable Customer Cards in Dynamics 365 CRM to display real-time insights about a customer, such as:

    • Purchase history

    • Engagement levels

    • Churn risk score

    • Recommended next actions

  • Steps to set up Customer Cards:

    1. Navigate to Settings in Dynamics 365 CRM.

    2. Go to Customer Insights Integration and authenticate your Customer Insights environment.

    3. Configure the fields and metrics to display within the CRM interface.

4. Utilize Prebuilt AI Models

Customer Insights includes AI models like:

  • Predictive churn: Identifies customers at risk of leaving.

  • Customer lifetime value (CLV): Calculates potential revenue from a customer.

  • Product recommendations: Provides personalized suggestions based on purchase patterns.

These insights can be directly integrated into Dynamics 365 CRM to drive smarter decision-making.

5. Enable Real-Time Data Synchronization

  • Set up data refresh schedules: Ensure your data in Customer Insights is updated frequently.

  • Use Power Automate for real-time syncing: Configure automated workflows to update records in Dynamics 365 CRM whenever changes occur in Customer Insights.

6. Build Custom Visualizations with Power BI

  • Use Power BI to create rich dashboards that combine Customer Insights data with CRM data. Examples include:

    • Customer segmentation analysis

    • Sales performance metrics

    • Customer journey tracking

7. Integrate with Marketing and Sales Tools

  • Connect Customer Insights with Dynamics 365 Marketing to deliver targeted campaigns.

  • Enable Sales Insights in Dynamics 365 CRM to give sales reps actionable recommendations during customer interactions.

Benefits of Integrating Customer Insights with Dynamics 365 CRM Online

1. Centralized Data for a Unified Customer View

Combining Customer Insights with Dynamics 365 CRM eliminates data silos and provides a holistic view of each customer, enabling better personalization.

2. Improved Customer Engagement

Personalized recommendations and insights empower sales and marketing teams to engage with customers more effectively.

3. AI-Powered Decision Making

With built-in AI models, organizations can predict customer behavior, reduce churn, and maximize lifetime value.

4. Increased Operational Efficiency

Automated workflows and real-time synchronization ensure that teams always have the latest data without manual intervention.

Advanced Use Cases

1. Event-Based Customer Journeys

Use real-time event triggers, like abandoned carts or website interactions, to automatically update Dynamics 365 CRM records and initiate follow-up actions.

2. Advanced Segmentation

Segment customers based on their behavior, preferences, and engagement history. Use these segments in targeted marketing campaigns.

3. Cross-Sell and Upsell Opportunities

Leverage AI-driven recommendations to identify products or services that a customer is likely to purchase next.

4. Customer Feedback Analysis

Integrate feedback from surveys (e.g., Microsoft Forms or third-party tools) into Customer Insights and display sentiment analysis in Dynamics 365 CRM.


Tools and Resources

1. Microsoft Power Platform

Use Power Automate and Power Apps to customize workflows and create apps that connect Customer Insights with Dynamics 365 CRM.

2. Azure Synapse Analytics

For advanced data processing and analytics, connect Customer Insights data with Azure Synapse Analytics.

3. Microsoft Learn and Documentation

Explore the official Customer Insights documentation to deepen your knowledge.

Calling Microsoft Dynamics 365 CRM Online APIs from External Applications

If you’re working with Microsoft Dynamics 365 CRM Online, you’ve probably encountered situations where you need to integrate it with other applications like your website, custom web APIs, or tools like KingswaySoft, XrmToolBox, or Change Data Capture (CDC) tools. API integration can unlock incredible automation and data synchronization, but with great power comes a few headaches.

Let’s dive into how you can make API calls to your MS CRM Online system, what limits and challenges you need to be aware of, and how to avoid common pitfalls while following best practices.


How MS CRM Online API Calls Work

The Microsoft Dataverse Web API (formerly known as the Common Data Service API) is at the heart of connecting with MS CRM Online. It uses OData and supports CRUD (Create, Read, Update, Delete) operations for your CRM data.

Common Use Cases for CRM API Calls:

  • Website Integration: Pull customer data, update records, or submit forms directly to CRM.
  • Middleware Tools: Automate imports, data migration, or bulk updates using KingswaySoft or SSIS packages.
  • Data Sync with External Systems: Use APIs to sync CRM data with ERP, marketing tools, or third-party systems in near-real time.
  • Automation Tools: Tools like Power Automate or XrmToolBox to streamline operations without reinventing the wheel.

What Makes MS CRM Online API Calls Different?

Dynamics 365 CRM Online operates in the cloud, which means Microsoft enforces strict rules to ensure the stability of its infrastructure. These include:

  1. Daily API Limits (Service Protection Limits):
    Each user and tenant in MS CRM Online has a daily API call limit based on the licenses they’re using.

    • For example, a Dynamics 365 Customer Service Enterprise license allows 40,000 daily API calls per user.
    • These limits are shared across all applications making calls on behalf of a user or application.
  2. Timeouts and Query Size Restrictions:

    • The maximum request timeout is 2 minutes.
    • API calls can fail if they try to retrieve too much data at once. Large datasets need to be retrieved in pages using $top and $skip.
  3. Concurrency Limits:

    • Too many concurrent calls from the same application or user can trigger throttling (HTTP 429 errors).
  4. Authentication:

    • CRM Online only supports OAuth 2.0 for authentication. No basic auth! This means you’ll need to register an app in Azure AD and obtain access tokens to make API calls.

Limitations You Need to Know About

  1. Throttling (HTTP 429 Errors):
    If you exceed API limits or send too many requests too quickly, Microsoft will throttle your app to ensure the CRM stays stable for all users. Throttling typically results in a “429 Too Many Requests” error.

  2. Complex Queries Can Fail:
    Using too many joins, nested filters, or retrieving large data sets can lead to timeout errors or performance issues.

  3. Eventual Consistency:
    Data changes made through the API might not be immediately available for reads, especially if you’re using background processes like workflows.

  4. Security Role Restrictions:
    Even if you have API access, the user or application making the call is still limited by their assigned security roles in CRM.

Best Practices for MS CRM Online API Calls

  1. Optimize Your Queries:

    • Only retrieve the data you need by using $select to specify columns and $filter to narrow down records. For example:
      GET https://<your-org>.crm.dynamics.com/api/data/v9.2/accounts?$select=name,revenue
  2. Use Pagination for Large Datasets:

    • MS CRM Online supports server-side paging using the @odata.nextLink property in responses. This is crucial when dealing with more than 5,000 records.
  3. Batch API Requests:

    • Instead of making multiple single calls, use batch operations to group several requests into one. This reduces the number of API calls and improves efficiency.
  4. Implement Retry Logic:

    • Throttling is inevitable if you’re working at scale. Implement retry logic with exponential backoff when you encounter HTTP 429 errors.
  5. Monitor API Usage:

    • Use the Power Platform Admin Center to monitor API call consumption and identify heavy users or apps. This helps you stay ahead of limits.
  6. Use Change Tracking for Incremental Data Sync:

    • Instead of polling all records repeatedly, use Change Tracking in CRM to fetch only the records that have changed since the last sync.
  7. Secure Your API Calls:

    • Use HTTPS for all communications. Ensure Azure AD tokens are stored securely, and rotate client secrets regularly.

How to Monitor API Usage in MS CRM Online

  1. Power Platform Admin Center:
    Go to Admin Center > Analytics > Common Data Service (Dataverse) to see the API usage summary for your environment. You’ll get a breakdown of calls by user, application, and type.

  2. Enable Logging:
    Turn on the Plug-in Trace Log to track failed calls or performance bottlenecks.

  3. Use Audit Logs:
    Dynamics 365 allows you to track API interactions via the audit log. It’s particularly useful for troubleshooting data updates or failed requests.

  4. Alerts for API Limits:
    Set up alerts to notify you when usage approaches the daily API limit. This can prevent unexpected disruptions.

What to Do If You Exceed the Limit

  1. Analyze Your Calls:
    Use the Power Platform Admin Center to identify which users or applications are consuming the most API calls. Common culprits include poorly optimized workflows or data sync jobs.

  2. Reduce API Calls:

    • Replace frequent polling with Change Tracking or webhooks.
    • Consolidate multiple small API calls into fewer, larger calls using batch operations.
  3. Purchase Additional Capacity:
    If you’re regularly exceeding limits, you can buy extra capacity from Microsoft as an add-on.

  4. Temporarily Slow Down Integrations:
    Adjust schedules for non-critical jobs or integrations to avoid peak times.


How to Monitor API Usage from the Microsoft Power Platform Admin Center

One of the best ways to keep an eye on your API usage in Dynamics 365 CRM Online is by leveraging the Microsoft Power Platform Admin Center. This portal provides real-time analytics and insights into API consumption for your environment. Here's how you can monitor your usage step by step:

Steps to Monitor API Usage in the Admin Center

  1. Log in to the Power Platform Admin Center

    • Go to Power Platform Admin Center.
    • Use your admin credentials to log in. You’ll need global admin or system admin permissions to access detailed metrics.
  2. Navigate to the Analytics Section

    • On the left-hand navigation pane, click on Analytics and select Dataverse (formerly Common Data Service).
    • Choose the Environment you want to monitor if you’re managing multiple environments (e.g., production, sandbox).
  3. View API Usage Overview

    • In the Dataverse Analytics dashboard, go to the Capacity or API Usage tab.
    • This section shows:
      • Daily API Usage: A graph or table displaying how many API calls have been consumed by the environment.
      • Users and Applications: Breakdowns of which users or applications are generating the most API traffic.
      • Remaining API Capacity: See how close you are to hitting your daily API limits.
  4. Filter by Time Period or User/Application

    • Use filters to narrow down API usage by:
      • Specific Users
      • Applications (e.g., integrations like KingswaySoft, Power Automate flows)
      • A specific time period (e.g., daily, weekly).
  5. Identify Problem Areas

    • Look for patterns in API consumption. For example:
      • A user making excessive API calls due to misconfigured workflows.
      • An application like a middleware tool consuming more calls than expected.
    • Drill down into specific calls to identify whether they are read-heavy or write-heavy.
  6. Set Up Alerts (Optional)

    • Currently, you cannot directly configure alerts from the admin center, but you can monitor daily API thresholds using Power BI dashboards or scripts. Microsoft may notify you by email if your usage consistently exceeds capacity.
  7. Access Logs for Detailed Troubleshooting

    • If you’re troubleshooting failures, you can enable and access:
      • Plug-in Trace Logs: Tracks issues with custom plugins or workflows that might be contributing to excessive API usage.
      • Audit Logs: Enables detailed tracking of API actions, including which records were updated or accessed.

Key Insights You Can Gather from the Admin Portal

  • Who or What Is Consuming the Most API Calls?
    The analytics dashboard shows a breakdown of API usage by user and application. This helps you pinpoint whether the problem lies with a specific integration (like KingswaySoft) or a user's actions (e.g., running an overly complex advanced find).

  • Are You Approaching Your Daily API Limits?
    The dashboard provides a visual representation of your daily API consumption and how much headroom you have left before hitting the limit.

  • Which API Calls Are Failing?
    If certain API calls are failing, you can cross-reference your logs to determine the cause. Look out for throttling errors (HTTP 429) or misconfigured apps making unnecessary requests.

Tips for Effective Monitoring

  1. Regularly Check the Dashboard
    Make it a habit to check API usage in the admin center weekly or after deploying a new integration to catch issues early.

  2. Set Thresholds for Heavy API Consumers
    If you find that specific apps (e.g., Power Automate flows) or users consistently hit the limits, optimize their behavior by reducing frequency or batching operations.

  3. Plan Capacity for Busy Periods
    If you know your environment will experience high API demand (e.g., a data migration or bulk import), consider purchasing additional capacity in advance.


Saturday, December 28, 2024

MS CRM Plugin Development: Real Talk from a Senior Dev

Hey there, fellow CRM developers! 

What The Heck Is A Plugin (And Why Should You Care)?

Think of plugins as your secret weapon in the CRM world. They're essentially .NET libraries that let you inject your own code into CRM's event pipeline. While Microsoft's marketing team might call them "extensibility points," I like to think of them as your chance to say "Hey CRM, let me handle this my way!"

The Real-World Use Cases 💡

Let me share some actual scenarios where plugins saved my bacon:

  • A client needed to validate complex pricing rules before saving opportunities (something Power Automate would choke on)
  • We had to sync data with an ancient ERP system that only spoke SOAP
  • Had to implement real-time fraud detection on lead creation
  • Needed to enforce business rules that would make a flowchart cry

Plugin Architecture: The Stuff That Matters 🏗️

Here's what you actually need to know:

public class YourAwesomePlugin : IPlugin { public void Execute(IServiceProvider serviceProvider) { // This is where the magic happens var context = (IPluginExecutionContext)serviceProvider .GetService(typeof(IPluginExecutionContext)); // Pro tip: Always log what you're doing var tracer = (ITracingService)serviceProvider .GetService(typeof(ITracingService)); tracer.Trace("Starting the awesome stuff..."); // Your business logic goes here } }

When & Where to Use Plugins 🎯

  • Pre-validation: When you need to stop bad data before it ruins your day
  • Pre-operation: For modifying data before it hits the database
  • Post-operation: When you need to trigger additional actions after the main operation
  • Async: For anything that might take longer than a coffee break

Quick Tip: Event Pipeline Explained Without the Jargon

Think of it like a security checkpoint:

  1. Pre-validation: The metal detector
  2. Pre-operation: The passport check
  3. Post-operation: The duty-free shopping
  4. Async: The stuff that happens after you're already on the plane

🎯 Why Should You Care About Plugins?

Let's be real - while Power Automate is great for simple stuff, plugins are where the real magic happens. They're like your Swiss Army knife for all things CRM customization. I've used them for everything from complex transaction validations to integrating with legacy systems that make COBOL look modern.

🔍 Plugin Basics (The Stuff Everyone Pretends They Already Know)

First, let's get our heads straight about what a plugin actually is - it's a .NET class library that hooks into CRM's event pipeline. Think of it as your bouncer at the club, deciding what data gets in and what happens to it.

Here's something they don't tell you in the docs: choosing between synchronous and asynchronous execution isn't just about speed - it's about not getting angry calls from users when their screen freezes. Trust me, I learned this the hard way.

💡 Real-World Example: The Lead Assignment Nightmare

Let me share a war story. We had a client who needed leads automatically assigned based on complex criteria (region, product interest, lead score, and current sales rep workload). Here's the battle-tested solution:


public class SmartLeadAssignment : IPlugin { public void Execute(IServiceProvider serviceProvider) { // Get the context and service objects var context = (IPluginExecutionContext)serviceProvider .GetService(typeof(IPluginExecutionContext)); var service = ((IOrganizationServiceFactory)serviceProvider .GetService(typeof(IOrganizationServiceFactory))) .CreateOrganizationService(context.UserId); var tracingService = (ITracingService)serviceProvider .GetService(typeof(ITracingService)); try { if (context.MessageName != "Create" || context.PrimaryEntityName != "lead") return; var lead = (Entity)context.InputParameters["Target"]; // Pro tip: Always check if the field exists! if (!lead.Contains("new_region") || !lead.Contains("new_productinterest")) { tracingService.Trace("Required fields missing"); return; } var assignee = GetOptimalSalesRep(service, lead); if (assignee != Guid.Empty) { lead["ownerid"] = new EntityReference("systemuser", assignee); } } catch (Exception ex) { // Always log the full exception - future you will thank present you tracingService.Trace($"Error: {ex.ToString()}"); throw new InvalidPluginExecutionException( "Error assigning lead. Check trace logs for details.", ex); } } }

🎓 Pro Tips That Saved My Bacon

  1. Always Use Early Returns

    if (!context.InputParameters.Contains("Target")) return;
    This prevents your plugin from running unnecessarily and saves precious CPU cycles.
  2. Transaction Context Is Your Friend

    if (!context.IsInTransaction) throw new InvalidPluginExecutionException();
    This has saved me from duplicate processing more times than I can count.
  3. Trace Everything (But Smart)

    tracingService.Trace($"Processing {lead.Id} for region {region}");
    Future you will buy present you a beer for this.

🚀 Deployment Like a Pro

Forget manually uploading DLLs (it's 2024, folks!). Here's my Azure DevOps pipeline that's saved hours of my life:

🔍 Debugging Like a Detective

When things go wrong (and they will), here's your survival kit:

  1. Plugin Profiler is your best friend
  2. Set up local debugging with the Plugin Registration Tool
  3. Use conditional breakpoints for specific scenarios

⚠️ Common Pitfalls (Learn From My Pain)

  • Never, ever make HTTP calls in sync plugins
  • Always check for null before accessing entity attributes
  • Don't trust the cache - always verify your data
  • Remember: plugins have a 2-minute timeout (yes, even async ones)

🎯 Advanced Techniques

For the real ninjas out there:

  • Use IOrganizationService sparingly (it's expensive)
  • Implement caching for frequently accessed data
  • Consider using Early-Bound entities for better performance
  • Use QueryExpression instead of FetchXML for complex queries

Want to see how I handle multi-threading in async plugins? Drop a comment below, and I'll share my battle-tested patterns!

🐛 Live Debugging Like a Pro

Here's my tried-and-true debugging workflow:

  1. Remote Debugging Setup

// Add this to your plugin constructor if (!Debugger.IsAttached && Debugger.Launch()) { DebuggerBreak(); }
  1. Structured Logging Pattern

public void Execute(IServiceProvider serviceProvider) { var tracer = (ITracingService)serviceProvider.GetService(typeof(ITracingService)); var context = new PluginTraceContext(tracer); context.Log($"Starting plugin execution for {context.MessageName}"); try { // Your logic here context.LogObject("Input parameters", context.InputParameters); } catch (Exception ex) { context.LogException(ex); throw; } }
🚨 Real Production Issues I've Faced (And How to Fix Them)
  1. The Infinite Loop Nightmare

    // Check if the plugin triggered itself if (context.Depth > 1) return;
  2. Memory Leaks in Async Plugins

    using var serviceFactory = (IOrganizationServiceFactory)serviceProvider .GetService(typeof(IOrganizationServiceFactory)); using var service = serviceFactory.CreateOrganizationService(null);
  3. Deadlocks in Transaction Processing

    // Always use optimistic concurrency entity.RowVersion = currentRowVersion;
📊 Production Monitoring That Actually Works

// Custom telemetry wrapper public class PluginTelemetry : IDisposable { private readonly Stopwatch _timer; private readonly ITracingService _tracer; public PluginTelemetry(ITracingService tracer) { _tracer = tracer; _timer = Stopwatch.StartNew(); } public void Dispose() { _timer.Stop(); _tracer.Trace($"Execution time: {_timer.ElapsedMilliseconds}ms"); } }
🌐 Extending with Web APIs (The Smart Way)

When plugins just won't cut it:


[RoutePrefix("api/v1/crm")] public class CrmExtensionController : ApiController { [HttpPost] [Route("bulkupdate")] public async Task<IHttpActionResult> BulkUpdateRecords([FromBody] BulkUpdateRequest request) { using var client = new CrmServiceClient(ConfigurationManager .ConnectionStrings["CRM"].ConnectionString); // Process in batches of 1000 foreach (var batch in request.Records.Chunk(1000)) { await ProcessBatchAsync(client, batch); } return Ok(); } }
💡 The Async Plugin Survival Guide

When to use async:

  • External API calls
  • Batch processing
  • Email notifications
  • File operations

Pro tip: Always implement retry logic:


private async Task ExecuteWithRetryAsync(Func<Task> operation) { var policy = Policy .Handle<Exception>() .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); await policy.ExecuteAsync(operation); }


Production-Ready Base Plugin Class

Here's my battle-tested base class that's saved countless hours:


public abstract class PluginBase : IPlugin { private readonly string _unsecureConfig; private readonly string _secureConfig; protected PluginBase(string unsecureConfig = null, string secureConfig = null) { _unsecureConfig = unsecureConfig; _secureConfig = secureConfig; } public void Execute(IServiceProvider serviceProvider) { var context = (IPluginExecutionContext)serviceProvider .GetService(typeof(IPluginExecutionContext)); var factory = (IOrganizationServiceFactory)serviceProvider .GetService(typeof(IOrganizationServiceFactory)); var tracer = (ITracingService)serviceProvider .GetService(typeof(ITracingService)); var service = factory.CreateOrganizationService(context.UserId); try { // Performance tracking using var performance = new PerformanceTracker(tracer); ExecutePluginLogic(new PluginContext { Context = context, Service = service, TracingService = tracer, UnsecureConfig = _unsecureConfig, SecureConfig = _secureConfig }); } catch (Exception ex) { tracer.Trace($"❌ Error: {ex}"); throw new InvalidPluginExecutionException( $"Plugin failed: {GetType().Name}", ex); } } protected abstract void ExecutePluginLogic(PluginContext context); } public class PluginContext { public IPluginExecutionContext Context { get; set; } public IOrganizationService Service { get; set; } public ITracingService TracingService { get; set; } public string UnsecureConfig { get; set; } public string SecureConfig { get; set; } }

🔐 Securing Your Plugin Configuration

Never hardcode sensitive stuff! Here's how to handle configs properly:


public class LeadScoringPlugin : PluginBase { public LeadScoringPlugin(string unsecureConfig, string secureConfig) : base(unsecureConfig, secureConfig) { } protected override void ExecutePluginLogic(PluginContext context) { var config = JsonConvert.DeserializeObject<ScoringConfig>( context.SecureConfig); context.TracingService.Trace($"Using API key: ***{ config.ApiKey.Substring(config.ApiKey.Length - 4)}"); } }

🚀 Spkl Plugin Registration - The Right Way

  1. First, your spkl.json:

{ "plugins": [ { "solution": "YourSolution", "assemblypath": "bin\\Debug\\YourPlugin.dll", "classRegex": ".*Plugin$", "excludePluginSteps": true } ] }
  1. deployment-config.json for different environments:

{ "dev": { "server": "https://dev.crm.dynamics.com", "solution": "YourSolution_1_0_0_1", "secureConfiguration": { "LeadScoringPlugin": { "ApiKey": "dev_api_key_here" } } }, "prod": { // Production config } }

🎯 Pro Tips for Spkl

  1. Version Control:

# Never commit secrets! deployment-config.json # Keep template deployment-config.template.json
  1. Automated Registration:

# Register plugins spkl plugins [path] # Update specific plugin spkl plugin [path] -name YourPlugin
  1. Debug Settings:

<!-- .spkl.json --> <setting name="debug" value="true" />

🔍 Real-World Example: Putting It All Together


public class CustomerValidationPlugin : PluginBase { private readonly ValidationSettings _settings; public CustomerValidationPlugin(string unsecureConfig, string secureConfig) : base(unsecureConfig, secureConfig) { _settings = JsonConvert.DeserializeObject<ValidationSettings>( unsecureConfig); } protected override void ExecutePluginLogic(PluginContext pluginContext) { var entity = (Entity)pluginContext.Context.InputParameters["Target"]; // Use config settings if (_settings.EnableStrictValidation) { // Validation logic here } pluginContext.TracingService.Trace( $"Validation completed with rules: {_settings.RuleSet}"); } }


🔑 Critical Things Every D365 Plugin Dev Should Know

  1. Thread Safety & Context

// Always use thread-safe collections private static readonly ConcurrentDictionary<string, object> Cache = new ConcurrentDictionary<string, object>(); // Check execution context if (context.Depth > 1) return; // Prevent recursive loops
  1. Performance Optimization

// Batch your requests var multipleRequest = new ExecuteMultipleRequest { Settings = new ExecuteMultipleSettings { ContinueOnError = false, ReturnResponses = true }, Requests = requests.ToArray() };
  1. Error Handling Best Practices

try { // Your logic } catch (FaultException<OrganizationServiceFault> ex) { // Handle CRM-specific errors tracer.Trace($"Error Code: {ex.Detail.ErrorCode}"); } catch (TimeoutException ex) { // Handle timeouts gracefully throw new InvalidPluginExecutionException( "Operation timed out. Please try again.", ex); }

Common Gotchas to Avoid

  1. Plugin Isolation
    • Don't share static state between plugin instances
    • Avoid file system operations
    • Never store sensitive data in static variables
  2. Transaction Management

// Check if you're in a transaction if (!context.IsInTransaction) { throw new InvalidPluginExecutionException( "This operation must be part of a transaction."); }
  1. Cache Usage

// Implement proper caching private static readonly MemoryCache Cache = new MemoryCache( new MemoryCacheOptions { SizeLimit = 1024 }); // Use cache with expiration Cache.Set(key, value, TimeSpan.FromMinutes(5));

Advanced Techniques

  1. Custom Actions Integration

public class CustomActionPlugin : PluginBase { protected override void ExecutePluginLogic(PluginContext context) { if (context.Context.MessageName != "my_customaction") return; var parameters = context.Context.InputParameters; // Process custom action } }
  1. Bulk Operation Handling

// Handle bulk operations efficiently private async Task ProcessBulkAsync( IOrganizationService service, List<Entity> entities) { var tasks = entities .Select(entity => Task.Run(() => ProcessSingle(service, entity))); await Task.WhenAll(tasks); }

Performance Metrics to Monitor

  1. Plugin execution time
  2. Database calls per operation
  3. Memory usage patterns
  4. Exception rates

public class PerformanceMetrics { private readonly Stopwatch _timer = new Stopwatch(); private int _dbCalls = 0; public void TrackDatabaseCall() { Interlocked.Increment(ref _dbCalls); } }

Final Words of Wisdom

  • Always test with large datasets
  • Plan for failure scenarios
  • Keep security at the forefront
  • Document your assumptions
  • Use code reviews religiously

🔚 Wrapping Up

Remember: a good plugin is like a good referee - it does its job without anyone noticing. Keep it simple, keep it fast, and always, always test in a sandbox first.


#MSDynamics #CRMDevelopment #Plugins #DotNet #RealTalk

Sri Lanka .NET 
                Forum Member