046 Preparing the Lookup resource application to run - chempkovsky/CS82ANGULAR GitHub Wiki
- We need to set up the
LpPhBkWebApp
-app for the first run and set up thePhBkWebApp
-app to be able to connect to the Service Bus.
- 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 theProgram.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 theLpPhBkViews
-project. In addition to interfaces, thePhbkDivisionViewExtForLkUpConf
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
- Section name of the
- read the article 034 The
LpPhBkViews
-project is shared byLpPhBkWebApp
andLpPhBkWebApp
. 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.
- open
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.
- 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
- the IP address RabbitMq host is equal to
- 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.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
- 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
- Open managamennt console of RabbitMq server:
Click to show the picture
- MassTransit creates two Exchanges for the consumer
- Expected only one
Click to show the picture
- MassTransit creates one Queue for the consumer
Click to show the picture
-
MassTransit creates two bindings for the consumer.
-
Close App
-
remove Queue and Exchanges by hand.
- we add the same
PhbkDivisionViewExtForLkUpConf
-section as for appsettings for LpPhBkWebApp
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": []
}
}
- add the following code to
Program.cs
-file ofPhBkWebApp
-
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
- Open managamennt console of RabbitMq server:
Click to show the picture
- Exchange is created only after any
PhbkDivisionView
changed (Only afterawait pe.Publish<IPhbkDivisionViewExtForLkUpMsg>(...)
-first method call).- No Queue, nor Bindings is created for the message producer!!! Consumer app must be started first.