Trying to create generic DbContext with generic DbSets Types

41 Views Asked by At

I'm still a beginner with EFCore and I was trying to do this

public class AuthDbContxt<TUser, TUserId> : DbContext where TUser : class, IUser<TUserId>
{

    public AuthDbContxt(DbContextOptions options) : base(options)
    {

    }

    public DbSet<TUser> Users { get; set; }

    public DbSet<Claim<TUser>> Claims { get; set; }

    public DbSet<Role<TUser>> Roles { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {

        modelBuilder.HasDefaultSchema("Auth");

        modelBuilder.Entity<TUser>()
            .ToTable("Users")
            .HasKey(x => x.Id);

        modelBuilder.Entity<TUser>()
            .HasMany(x => x.Claims)
            .WithMany(x => (IEnumerable<TUser>)x.Users)
            .UsingEntity(j => j.ToTable("UserClaims"));

        modelBuilder.Entity<TUser>()
            .HasMany(x => x.Roles)
            .WithMany(x => (IEnumerable<TUser>)x.Users)
            .UsingEntity(j => j.ToTable("UserRoles"));

        modelBuilder.Entity<Claim<TUser>>()
            .ToTable("Claims")
            .HasKey(x => x.Id);

        modelBuilder.Entity<Role<TUser>>()
            .ToTable("Roles")
            .HasKey(x => x.Id);

        modelBuilder.Entity<Role<TUser>>()
            .HasMany(x => x.Claims)
            .WithMany(x => x.Roles)
            .UsingEntity(j => j.ToTable("RoleClaims"));
    }

}

I was basically trying to create an EFCore DbContext class with a generic Parameter "TUser" so any one can pass his user class to it and the Db Context uses it, I figured the other DbSet classes would need to take the type as a parameter to use it for the reference property so i made them generic also and passed TUser to them, now with this configuration when I do Add-Migration this is what is generated

protected override void Up(MigrationBuilder migrationBuilder)
{
    migrationBuilder.EnsureSchema(
        name: "Auth");

    migrationBuilder.CreateTable(
        name: "Claim<IUser<int>>",
        schema: "Auth",
        columns: table => new
        {
            Id = table.Column<string>(type: "text", nullable: false),
            Name = table.Column<string>(type: "text", nullable: false),
            Value = table.Column<string>(type: "text", nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Claim<IUser<int>>", x => x.Id);
        });

    migrationBuilder.CreateTable(
        name: "Claims",
        schema: "Auth",
        columns: table => new
        {
            Id = table.Column<string>(type: "text", nullable: false),
            Name = table.Column<string>(type: "text", nullable: false),
            Value = table.Column<string>(type: "text", nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Claims", x => x.Id);
        });

    migrationBuilder.CreateTable(
        name: "Role<IUser<int>>",
        schema: "Auth",
        columns: table => new
        {
            Id = table.Column<int>(type: "integer", nullable: false)
                .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
            Name = table.Column<string>(type: "text", nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Role<IUser<int>>", x => x.Id);
        });

    migrationBuilder.CreateTable(
        name: "Roles",
        schema: "Auth",
        columns: table => new
        {
            Id = table.Column<int>(type: "integer", nullable: false)
                .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
            Name = table.Column<string>(type: "text", nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Roles", x => x.Id);
        });

    migrationBuilder.CreateTable(
        name: "Claim<IUser<int>>Role<IUser<int>>",
        schema: "Auth",
        columns: table => new
        {
            ClaimsId = table.Column<string>(type: "text", nullable: false),
            RolesId = table.Column<int>(type: "integer", nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Claim<IUser<int>>Role<IUser<int>>", x => new { x.ClaimsId, x.RolesId });
            table.ForeignKey(
                name: "FK_Claim<IUser<int>>Role<IUser<int>>_Claim<IUser<int>>_ClaimsId",
                column: x => x.ClaimsId,
                principalSchema: "Auth",
                principalTable: "Claim<IUser<int>>",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
            table.ForeignKey(
                name: "FK_Claim<IUser<int>>Role<IUser<int>>_Role<IUser<int>>_RolesId",
                column: x => x.RolesId,
                principalSchema: "Auth",
                principalTable: "Role<IUser<int>>",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
        });

    migrationBuilder.CreateTable(
        name: "RoleClaims",
        schema: "Auth",
        columns: table => new
        {
            ClaimsId = table.Column<string>(type: "text", nullable: false),
            RolesId = table.Column<int>(type: "integer", nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_RoleClaims", x => new { x.ClaimsId, x.RolesId });
            table.ForeignKey(
                name: "FK_RoleClaims_Claims_ClaimsId",
                column: x => x.ClaimsId,
                principalSchema: "Auth",
                principalTable: "Claims",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
            table.ForeignKey(
                name: "FK_RoleClaims_Roles_RolesId",
                column: x => x.RolesId,
                principalSchema: "Auth",
                principalTable: "Roles",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
        });

    migrationBuilder.CreateTable(
        name: "Users",
        schema: "Auth",
        columns: table => new
        {
            Id = table.Column<int>(type: "integer", nullable: false)
                .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
            Handle = table.Column<string>(type: "text", nullable: false),
            Password = table.Column<string>(type: "text", nullable: false),
            ClaimUserId = table.Column<string>(name: "Claim<User>Id", type: "text", nullable: true),
            RoleUserId = table.Column<int>(name: "Role<User>Id", type: "integer", nullable: true)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_Users", x => x.Id);
            table.ForeignKey(
                name: "FK_Users_Claims_Claim<User>Id",
                column: x => x.ClaimUserId,
                principalSchema: "Auth",
                principalTable: "Claims",
                principalColumn: "Id");
            table.ForeignKey(
                name: "FK_Users_Roles_Role<User>Id",
                column: x => x.RoleUserId,
                principalSchema: "Auth",
                principalTable: "Roles",
                principalColumn: "Id");
        });

    migrationBuilder.CreateTable(
        name: "UserClaims",
        schema: "Auth",
        columns: table => new
        {
            ClaimsId = table.Column<string>(type: "text", nullable: false),
            UsersId = table.Column<int>(type: "integer", nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_UserClaims", x => new { x.ClaimsId, x.UsersId });
            table.ForeignKey(
                name: "FK_UserClaims_Claim<IUser<int>>_ClaimsId",
                column: x => x.ClaimsId,
                principalSchema: "Auth",
                principalTable: "Claim<IUser<int>>",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
            table.ForeignKey(
                name: "FK_UserClaims_Users_UsersId",
                column: x => x.UsersId,
                principalSchema: "Auth",
                principalTable: "Users",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
        });

    migrationBuilder.CreateTable(
        name: "UserRoles",
        schema: "Auth",
        columns: table => new
        {
            RolesId = table.Column<int>(type: "integer", nullable: false),
            UsersId = table.Column<int>(type: "integer", nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_UserRoles", x => new { x.RolesId, x.UsersId });
            table.ForeignKey(
                name: "FK_UserRoles_Role<IUser<int>>_RolesId",
                column: x => x.RolesId,
                principalSchema: "Auth",
                principalTable: "Role<IUser<int>>",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
            table.ForeignKey(
                name: "FK_UserRoles_Users_UsersId",
                column: x => x.UsersId,
                principalSchema: "Auth",
                principalTable: "Users",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
        });

    migrationBuilder.CreateIndex(
        name: "IX_Claim<IUser<int>>Role<IUser<int>>_RolesId",
        schema: "Auth",
        table: "Claim<IUser<int>>Role<IUser<int>>",
        column: "RolesId");

    migrationBuilder.CreateIndex(
        name: "IX_RoleClaims_RolesId",
        schema: "Auth",
        table: "RoleClaims",
        column: "RolesId");

    migrationBuilder.CreateIndex(
        name: "IX_UserClaims_UsersId",
        schema: "Auth",
        table: "UserClaims",
        column: "UsersId");

    migrationBuilder.CreateIndex(
        name: "IX_UserRoles_UsersId",
        schema: "Auth",
        table: "UserRoles",
        column: "UsersId");

    migrationBuilder.CreateIndex(
        name: "IX_Users_Claim<User>Id",
        schema: "Auth",
        table: "Users",
        column: "Claim<User>Id");

    migrationBuilder.CreateIndex(
        name: "IX_Users_Role<User>Id",
        schema: "Auth",
        table: "Users",
        column: "Role<User>Id");
}

I noticed it duplicated the tables once with the Type name of the entity and once with the name I gave it which is not what I want, and even when I try to do update-database it gives me Build Failed error and I don't understand why.

am I using generics with ef core in a wrong way? is there a proper way to do this?

I tried removing the ToTable function calls from the OptionsBuilder and let ef core do the work, it names the tables with it's entity type names and also fails to update the database, I thought of making the generic classes a base abstract class to a non generic class that implements them but this way I don't know how to pass TUser to it.

0

There are 0 best solutions below