(MVC) MVC (2020)

TDD in Core Linux (Middleware, IdentityServer, SignalR, Quartz, RabbitMQ, Redis and so on)

Net core Test Driven development is significantly different than TDD of MVC project development I described four years ago Unit-тести для ASP.NET MVC and something testing technique I invented by himself Моя CMS для ASP.NET MVC, and it this page I want to describe new pattern of TDD in Net Core.

At common there are no big difference in 3 big competitor test framework xUnit, nUnit and MSUnitTest, because ant operation is possible to make in any framework - Comparisons testing framework, and full list of testing framework see please in this page Comparisons testing framework (wikipedia). Main difference is how to write to log, how to initialize test environment and so on, but if you use one framework than answer to any your questions about another framework you will receive in seconds by googling.

What benefits and disadvantages of TDD?

Any other system from Microsoft what pass TDD pattern is similar. For example look to brand new Visual Studio 2019. Even in simplest moment is not working correctly. For example, look at the screen below, both of my project is uploaded to local GIT, as you can see, but lock not showing and I can not opportunity to track changing my code.



Even I want to add whole solution to GIT, VS2019 not propose me add code to local repository! This is simplest pattern to working with GIT and even it working incorrectly in VS2019 !



In fact, brand new TDD design product, like VS2019 has huge amount of bugs. I see 778 in moment I write this post.



It concerns everything what made in TDD design, not only Visual Studio or operation system. Any library in NET Framework pass all Microsoft test and for Microsoft point of view is high quality software. But look to reality. For example, this is Microsoft converter VB<->C# https://converter.telerik.com/, and trying 50% of normal C# or VB code is finished by crash Microsoft converter!

Not only Microsoft software is terrible, even more stable software not working correctly time on time, even when I write this post, Notepad++ crashed (1) and Notepad++ can not create report about crash (2).




What is TDD design at all in reality? Only spend time? Only formal report for boss? Because without TDD in most case projects has more quality and less amount of bugs and created faster than with testing projects and formal reports.

This is my common doubt about TDD and testing - reality disproves positive result of TDD, testing spend more time than time to software to be testing, TDD no more than alternatives for (1) interacting debugging technique (including remote debugging in real environment) and (2) self-testing application (when application show needed data step-by-step by development. And formal dedicated test project not need all if you working without boss who like a lot of various reports and without huge CI system with mandatory test projects.

But... what is interesting... In some case testing projects has huge benefits and very useful!!!




1. Test identity in Net Core projects.

I upload this project to the public:

2. Test WCF reference and config of WCF reference.

I show screen of this project in this topic - How Linux Net Core 3.1 daemon (in VB.NET) can read/write data from/to ancient MS SQL 2005

3. Test Middleware in Core DI container and Controller with Middleware (with Mock and real Logger).

This project I also upload to the public:

4. Interactive Blazor tests backend services and controllers.

Swagger has been created API specification only for controllers, therefore if you want test separate test you can perform this steps.

  • Define each service


  • Create test controller and inject each services to it.

  • Add Swagger libraly and create Swagger API endpoint.

  •  217:              // Enable middleware to serve generated Swagger as a JSON endpoint.
     218:              app.UseSwagger(prm =>
     219:              {
     220:                  //change the path to include /CS
     221:                  //prm.RouteTemplate = "CS/swagger/V2/swagger.json";
     222:              });
     223:   
     224:              // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
     225:              // specifying the Swagger JSON endpoint.
     226:              app.UseSwaggerUI(prm =>
     227:              {
     228:                  //Notice the lack of / making it relative
     229:                  prm.SwaggerEndpoint("/swagger/V2/swagger.json", "CryptoChestNew V2");
     230:                  //This is the reverse proxy address
     231:                  //prm.RoutePrefix = "CS";
     232:              });

    Workable microservices example with Swagger endpoint you can see in this my example.


  • Look to Swagger OpenAPI endpoint specification.
  • Than I have created by special Win-batch script frontend page for testing each API and list of my pages.
  • Than I have created Interface to call beckend API from frontend. I use SwaggerUI.
  • And finally I have to created manually thousands of special page for each API similar to this.


       1:  @page "/Admin/Service/BalanceUserDepositByCoin" 
       2:  @using CoreObjectDumper;
       3:  @using System.Net.Http.Headers
       4:  @using System.Threading
       5:  @using Frontend1.Areas.Admin.Pages
       6:  @using CryptoChestNewApi
       7:   
       8:   
       9:   
      10:  <h3>Balance/UserDepositByCoin</h3>
      11:   
      12:   
      13:  <div class="row">
      14:      <JwtAuSelector @ref="JwtSelectorRef"></JwtAuSelector>
      15:  </div>
      16:  <div class="row">
      17:      <div class="row" style="margin:20px;">
      18:          <div class="form-group" style="margin:10px">
      19:              <label>userId (string)</label><br />
      20:              <GetAvailableUserID UserId="userId" ValueChanged="@((x)=> userId=x)"></GetAvailableUserID>
      21:          </div>
      22:          <div class="form-group" style="margin:10px">
      23:              <label>CoinName (string)</label><br />
      24:              <GetAvailableCoinName CoinName="coinName" ValueChanged="@((x)=> coinName=x)"></GetAvailableCoinName>
      25:          </div>
      26:          <div class="form-group" style="margin:10px">
      27:              <label>amount (double)</label><br />
      28:              <input type="number" @bind-value=amount />
      29:          </div>
      30:      </div>
      31:  </div>
      32:  <div style="margin:30px;" class="row">
      33:      <button class="btn btn-primary" @onclick="@call">Execute</button>&nbsp;
      34:  </div>
      35:  <div class="text-primary">
      36:      NoResult
      37:  </div>
      38:  <br />
      39:  @Result
      40:   
      41:  @code {
      42:   
      43:      string userId;
      44:      string coinName;
      45:      double amount;
      46:   
      47:      JwtAuSelector JwtSelectorRef;
      48:      MarkupString Result;
      49:      CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
      50:   
      51:      protected async void call()
      52:      {
      53:          try
      54:          {
      55:              Result = (MarkupString)"waiting ...";
      56:              StateHasChanged();
      57:              await Task.Delay(1000);
      58:              cancelTokenSource.Dispose();
      59:              cancelTokenSource = new CancellationTokenSource();
      60:              HttpClient httpClient = new HttpClient();
      61:              httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", JwtSelectorRef.JWT);
      62:              httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
      63:              var Api = new CryptoChestNewApi.Client(Program.ApiUrl, httpClient);
      64:              Task Res1 = Task.Run(() => Api.BalanceUserDepositByCoinAsync(userId, coinName, amount, cancelTokenSource.Token));
      65:              Res1.Wait();
      66:              if (Res1.IsCompletedSuccessfully) { Result = (MarkupString)"Success"; };
      67:          }
      68:          catch (Exception e)
      69:          {
      70:              Result = (MarkupString)$"<span style='color:red'>{e.Message.Replace("\n", "<br>")}</span>";
      71:          }
      72:          StateHasChanged();
      73:      }
      74:  } 
  • Because each page use similar parameters I have created a lot of controls like this.

  • This control use Select/options.


       1:  @using Models
       2:  @inject CryptoChestNewDb.Models.ApplicationDbContext MySQL
       3:  @inject UserManager<ApplicationUser> userManager
       4:  @namespace Frontend1.Areas.Admin.Pages
       5:  @*<input @onchange="@((e)=> OnChange(e))" />*@
       6:   
       7:  <select @onchange="@((e)=> OnChange(e))">
       8:      @foreach (string item in AvailableDiscordID)
       9:      {
      10:          <option>@item</option>
      11:      }
      12:  </select>
      13:   
      14:  @code {
      15:   
      16:      [Parameter]
      17:      public string DiscordID { get; set; }
      18:   
      19:      [Parameter]
      20:      public EventCallback<string> ValueChanged { get; set; }
      21:   
      22:      List<string> AvailableDiscordID;
      23:   
      24:      void OnChange(ChangeEventArgs e)
      25:      {
      26:          DiscordID = e.Value.ToString();
      27:          ValueChanged.InvokeAsync(DiscordID);
      28:      }
      29:   
      30:      protected override void OnInitialized()
      31:      {
      32:          AvailableDiscordID = userManager.Users.Where(x => !string.IsNullOrEmpty(x.DiscordId)).Select(x => x.DiscordId).ToList();
      33:          if (AvailableDiscordID.Count > 0)
      34:          {
      35:              DiscordID = AvailableDiscordID[0];
      36:              ValueChanged.InvokeAsync(DiscordID);
      37:          }
      38:      }
      39:  }

    This control use DropdownList.


       1:  @using Models
       2:  @inject CryptoChestNewDb.Models.ApplicationDbContext MySQL
       3:  @inject UserManager<ApplicationUser> userManager
       4:  @namespace Frontend1.Areas.Admin.Pages
       5:   
       6:  <input @onchange="@((e)=> OnChange(e))" list="HtmlDataList" />
       7:  <datalist id="HtmlDataList">
       8:      @foreach (string item in AvailableUserID)
       9:      {
      10:          <option value="@item"></option>
      11:      }
      12:  </datalist>
      13:   
      14:   
      15:  @code {
      16:   
      17:      [Parameter]
      18:      public string UserId { get; set; }
      19:   
      20:      [Parameter]
      21:      public EventCallback<string> ValueChanged { get; set; }
      22:   
      23:      List<string> AvailableUserID;
      24:   
      25:      void OnChange(ChangeEventArgs e)
      26:      {
      27:          UserId = e.Value.ToString();
      28:          ValueChanged.InvokeAsync(UserId);
      29:      }
      30:   
      31:      protected override void OnInitialized()
      32:      {
      33:          AvailableUserID = userManager.Users.Select(x => x.Id).ToList();
      34:      }
      35:  }

    Also I have created control to show return type.


       1:  @using  Models
       2:  @using System.Reflection
       3:  @using System.Text
       4:   
       5:  <span style="cursor:pointer" @onclick="@(()=>ModelOpen=!ModelOpen)">
       6:      @ModelName
       7:  </span>
       8:  @if (ModelOpen)
       9:  {
      10:      <div class="text-secondary">
      11:          @SerializedModelStructure
      12:      </div>
      13:  }
      14:   
      15:   
      16:   
      17:  @code {
      18:   
      19:      [Parameter]
      20:      public string ModelName { get; set; }
      21:   
      22:      bool ModelOpen;
      23:      MarkupString SerializedModelStructure;
      24:      StringBuilder sb = new StringBuilder();
      25:   
      26:      protected override void OnInitialized()
      27:      {
      28:          Type Obj = Type.GetType("CryptoChestNewApi." + ModelName);
      29:          if (!(Obj is null)) {
      30:              PropertyInfo[] Prop = Obj.GetProperties();
      31:              Prop.ToList().OrderBy(x => x.Name).ToList().ForEach(x => sb.Append($"{x.Name} ({x.PropertyType.Name}) \n"));
      32:              SerializedModelStructure = (MarkupString)sb.ToString(0, sb.Length - 1).Replace("\n","<br>");
      33:          }
      34:      }
      35:  }

5. Blazor tests of Quartz jobs.

In this section I describe my way of periodic test. Usually main STA-operation of whole site, so named "project engine" or "project processor" doing as one thread for all site user. There are different way to perform site engine, for example its possible call it by timer:

Another way in perform periodic task by SQL server timer. Look for example this pages:

But in current project to perform site engine I use Quartz. This is my common templates to use Quarts for perform site engine:

But what difficulties to test periodic Job. First future usually you have - many job with single interface, so need to add many services with one interface, secondary future - usually job return nothing, and third future is - need to delay because you have a lot of processor usage and StateChange on Blazor frontend not receive time quantum and frontend not refreshed. And, of course, for testing need to transform IJob to testable form.

So, as first step I have transformed Quartz IJOB interface to ImyJob.


   1:  using Quartz;
   2:  using System.Threading.Tasks;
   3:   
   4:  namespace Processing
   5:  {
   6:      public interface ImyJob
   7:      {
   8:          Task Execute(IJobExecutionContext context);
   9:      }
  10:  }


As a result I have received more than ten separate service prepared for testing. All services has the same interface.



Than need to perform main step - create service resolver. This is a separate service.


   1:              services.AddTransient<ServiceResolver>(serviceProvider => prm =>
   2:              {
   3:                  switch (prm)
   4:                  {
   5:                      case "AddressImporter":
   6:                          return (ImyJob) serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "AddressImporter");
   7:                      case "CryptoProcessor":
   8:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "CryptoProcessor");
   9:                      case "DepositProcessor":
  10:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "DepositProcessor");
  11:                      case "DockerDeploymentEngine":
  12:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "DockerDeploymentEngine");
  13:                      case "DockerPendingCommandEngine":
  14:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "DockerPendingCommandEngine");
  15:                      case "GetCryptoRates":
  16:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "GetCryptoRates");
  17:                      case "MasternodeDepositEngine":
  18:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "MasternodeDepositEngine");
  19:                      case "MasternodePayoutEngine":
  20:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "MasternodePayoutEngine");
  21:                      case "ProcessRenewalsEngine":
  22:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "ProcessRenewalsEngine");
  23:                      case "ProcessUnpaidTerminations":
  24:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "ProcessUnpaidTerminations");
  25:                      case "PruneContainerData":
  26:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "PruneContainerData");
  27:                      case "RunAutoBuy":
  28:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "RunAutoBuy");
  29:                      case "StakeProcessor":
  30:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "StakeProcessor");
  31:                      case "UpdateFiatPrices":
  32:                          return (ImyJob)serviceProvider.GetServices(typeof(ImyJob)).FirstOrDefault(x => x.GetType().Name == "UpdateFiatPrices");
  33:                      default:
  34:                          throw new KeyNotFoundException();
  35:                  }
  36:              });

Then I have created a wrapper to call this backend service from my frontend test. And I have inject all ImyJob service to test controller.


  21:  namespace WebApi1.Controllers
  22:  {
  23:      [Route("CS/[controller]/[action]")]
  24:      //[ApiExplorerSettings(IgnoreApi = true)]
  25:      [ApiController]
  26:      [Produces("application/json")]
  27:      public class TestController : ControllerBase
  28:      {
  ...  
  52:          private readonly ImyJob AddressImporter;
  53:          private readonly ImyJob CryptoProcessor;
  54:          private readonly ImyJob DepositProcessor;
  55:          private readonly ImyJob DockerDeploymentEngine;
  56:          private readonly ImyJob DockerPendingCommandEngine;
  57:          private readonly ImyJob GetCryptoRates;
  58:          private readonly ImyJob MasternodeDepositEngine;
  59:          private readonly ImyJob MasternodePayoutEngine;
  60:          private readonly ImyJob ProcessRenewalsEngine;
  61:          private readonly ImyJob ProcessUnpaidTerminations;
  62:          private readonly ImyJob PruneContainerData;
  63:          private readonly ImyJob RunAutoBuy;
  64:          private readonly ImyJob StakeProcessor;
  65:          private readonly ImyJob UpdateFiatPrices;
  ...   
69: public TestController(ILogger<TestController> logger, ApplicationDbContext dbContext, IConfiguration Configuration, CoreObjectDumper.CoreObjectDumper dump, IHostingHandler _HostingHandler, IAPIHandler _APIHandler, ICryptoHandler _CryptoHandler, IUserHandler _UserHandler, IAutoBuyHandler _AutoBuyHandler, IBalanceHandler _BalanceHandler, IExchangeRateHandler _ExchangeRateHandler, IFeeHandler _FeeHandler, IMailHandler _MailHandler, IMasternodeHandler _MasternodeHandler, ISharesHandler _SharesHandler, IWebsiteHandler _WebsiteHandler, ServiceResolver _AddressImporter, ServiceResolver _CryptoProcessor, ServiceResolver _DepositProcessor, ServiceResolver _DockerDeploymentEngine, ServiceResolver _DockerPendingCommandEngine, ServiceResolver _GetCryptoRates, ServiceResolver _MasternodeDepositEngine, ServiceResolver _MasternodePayoutEngine, ServiceResolver _ProcessRenewalsEngine, ServiceResolver _ProcessUnpaidTerminations, ServiceResolver _PruneContainerData, ServiceResolver _RunAutoBuy, ServiceResolver _StakeProcessor, ServiceResolver _UpdateFiatPrices, IHubContext<OutputMessages, IOutputMessages> _outputMessages, IHubContext<ProcessorHub, IProcessorHub> _processorHub)
  ...          {
  ...          
  89:              AddressImporter = _AddressImporter("AddressImporter");
  90:              CryptoProcessor = _CryptoProcessor("CryptoProcessor");
  91:              DepositProcessor = _DepositProcessor("DepositProcessor");
  92:              DockerDeploymentEngine = _DockerDeploymentEngine("DockerDeploymentEngine");
  93:              DockerPendingCommandEngine = _DockerPendingCommandEngine("DockerPendingCommandEngine");
  94:              GetCryptoRates = _GetCryptoRates("GetCryptoRates");
  95:              MasternodeDepositEngine = _MasternodeDepositEngine("MasternodeDepositEngine");
  96:              MasternodePayoutEngine = _MasternodePayoutEngine("MasternodePayoutEngine");
  97:              ProcessRenewalsEngine = _ProcessRenewalsEngine("ProcessRenewalsEngine");
  98:              ProcessUnpaidTerminations = _ProcessUnpaidTerminations("ProcessUnpaidTerminations");
  99:              PruneContainerData = _PruneContainerData("PruneContainerData");
 100:              RunAutoBuy = _RunAutoBuy("RunAutoBuy");
 101:              StakeProcessor = _StakeProcessor("StakeProcessor");
 102:              UpdateFiatPrices = _UpdateFiatPrices("UpdateFiatPrices");
 103:          }


Than I have created wrapper to call each job in Test controller.



Thats it in backend side. Next step need to create in frontend.


Firstly, this is a page with list of services to be testing.



Each page has the same structure.



   1:  @page "/Admin/Service/JobDepositProcessor"
   2:  @using CoreObjectDumper;
   3:  @using System.Net.Http.Headers
   4:  @using System.Threading
   5:  @using Frontend1.Areas.Admin.Pages
   6:  @using CryptoChestNewApi
   7:   
   8:   
   9:   
  10:  <h3>Job/DepositProcessor</h3>
  11:   
  12:   
  13:  <div class="row">
  14:      <JwtAuSelector @ref="JwtSelectorRef"></JwtAuSelector>
  15:  </div>
  16:  <div style="margin:30px;" class="row">
  17:      <button class="btn btn-primary" @onclick="@call">Execute</button>&nbsp;
  18:  </div>
  19:  <div class="text-primary">
  20:      NoResult
  21:  </div>
  22:  <br />
  23:  @Result
  24:   
  25:  @code {
  26:   
  27:   
  28:   
  29:      JwtAuSelector JwtSelectorRef;
  30:      MarkupString Result;
  31:      CancellationTokenSource cancelTokenSource = new CancellationTokenSource();
  32:   
  33:      protected async void call()
  34:      {
  35:          try
  36:          {
  37:              Result = (MarkupString)"waiting ...";
  38:              StateHasChanged();
  39:              await Task.Delay(1000);
  40:              cancelTokenSource.Dispose();
  41:              cancelTokenSource = new CancellationTokenSource();
  42:              HttpClient httpClient = new HttpClient();
  43:              httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", JwtSelectorRef.JWT);
  44:              httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var Api = new CryptoChestNewApi.Client(Program.ApiUrl, httpClient);
  45:              Task Res1 = Task.Run(() => Api.RunDepositProcessorAsync(cancelTokenSource.Token));
  46:              Res1.Wait();
  47:              if (Res1.IsCompletedSuccessfully) { Result = (MarkupString)"Success"; };
  48:          }
  49:          catch (Exception e)
  50:          {
  51:              Result = (MarkupString)$"<span style='color:red'>{e.Message.Replace("\n", "<br>")}</span>";
  52:          }
  53:          StateHasChanged();
  54:      }
  55:  }

So, this is what the my test looks like.



6. SignalR messenger function tests.

Correct way to inject typed SignalR server you can see in this my page.


Also you can create simplest console test for SignalR messenger.

For testing SignalR messenger in Blazor I have created special page.


   1:  @page "/Admin/SignalClient"
   2:  @*@using Blazor.Extensions*@
   3:  @using Microsoft.AspNetCore.SignalR.Client
   4:  @inject CryptoChestNewDb.Models.ApplicationDbContext MySQL
   5:   
   6:  <span style="margin:30px;"> @HubURL</span><span>@ConnectionID</span> &nbsp;<span style="color:red">@ConnectionStatus</span>
   7:  <div style="border:solid 1px; width:500px">
   8:   
   9:      <JwtAuSelector @ref="JwtSelectorRef" UserChanged="@((x)=>userChanged(x))"></JwtAuSelector>
  10:      <div style="margin-left:30px">
  11:          Hub:<br />
  12:          <select @bind-value="HubSelectedVal" @bind-value:event="HubSelected" @onchange="(e)=> HubSelected(e)">
  13:              @foreach (string one in HubMethods)
  14:              {
  15:                  @if (one == "AdminMessages2(OutputMessages)")
  16:                  {
  17:                      <option value="@one" selected>@one</option>
  18:                  }
  19:                  else
  20:                  {
  21:                      <option value="@one">@one</option>
  22:                  }
  23:   
  24:              }
  25:          </select>
  26:      </div>
  27:   
  28:      <input type="button" class="btn btn-primary" style="margin:30px;" @onclick="@Connect" value="connect" />
  29:  </div>
  30:  <br />
  31:  <br />
  32:  <div style="border:solid 1px; width:500px;padding:30px">
  33:      <input type="text" class="form-control" @bind="@message" placeholder="message" /><br />
  34:      <input type="button" value="Send" class="btn btn-primary" @onclick="@SendMessage" />
  35:  </div>
  36:  <div class="container">
  37:      <ul id="discussion">
  38:          @foreach (var message in messages)
  39:          {
  40:              <li>@message</li>
  41:          }
  42:      </ul>
  43:  </div>
  44:  <div class="container" style="color:red">
  45:      @lErr1
  46:  </div>
  47:   
  48:   
  49:  @code {
  50:   
  51:      List<string> HubMethods = new List<string> {
  52:          "AdminMessages(OutputMessages)",
  53:          "AdminMessages2(OutputMessages)",
  54:          "CrossServerNotice(OutputMessages)",
  55:          "NewDepositNotice(OutputMessages)",
  56:          "NewMasternodePayoutNotice(OutputMessages)",
  57:          "NewSharePurchase(OutputMessages)",
  58:          "NewShareSale(OutputMessages)",
  59:          "SendUserMessage(OutputMessages)",
  60:          "SendUserMessageNoContainer(OutputMessages)",
  61:          "UserPayoutNotice(OutputMessages)",
  62:          "RestartProcessor(ProcessorHub)",
  63:          "StartProcessor(ProcessorHub)",
  64:          "StopProcessor(ProcessorHub)",
  65:          "ReinstallProcessor(ProcessorHub)",
  66:          "TerminateProcessor(ProcessorHub)",
  67:          "SystemAlert(ProcessorHub)",
  68:          "ApplicationApiAlert(ProcessorHub)"
  69:  };
  70:      MarkupString lErr1;
  71:      string JWT;
  72:      Guid sessionId;
  73:      JwtAuSelector JwtSelectorRef;
  74:      void userChanged(object UserID)
  75:      {
  76:          var apiSessionKey = MySQL.ApiSessionKeys.Where(x => x.UserId == Guid.Parse(UserID.ToString()).ToString()).FirstOrDefault();
  77:          if (apiSessionKey != null)
  78:          {
  79:              sessionId = apiSessionKey.SessionId;
  80:              JWT = JwtSelectorRef.JWT;
  81:          }
  82:      }
  83:   
  84:      protected override void OnInitialized()
  85:      {
  86:          HubURL = Program.ApiUrl + "CS/OutputMessages";
  87:      }
  88:   
  89:      void HubSelected(ChangeEventArgs e)
  90:      {
  91:          int s1 = e.Value.ToString().IndexOf("(");
  92:          HubMethod = e.Value.ToString().Substring(0, s1);
  93:          HubURL = Program.ApiUrl + "CS/" + e.Value.ToString().Substring(s1 + 1).TrimEnd(')');
  94:      }
  95:   
  96:      string ConnectionID;
  97:      string ConnectionStatus;
  98:      string HubSelectedVal;
  99:      string HubMethod = "AdminMessages2";
 100:      string HubURL;
 101:      HubConnection connection;
 102:      string message = "";
 103:      IList<string> messages = new List<string>();
 104:   
 105:      async Task Connect()
 106:      {
 107:   
 108:          connection = new HubConnectionBuilder().WithUrl(HubURL, opt => opt.Headers.Add("Authorization", JWT)).
 109:              WithAutomaticReconnect(new[] { TimeSpan.Zero, TimeSpan.Zero, TimeSpan.FromSeconds(10) }).
 110:              Build();
 111:          connection.On<string, string, Boolean>(HubMethod, async (a, b, c) => await ReceiveMessage(a, b, c));
 112:          connection.Closed += async (error) =>
 113:          {
 114:              await Task.Delay(new Random().Next(0, 5) * 1000);
 115:              await connection.StartAsync();
 116:          };
 117:          connection.Reconnecting += async (e) =>
 118:          {
 119:              await Task.Run(() =>
 120:              {
 121:                  ConnectionStatus = "...";
 122:                  lErr1 = (MarkupString)e.Message.Replace("\n", "<br>");
 123:              });
 124:          };
 125:   
 126:          try
 127:          {
 128:              await connection.StartAsync();
 129:          }
 130:          catch (Exception e)
 131:          {
 132:              string lErr = $"URL={HubURL}<br>HubMethod={HubMethod}<br>JWT={JWT}";
 133:              lErr += e.ToString().Replace("\n", "<br>");
 134:              lErr1 = (MarkupString)lErr;
 135:          }
 136:          lErr1 = (MarkupString)"";
 137:          ConnectionID = connection.ConnectionId;
 138:          ConnectionStatus = connection.State.ToString();
 139:          StateHasChanged();
 140:   
 141:      }
 142:   
 143:      Task ReceiveMessage(string UserId, string message, Boolean b)
 144:      {
 145:          messages.Add(UserId + " : " + message);
 146:          StateHasChanged();
 147:          return Task.CompletedTask;
 148:      }
 149:   
 150:      async Task SendMessage()
 151:      {
 152:          await connection.InvokeAsync(HubMethod,  message);
 153:          message = "";
 154:      }
 155:  }



Comments ( )
<00>  <01>  <02>  <03>  <04>  <05>  <06>  <07>  <08>  <09>  <10>  <11>  <12>  <13>  <14>  <15>  <16>  <17>  <18>  <19
Link to this page: http://www.vb-net.com/CoreLinuxMiddlewareTest/Index.htm
<SITEMAP>  <MVC>  <ASP>  <NET>  <DATA>  <KIOSK>  <FLEX>  <SQL>  <NOTES>  <LINUX>  <MONO>  <FREEWARE>  <DOCS>  <ENG>  <MAIL ME>  <ABOUT ME>  < THANKS ME>