Reputation: 1614
Given the two classes below:
public class PortalUser : IdentityUser
{
public Guid? RefreshTokenId { get; set; }
public RefreshToken RefreshToken { get; set; }
}
public class RefreshToken
{
public Guid Id { get; set; }
public string Token { get; set; }
public string UserId { get; set; }
public PortalUser User { get; set; }
public RefreshToken(string token, string userId)
{
Token = token;
UserId = userId;
}
}
I set up a one to one relationship - a user can have a single refresh token or none at all. See the configuration below.
public void Configure(EntityTypeBuilder<PortalUser> builder)
{
builder.HasOne(x => x.RefreshToken).WithOne(x => x.User)
.HasForeignKey<PortalUser>(x => x.RefreshTokenId).OnDelete(DeleteBehavior.SetNull);
}
public class RefreshTokenConfiguration : IEntityTypeConfiguration<RefreshToken>
{
public void Configure(EntityTypeBuilder<RefreshToken> builder)
{
builder.ToTable("AspNetUserRefreshTokens");
builder.HasKey(x => x.Id);
builder.Property(x => x.Id).ValueGeneratedOnAdd();
builder.Property(x => x.Token).IsRequired();
builder.HasOne(x => x.User).WithOne(x => x.RefreshToken)
.HasForeignKey<RefreshToken>(x => x.UserId).OnDelete(DeleteBehavior.Cascade);
}
}
Now when deleting a user the cascading delete works fine - the refresh token gets deleted aswell but when I delete a single refresh token , the FK in the PortalUser is STILL SET.
This doesnt seem right to me , any idea what I am doing wrong?
Upvotes: 0
Views: 100
Reputation: 14567
I tried to reproduce your issue and as far as I can tell your configuration is correct.
One thing that you might have missed (or just didn't include into the question) is how you wire the EntityTypeBuilder<PortalUser>
into your DB context. I was successful with the following configuration:
public class DbContext: IdentityDbContext<PortalUser>
{
public DbSet<RefreshToken> AspNetUserRefreshTokens { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder builder)
{
builder.UseSqlServer("needs a connection string");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new RefreshTokenConfiguration());
modelBuilder.ApplyConfiguration(new PortalUserConfiguration()); // i suspect this piece can be your issue, check if you've got it hooked up
}
public class PortalUserConfiguration : IEntityTypeConfiguration<PortalUser>
{
public void Configure(EntityTypeBuilder<PortalUser> builder)
{
builder.HasOne(x => x.RefreshToken)
.WithOne(x => x.User)
.HasForeignKey<PortalUser>(x => x.RefreshTokenId)
.OnDelete(DeleteBehavior.SetNull);
}
}
public class RefreshTokenConfiguration : IEntityTypeConfiguration<RefreshToken>
{
public void Configure(EntityTypeBuilder<RefreshToken> builder)
{
builder.ToTable("AspNetUserRefreshTokens");
builder.HasKey(x => x.Id);
builder.Property(x => x.Id).ValueGeneratedOnAdd();
builder.Property(x => x.Token).IsRequired();
builder.HasOne(x => x.User).WithOne(x => x.RefreshToken)
.HasForeignKey<RefreshToken>(x => x.UserId).OnDelete(DeleteBehavior.Cascade);
}
}
}
My test bench looks like so:
class Program
{
static void Main(string[] args)
{
var ctx = new DbContext();
//ctx.Database.EnsureCreated(); // you probably don't need this step
var user = new PortalUser();
var t = new RefreshToken("test", user.Id);
user.RefreshToken = t;
ctx.Users.Add(user);
ctx.SaveChanges();
var token = ctx.AspNetUserRefreshTokens.Find(t.Id);
ctx.Entry(token).State = EntityState.Deleted;
ctx.SaveChanges();
var u = ctx.Users.Find(user.Id);
Console.Write($"RefreshTokenId {u.RefreshTokenId}");
Console.ReadKey();
}
}
Upvotes: 1