How to call custom class from custom startup?

Hello to all,

I wrote the following Custom.Startup.cs:

namespace GslpaMdev

{

public partial class Startup

{

    partial void OnConfigureServices(IServiceCollection services)

    {

            services.AddDbContext<GslpaMdev.Data.GslpamDbContextRLS>(options =>

            {

                options.UseSqlServer(Configuration.GetConnectionString("GslpamDbConnection"));

            });

                    

    }

}

}

This should call the following custom class "GslpamDbContext.Custom.cs":

namespace GslpaMdev.Data

{

public partial class GslpamDbContextRLS : Microsoft.EntityFrameworkCore.DbContext

{

    private readonly string UserId;

    private readonly IHttpContextAccessor httpAccessor;



    //Setting sql session state in constructor

    public GslpamDbContextRLS(IHttpContextAccessor httpAccessor, DbContextOptions<GslpamDbContextRLS> options):base(options)

    {

        this.Database.OpenConnection();

        this.httpAccessor = httpAccessor;

        UserId = httpAccessor.HttpContext.User.FindFirst(ClaimTypes.Name).Value;

        SetUserIdInSqlSession();

    }



    private void SetUserIdInSqlSession()

    {



    var sqlParameter = new SqlParameter("@UserId", UserId);

    this.Database.ExecuteSqlRaw(

        sql: "EXEC SP_SET_SESSION_CONTEXT @key=N'UserId' ,@value=@UserId",

        parameters: sqlParameter);

    }

    public GslpamDbContextRLS(IHttpContextAccessor httpAccessor)

    {

        this.httpAccessor = httpAccessor;

    }

}

}

But it doesn`t work at all.

Note: it works if I paste the code to my default generated "GslpamDbContext.cs" and let it call from default Startup.cs, but I don`t want to add these files to code generation ignore list...

Can somebody help me with this issue?

Thanks in advance,

Martin

If this is a partial class on an already generated DbContext by Radzen you do not have to execute again

since it's already added in the auto-generated Startup.cs.

What's the desired result? To access IHttpContextAccessor in the DbContext? This is already there in the generated DbContext

Not sure where its actually stored but in VS the file has a build action.
image

I want to inject current UserId in DB SessionContext, so I need access to the httpAccessor to get current UserId from context. The default generated GslpamDbContext.cs has all the properties to achieve this. So I set up a custom DbContext class for this purpose only.

See my previous reply. It's already generated by Radzen in the main partial DbContext class.

Ok, I changed it to the following and expected it to get executed by startup.cs (services.AddDbContext...):

namespace GslpaMdev.Data

{

public partial class GslpamDbContext

{

    private string UserId;



    //Setting sql session state in constructor

    public void RLS(IHttpContextAccessor httpAccessor, DbContextOptions<GslpamDbContext> options)

    {

        this.Database.OpenConnection();


        UserId = httpAccessor.HttpContext.User.FindFirst(ClaimTypes.Name).Value;

        SetUserIdInSqlSession();

    }



    private void SetUserIdInSqlSession()

    {



        var sqlParameter = new SqlParameter("@UserId", UserId);

        this.Database.ExecuteSqlRaw(

            sql: "EXEC SP_SET_SESSION_CONTEXT @key=N'UserId' ,@value=@UserId",

            parameters: sqlParameter);

    }

}

}

It is still not executed :frowning:
It seems i'm missing a step....

Thanks in advance,

Martin

Hey @mkfromhighlands,

This is again not a proper way to use a partial class not to mention that the constructor of the DbContext and the DbContext in general is too early to get the authenticated user. The extension point in the DbContext that is added by Radzen is OnModelBuilding partial method


however again this is too early to get the user:

You should consider moving your code at later stage in the application cycle where there is already available user - for example in your controllers.

Thank you for your advice but

  1. I need it as early as possible to write DB SessionContext immediately after logging on to my app
  2. again: it works if I use it in default DBContext:

pic

Hi,

If it’s available for you in constructor then it will be in the partial method I’ve showed you how to use. I’m afraid that I cannot add anything else to this thread.

Hi,

I also tried your approach with using "OnModelBuilding" partial method, but this doesn`t work:

pic2

It's not clear exactly what you are trying to achieve.
Does the user log in and if so how?
Don't you need to run your code after the user successfully logs in?

Hello there,

User logs on at my radzen app (radzen internal security). At this moment, DB Connection has to be injected with "SP_SET_SESSION_CONTEXT" which writes current UserId to DB_SessionContext to enable RowLevelSecurityPolicy.

Please see this article for further details:
https://hryniewski.net/2018/02/22/using-ms-sqls-row-level-security-policy-in-entity-framework/

I want to achieve exactly what is described in this article, but I need to do it with custom classes as it wouldn´t be good to add default DbContext.cs to the code generation ignore list of radzen.

Those articles appear to be dated and based on EF6 and .net 4.7
Have you checked out Global Query Filters?


Hello @mumfie, thanks for your effort.

in the posted article partly EF6 has been used ("state change event"), but the approaches with "constructor" and "interceptors" work with earlier versions.

Again to clarify: i have already written several times that the constructor variant works. So I don't understand why i can't call a custom constructor in a custom class that inherits from default dbcontext.

There must be an error on my part that prevents the call from startup.cs.

You can create a custom constructor with different parameters the issue is getting EF to automatically use it.

@mumfie: Yes, this is exactly what I´m trying to do but at "base(options)", it shows me the following error:

Argument 1: cannot convert from 'Microsoft.EntityFrameworkCore.DbContextOptions<GslpaMdev.Data.GslpamDbContext>' to 'Microsoft.AspNetCore.Http.IHttpContextAccessor' [project]csharp(CS1503)

I also have seen your approach here, too: AddDbContext/src/DbContextInherit at master · Ettery/AddDbContext · GitHub which uses many inherited DbContexts.