"тнιѕ вℓσg ¢συℓ∂ ѕανє уσυя мσηєу ιƒ тιмє = мσηєу" - ∂.мαηנαℓу

Saturday, 19 December 2020

Changes (deprecations) coming in Power Apps, Power Automate, and customer engagement apps

 Changes  for Power Apps, Power Automate and CE are listed in the below link.

 I would recommend special attention to this part "Deprecation of Office365 authentication type and OrganizationServiceProxy class for connecting to Dataverse". Authentication type 'Office365' is commonly used. So be ready!

    Effective April 2021, the authentication protocol will be retired for all new environments within a tenant.
    Effective April 2022, the authentication protocol will be retired for all new and existing environments within a tenant.

For more details, please refer the below link

https://docs.microsoft.com/en-us/power-platform/important-changes-coming#deprecation-of-office365-authentication-type-and-organizationserviceproxy-class-for-connecting-to-common-data-service

 

Thursday, 10 December 2020

ER Diagram for Dynamics 365 CRM Entities

 Needed to generate the ER diagram of Dynamics 365 CRM entities. After some research, found the below link and it was very useful! Good part is that it is generated from Dynamics CRM which is our source of truth.

https://docs.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/use-metadata-generate-entity-diagrams

Sunday, 6 December 2020

Dynamics 365 Team Member License

Recently came across team member license. For users, who don't need full user capababilities it is a good option. Much cheaper!!!! I was a bit surprised with the price difference. In terms of what is possible with this license, still need to explore. It can utilize a model driven app. 

For instance, for Sales module it would be sale team member app and for customer service module, customer service team member app etc.

I would recommend to try out by setting up a pilot test user with this license / assign this to one of the users and test what the functionalities available. And also confirm the limitations.

More details can be found in the below link. It is a money saver option!  So worth exploring.

https://docs.microsoft.com/en-gb/dynamics365/get-started/team-members-license

 

 

Sunday, 29 November 2020

Dynamics 365 CRM API Limits

Dynamics 365 API limit is a big topic. It was a bit confusing to understand the whole thing. From my understanding, there are two types of API limits. Please refer Microsoft documentation links for accurate info. This post is based on the info that I collected upto November 2020. Micorosoft might change these in the future.

1) Requests limits & allocation 

 This one, your system wouldn't throttle so operations can continue without issues. However there may be cost implications if you exceed the daily API limit. Might need to buy API Limit licenses( new license introduced by Microsoft). It includes most of the operations like plugins, custom workflow activity etc. 

For instance, Dynamics 365 Enterprise application user license has a limit of 20000 API requests per 24 hours

More details are in the below link.

https://docs.microsoft.com/en-us/power-platform/admin/api-request-limits-allocations

2) Service Protection API limits

This one , your system would throttle if you exceed this sliding window limit( 5minutes). So it is necessary that a retry mechanism should be in place ( For both connection types-  organisation service SOAP Api and CRM Web Api)  It excludes plugins, custom workflow activity. It is explained in the below link.

"The service protection API limits are evaluated within a 5 minute (300 second) sliding window. If any of the limits are exceeded within the preceding 300 seconds, a service protection API Limit error will be returned on subsequent requests to protect the service until the Retry-After duration has ended." 


MeasureDescriptionLimit per web server
Number of requests The cumulative number of requests made by the user. 6000 withing 5min sliding window
Execution time The combined execution time of all requests made by the user. 20 minutes (1200 seconds)
Number of concurrent requestsThe number of concurrent requests made by the user52

Ref:https://docs.microsoft.com/en-us/powerapps/developer/common-data-service/api-limits#retry-operations


Saturday, 28 November 2020

Generic Repository Design Pattern with Entity Framework Core

Generic Repository pattern is a design pattern available in C#. Found it very cool!. Generic repository design pattern is very useful when we have more than one entity to deal with in the Entity Framework Core ( EF Core Basics are explained here -https://crmdm.blogspot.com/2020/05/how-to-write-to-sql-table-using-entity.html ). This  design pattern avoids a lot of repetitive code.

For instance, consider two entities used in the entity framework core.

  • Contact
  • Loan

Now these two entites have common operations like inserting to a sql table, read from sql table, removing from the sql table. So we could utilise a generic repository for this. This would avoid repetitive code throughout. Code looks clean too.

In this post I have followed TDD approach. Entity Framework Core unit testing is done using Xunit and Sqlite

 There are 3 options for Entity Framework Core unit testing. Had discussion with my colleague on this specific part as well. It turned out that the sqlite is the better option as other ones have limiations.Hence it is used here. ( ref :https://docs.microsoft.com/en-us/ef/core/testing/ )

First things First. Model is one of our first bricks.

Models: These are a couple of sample models. Feel free to define yours. Please note that Key is a must. Key could be of any data type.

Contact Model.

using System.ComponentModel.DataAnnotations;

namespace RepositoryDesignPattern.Domain
{
    public class Contact
    {
        [Key]
        public int CustomerId { get; set; }

        public string Name { get; set; }

        public string Description { get; set; }
    }
}

Loan Model

using System.ComponentModel.DataAnnotations;

namespace RepositoryDesignPattern.Domain.Model
{
    public class Loan
    {
        [Key]
        public int LoanId { get; set; }

        public decimal Balance { get; set; }

        public string Description { get; set; }

    }
}

Next is our generic interface- IRepository - feel free to name as per your preference. Also more generic methods can be included as per the requirements.

using System.Collections.Generic;

namespace RepositoryDesignPattern.Application.Repositories
{
    public interface IRepository<T> where T : class
    {
        void Add(T entity);

        IEnumerable<T> All();

        void SaveChanges();

        void Remove(T entity);
    }
}

Now the generic repository class which is implemented from the above interface.

using Microsoft.EntityFrameworkCore;
using RepositoryDesignPattern.Application.Repositories;
using System.Collections.Generic;
using System.Linq;

namespace RepositoryDesignPattern.Infrastructure.Repositories
{
    public class Repository<T> : IRepository<T> where T : class
    {
        protected readonly DbContext _context;
        protected readonly DbSet<T> _entities;

        public Repository(DbContext context)
        {
            _context = context;
            _entities = context.Set<T>();
        }

        public void Add(T entity) => _entities.Add(entity);

        public  IEnumerable<T> All() => _context.Set<T>().ToList();

        public  void SaveChanges() => _context.SaveChanges();

        public  void Remove(T entity) => _context.Remove(entity);
    }
}

-----------------------------------------------------------------------------------------------------

Optional Note: This part is useful when the repos get expanded. Not required for demoing generic repository.
It is possible to override these methods from another repository if needed. In that case we need to add virtual keyword for the method in the generic repo.

Also generic repository can be marked as an abstract class at a later stage when other repos are defined properly.

For instance, 

  public virtual void Add(T entity) => _entities.Add(entity);

Overriding from another repo. For instance if we go with a new loan repo,

using RepositoryDesignPattern.Domain.Model;

namespace RepositoryDesignPattern.Infrastructure.Repositories
{
    public class LoanRepository : Repository<Loan>
    {
        public LoanRepository(SqlDbContext dbContext) : base(dbContext)
        {

        }

        public override void Add(Loan loan)
        {
            loan.Description = "Override";
            base.Add(loan);
        }
    }
}

----------------------------------------------------------------------------------------------------

So we are mainly interested in Adding a new record, Retrieving all records from a specific table, Save changes to the actual table, Remove a record. Add or remove operations get completed after applying savechanges. T represents a type parameter in C#. In this scenario it could be Loan or Contact entity.

Next part is to define a custom Dbcontext. This is to handle our own Sql DB and tables. DbContextOptions is an important part. This could be an actual sql db or in memory db or any other supported db by Entity Framework Core. You could see two tables Contact and Loan. DbContextOptions value is passed in the constructor.

using Microsoft.EntityFrameworkCore;
using RepositoryDesignPattern.Domain;
using RepositoryDesignPattern.Domain.Model;

namespace RepositoryDesignPattern.Infrastructure
{
    public class SqlDbContext : DbContext
    {
        public SqlDbContext(DbContextOptions options) : base(options) { }

        public DbSet<Contact> Contacts { get; set; }

        public DbSet<Loan> Loans { get; set; }
    }
}

As per the TDD approach we use the unit tests to provide in memory db and test our code.

Arrange part is set up using Sqlite ( Please note the part .UseSqlite. In a real scenario this part would be .UseSqlServer ). Act part is calling the actual code. And assert part evaluates expected result and output.

using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using RepositoryDesignPattern.Domain.Model;
using RepositoryDesignPattern.Infrastructure;
using RepositoryDesignPattern.Infrastructure.Repositories;
using System.Data.Common;
using System.Linq;
using Xunit;

namespace RepositoryDesignPattern.Unit.Tests
{
    public class SqlRepositoryTests
    {
        [Fact]
        public void Insert_New_LoanRecord_And_Read_Successfully()
        {
            //Arrange
            var dbContextOptions = new DbContextOptionsBuilder<SqlDbContext>()
                .UseSqlite(CreateInMemoryDatabase())
                .Options;
            var context = new SqlDbContext(dbContextOptions);
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            Loan loanRecord = GetSampleLoanRecord();
            var expectedCount = 1;

            //Act
            var repository = new Repository<Loan>(context);
            repository.Add(loanRecord);
            repository.SaveChanges();
            var result = repository.All();

            //Assert
            Assert.Equal(expectedCount, result.Count());
            Assert.Equal(loanRecord.LoanId, result.FirstOrDefault().LoanId);
            Assert.Equal(loanRecord.Description, result.FirstOrDefault().Description);
            Assert.Equal(loanRecord.Balance, result.FirstOrDefault().Balance);
        }

        [Fact]
        public void Insert_New_LoanRecord_And_Removes_Successfully()
        {
            //Arrange
            var dbContextOptions = new DbContextOptionsBuilder<SqlDbContext>()
                .UseSqlite(CreateInMemoryDatabase())
                .Options;
            var context = new SqlDbContext(dbContextOptions);
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            Loan loanRecord = GetSampleLoanRecord();
            var expectedCount = 0;

            //Act
            var repository = new Repository<Loan>(context);
            repository.Add(loanRecord);
            repository.SaveChanges();
            repository.Remove(loanRecord);
            repository.SaveChanges();
            var result = repository.All();

            //Assert
            Assert.Equal(expectedCount, result.Count());
        }

        private DbConnection CreateInMemoryDatabase()
        {
            var connection = new SqliteConnection("Filename=:memory:");
            connection.Open();

            return connection;
        }

        private Loan GetSampleLoanRecord()
        {
            Loan loanRecord = new Loan();
            loanRecord.LoanId = 22;
            loanRecord.Description = "unit test";
            loanRecord.Balance = 1000.00m;

            return loanRecord;
        }
    }
}
 

Green is a good sign : )







Friday, 25 September 2020

Flow.Run failed: Connection not configured for this service error

Scenario : Power App triggers a Flow ( Power Automate ) . After deployment  ( could be a manual solution import or Azure Pipeline with Power platform steps in it) it throws an error

"Flow.Run failed: Connection not configured for this service error"

Flow - Represents your flow name.

 Please note that this issue might get resolved by Microsoft soon. But it is happening as of 25/09/2020.

As per Microsoft Documentation workaround is given below. Section-Known Limitations.

"The app and flow will not currently be connected in the target environment after deployment. First associate valid connections with the flow and activate the flow. Then edit the app and re-associate the flow to the button"

Ref: https://docs.microsoft.com/en-us/powerapps/maker/common-data-service/solutions-overview