046 Preparing the Lookup resource application to run - chempkovsky/CS82ANGULAR GitHub Wiki

Notes

  • We need to set up the LpPhBkWebApp-app for the first run and set up the PhBkWebApp-app to be able to connect to the Service Bus.

Steps required to accomplish the task

  • Working with appsettings.json for RabbitMq we need to have a way to detect if it's a one node server or it's a multi-node cluster. If it's a cluster we should have a way to get a variable length array of IP addresses.
  • Read Options pattern first.
  • Now it's clear that the simplest implementation is to declare custom options-class and to use the code like below in the Program.cs-file.
   ...
   ConfigurationManager configuration = builder.Configuration;
   ...
   var myCustomOptions = new MyCustomOptions();
   configuration.GetSection("section_name_here").Bind(myCustomOptions);
  • In the article 01005 extforlkup interface for PhbkDivisionView, we described the steps to generate the interfaces.
    • open PhBk/phbk-division-view.extforlkup.interface.cs-file of the LpPhBkViews-project. In addition to interfaces, the PhbkDivisionViewExtForLkUpConf class has been declared along with instructions on how to use it. These instructions include
      • Section name of the appsettings.json-file
      • The sample code which must be added to Program.cs-file
    • read the article 034 The LpPhBkViews-project is shared by LpPhBkWebApp and LpPhBkWebApp. Thus, PhbkDivisionViewExtForLkUpConf-class you can use in both apps. LpPhBkWebApp sends messages. LpPhBkWebApp consumes messages. If both apps use the same RabbitMq-server(or cluster) then both apps must use the same RabbitMq-settings.
Click to show the code
#nullable disable
using System.Text.Json.Serialization;
using System.ComponentModel.DataAnnotations;




namespace LpPhBkViews.PhBk {

    public interface IPhbkDivisionViewExtForLkUp {
        public System.Int32  DivisionId { get; set; }

        public System.String  DivisionName { get; set; }

        public System.Int32  EntrprsIdRef { get; set; }

    }

    public interface IPhbkDivisionViewExtForLkUpMsg {
        // 1 - insert; 2 - update; 3 - delete;
        public int Action { get; set; } 
        public IPhbkDivisionViewExtForLkUp OldVals { get; set; } 
        public IPhbkDivisionViewExtForLkUp NewVals { get; set; } 
    }

/*
  
  In the appsettings.json file, you need to add a section as shown below:
  1. If a RabbitMq cluster is present:

  "PhbkDivisionViewExtForLkUpConf": {
    "HostName ": "192.168.100.3",
    "Username": "Admin",
    "Password": "Admin",
    "VirtualHostName": "phbkhost",
    "ClusterIpAddresses": [
      "192.168.100.4",
      "192.168.100.5",
      "192.168.100.6"
    ]
  }


  2. If a RabbitMq cluster is not present:

  "PhbkDivisionViewExtForLkUpConf": {
    "HostName ": "192.168.100.3",
    "Username": "Admin",
    "Password": "Admin",
    "VirtualHostName": "phbkhost",
    "ClusterIpAddresses": []
  }

  3. Add code to the Program.cs file as shown below:

  var builder = WebApplication.CreateBuilder(args);
  ...
  ConfigurationManager configuration = builder.Configuration;
   ...
  var myOptions = new PhbkDivisionViewExtForLkUpConf();
  configuration.GetSection(PhbkDivisionViewExtForLkUpConf.ConfName).Bind(myOptions);

   ...

*/

    public class PhbkDivisionViewExtForLkUpConf {
        public static string ConfName = "PhbkDivisionViewExtForLkUpConf";
        public string HostName { get; set; } = String.Empty;
        public string Username { get; set; } = String.Empty;
        public string Password { get; set; } = String.Empty;
        public string VirtualHostName { get; set; } = String.Empty;
        public string[] ClusterIpAddresses { get; set; } = null!;
    }
}
  • Here is the last thing we have to mention. What if the application requires more than one lookup resource and the requirements claim: different lookup resources must use different RabbitMq servers. It means, app requires more than one Service Bus to connect to. To answer this question read the article MultiBus.

Set up LpPhBkWebApp

appsettings for LpPhBkWebApp

  • RabbitMq
    • the IP address RabbitMq host is equal to 192.168.100.3.
    • RabbitMq Virtual Host name is phbkhost
    • there is no RabbitMq cluster
    • admin/admin is RabbitMq user
  • Expected database connection name is LpPhBkConnection
  • appsettings.json-file will be as folows:
Click to show the code
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "LpPhBkConnection": "Data Source=SVR2016SQL2017;Initial Catalog=LpPhBkDbDef;Persist Security Info=True;User ID=sa;Password=password_here"
  },
  "PhbkDivisionViewExtForLkUpConf": {
    "HostName": "192.168.100.3",
    "Username": "admin",
    "Password": "admin",
    "VirtualHostName": "phbkhost",
    "ClusterIpAddresses": []
  }
}

Program for LpPhBkWebApp

  • Program.cs must include
    • database configuration
    • RabbitMq configuration
      • x.AddConsumer<PhbkDivisionViewExtForLkUpMsgConsumer>(typeof(PhbkDivisionViewExtForLkUpMsgConsumerDefinition)); //.Endpoint(e => { }); line of code declares the consumer.
      • configurator.ConfigureEndpoints(context);-method call required for the consumer.
Click to show the code
using LpPhBkContext.PhBk;
using LpPhBkViews.PhBk;
using MassTransit;
using Microsoft.EntityFrameworkCore;
...

var builder = WebApplication.CreateBuilder(args);

ConfigurationManager configuration = builder.Configuration;
builder.Services.AddDbContext<LpPhbkDbContext>(options =>
    options.UseSqlServer(configuration.GetConnectionString("LpPhBkConnection")));

#region MassTransit config
var phbkDivisionViewExtForLkUpConf = new PhbkDivisionViewExtForLkUpConf();
configuration.GetSection(PhbkDivisionViewExtForLkUpConf.ConfName).Bind(phbkDivisionViewExtForLkUpConf);
builder.Services.AddMassTransit(x => {

    x.AddConsumer<PhbkDivisionViewExtForLkUpMsgConsumer>(typeof(PhbkDivisionViewExtForLkUpMsgConsumerDefinition)); //.Endpoint(e => { });
    x.UsingRabbitMq((context, configurator) => {
        configurator.Host(phbkDivisionViewExtForLkUpConf.HostName, phbkDivisionViewExtForLkUpConf.VirtualHostName, h => {
            h.Username(phbkDivisionViewExtForLkUpConf.Username);
            h.Password(phbkDivisionViewExtForLkUpConf.Password);
            if(phbkDivisionViewExtForLkUpConf.ClusterIpAddresses != null)
            {
                if(phbkDivisionViewExtForLkUpConf.ClusterIpAddresses.Length > 0)
                {

                     h.UseCluster((configureCluster) =>
                     {
                         for(int i = 0; i < phbkDivisionViewExtForLkUpConf.ClusterIpAddresses.Length; i++)
                         {
                             configureCluster.Node(phbkDivisionViewExtForLkUpConf.ClusterIpAddresses[i]);
                         }
                     });
                }
            }
            // h.PublisherConfirmation = true;
            // h.ConfigureBatchPublish(configure => { });
        });
        // 
        // Quorum Queue settings
        //
        // configurator.SetQuorumQueue(3);
        //

        configurator.ConfigureEndpoints(context);
    });
});
#endregion

LpPhbkDbContext

  • open LpPhbkDbContext.cs-file
    • add the constructor
    • modify OnModelCreating-method to populate with lookup data
Click to show the code
    public class LpPhbkDbContext : DbContext
    {
        public LpPhbkDbContext(DbContextOptions<LpPhbkDbContext> options)
          : base(options)
        {
            Database.EnsureCreated();
        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<LprDivision02>().HasKey(p => new { p.EntrprsIdRef, p.DivisionNameIdRef, p.DivisionId });
            modelBuilder.Entity<LprDivision01>().HasKey(p => new { p.DivisionNameIdRef, p.DivisionId });
            modelBuilder.Entity<LpdDivision>().HasAlternateKey(p => p.DivisionName).HasName("LpdDivisionNameUk");
            modelBuilder.Entity<LpdDivision>().HasKey(p => p.DivisionNameId);
            modelBuilder.Entity<LprDivision01>().HasOne(d => d.DivisionNameDict)
                .WithMany(m => m.DivisionRef01)
                .HasForeignKey(d => d.DivisionNameIdRef)
                .HasPrincipalKey(p => p.DivisionNameId)
                .IsRequired(true)
                .OnDelete(DeleteBehavior.NoAction);
            modelBuilder.Entity<LprDivision02>().HasOne(d => d.DivisionNameDict)
                .WithMany(m => m.DivisionRef02)
                .HasForeignKey(d => d.DivisionNameIdRef)
                .HasPrincipalKey(p => p.DivisionNameId)
                .IsRequired(true)
                .OnDelete(DeleteBehavior.NoAction);


            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 1,  DivisionName = "Division 11"  });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 2,  DivisionName = "Division 12" });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 3,  DivisionName = "Division 13" });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 4,  DivisionName = "Division 14" });

            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 5,  DivisionName = "Division 21" });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 6,  DivisionName = "Division 22" });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 7,  DivisionName = "Division 23" });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 8,  DivisionName = "Division 24" });

            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 9,  DivisionName = "Division 31" });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 10, DivisionName = "Division 32" });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 11, DivisionName = "Division 33" });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 12, DivisionName = "Division 34" });

            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 13, DivisionName = "Division 41" });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 14, DivisionName = "Division 42" });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 15, DivisionName = "Division 43" });
            modelBuilder.Entity<LpdDivision>().HasData(new { DivisionNameId = 16, DivisionName = "Division 44" });

            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 11, DivisionNameIdRef = 1 });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 12, DivisionNameIdRef = 2 });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 13, DivisionNameIdRef = 3 });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 14, DivisionNameIdRef = 4 });

            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 21, DivisionNameIdRef = 5 });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 22, DivisionNameIdRef = 6 });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 23, DivisionNameIdRef = 7 });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 24, DivisionNameIdRef = 8 });

            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 31, DivisionNameIdRef = 9   });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 32, DivisionNameIdRef = 10  });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 33, DivisionNameIdRef = 11  });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 34, DivisionNameIdRef = 12  });

            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 41, DivisionNameIdRef = 13 });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 42, DivisionNameIdRef = 14 });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 43, DivisionNameIdRef = 15 });
            modelBuilder.Entity<LprDivision01>().HasData(new { DivisionId = 44, DivisionNameIdRef = 16 });

            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 11, DivisionNameIdRef = 1,  EntrprsIdRef = 1 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 12, DivisionNameIdRef = 2,  EntrprsIdRef = 1 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 13, DivisionNameIdRef = 3,  EntrprsIdRef = 1 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 14, DivisionNameIdRef = 4,  EntrprsIdRef = 1 });

            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 21, DivisionNameIdRef = 5,  EntrprsIdRef = 2 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 22, DivisionNameIdRef = 6,  EntrprsIdRef = 2 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 23, DivisionNameIdRef = 7,  EntrprsIdRef = 2 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 24, DivisionNameIdRef = 8,  EntrprsIdRef = 2 });

            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 31, DivisionNameIdRef = 9,  EntrprsIdRef = 3 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 32, DivisionNameIdRef = 10, EntrprsIdRef = 3 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 33, DivisionNameIdRef = 11, EntrprsIdRef = 3 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 34, DivisionNameIdRef = 12, EntrprsIdRef = 3 });

            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 41, DivisionNameIdRef = 13, EntrprsIdRef = 4 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 42, DivisionNameIdRef = 14, EntrprsIdRef = 4 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 43, DivisionNameIdRef = 15, EntrprsIdRef = 4 });
            modelBuilder.Entity<LprDivision02>().HasData(new { DivisionId = 44, DivisionNameIdRef = 16, EntrprsIdRef = 4 });
        }
    }

run LpPhBkWebApp

  • run LpPhBkWebApp
  • Open managamennt console of RabbitMq server:
Click to show the picture

project structure

  • MassTransit creates two Exchanges for the consumer
    • Expected only one
Click to show the picture

project structure

  • MassTransit creates one Queue for the consumer
Click to show the picture

project structure

  • MassTransit creates two bindings for the consumer.

  • Close App

  • remove Queue and Exchanges by hand.

Set up PhBkWebApp

appsettings for PhBkWebApp

Click to show the code
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "PhBkConnection": "Data Source=SVR2016SQL2017;Initial Catalog=PhBkDbDef;Persist Security Info=True;User ID=sa;Password=your_passwork_here"
  },
  "JWT": {
    "ValidAudience": "PhBkAudience",
    "ValidIssuer": "PhBkIssuer",
    "Secret": "JWTAuthenticationHIGHsecuredPasswordVVVp1OH7Xzyr"
  },
  "PhbkDivisionViewExtForLkUpConf": {
    "HostName": "192.168.100.3",
    "Username": "admin",
    "Password": "admin",
    "VirtualHostName": "phbkhost",
    "ClusterIpAddresses": []
  }

}

Program for PhBkWebApp

  • add the following code to Program.cs-file of PhBkWebApp
    • x.AddRequestClient<IPhbkDivisionViewExtForLkUpMsg>();-line of code declares the message producer
    • configurator.ConfigureEndpoints(context); method call is NOT used.
Click to show the code
...
#region MassTransit config
var phbkDivisionViewExtForLkUpConf = new PhbkDivisionViewExtForLkUpConf();
configuration.GetSection(PhbkDivisionViewExtForLkUpConf.ConfName).Bind(phbkDivisionViewExtForLkUpConf);
builder.Services.AddMassTransit(x => {

    x.AddRequestClient<IPhbkDivisionViewExtForLkUpMsg>();

    x.UsingRabbitMq((context, configurator) => {
        configurator.Host(phbkDivisionViewExtForLkUpConf.HostName, phbkDivisionViewExtForLkUpConf.VirtualHostName, h => {
            h.Username(phbkDivisionViewExtForLkUpConf.Username);
            h.Password(phbkDivisionViewExtForLkUpConf.Password);
            if (phbkDivisionViewExtForLkUpConf.ClusterIpAddresses != null)
            {
                if (phbkDivisionViewExtForLkUpConf.ClusterIpAddresses.Length > 0)
                {

                    h.UseCluster((configureCluster) => {
                        for (int i = 0; i < phbkDivisionViewExtForLkUpConf.ClusterIpAddresses.Length; i++)
                        {
                            configureCluster.Node(phbkDivisionViewExtForLkUpConf.ClusterIpAddresses[i]);
                        }
                    });
                }
            }
            // h.PublisherConfirmation = true;
            // h.ConfigureBatchPublish(configure => { });
        });
        // 
        // Quorum Queue settings
        //
        // configurator.SetQuorumQueue(3);
        //
    });
});
#endregion
...

run PhBkWebApp

  • run PhBkWebApp
  • Open managamennt console of RabbitMq server:
Click to show the picture

project structure

  • Exchange is created only after any PhbkDivisionView changed (Only after await pe.Publish<IPhbkDivisionViewExtForLkUpMsg>(...)-first method call).
    • No Queue, nor Bindings is created for the message producer!!! Consumer app must be started first.
⚠️ **GitHub.com Fallback** ⚠️