Still using Radzen Studio?
Achieve more with Radzen Blazor Studio

Radzen Blazor Studio is our new flagship product and vision of how rapid Blazor application development should be done.

Go to Radzen Blazor Studio

    Migrating from LightSwitch

    This guide demonstrates how to migrate a Microsoft LightSwitch application with Radzen.

    1. Create а new Microsoft LightSwitch application that displays the Products table from the Northwind database:

    2. Enable Forms authentication for application access control:

    3. Publish the app to IIS using an admin user as the application administrator:

    This will create the ASP.NET membership tables. LightSwitch uses those to store users, passwords, roles, etc:

    4. Create a new database as a copy of the existing one used in the LightSwitch application:

    5. Create a new Radzen application that displays the Products table:

    6. Enable security with email confirmation:

    7. Deploy the application to IIS from Radzen:

    We now have the ASP.NET Core 2.0 Identity tables in our new database:

    and we can use the following SQL script to migrate our users, passwords, roles, etc. to the new database:

    -- THIS SCRIPT NEEDS TO RUN FROM THE CONTEXT OF THE MEMBERSHIP DB
    BEGIN TRANSACTION MigrateUsersAndRoles
    USE Northwind
    
    -- INSERT USERS
    INSERT INTO NorthwindNew.dbo.AspNetUsers
              (Id,
               UserName,
               NormalizedUserName,
               PasswordHash,
               SecurityStamp,
               EmailConfirmed,
               PhoneNumber,
               PhoneNumberConfirmed,
               TwoFactorEnabled,
               LockoutEnd,
               LockoutEnabled,
               AccessFailedCount,
               Email,
               NormalizedEmail)
    SELECT aspnet_Users.UserId,
         aspnet_Users.UserName,
         -- The NormalizedUserName value is upper case in ASP.NET Core Identity
         UPPER(aspnet_Users.UserName),
         -- Creates an empty password since passwords don't map between the 2 schemas
         '',
         /*
          The SecurityStamp token is used to verify the state of an account and
          is subject to change at any time. It should be initialized as a new ID.
         */
         NewID(),
         /*
          EmailConfirmed is set when a new user is created and confirmed via email.
          Users must have this set during migration to reset passwords.
         */
         1,
         aspnet_Users.MobileAlias,
         CASE
           WHEN aspnet_Users.MobileAlias IS NULL THEN 0
           ELSE 1
         END,
         -- 2FA likely wasn't setup in Membership for users, so setting as false.
         0,
         CASE
           -- Setting lockout date to time in the future (1,000 years)
           WHEN aspnet_Membership.IsLockedOut = 1 THEN Dateadd(year, 1000,
                                                       Sysutcdatetime())
           ELSE NULL
         END,
         aspnet_Membership.IsLockedOut,
         /*
          AccessFailedAccount is used to track failed logins. This is stored in
          Membership in multiple columns. Setting to 0 arbitrarily.
         */
         0,
         aspnet_Membership.Email,
         -- The NormalizedEmail value is upper case in ASP.NET Core Identity
         UPPER(aspnet_Membership.Email)
    FROM   aspnet_Users
         LEFT OUTER JOIN aspnet_Membership
                      ON aspnet_Membership.ApplicationId =
                         aspnet_Users.ApplicationId
                         AND aspnet_Users.UserId = aspnet_Membership.UserId
         LEFT OUTER JOIN NorthwindNew.dbo.AspNetUsers
                      ON aspnet_Membership.UserId = AspNetUsers.Id
    WHERE  AspNetUsers.Id IS NULL
    
    -- INSERT ROLES
    INSERT INTO NorthwindNew.dbo.AspNetRoles(Id, Name)
    SELECT RoleId, RoleName
    FROM aspnet_Roles;
    
    -- INSERT USER ROLES
    INSERT INTO NorthwindNew.dbo.AspNetUserRoles(UserId, RoleId)
    SELECT UserId, RoleId
    FROM aspnet_UsersInRoles;
    
    IF @@ERROR <> 0
    BEGIN
      ROLLBACK TRANSACTION MigrateUsersAndRoles
      RETURN
    END
    
    COMMIT TRANSACTION MigrateUsersAndRoles
    

    We are done!

    Now we only need to reset the password from the deployed Radzen application. Microsoft LightSwitch apps needed either a desktop app or custom pages for user management. For the purpose of this blog post I’ll take the shortcut and I will enter my admin user email directly in the database:

    Now we can reset the password from the deployed Radzen application:

    You will receive email with a link to generate new password and you can use it to login:

    If you don’t want your users to reset their passwords you can use a custom password hasher which makes ASP.NET Membership (LightSwitch) passwords work with ASP.NET Core Identity (Radzen):

    We need first to copy both Password and PasswordSalt from old aspnet_Membership table:

    -- THIS SCRIPT NEEDS TO RUN FROM THE CONTEXT OF THE MEMBERSHIP DB
    BEGIN TRANSACTION MigrateUsersAndRoles
    USE Northwind
    
    -- INSERT USERS
    INSERT INTO NorthwindNew.dbo.AspNetUsers
              (Id,
               UserName,
               NormalizedUserName,
               PasswordHash,
               SecurityStamp,
               EmailConfirmed,
               PhoneNumber,
               PhoneNumberConfirmed,
               TwoFactorEnabled,
               LockoutEnd,
               LockoutEnabled,
               AccessFailedCount,
               Email,
               NormalizedEmail)
    SELECT aspnet_Users.UserId,
         aspnet_Users.UserName,
         -- The NormalizedUserName value is upper case in ASP.NET Core Identity
         UPPER(aspnet_Users.UserName),
         -- Copy both Password and PasswordSalt
         aspnet_Membership.Password + ':' + aspnet_Membership.PasswordSalt,
         /*
          The SecurityStamp token is used to verify the state of an account and
          is subject to change at any time. It should be initialized as a new ID.
         */
         NewID(),
         /*
          EmailConfirmed is set when a new user is created and confirmed via email.
          Users must have this set during migration to reset passwords.
         */
         1,
         aspnet_Users.MobileAlias,
         CASE
           WHEN aspnet_Users.MobileAlias IS NULL THEN 0
           ELSE 1
         END,
         -- 2FA likely wasn't setup in Membership for users, so setting as false.
         0,
         CASE
           -- Setting lockout date to time in the future (1,000 years)
           WHEN aspnet_Membership.IsLockedOut = 1 THEN Dateadd(year, 1000,
                                                       Sysutcdatetime())
           ELSE NULL
         END,
         aspnet_Membership.IsLockedOut,
         /*
          AccessFailedAccount is used to track failed logins. This is stored in
          Membership in multiple columns. Setting to 0 arbitrarily.
         */
         0,
         aspnet_Membership.Email,
         -- The NormalizedEmail value is upper case in ASP.NET Core Identity
         UPPER(aspnet_Membership.Email)
    FROM   aspnet_Users
         LEFT OUTER JOIN aspnet_Membership
                      ON aspnet_Membership.ApplicationId =
                         aspnet_Users.ApplicationId
                         AND aspnet_Users.UserId = aspnet_Membership.UserId
         LEFT OUTER JOIN NorthwindNew.dbo.AspNetUsers
                      ON aspnet_Membership.UserId = AspNetUsers.Id
    WHERE  AspNetUsers.Id IS NULL
    
    -- INSERT ROLES
    INSERT INTO NorthwindNew.dbo.AspNetRoles(Id, Name)
    SELECT RoleId, RoleName
    FROM aspnet_Roles;
    
    -- INSERT USER ROLES
    INSERT INTO NorthwindNew.dbo.AspNetUserRoles(UserId, RoleId)
    SELECT UserId, RoleId
    FROM aspnet_UsersInRoles;
    
    IF @@ERROR <> 0
    BEGIN
      ROLLBACK TRANSACTION MigrateUsersAndRoles
      RETURN
    END
    
    COMMIT TRANSACTION MigrateUsersAndRoles
    

    Just create a server\Startup.Custom.cs file in the Radzen application with the following content:

    namespace NorthwindNew
    {
        public partial class Startup
        {
            partial void OnConfigureServices(IServiceCollection services)
            {
                services.AddScoped<IPasswordHasher<ApplicationUser>, CustomPasswordHasher>();
            }
        }
    
        public class CustomPasswordHasher : PasswordHasher<ApplicationUser>
        {
            public static string GenerateHash(string providedPassword, byte[] saltAsByteArray)
            {
                var sha = new System.Security.Cryptography.SHA1CryptoServiceProvider();
    
                byte[] p1 = saltAsByteArray;
                byte[] p2 = System.Text.Encoding.Unicode.GetBytes(providedPassword);
    
                byte[] data = new byte[p1.Length + p2.Length];
    
                p1.CopyTo(data, 0);
                p2.CopyTo(data, p1.Length);
    
                return Convert.ToBase64String(sha.ComputeHash(data));
            }
    
            public override PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPasswordAndSalt,
               string providedPassword)
            {
                if (hashedPasswordAndSalt.IndexOf(":") != -1)
                {
                    var hashedPassword = hashedPasswordAndSalt.Split(':')[0];
                    var salt = hashedPasswordAndSalt.Split(':')[1];
    
                    var hashedProvidedPassword = GenerateHash(providedPassword, Convert.FromBase64String(salt));
    
                    return hashedPassword == hashedProvidedPassword
                        ? PasswordVerificationResult.SuccessRehashNeeded
                        : base.VerifyHashedPassword(user, hashedPassword, providedPassword);
                }
                else
                {
                    return base.VerifyHashedPassword(user, hashedPasswordAndSalt, providedPassword);
                }
            }
        }
    }