Archive

Archive for the ‘CRM’ Category

Power Apps | Microsoft Dataverse

November 17, 2020 2 comments

Microsoft Dataverse

Common Data Service (CDS), the sophisticated and secure backbone that powers Dynamics 365 and Power Platform, has been renamed to Microsoft Dataverse

Some terminology in Microsoft Dataverse has been updated. For example, entity is now ‘Table’ and field is now ‘Column’. Learn more.

Microsoft Dataverse for Teams

Microsoft Dataverse for Teams (formerly known as Project Oakdale), a low code built-in data platform for Teams, is generally available now.

Microsoft Dataverse for Teams follows existing data governance rules established by the Power Platform and enables access control in the Teams Admin Center like any other Teams feature. Within the Teams Admin center, you can allow or block apps created by users at the individual level, group level, or org level.

Refer my article on Project Oakdale to know more.

Power BI Teams App

Licensed Microsoft Power BI users can enjoy the full capabilities of Power BI in Teams with the Power BI Teams App.

Power Automate App for Teams

Power Automate App for Teams lets you automate your Microsoft Teams activities or and connect Microsoft Teams to other apps and services.

Click here to know more on Dataverse.

🙂

Categories: CRM

Canvas Apps | Useful functions

November 15, 2020 Leave a comment

In this article, I am collating the useful formulas which are frequently used in Canvas Apps.

For all functions, I am going to use a Textbox control ‘txtInput’ as reference.

Match

Validates User inputs based on predefined or custom patterns. For example, you can confirm whether the user has entered a valid email address, SSN, etc…

Validate Email:
  • IsMatch(txtInput.Text,Email)
Validate SSN:
  • IsMatch( txtInput.Text, Digit & Digit & Digit & Hyphen & Digit & Digit & Hyphen & Digit & Digit & Digit & Digit )
Validate SSN using Regular Expression (RegEx)
  • IsMatch( txtInput.Text, “\d{3}-\d{2}-\d{4}” )
Check presence of string
  • IsMatch( txtInput.Text, “hello”, Contains & IgnoreCase)
    • Check if ‘hello’ exists in the Text input.

User

Get Current User Email
  • User().Email
Get Current User Fullname
  • User().FullName
Get Profile Image
  • User().Image
    • Add an Image control and set ‘Image’ property with User().Image.

Date functions

Today()
  • Returns the current date as a date/time value.
  • The time portion is always midnight (i.e., 12:00:00). 
Now()
  • Returns the current date and time as a date/time value
IsToday()
  • Checks whether a date/time value is between midnight today and midnight tomorrow. Returns a Boolean (true or false) value.
Weekday()
  • Weekday(Date)
    • Returns between 1-7 and Sunday is ‘1’.
DateDiff()
  • DateDiff(Date1.SelectedDate, Date2.SelectedDate, Days)
    • ‘Date1’ and ‘Date2’ are ‘Date Picker’ controls.

Miscellaneous

  • IsBlank(txtInput)
    • Checks for a blank value or an empty string.
  • Coalesce
    • Evaluates its arguments in order and returns the first value that isn’t blank or an empty string.
    • Use this function to replace a blank value or empty string with a different value but leave non-blank and non-empty string values unchanged
  • GUID
    • GUID() – Returns a new Guid.
      • Set( NewGUID, GUID() )
    • To generate 5 new GUIDs and set to Collection
      • ClearCollect( NewGUIDs, ForAll( Sequence(5), GUID() ) )

🙂

Categories: CRM

[Code Snippet] Set Business process flow (BPF) stage using C#

October 15, 2020 Leave a comment

Assume you have a BPF with 3 Stages on an Entity ‘Employer’. When you create a new ‘Employer’ record, by default ‘Stage-1’ gets set.

What if you have to create the ‘Employer’ record with a different stage.

Lets see how to create a record and set the desired BPF stage with an example entity ‘Employer’.

I’ve an ‘Employer’ entity and a BPF name ‘Employer flow’ with 3 stages named ‘Basic Details’, ‘Address’ and ‘Experience’.

Key point to notice is, when ever you create a BPF a new entity gets created with the given BPF name.

‘Employer flow’ is BPF Entity

Following are the steps to create a ‘Employer’ record and set the BPF stage to ‘Experience’.

  • Query ‘Workflow’ entity to get the ‘BPF ID’ by passing the BPF entity schema name (i.e., crf10_employerflow).
var queryEmployerBPF = new QueryExpression
                {
                    EntityName = "workflow",
                    ColumnSet = new ColumnSet(true),
                    Criteria = new FilterExpression
                    {
                        Conditions =
                        {
                            new ConditionExpression
                            {
                                AttributeName = "uniquename",
                                Operator = ConditionOperator.Equal,
                                Values = { "crf10_employerflow" }
                            }
                        }
                    }
                };
                var retrievedBPF = ConnectionManager.CrmService.RetrieveMultiple(queryEmployerBPF).Entities[0];
                var _bpfId = retrievedBPF.Id;
  • Query Process Stage by passing ‘BPF ID’ fetched in previous step.
var queryPS = new QueryExpression{
EntityName = "processstage",
ColumnSet = new ColumnSet(true),
Criteria = new FilterExpression{
Conditions ={
new ConditionExpression{
AttributeName = "processid",
Operator = ConditionOperator.Equal,
Values={ _bpfId }
}
}
}
};
  • Copy the ‘Stage’ GUID’s which will be used in next steps.
  • Create the ‘Employer’ record, which also creates record in ‘BPF entity’ (i.e., Employer Flow).
            Entity entEmployer = new Entity("crf10_employer");
            entEmployer["crf10_name"] = "BPF Test";
            //entEmployer["processid"] = Guid.Empty;
            var violationID = ConnectionManager.CrmService.Create(entEmployer);
  • Fetch the ‘BPF entity’ (i.e., Employer Flow) record, which auto created in previous step, using ‘RetrieveProcessInstancesRequest’ request.
                var procOpp2Req = new RetrieveProcessInstancesRequest
                {
                    EntityId = violationID,
                    EntityLogicalName = "crf10_employer"
                };

                var procOpp2Resp = (RetrieveProcessInstancesResponse)ConnectionManager.CrmService.Execute(procOpp2Req);
  • Update ‘activestageid’ field of the ‘BPF entity’ (i.e., Employer Flow) record fetched in previous step, with the desired stage GUID captured in Step #2.
// Declare variables to store values returned in response
                int processCount = procOpp2Resp.Processes.Entities.Count;
                var activeProcessInstance = procOpp2Resp.Processes.Entities[0]; // First record is the active process instance
                var _processOpp2Id = activeProcessInstance.Id; // Id of the active process instance, which will be used

                // Retrieve the process instance record to update its active stage
                ColumnSet cols1 = new ColumnSet();
                cols1.AddColumn("activestageid");
                Entity retrievedProcessInstance = ConnectionManager.CrmService.Retrieve("crf10_employerflow", _processOpp2Id, cols1);

                // Update the stage to 'Experience' by passing GUID (i.e.,"05aeaf03-e135-40ac-8ae7-cafc7d746a02") 
                retrievedProcessInstance["activestageid"] = new EntityReference("processstage", new Guid("05aeaf03-e135-40ac-8ae7-cafc7d746a02"));
                ConnectionManager.CrmService.Update(retrievedProcessInstance);
  • Open the record from the App and the stage should set to ‘Experience’.
‘BPF Test’ record’s stage set to ‘Experience’
  • Check the BPF records and you should see ‘Active Stage’ got set to ‘Experience’ (This is optional step and for your learning).
  • Below is the complete snippet.
                var queryEmployerBPF = new QueryExpression
                {
                    EntityName = "workflow",
                    ColumnSet = new ColumnSet(true),
                    Criteria = new FilterExpression
                    {
                        Conditions =
                        {
                            new ConditionExpression
                            {
                                AttributeName = "uniquename",
                                Operator = ConditionOperator.Equal,
                                Values = { "crf10_employerflow" }
                            }
                        }
                    }
                };
                var retrievedBPF = ConnectionManager.CrmService.RetrieveMultiple(queryEmployerBPF).Entities[0];
                var _bpfId = retrievedBPF.Id;

                var queryPS = new QueryExpression
                {
                    EntityName = "processstage",
                    ColumnSet = new ColumnSet(true),
                    Criteria = new FilterExpression
                    {
                        Conditions =
                        {
                            new ConditionExpression
                            {
                                AttributeName = "processid",
                                Operator = ConditionOperator.Equal,
                                Values={ _bpfId }
                            }
                        }
                    }
                };
                var retrievedPS = ConnectionManager.CrmService.RetrieveMultiple(queryPS);
// Copy the Stage GUID's using below loop.
                foreach (var stage in retrievedPS.Entities)
                {
                    Console.WriteLine($"Stage Name : {stage["stagename"]}");
                    Console.WriteLine($"Stage ID : {stage["processstageid"]}");
                }
//Create 'Employer' record
                var entEmployer = new Entity("crf10_employer");
                entEmployer["crf10_name"] = "BPF Test";
                //entEmployer["processid"] = Guid.Empty;
                var violationID = ConnectionManager.CrmService.Create(entEmployer);

                var procOpp2Req = new RetrieveProcessInstancesRequest
                {
                    EntityId = violationID,
                    EntityLogicalName = "crf10_employer"
                };

                var procOpp2Resp = (RetrieveProcessInstancesResponse)ConnectionManager.CrmService.Execute(procOpp2Req);

                // Declare variables to store values returned in response
                int processCount = procOpp2Resp.Processes.Entities.Count;
                var activeProcessInstance = procOpp2Resp.Processes.Entities[0]; // First record is the active process instance
                var _processOpp2Id = activeProcessInstance.Id; // Id of the active process instance, which will be used

                // Retrieve the process instance record to update its active stage
                var cols1 = new ColumnSet();
                cols1.AddColumn("activestageid");
                var retrievedProcessInstance = ConnectionManager.CrmService.Retrieve("crf10_employerflow", _processOpp2Id, cols1);

                // Update the stage to 'Experience' by passing GUID (i.e.,"05aeaf03-e135-40ac-8ae7-cafc7d746a02") 
                retrievedProcessInstance["activestageid"] = new EntityReference("processstage", new Guid("05aeaf03-e135-40ac-8ae7-cafc7d746a02"));
                ConnectionManager.CrmService.Update(retrievedProcessInstance);

🙂

Categories: CRM Tags: , , ,

Power Platform Admin Analytics

September 27, 2020 Leave a comment

In previous Dynamics versions, with the help of Organization Insights, available as a preferred solution from AppSource, we can get the organization level insights such as No of Active Users, API Calls, etc.

Now  these analytics can be viewed right from the Power Platform Admin Center with no need of additional solutions.

Currently there are 3 types of Analytics are available.

Who can view the Analytics reports:

Admins with the following roles and a license can view the analytics:

  • Environment Admin – can view reports for the environments that the admin has access to.
  • Power Platform admin – can view reports for all environments.
  • Dynamics 365 admin – can view reports for all environments.
  • Microsoft 365 Global admin – can view reports for all environments.

Refer this article for more details

🙂

Categories: CRM, PowerApps Tags: ,

[Code Snippet] Using Microsoft.Pfe.Xrm library

September 20, 2020 Leave a comment

Pfe.Xrm library allows us to submit a bunch of XRM requests as batches (using ExecuteMultipleRequest) and execute them in parallel.

In this article, lets see how to use Microsoft.Pfe.Xrm library with a simple ‘Retrieve Request’.

What is Microsoft.Pfe.Xrm library?

  • Contains a set of common components for building solutions using the Dynamics 365 SDK and is developed by Microsoft Premier Field Engineering (PFE) and core engineering teams.

Download Microsoft.Pfe.Xrm

  • Use ‘Tools -> Nuget package manager’ from your Visual Studio to download the Nuget package.

Building blocks of Microsoft.Pfe.Xrm library

‘Microsoft.Pfe.Library’ allows us to trigger any ‘OrganizationRequest’ in batches (i.e.,ExecuteMultipleRequest) parallelly. Lets understand the building blocks of Pfe library.

  • List<OrganizationRequest>
    • As a first step prepare your ‘RetrieveMultipleRequest’ objects and add them to a List.
  • AsBatches() method
    • Call Pfe libraries AsBatches() method by passing the List prepared in previous step.
    • ‘AsBatches()’ returns IDictionary<string, ExecuteMultipleRequest> object.
  • OrganizationServiceManager
    • Trigger ‘Microsoft.Pfe.Xrm’ OrganizationServiceManager() method to create connection object.
    • There are multiple ‘OrganizationServiceManager’ overload methods.
    • In this article, I am using the following method.
  • ParallelProxy.Execute
    • This is the final method which accepts the ‘IDictionary<string, ExecuteMultipleRequest>’ object returned by AsBatches() method which we discussed in previous sections.
    • ParallelProxy.Execute also requires OrganizationServiceManager which we created in previous step.

IDictionary<string, ExecuteMultipleResponse> batchResponses = pfeXrmConnection.ParallelProxy.Execute(batchesOrgRequest);

Code Snippet

Now lets put all the building blocks together. It becomes as the following code snippet.

    public void RetrieveUsingPFE()
    {
        // Retrieve 'Account' and 'Contact'. You can prepare your list of entities.
        var listEntities=new List<>{ "account", "contact" };
        var batchRequests = new List<OrganizationRequest>();            

        int pageNumber = 1;
        string pagingCookie = null;

        // Filter for Active records. You can prepare your required filters as below.
        var filterActive = new FilterExpression(LogicalOperator.And);
        var condActive = new ConditionExpression("statecode", ConditionOperator.Equal, 0);
        filterActive.AddCondition(condActive);

        try
        {
            foreach (var entity in listEntities)
            {
                // Prepare 'Query Expression'
                var query = new QueryExpression(entity)
                {
                    ColumnSet = new ColumnSet(true),
                    PageInfo = new PagingInfo()
                };
                query.PageInfo.Count = 5000;
                query.PageInfo.PageNumber = pageNumber;
                query.PageInfo.PagingCookie = pagingCookie;

                query.Criteria.AddFilter(filterActive);

                var request = new RetrieveMultipleRequest
                {
                    Query = query
                };

                batchRequests.Add(request);
            }

            do
            {
                // Trigger Pfe.Execute
                var batchResponses = TriggerRequests(batchRequests);
                batchRequests = new List<OrganizationRequest>();
                pageNumber++;
                foreach (var responseItem in batchResponses)
                {
                    if (responseItem.EntityCollection.Entities.Count > 0)
                    {                            
                        var currEntityName = responseItem.EntityCollection.Entities[0].LogicalName;

                        foreach (var record in responseItem.EntityCollection.Entities)
                        {
                            // 'record' is the Retreieved record
                            // Perform your desired operations
                        }
                    }

                    if (responseItem.EntityCollection.MoreRecords)
                    {
                        var query = new QueryExpression(responseItem.EntityCollection.EntityName)
                        {
                            ColumnSet = new ColumnSet(true)
                        };

                        query.Criteria.AddFilter(filterActive);

                        var request = new RetrieveMultipleRequest();
                        query.PageInfo = new PagingInfo
                        {
                            Count = 5000,
                            PageNumber = pageNumber,
                            PagingCookie = responseItem.EntityCollection.PagingCookie
                        };

                        request.Query = query;
                        batchRequests.Add(request);
                    }
                }
            } while (batchRequests.Count > 0);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error occurred, error message is {0}", ex.Message);
            throw ex;
        }            
    }

    public List<RetrieveMultipleResponse> TriggerRequests(List<OrganizationRequest> batchRequests)
    {
        var retrieveRequestBatches = batchRequests.AsBatches(recordsPerRequest);
        var batchResponses = ExecuteParallelProxy(batchRequests);            
        var listResponses = new List<RetrieveMultipleResponse>();

        foreach (var key in batchResponses.Keys)
        {
            foreach (var result in batchResponses[key].Responses)
            {
                if (retrieveRequestBatches[key].Requests[result.RequestIndex] is RetrieveMultipleRequest)
                {
                    var originalRequest = (RetrieveMultipleRequest)retrieveRequestBatches[key].Requests[result.RequestIndex];
                    // Capture failed records
                    if (result.Fault != null)
                    {
                        Console.WriteLine($" Exception : {result.Fault.Message}");
                    }
                    else if (result.Response != null && result.Response is RetrieveMultipleResponse) // Capture success records
                    {
                        var responseItem = (RetrieveMultipleResponse)result.Response;
                        listResponses.Add(responseItem);
                    }
                }                    
            }
        }

        return listResponses;
    }

    private IDictionary<string, ExecuteMultipleResponse> ExecuteParallelProxy(IDictionary<string, ExecuteMultipleRequest> retrieveRequestBatches)
    {            
        IDictionary<string, ExecuteMultipleResponse> batchResponses = null;
        try
        {
            batchResponses = CRMPfeConnection.ParallelProxy.Execute<ExecuteMultipleRequest, ExecuteMultipleResponse>(retrieveRequestBatches);                
        }
        catch (AggregateException ae)
        {
            foreach (var ex in ae.InnerExceptions)
            {
                Console.WriteLine($"Aggregate Error in ExecuteParallelProxy : {ex.Message.ToString()}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error in ExecuteParallelProxy : {ex.Message.ToString()}");
        }

        return batchResponses;
    }

    public static OrganizationServiceManager CRMPfeConnection
    {
        get
        {
            if (crmPfeConnection == null)
            {
                var UserName = ConfigurationManager.AppSettings["UserNameSrc"].ToString();
                var Password = ConfigurationManager.AppSettings["PasswordSrc"].ToString();
                var OrganizationUri = ConfigurationManager.AppSettings["OrganizationUriSrc"].ToString();
                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
          crmPfeConnection = new OrganizationServiceManager(XrmServiceUriFactory.CreateOrganizationServiceUri(OrganizationUri), UserName, Password);
            }
            return crmPfeConnection;
        }
    }
  • Similar to ‘RetrieveRequest’, you can trigger all kinds of requests.

🙂

Power Apps | CDS | Track deleted records using ‘Change Tracking’

For unversed, Change Tracking in CDS allows data to be synchronized by detecting what data has changed since the data was initially extracted or last synchronized.

Its useful in Integration scenarios, where you have to keep data between CDS and external systems in sync.

Assume that your customer has a LOB application along with CDS and you want sync the LOB application (i.e., When record gets Created/Updated/Deleted in CDS), this can be achieved using ‘Change Tracking’ feature.

In this article, lets understand how ‘Change Tracking’ works in CDS using ‘Organization Service’, to:

  • Get the Created or Updated records from last sync.
  • Get Deleted records from last sync.

Enabling Change Tracking for an Entity:

  • To use ‘Change Tracking’ make sure that the change tracking feature is enabled for that entity.
  • This feature can be enabled by using the customization user interface.
Enable Change Tracking

‘Change Tracking’ using the Organization Service:

  • When change tracking is enabled for an entity, RetrieveEntityChangesRequest message can be used to retrieve the changes for that entity.
  • In my example, I am using OOB ‘Account’ entity which has 10 records.
  • When ‘RetrieveEntityChangesRequest’ triggered for the first time, we would get all 10 records in ‘Changes’ object and a ‘Data Token’.
  • Copy the ‘Data Token’.
  • Now lets delete an Account and add a new Account in CDS.
  • Trigger ‘RetrieveEntityChangesRequest’ again by passing the copied ‘Data Token’.
    • Passing ‘Data Token’ in ‘RetrieveEntityChangesRequest’, fetches only data for those changes that occurred since that version will be returned.
  • This time we would get 2 records in ‘Changes’ object.
    • 1 NewOrUpdatedItem
    • 1 RemovedOrDeletedItem.
  • ‘NewOrUpdatedItem’ is object of type ‘Entity’ with all the properties. ‘RemovedOrDeletedItem’ is object of type ‘EntityReference’ contain only GUID of deleted record.
  • Following is the code snippet to track changes and display in console.
// Created or Updated records will be returned as 'Entity' object.
var createdorUpdateRecords = new List<Entity>();
// Deleted records will be returned as 'Entity Reference' object.
var deletedRecords = new List<EntityReference>();

// Retrieve records by using Change Tracking feature.
var request = new RetrieveEntityChangesRequest
{
    EntityName = "account",
    Columns = new ColumnSet("name"),
    PageInfo = new PagingInfo() { 
       Count = 5000, 
       PageNumber = 1, 
       ReturnTotalRecordCount = false 
    },
    // DataVersion is an optional attribute
    // If not passed system returns all the records. 
    // Dont pass this attribute when calling for the first time.
     DataVersion = "706904!07/24/2020 08:00:32"
};

while (true)
{
  var response = (RetrieveEntityChangesResponse)_serviceProxy.Execute(request);

  if (response != null && 
      response.EntityChanges != null &&   response.EntityChanges.Changes != null)
  {
    foreach (var record in response.EntityChanges.Changes)
    {
      // Read NewOrUpdatedItem in to 'createdorUpdateRecords' list. 
    if (record is NewOrUpdatedItem)
    {
     createdorUpdateRecords.Add((record as NewOrUpdatedItem).NewOrUpdatedEntity);
    }

    // Read RemovedOrDeletedItem in to 'deletedRecords' list.
    if (record is RemovedOrDeletedItem) 
   {        
     deletedRecords.Add((record as RemovedOrDeletedItem).RemovedItem);
       }
      }
   }
    // Display Created or Updated records
    createdorUpdateRecords.ForEach(x => Console.WriteLine("Created or Updated record id:{0}", x.Id));
    // Display Deleted records
    deletedRecords.ForEach(x => Console.WriteLine("Deleted record id:{0}", x.Id));

    // Logic for Pagination
    if (!response.EntityChanges.MoreRecords)
    {
      //Store token for later query
      token = response.EntityChanges.DataToken;
      break;
    }
   // Increment the page number to retrieve the next page.
   request.PageInfo.PageNumber++;
   // Set the paging cookie to the paging cookie returned from current results.
   request.PageInfo.PagingCookie = response.EntityChanges.PagingCookie;
  • Run the code and you would get response as below

🙂

Categories: CRM Tags: , ,

Azure function and Cosmos DB Table API – Method not found exception

While creating ‘Entities’ in Azure cosmos Table API from Azure function we encountered following exception.

Reason:

  • Azure Function application project was configured using ‘Azure Function v1 (.NET Framework)’.
  • “Microsoft.Azure.Cosmos.Table” nuget package is not compatible with ‘Azure Function v1 (.NET Framework)’.

Fix:

  • Change the Azure Function application project type to .NET Core.

Now lets understand the technical Know-how to configure ‘Azure Table API’ and interact from Azure Function application project.

Configure ‘Azure Table API’

  • Login to your Azure Portal.
  • Create a new ‘Azure Cosmos DB Account’ by selecting ‘API’ as ‘Azure Table’.
  • Once deployment is complete, open the ‘Resource’.
  • Lets create a new table by clicking on ‘New Table’ from ‘Data Explorer’. Provide the ‘Table id’ and click ‘OK’.
  • In ‘Azure Table API’ record/row is referred as ‘Entity’ and every ‘Entity’ must have ‘PartitionKey’ and ‘RowKey’ values.
    • You must include the PartitionKey and RowKey properties in every insert, update, and delete operation.
  • To connect to ‘Azure Table API’ from external applications (i.e., Azure Function,etc..) we need the ‘Connection String’.
  • Go to ‘Connection String’ tab and copy the ‘PRIMARY CONNECTION STRING’.

Create ‘Azure Function Application’ project

  • Open Visual Studio and create a new ‘Azure Functions’ project.
  • Select .NET version either v2 or v3 .NET Core.
  • Rename the ‘Function Name’ to a meaningful one. I set my function name as ‘CosmosTableAPI’.
  • Add ‘Microsoft.Azure.Cosmos.Table’ NuGet package.
  • Following is the code snippet to connect to ‘Azure Table API’ and Insert entities.

public static async Task Run(
[HttpTrigger(AuthorizationLevel.Function, “get”, “post”, Route = null)] HttpRequest req,
ILogger log)
{
CreateTableandAddData().Wait();

}

public static async Task CreateTableandAddData()
{
string tableName = “customers”;

// Create or reference an existing table
CloudTable table = await Common.CreateTableAsync(tableName);

IndividualEntity customer = new IndividualEntity(“Harp”, “Walter”)
{
Email = “Walter@contoso.com”,
PhoneNumber = “425-555-0101”
};

Console.WriteLine(“Insert an Entity.”);
customer = await InsertOrMergeEntityAsync(table, customer);
}

public static async Task<IndividualEntity> InsertOrMergeEntityAsync(CloudTable table, IndividualEntity entity)
{
try
{
// Create the InsertOrReplace table operation
TableOperation insertOrMergeOperation = TableOperation.InsertOrMerge(entity);

// Execute the operation.
TableResult result = await table.ExecuteAsync(insertOrMergeOperation);
IndividualEntity insertedCustomer = result.Result as IndividualEntity;

// Get the request units consumed by the current operation. RequestCharge of a TableResult is only applied to Azure Cosmos DB
if (result.RequestCharge.HasValue)
{
Console.WriteLine(“Request Charge of InsertOrMerge Operation: ” + result.RequestCharge);
}

return insertedCustomer;
}
catch (StorageException e)
{
Console.WriteLine(e.Message);
Console.ReadLine();
throw;
}
}

public static async Task<CloudTable> CreateTableAsync(string tableName)
{
string storageConnectionString = “{Connection string copied from Azure Table API in previous section}”;
// Retrieve storage account information from connection string.
CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnectionString);

// Create a table client for interacting with the table service
CloudTableClient tableClient = storageAccount.CreateCloudTableClient(new TableClientConfiguration());
tableClient.TableClientConfiguration.UseRestExecutorForCosmosEndpoint = true;

// Create a table client for interacting with the table service
CloudTable table = tableClient.GetTableReference(tableName);
if (await table.CreateIfNotExistsAsync())
{
Console.WriteLine(“Created Table named: {0}”, tableName);
}
else
{
Console.WriteLine(“Table {0} already exists”, tableName);
}

return table;
}

public class IndividualEntity : TableEntity
{
public IndividualEntity()
{
}

public IndividualEntity(string lastName, string firstName)
{
PartitionKey = lastName;
RowKey = firstName;
}

public string Email { get; set; }
public string PhoneNumber { get; set; }
}

  • Compile and run the project.
  • Visual studio opens up a console (Storage Simulator) as below. Copy the URL.
  • Hit the URL either from browser or Postman tool.
  • You would get following output from the console.
  • Go to Azure Portal and you should see a new entity created

🙂

Categories: CRM

Embed Canvas App Error – Environment doesn’t have any CDS database

If you encountered following exception while embedding Canvas app in Model Driven App, then this article is for you.

Canv_4

Reason:

  • You must have a CDS database in your environment to configure Embed Canvas App, as the Canvas App metadata gets stored in CDS database.

Fix:

  • Connect to PowerApps Admin Center
  • Open the ‘default’ environment.
  • Click on ‘Add database’.

PAPPS_1

  • Give it sometime to complete the database setup.
  • Once done, you would get ‘Database version’ rendered as below:

Canv_5

  • Retry embedding Canvas app.

Refer article for step by step details to embed Canvas app in Model driven app.

🙂

 

DataMigrationUtility – Unable to connect to the Dynamics instance

April 28, 2020 1 comment

I was getting following error while connecting to dynamics instance using ‘DataMigrationUtility’ comes with SDK.

You don’t have permission to access any of the organizations in the Microsoft Common Data Service region that you specified. If you’re not sure which region your organization resides in, choose “Don’t know” for the CDS region and try again. Otherwise check with your CDS administrator.

 

MigrationUtility_1

 

Fix:

It seems the issue is with ‘DataMigrationUtility’ and following workaround resolved the issue in my case

  • Get the latest version of the ‘DataMigrationUtility’ tool.
  • In the Login screen,
    • Select ‘Online Region’
    • Leave ‘User Name’ and ‘Password’ fields blank

MigrationUtility_2

  • Click ‘Login’
  • Tool will prompt you the credentials in a new popup.
  • Enter your credentials and ‘Connect’

Notes:

  • I tried by unchecking ‘Show Advanced’ option but had no luck.
  • Also, I could not connect when I chose “Don’t Know” option in ‘Online Region’ field and left the ‘User Name’ and ‘Password’ fields blank.
  • The exact combination specified in ‘Fix’ worked for me.
  • After the workaround, I could connect to the Dynamics instance from ‘DataMigrationUtility’ normally from next time.

🙂

StyleCop error – The parameter is incorrect

Other day, I encountered following error while triggering ‘Run StyleCop‘ from Visual Studio.

StyleCop_Error

Reason & Fix:

  • In my case, I cloned a DevOps branch and ran the ‘Run StyleCop’ without building the code.
  • Since there were no executable’s, StyleCop thrown error ‘The parameter is incorrect‘.
  • Build the solution and ‘Run StyleCop’ should fix the issue.

StyleCop_Error_1

🙂