0

I have a Hangfire job that tries to create Identity users based on an old database. Unfortunately it results in this error (coming from Hangfire):

Failed to process the job '24': an exception occurred. Retry attempt 6 of 10 will be performed in 00:12:04.
System.NullReferenceException: Object reference not set to an instance of an object.
   at userpanel.BatchRegistration.CreateUser(String providedEmail, String providedPassword, String providedTraccarId, Nullable\`1 providedIsAdmin) in C:\Users\Vostro2017\source\repos\userpanel\Jobs.cs:line 83
   at userpanel.Jobs.UpdateUsers() in C:\...\userpanel\Jobs.cs:line 40
   at userpanel.Jobs.UpdateUsers() in C:\...\userpanel\Jobs.cs:line 42
   at userpanel.Jobs.UpdateUsers() in C:\...\userpanel\Jobs.cs:line 42
   at userpanel.Jobs.UpdateUsers() in C:\...\userpanel\Jobs.cs:line 44
   at System.Runtime.CompilerServices.TaskAwaiter\`1.GetResult()

I expected it to create users instead.

Jobs.cs:

namespace userpanel {
    public class Jobs {
        public static async Task<bool> UpdateUsers() {
            await using var conn = new NpgsqlConnection("Server=localhost;Port=5432;Database=Old;Username=postgres;Password=123456");
            await conn.OpenAsync();

            //
            // 1. Download old users and create Identity versions
            //
            await using (var cmd = new NpgsqlCommand("SELECT * FROM public.old_users ORDER BY id;", conn))
            await using (var reader = await cmd.ExecuteReaderAsync()) {
                while (await reader.ReadAsync()) {
                    string providedOldId = reader.GetInt32(0).ToString();
                    string providedEmail = reader.GetString(2);
                    bool? providedIsAdmin = reader.GetBoolean(6);
                    string providedPassword = "123456";
                    await BatchRegistration.CreateUser(providedEmail, providedPassword, providedOldId, providedIsAdmin);
                }
            }
            await conn.CloseAsync();
            return true;
        }
    }

    public class BatchRegistration : PageModel {
        public static SignInManager<ApplicationUser> _signInManager;
        public static UserManager<ApplicationUser> _userManager;
        public static ILogger<RegisterModel> _logger;

        public BatchRegistration(
            UserManager<ApplicationUser> userManager,
            SignInManager<ApplicationUser> signInManager,
            ILogger<RegisterModel> logger) {
            _userManager = userManager;
            _signInManager = signInManager;
            _logger = logger;
        }

        public static async Task CreateUser(string providedEmail, string providedPassword, string providedOldId, bool? providedIsAdmin) {
            bool IsAdmin = providedIsAdmin.HasValue != false;
            var user = new ApplicationUser {
                UserName = providedEmail,
                Email = providedEmail,
                EmailConfirmed = true,
                UserOldEmail = providedEmail,
                UserOldId = providedOldId,
                IsPaymentRequired = true,
                IsAdministrator = IsAdmin,
                Currency = 0.00
                };
            foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(user)) {
                string name = descriptor.Name;
                object value = descriptor.GetValue(user);
                Console.WriteLine("{0}={1}", name, value);
            }
            await _userManager.CreateAsync(user, providedPassword);
        }
    }
}

My model:

public class ApplicationUser : IdentityUser {
    public string UserOldId { get; set; } // actually int, wouldn't let me migrate
    public string UserOldEmail { get; set; } 
    public bool IsPaymentRequired { get; set; }
    public bool IsAdministrator { get; set; }
    public double Currency { get; set; }
    public ICollection<UserDevice> Devices { get; } = new List<UserDevice>();
}

I couldn't find a way to create users like this, so I just copied registration form backend and modified it to my needs.

I already tried filling my new ApplicationUser with IdentityUser data, but it didn't work.
I also checked if any entry is empty/null - everything seems to be fine.

I do not believe it is similar to this question - I know what this exception means, but I'm completely green to ASP.NET and I assume it's related to binding to UserManager somehow, since all data provided to _userManager.CreateAsync are valid.

Edit - I add my Hangfire job in Startup.cs, at the end of Configure method, like this:

BackgroundJob.Enqueue(() => Jobs.UpdateUsers());
xtl
  • 105
  • 1
  • 9
  • Does this answer your question? [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) – DavidG Jan 08 '20 at 09:37
  • you should provide the code showing how you enqueue your job in hangfire – jbl Jan 08 '20 at 11:06
  • 1
    I don't see how this can work. Nothing sets the static properties of BatchRegistrations. Assuming you are using a dependency injection system, branch this injection to hangfire, then remove all the statics, have an instance of BatchRegistration be constructor injected to Jobs, then enqueue like this `BackgroundJob.Enqueue(j => j.UpdateUsers());` – jbl Jan 08 '20 at 11:47
  • @jbl Looks like I bit more than I could chew. I managed to make it work, but I had to read up on some things, such as dependency injection (wasn't even aware it existed). I realized I messed up on multiple fronts, ie. `BatchRegistrations` wasn't necessary or useful in any way. Thanks a lot. – xtl Jan 08 '20 at 13:16
  • Great, you should post your solution as answer to your own question – jbl Jan 08 '20 at 14:59

1 Answers1

0

I realized I tried doing this without understanding anything. By jbl's advice I changed my Jobs.cs into this:

namespace userpanel {
    public class Jobs : PageModel {

        public UserManager<ApplicationUser> _userManager;

        public Jobs(UserManager<ApplicationUser> userManager) {
            _userManager = userManager;
        }

        public async Task<bool> UpdateUsers() {
            await using var conn = new NpgsqlConnection("Server=localhost;Port=5432;Database=Old;Username=postgres;Password=123456");
            await conn.OpenAsync();

            //
            // 1. Download old users and create Identity versions
            //
            await using (var cmd = new NpgsqlCommand("SELECT * FROM public.old_users ORDER BY id;", conn))
            await using (var reader = await cmd.ExecuteReaderAsync()) {
                while (await reader.ReadAsync()) {
                    string providedOldId = reader.GetInt32(0).ToString();
                    string providedEmail = reader.GetString(2);
                    bool? providedIsAdmin = reader.GetBoolean(6);
                    string providedPassword = "123456";
                    await CreateUser(providedEmail, providedPassword, providedOldId, providedIsAdmin);
                }
            }
            await conn.CloseAsync();
            return true;

            // ...
        }

        public async Task CreateUser(string providedEmail, string providedPassword, string providedOldId, bool?     providedIsAdmin) {
            bool IsAdmin = providedIsAdmin.HasValue != false;
            var user = new ApplicationUser {
                UserName = providedEmail,
                Email = providedEmail,
                EmailConfirmed = true,
                UserOldEmail = providedEmail,
                UserOldId = providedOldId,
                IsPaymentRequired = true,
                IsAdministrator = IsAdmin,
                Currency = 0.00
                };
            await _userManager.CreateAsync(user, providedPassword);
        }
    }
}

Also my Hangfire job is now executed using this line:

BackgroundJob.Enqueue<Jobs>(j => j.UpdateUsers());
xtl
  • 105
  • 1
  • 9