在事务中创建用户

Create user inside a transaction

像这样创建用户时:

using var trans = Db.BeginTransaction();
AuthRepository.CreateUserAuth(newUser, request.Password);
AuthRepository.AssignRoles(created, new List<string> { request.role });
//.. do other stuff
throw new Exception("other code may throw this");
trans.Commit();

Auth 存储库有自己的连接,因此它不是交易的一部分。这意味着,如果我的代码退出,我最终会遇到不需要的用户。

有什么方法可以使用 AuthRepository 进行事务处理,或者只能手动写入用户和角色表吗?手动保存时在文档中找不到创建密码哈希的示例,有没有示例?

您不能在事务中使用现有的 Auth Repository API,因为每个 API 都使用自己的数据库连接。

如果您愿意,可以在 OrmLiteAuthRepository.cs 中实现那些 OrmLite Auth Respository API,并将它们移动到您的方法中,以便它们都使用相同的数据库连接 + 事务。

另请注意,要使用 Transactions in OrmLite,您应该使用 OpenTransaction(),例如:

using (var dbTrans = db.OpenTransaction())
{
}

添加 class 我在这里使用,以防它可以帮助任何需要的人进行一些重构:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using OutReachPete.ServiceModel.User;
using ServiceStack;
using ServiceStack.Auth;
using ServiceStack.OrmLite;

namespace OutReachPete.ServiceInterface.User
{
    public class UserHelper
    {
        public static bool ForceCaseInsensitiveUserNameSearch { get; set; } = true;
        public static bool UseDistinctRoleTables { get; set; } = true;

        public static UserAuthCustom CreateUserAuth(UserAuthCustom newUser, string password, IDbConnection db)
        {
            newUser.ValidateNewUser(password);

            AssertNoExistingUser(db, newUser);

            newUser.PopulatePasswordHashes(password);
            newUser.CreatedDate = DateTime.UtcNow;
            newUser.ModifiedDate = newUser.CreatedDate;

            db.Save(newUser);

            newUser = db.SingleById<UserAuthCustom>(newUser.Id);

            return newUser;
        }

        public static void AssertNoExistingUser(IDbConnection db, IUserAuth newUser,
            IUserAuth exceptForExistingUser = null)
        {
            if (newUser.UserName != null)
            {
                var existingUser = GetUserAuthByUserName(db, newUser.UserName);
                if (existingUser != null
                    && (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
                    throw new ArgumentException(string.Format(ErrorMessages.UserAlreadyExistsTemplate1,
                        newUser.UserName.SafeInput()));
            }

            if (newUser.Email != null)
            {
                var existingUser = GetUserAuthByUserName(db, newUser.Email);
                if (existingUser != null
                    && (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
                    throw new ArgumentException(string.Format(ErrorMessages.EmailAlreadyExistsTemplate1,
                        newUser.Email.SafeInput()));
            }
        }

        public static UserAuthCustom GetUserAuthByUserName(IDbConnection db, string userNameOrEmail)
        {
            var isEmail = userNameOrEmail.Contains("@");
            var lowerUserName = userNameOrEmail.ToLower();

            UserAuthCustom userAuth = null;

            // Usernames/Emails are saved in Lower Case so we can do an exact search using lowerUserName
            if (HostContext.GetPlugin<AuthFeature>()?.SaveUserNamesInLowerCase == true)
            {
                return isEmail
                    ? db.Select<UserAuthCustom>(q => q.Email == lowerUserName).FirstOrDefault()
                    : db.Select<UserAuthCustom>(q => q.UserName == lowerUserName).FirstOrDefault();
            }

            // Try an exact search using index first
            userAuth = isEmail
                ? db.Select<UserAuthCustom>(q => q.Email == userNameOrEmail).FirstOrDefault()
                : db.Select<UserAuthCustom>(q => q.UserName == userNameOrEmail).FirstOrDefault();

            if (userAuth != null)
                return userAuth;

            // Fallback to a non-index search if no exact match is found
            if (ForceCaseInsensitiveUserNameSearch)
            {
                userAuth = isEmail
                    ? db.Select<UserAuthCustom>(q => q.Email.ToLower() == lowerUserName).FirstOrDefault()
                    : db.Select<UserAuthCustom>(q => q.UserName.ToLower() == lowerUserName).FirstOrDefault();
            }

            return userAuth;
        }

        public static void AssignRoles(IAuthRepository userAuthRepo, IUserAuth userAuth, IDbConnection db,
            ICollection<string> roles = null, ICollection<string> permissions = null)
        {
            if (userAuthRepo is IManageRoles managesRoles)
            {
                AssignRoles(userAuth.Id.ToString(), db, roles, permissions);
            }
            else
            {
                AssignRolesInternal(userAuth, roles, permissions);

                SaveUserAuth(userAuth, db);
            }
        }

        public static IUserAuth GetUserAuth(string userAuthId, IDbConnection db)
        {
            if (string.IsNullOrEmpty(userAuthId))
                throw new ArgumentNullException(nameof(userAuthId));

            return db.SingleById<UserAuthCustom>(int.Parse(userAuthId));
        }

        public static void AssignRoles(string userAuthId, IDbConnection db, ICollection<string> roles = null, ICollection<string> permissions = null)
        {
            var userAuth = GetUserAuth(userAuthId, db);
            if (!UseDistinctRoleTables)
            {
                if (!roles.IsEmpty())
                {
                    foreach (var missingRole in roles.Where(x => userAuth.Roles == null || !userAuth.Roles.Contains(x)))
                    {
                        if (userAuth.Roles == null)
                            userAuth.Roles = new List<string>();

                        userAuth.Roles.Add(missingRole);
                    }
                }

                if (!permissions.IsEmpty())
                {
                    foreach (var missingPermission in permissions.Where(x => userAuth.Permissions == null || !userAuth.Permissions.Contains(x)))
                    {
                        if (userAuth.Permissions == null)
                            userAuth.Permissions = new List<string>();

                        userAuth.Permissions.Add(missingPermission);
                    }
                }

                SaveUserAuth(userAuth, db);
            }
            else
            {

                var now = DateTime.UtcNow;
                var userRoles = db.Select<UserAuthRole>(q => q.UserAuthId == userAuth.Id);

                if (!roles.IsEmpty())
                {
                    var roleSet = userRoles.Where(x => x.Role != null).Select(x => x.Role).ToHashSet();
                    foreach (var role in roles)
                    {
                        if (!roleSet.Contains(role))
                        {
                            db.Insert(new UserAuthRole
                            {
                                UserAuthId = userAuth.Id,
                                Role = role,
                                CreatedDate = now,
                                ModifiedDate = now,
                            });
                        }
                    }
                }

                if (!permissions.IsEmpty())
                {
                    var permissionSet = userRoles.Where(x => x.Permission != null).Select(x => x.Permission).ToHashSet();
                    foreach (var permission in permissions)
                    {
                        if (!permissionSet.Contains(permission))
                        {
                            db.Insert(new UserAuthRole
                            {
                                UserAuthId = userAuth.Id,
                                Permission = permission,
                                CreatedDate = now,
                                ModifiedDate = now,
                            });
                        }
                    }
                }

            }
        }

        private static void AssignRolesInternal(IUserAuth userAuth, ICollection<string> roles, ICollection<string> permissions)
        {
            if (!roles.IsEmpty())
            {
                foreach (var missingRole in roles.Where(x => userAuth.Roles == null || !userAuth.Roles.Contains(x)))
                {
                    if (userAuth.Roles == null)
                        userAuth.Roles = new List<string>();

                    userAuth.Roles.Add(missingRole);
                }
            }

            if (!permissions.IsEmpty())
            {
                foreach (var missingPermission in permissions.Where(x =>
                    userAuth.Permissions == null || !userAuth.Permissions.Contains(x)))
                {
                    if (userAuth.Permissions == null)
                        userAuth.Permissions = new List<string>();

                    userAuth.Permissions.Add(missingPermission);
                }
            }
        }

        public static void SaveUserAuth(IUserAuth userAuth, IDbConnection db)
        {
            if (userAuth == null)
                throw new ArgumentNullException(nameof(userAuth));

            userAuth.ModifiedDate = DateTime.UtcNow;
            if (userAuth.CreatedDate == default(DateTime))
                userAuth.CreatedDate = userAuth.ModifiedDate;
            
            db.Save((UserAuthCustom)userAuth);
            
        }

    }
}

然后就这样称呼它:

var created = UserHelper.CreateUserAuth(newUser, request.Password, Db);

UserHelper.AssignRoles(AuthRepository, created, Db, new List<string> { request.CreateUserType.ToString() });

它只会使用传递的连接。只需将 UserAuthCustom 更改为您的用户 class 即可。