Calling SignalR functions outside of Hub Class - dependency-injection

I am are trying to get an instance of a Hub Class to call front-end methods from the backend from a class outside of the Hub class.
I am using IHostLifeTime that has a register function that will be running in the background while the server is running in a while loop.
There will be events in the while loop that will trigger signalR to send a message to the client.
Question: How am I supposed to get access to the hub and send a message to the client inside of my manager class in the ApplicationReady() function?
TestHub.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
namespace SignalREventHandle
{
public class TestHub : Hub
{
public async Task SendMessage(string user, string message)
{
Console.WriteLine($"user: {user} message:{message}");
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
}
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using System.Threading;
namespace SignalREventHandle
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSignalR();
}
// This method gets called by the runtime. Use this method to configure the HTTP request //pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHostApplicationLifetime lifetime, IHubContext<TestHub> hubContext)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
lifetime.ApplicationStarted.Register(OnAppStarted);
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<TestHub>("/testHub");
});
}
public async void OnAppStarted()
{
//Get Singleton Instance of Manager and then start the application
var manager = Manager.Instance;
manager.ApplicationReady();
}
}
}
Manager.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
namespace SignalREventHandle
{
public class Manager
{
private bool _isServerRunning;
/// <summary>
/// Instance of class to implement Singleton
/// </summary>
private static readonly Manager _instance = new();
/// <summary>
/// Getter for Class instance
/// </summary>
public static Manager Instance
{
get => _instance;
}
public async void ApplicationReady()
{
var task = Task.Run(() =>
{
_isServerRunning = true;
while (_isServerRunning)
{
// Want to Send Message to Client with SignalR here
Thread.Sleep(10000);
}
});
}
}
}

In ASP.NET 4.x SignalR use GlobalHost to provide access to the IHubContext:
public static async Task SendMessage(string user, string message)
{
Console.WriteLine($"user: {user} message: {message}");
// Get an instance of IHubContext from GlobalHost
var hubContext = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
await hubContext.Clients.All.SendAsync("ReceiveMessage", user, message);
}
In ASP.NET Core SignalR, you can access an instance of IHubContext from the web host.
Program.cs
public class Program
{
public static IHost WebHost;
public static void Main(string[] args)
{
WebHost = CreateHostBuilder(args).Build();
WebHost.Run();
}
...
}
Then:
public static async Task SendMessage(string user, string message)
{
Console.WriteLine($"user: {user} message: {message}");
// Get an instance of IHubContext from IHost
var hubContext = Program.WebHost.Services.GetService(typeof(IHubContext<ChatHub>)) as IHubContext<ChatHub>;
await hubContext.Clients.All.SendAsync("ReceiveMessage", user, message);
}
Documentation:
https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-5.0
signalr how i can i post a message from server to caller

Related

How to simply execute stored procedure in blazor server app?

I am building a Blazor server app using a repository pattern and am trying to execute a stored procedure from a Razor component page.
However I am getting an error when I run it:
blazor.server.js:21 [2021-03-29T02:03:38.207Z] Error: System.InvalidOperationException: Cannot provide a value for property 'ICalculateImportanceService' on type 'ThePositionerBlazorServerDapperSyncfusion.Pages.DBProcessing.CalculateItemImportance'. There is no registered service of type 'ThePositionerBlazorServerDapperSyncfusion.Data.CalculateImportanceService'.
Not sure why I am getting the error because I believe I registered the service.
Here is the relevant code:
CalculateItemImportance.razor
#using ThePositionerBlazorServerDapperSyncfusion.Data
#page "/DBProcessing/calcultateitemImportance"
#inject CalculateImportanceService ICalculateImportanceService
#inject NavigationManager NavigationManager
<h1 style="text-align:center">#pagetitle</h1>
#code {
public string pagetitle = "Calculate Importance";
protected override async Task OnInitializedAsync()
{
// Kick off stored procedure to calculate importance
await ICalculateImportanceService.CalculateImportance();
}
}
ICalculateImportanceService.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using ThePositionerBlazorServerDapperSyncfusion.Data;
namespace ThePositionerBlazorServerDapperSyncfusion.Data
{
public interface ICalculateImportanceService
{
Task<bool> CalculateImportance();
}
}
CalculateImportanceService.cs
using Dapper;
using Microsoft.Data.SqlClient;
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
namespace ThePositionerBlazorServerDapperSyncfusion.Data
{
public class CalculateImportanceService : ICalculateImportanceService
{
private readonly SqlConnectionConfiguration _configuration;
public CalculateImportanceService(SqlConnectionConfiguration configuration)
{
_configuration = configuration;
}
public async Task<bool> CalculateImportance()
{
using (var conn = new SqlConnection(_configuration.Value))
{
await conn.ExecuteAsync("CalculateTheItemImportance", commandType: CommandType.StoredProcedure);
}
return true;
}
}
}
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ThePositionerBlazorServerDapperSyncfusion.Data;
using Syncfusion.Blazor;
namespace ThePositionerBlazorServerDapperSyncfusion
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
//services.AddSingleton<WeatherForecastService>();
//Syncfusion support
services.AddSyncfusionBlazor();
services.AddControllers().AddNewtonsoftJson();
var sqlConnectionConfiguration = new SqlConnectionConfiguration(Configuration.GetConnectionString("SqlDBContext"));
services.AddSingleton(sqlConnectionConfiguration);
services.AddScoped<IApplicConfsService, ApplicConfsService>();
services.AddScoped<IPOSSUMMARYService, POSSUMMARYService>();
services.AddScoped<IPOSDETAILService, POSDETAILService>();
services.AddScoped<IDESCRIPTIONTYPEService, DESCRIPTIONTYPEService>();
services.AddScoped<IIMPService, IMPService>();
services.AddScoped<IITEMCATEGORYService, ITEMCATEGORYService>();
services.AddScoped<IKNOWDEPService, KNOWDEPService>();
services.AddScoped<IMembersService, MembersService>();
services.AddScoped<IPRDSRVService, PRDSRVService>();
services.AddScoped<IPROCESSESService, PROCESSESService>();
services.AddScoped<ITASKKNOService, TASKKNOService>();
services.AddScoped<ITMPOVERLAPService, TMPOVERLAPService>();
services.AddScoped<ITEXTUALService, TEXTUALService>();
services.AddScoped<ITIMESCALEService, TIMESCALEService>();
services.AddScoped<IWORKHIERService, WORKHIERService>();
services.AddScoped<ICalculateImportanceService, CalculateImportanceService>();
services.AddScoped<ICalculateFTEService, CalculateFTEService>();
}
}
}
The stored procedure has no parameters and simply executes processing on the database.
Any help would be appreciated. Thanks
Change
#inject CalculateImportanceService ICalculateImportanceService
to
#inject ICalculateImportanceService CalculateImportanceService
and further down
// await ICalculateImportanceService.CalculateImportance();
await CalculateImportanceService.CalculateImportance();

Stored procedure ADO.NET .NET Core Web API

I am building a Web API in ASP.NET Core, I am using stored procedures to be able to handle more complex queries, which with the Entity Framework is too complicated for me, I am using ADO.NET to make this connection.
I have managed to connect to a stored procedure and use the get and post methods, the point is that I don't know how to do it in order to call the other stored procedures and map a route to interact via get or post in the same project. I have only been able to do one, and I don't think it would be more convenient to create a Web API for each function that complies with a stored procedure.
My project is made up of three folders called Controller, Data, Models.
Within Models is the Value class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ASPNETCore_StoredProcs.Models
{
public class Value
{
public int Id { get; set; }
public int Value1 { get; set; }
public string Value2 { get; set; }
}
}
Data folder has a class called ValueRepository
using ASPNETCore_StoredProcs.Models;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;
namespace ASPNETCore_StoredProcs.Data
{
public class ValuesRepository
{
private readonly string _connectionString;
public ValuesRepository(IConfiguration configuration)
{
_connectionString = configuration.GetConnectionString("defaultConnection");
}
public async Task<List<Value>> GetAll()
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("GetAllValues", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
var response = new List<Value>();
await sql.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
response.Add(MapToValue(reader));
}
}
return response;
}
}
}
private Value MapToValue(SqlDataReader reader)
{
return new Value()
{
Id = (int)reader["Id"],
Value1 = (int)reader["Value1"],
Value2 = reader["Value2"].ToString()
};
}
public async Task<Value> GetById(int Id)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("GetValueById", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#Id", Id));
Value response = null;
await sql.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
response = MapToValue(reader);
}
}
return response;
}
}
}
public async Task Insert(Value value)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("InsertValue", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#value1", value.Value1));
cmd.Parameters.Add(new SqlParameter("#value2", value.Value2));
await sql.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return;
}
}
}
public async Task DeleteById(int Id)
{
using (SqlConnection sql = new SqlConnection(_connectionString))
{
using (SqlCommand cmd = new SqlCommand("DeleteValue", sql))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#Id", Id));
await sql.OpenAsync();
await cmd.ExecuteNonQueryAsync();
return;
}
}
}
}
}
and finally I have a controller class called ValuesController:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ASPNETCore_StoredProcs.Data;
using ASPNETCore_StoredProcs.Models;
using Microsoft.AspNetCore.Mvc;
namespace ASPNETCore_StoredProcs.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly ValuesRepository _repository;
public ValuesController(ValuesRepository repository)
{
this._repository = repository ?? throw new ArgumentNullException(nameof(repository));
}
// GET api/values
[HttpGet]
public async Task<ActionResult<IEnumerable<Value>>> Get()
{
return await _repository.GetAll();
}
// GET api/values/5
[HttpGet("{id}")]
public async Task<ActionResult<Value>> Get(int id)
{
var response = await _repository.GetById(id);
if (response == null) { return NotFound(); }
return response;
}
// POST api/values
[HttpPost]
public async Task Post([FromBody] Value value)
{
await _repository.Insert(value);
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody] string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public async Task Delete(int id)
{
await _repository.DeleteById(id);
}
}
}
This is my startup class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ASPNETCore_StoredProcs.Data;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace ASPNETCore_StoredProcs
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<ValuesRepository>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
}
}
I appreciate if you can help me or if it is how I think I should create an API for each procedure that is performed thanks

Appsettings and middleware Core 2.1

I have made a custom middleware and now I want to access the appsettings that are in another project in my solution. Should I inject the IConfiguration object into the middleware constructor, and add the using statement of Microsoft.Extensions.Configuration? Or is there a better way to do this?
I am working with ASP.net Web page with Core 2.1.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using System;
using System.Threading.Tasks;
public class MyMiddleware
{
public IConfiguration _configuration;
public MyMiddleware(RequestDelegate next, IConfiguration config)
{
_next = next;
_ configuration = config;
}
If you don't need all your configuration passed to your middleware but just a section you can use
IOptions<T>
Create MyConfig.cs Class file:
public class MyConfig
{
public string MyConfig1 {get; set;}
public string MyConfig2 {get; set;}
}
In ConfigureServices method in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyConfig>(Configuration.GetSection("MyConfig"));
}
In your middleware
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using System;
using System.Threading.Tasks;
public class MyMiddleware
{
private readonly IOptions<MyConfig> _appSettings;
public MyMiddleware(RequestDelegate next, IOptions<MyConfig> config)
{
_next = next;
_configuration = config;
}
public MyMethod()
{
_configuration.Value.MyConfig1
}
}
In appsettings.json file:
{
"AppSettings": {
"AppId": "0001",
"AppName": "xxxx",
},
"MyConfig": {
"MyConfig1": "xxxxxxx",
"MyConfig2": "xxxxxxx",
},
}

Is there a session start equivalent in .Net Core MVC 2.1?

In MVC 5 you could assign a value to session in global.asx when the session started. Is there a way you can do this in .Net Core MVC? I have session configured but in the middleware it seems to get called on every request.
nercan's solution will work, but I think I found a solution that requires less code and may have other advantages.
First, wrap DistributedSessionStore like this:
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Session;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
public interface IStartSession
{
void StartSession(ISession session);
}
public class DistributedSessionStoreWithStart : ISessionStore
{
DistributedSessionStore innerStore;
IStartSession startSession;
public DistributedSessionStoreWithStart(IDistributedCache cache,
ILoggerFactory loggerFactory, IStartSession startSession)
{
innerStore = new DistributedSessionStore(cache, loggerFactory);
this.startSession = startSession;
}
public ISession Create(string sessionKey, TimeSpan idleTimeout,
TimeSpan ioTimeout, Func<bool> tryEstablishSession,
bool isNewSessionKey)
{
ISession session = innerStore.Create(sessionKey, idleTimeout, ioTimeout,
tryEstablishSession, isNewSessionKey);
if (isNewSessionKey)
{
startSession.StartSession(session);
}
return session;
}
}
Then register this new class in Startup.cs:
class InitSession : IStartSession
{
public void StartSession(ISession session)
{
session.SetString("Hello", "World");
}
}
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSingleton<IStartSession, InitSession>();
services.AddSingleton<ISessionStore, DistributedSessionStoreWithStart>();
services.AddSession();
...
}
Full code is here:
https://github.com/SurferJeffAtGoogle/scratch/tree/master/StartSession/MVC
I use it in a live project. It works correctly. if you want to keep it when the application stops. You should use DistributedCache. For example, I'm using DistributedRedisCache.
Add to startup this code;
public void ConfigureServices(IServiceCollection services)
{
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(60);
options.Cookie.HttpOnly = true;
});
// for redis distributed cache
//services.AddDistributedRedisCache(options =>
// {
// options.InstanceName = $"{Configuration["DistributedRedisCacheInstance"]}";
// options.Configuration = $"{Configuration["DistributedRedisCacheHost"]}";
// });
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IHttpContextAccessor acc)
{
app.UseSession();
}
And add new session extension;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System.Text;
namespace SampleApp
{
public static class SessionExtensions
{
public static void SetObjectAsJson<T>(this ISession session, string key, T value)
{
session.Set(key, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)));
}
public static T GetObjectFromJson<T>(this ISession session, string key)
{
session.TryGetValue(key, out byte[] dataByte);
string data = dataByte != null ? Encoding.UTF8.GetString(dataByte) : null;
return data == null ? default(T) : JsonConvert.DeserializeObject<T>(data);
}
}
}
And use get or set same this;
var sessionItem = httpContext.Session.GetObjectFromJson<string>("sessionItem");
//or
ContextProviderExtension.HttpContextAccessor.HttpContext.Session.SetObjectAsJson("sessionItem", sessionItem);
you need this extension;
using Microsoft.AspNetCore.Http;
using System;
namespace SampleApp
{
public static class ContextProviderExtension
{
static IHttpContextAccessor httpContextAccessor = null;
public static IHttpContextAccessor HttpContextAccessor
{
get { return httpContextAccessor; }
set
{
if (httpContextAccessor != null)
{
throw new Exception("");
}
httpContextAccessor = value;
}
}
}
}
I suppose it will work.
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
namespace SampleApp
{
public class SessionMiddleware
{
private readonly RequestDelegate _next;
public SessionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
var sessionItem = httpContext.Session.GetObjectFromJson<string>("test");
if (sessionItem == null)
httpContext.Session.SetObjectAsJson<string>("test", httpContext.Session.Id);//httpContext.Session.Id or set a value
await _next.Invoke(httpContext);
}
}
public static class SessionMiddlewareExtensions
{
public static IApplicationBuilder UseSessionMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<SessionMiddleware>();
}
}
}
and add startup.cs Configure method after app.UseSession();
app.UseSessionMiddleware();

ASPNETCORE ConfigureServices does not run

I follow the Microsoft document to implement the policy-based authorization in my web service but the function "ConfigureServices" does not run. Please let me know if I have something missing.
Startup.cs
using Microsoft.Owin;
using Owin;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
[assembly: OwinStartupAttribute(typeof(WebApplication1.Startup))]
namespace WebApplication1
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureServices(IServiceCollection services)
{
//Some codes here...
}
}
}

Resources