locked
Manually setting connection string of DbContext in a scope in a SignalR Core hub RRS feed

  • Question

  • User-1142747527 posted

    I have an ASP .Net Core 2,2 Web API, which uses SignalR. I need to save messages sent to the hub (from the client) to the database, so I need an instance of DbContext in my hub. I've got that, but because this is a multi-tenant application, I need to set the connection string of the DbContext depending which client is sending the message.

    My code:

    public class ChatHub : Hub
    {
    
        private IServiceProvider _serviceProvider;
    
        public ChatHub(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
    
        public async Task SendMessage(string user, string message)
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                var dbContext = scope.ServiceProvider.GetRequiredService<TenantContext>();
                // Set dbContext ConnectionString
                Message newMessage = new Message(user, message);
                dbContext.Messages.Add(newMessage);
                dbContext.SaveChanges();
            }
            await Clients.All.SendAsync("ReceiveMessage", user, message); } }

    As you can see, I'm creating <g class="gr_ gr_23 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar only-del replaceWithoutSep" id="23" data-gr-id="23">a scope</g> in my <g class="gr_ gr_24 gr-alert gr_gramm gr_inline_cards gr_run_anim Punctuation only-del replaceWithoutSep" id="24" data-gr-id="24">hub,</g> and then requesting the DbContext. However, because this is a multi-tenant application, the DbContext's <g class="gr_ gr_20 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace" id="20" data-gr-id="20">ConnectionString</g> isn't set (it's NULL). The connection string normally gets set by reading a TenantId header in the HTTP requests from the client to the API (in some middleware) which gets saved in HttpContext. Then, in my DbContext.OnConfiguring() method, I create the connection string based on what was saved in HttpContext (in the middleware).

    However, when the client sends a message to the hub, it doesn't trigger the middleware. This means that when I create a scope and grab a DbContext, DbContext.OnConfiguring() isn't able to set the connection string because the middleware never fired to read the TeantId off of the HTTP request header.

    My question is, Is it possible to manually set the DbContext's connection string in my hub?

    Wednesday, June 12, 2019 7:47 AM

Answers

  • User765422875 posted

    This is how you can do it.

    var options = new DbContextOptionsBuilder<TenantContext>()
                    .UseSqlServer("YOUR CONNECTION STRING")
                    .Options;
    
    using (var context = new TenantContext(options))
    {
    
    }
    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, June 12, 2019 11:43 AM

All replies

  • User1120430333 posted

    My question is, Is it possible to manually set the DbContext's connection string in my hub?

    Yeah, you can probably do it, but it would have to be based on some functionality you created yourself to get the connectionstring to the  DbContext.OnConfiguring() at time of execution. 

    You could look into using IOptions, a custom class of connectionstrings, and a tenant indicator being passed into the DBContext's constructor.  The  OnConfiguring() would call this connectionstring selection function to select the connectionstring to use at runtime.

    I kind of show how to get the connectionstring to OnConfiguring() that uses the custom function that returns the connectionstring in the below thread.

    https://social.msdn.microsoft.com/Forums/vstudio/en-US/64aa3ae1-b82e-4b79-be45-f193e1d97876/how-do-i-access-class-propertysettings-in-net-core?forum=csharpgeneral

    Maybe, the light bulb will come on for you where you can create something that will work for you.

    Keep in mind that there are EF Core tools or techniques that will allow you  to update the Dbcontext and not disturb existing code in the Dbcontext. 

    Wednesday, June 12, 2019 11:41 AM
  • User765422875 posted

    This is how you can do it.

    var options = new DbContextOptionsBuilder<TenantContext>()
                    .UseSqlServer("YOUR CONNECTION STRING")
                    .Options;
    
    using (var context = new TenantContext(options))
    {
    
    }
    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, June 12, 2019 11:43 AM
  • User-1142747527 posted

    Thanks <g class="gr_ gr_30 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace" id="30" data-gr-id="30">deepalgorithm</g>. I'm just not sure how to provide the options object o the TenantContext object the way I'm calling the service:

    using (var scope = _serviceProvider.CreateScope())
           
    {
               
    var dbContext = scope.ServiceProvider.GetRequiredService<TenantContext>();
            }

    Where do I stick options in the above example?

    Wednesday, June 12, 2019 11:56 AM
  • User765422875 posted

    If I understood your question, you want to dynamically create an instance of your DbContext class within your Hub method. You do not need to do anything with scope. Just do what I showed you. I'm not sure where you will get the connection string though. Regardless, this is how you can manually set up a DbContext.

    Wednesday, June 12, 2019 12:23 PM
  • User-1142747527 posted

    Thanks <g class="gr_ gr_5 gr-alert gr_spell gr_inline_cards gr_run_anim ContextualSpelling ins-del multiReplace" id="5" data-gr-id="5">deepalgorithm</g>. I was creating a scope because I read that's how one should request a DbContext service in a SignalR hub. But I just tried without the scope (like you suggested) and it worked perfectly. Thank you!

    Wednesday, June 12, 2019 12:36 PM
  • User765422875 posted

    Great. Happy I was able to help you out.

    Wednesday, June 12, 2019 12:54 PM