Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System.InvalidOperationException: 'Unable to track an entity of type 'IdentityUserLogin<string>' because its primary key property 'TenantId' is null.' #881

Open
MayurPatel-BlobStation opened this issue Oct 8, 2024 Discussed in #879 · 8 comments

Comments

@MayurPatel-BlobStation
Copy link

Discussed in #879

Originally posted by MayurPatel-BlobStation October 4, 2024
"When I attempt to insert data into the AspNetUserLogin table, I encounter the following error: System.InvalidOperationException: 'Unable to track an entity of type 'IdentityUserLogin' because its primary key property 'TenantId' is null.'. However, inserting data into the AspNetUser table works correctly."

var userResult = await _userManager.FindByEmailAsync("abc@gmail.com");
var result = await _userManager.AddLoginAsync(userResult, externalLoginInfo);

@AndrewTriesToCode
Copy link
Contributor

Thanks I am looking into it. Does this happen during the third party sign in login flow or are you inserting into the table somewhere else in your code?

@MayurPatel-BlobStation
Copy link
Author

Thanks I am looking into it. Does this happen during the third party sign in login flow or are you inserting into the table somewhere else in your code?

Yes, In application I am implementing google and Microsoft SSO login, From UI side I have received Google token, so first I have check token is verified or not, if token is verified then check Login exist or not with

var result = await _userManager.FindByLoginAsync(loginProvider,providerKey);

If no login exist then inserting data in AspNetUser table then AspNetUserLogin table. but in AspNetUser data inserted successfully but AspNetUserLogin cause issue.

@AndrewTriesToCode
Copy link
Contributor

Are you using Asp.net Identity? Also can you please post what your Finbuckle setup looks like in your program.cs file?

@MayurPatel-BlobStation
Copy link
Author

MayurPatel-BlobStation commented Oct 10, 2024

Yes I have use Asp.net identity.

program.cs file code :

----------Program.cs----------

builder.Services.AddAppServices(configuration);

var app = builder.Build();
app.UseMultiTenant();

----------ServiceCollectionExtensions.cs----------

  public static class ServiceCollectionExtensions
  {
  
        public static void AddAppServices(this IServiceCollection services, ApiConfiguration configuration)
        {
              services.AddSubscriptionManagmentDbContext(configuration!.DbSettings);
              services.AddMultiTenantStoreDbContext(configuration!.DbSettings);
              services.AddMultiTenant<TenantInfo>()
              .WithRouteStrategy("TenantId")
              .WithHeaderStrategy("TenantIdKey")
              .WithPerTenantAuthentication()
              .WithEFCoreStore<MultiTenantStoreDbContext, TenantInfo>();
        }
  
        public static void AddSubscriptionManagmentDbContext(this IServiceCollection services, DbSettings dbSettings)
        {
              NpgsqlConnection.GlobalTypeMapper.EnableDynamicJson();
              services.AddDbContext<SubscriptionManagmentDbContext>(opts =>
              {
              opts.UseNpgsql(Convert.ToString(dbSettings.ConnectionString));
              opts.ConfigureWarnings(w => w.Ignore(RelationalEventId.MultipleCollectionIncludeWarning));
              });
        }
  
        public static void AddMultiTenantStoreDbContext(this IServiceCollection services, DbSettings dbSettings)
        {
              services.AddDbContext<MultiTenantStoreDbContext>(opts =>
              {
                    opts.UseNpgsql(Convert.ToString(dbSettings.ConnectionString));
                    opts.ConfigureWarnings(w => w.Ignore(RelationalEventId.MultipleCollectionIncludeWarning));
              });
        }
  }

----------MultiTenantStoreDbContext.cs----------

public class MultiTenantStoreDbContext : EFCoreStoreDbContext<TenantInfo> { public MultiTenantStoreDbContext(DbContextOptions options) : base(options){} }

----------SubscriptionManagmentDbContext.cs----------

  public class SubscriptionManagmentDbContext : MultiTenantIdentityDbContext
  {
      private readonly IMultiTenantContextAccessor _multiTenantContextAccessor;
      protected readonly IHttpContextAccessor _httpContextAccessor;
      public SubscriptionManagmentDbContext(DbContextOptions<SubscriptionManagmentDbContext> options, 
                         IMultiTenantContextAccessor multiTenantContextAccessor
    		      , IHttpContextAccessor httpContextAccessor) : base(multiTenantContextAccessor, options)
      {
	      _multiTenantContextAccessor = multiTenantContextAccessor;
	      _httpContextAccessor = httpContextAccessor;
      }
  
      public SubscriptionManagmentDbContext(IMultiTenantContextAccessor multiTenantContextAccessor) : base(multiTenantContextAccessor)
      {}
  
      public SubscriptionManagmentDbContext(ITenantInfo tenantInfo, DbContextOptions<SubscriptionManagmentDbContext> options) :
	      base(tenantInfo, options){}
  
      protected override void OnModelCreating(ModelBuilder modelBuilder)
      {
	      base.OnModelCreating(modelBuilder);
      }
  }

@AndrewTriesToCode
Copy link
Contributor

Thanks. I don’t see any issues with this code. Where exactly in your app is AddLoginAsync getting called?

@MayurPatel-BlobStation
Copy link
Author

I have created a repository called IdentityRepository, which I am currently using.

Repository Code:

   public class IdentityRepository : IIdentityRepository
   {
       private readonly UserManager<IdentityUser> _userManager;
       private readonly SignInManager<IdentityUser> _signInManager;
       public IdentityRepository(
           UserManager<IdentityUser> userManager,
           SignInManager<IdentityUser> signInManager,
           )
      {
           _userManager = userManager;
           _signInManager = signInManager;
      }
      
      public async Domains.IdentityUser AddLoginAsync(Domains.IdentityUser users, UserLoginInfo externalLoginInfo)
      {
	      try
	      {
            var insertUserModel = _mapper.Map<IdentityUser>(users);
            var result = await _userManager.AddLoginAsync(insertUserModel, externalLoginInfo);
	        return _mapper.Map<Domains.IdentityUser>(insertUserModel);
	      }
	      catch(){}
      }
  }

@AndrewTriesToCode
Copy link
Contributor

Is your identity repository registered in DI? What is the lifetime? The user manager internally will have a user store which internally uses your dbcontext which was instantiated for the current tenant. If you also inject IMultitenantAccessor into this class does it resolve the correct tenant right before the add user login call?

@MayurPatel-BlobStation
Copy link
Author

I have registered the identity repository in dependency injection with a scoped lifetime. However, I cannot find IMultitenantAccessor, which I believe refers to IMultiTenantContextAccessor. Even after using this, it is still not working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants