“跟随”功能的业务逻辑。并发问题

时间:2018-10-18 10:54:37

标签: asp.net sql-server asp.net-mvc entity-framework

我使用asp.net核心和实体框架。我有一个需要创建“关注”按钮的任务。 此刻,我的模型如下:

public class Following
{
    [Key, Column(Order = 0), ForeignKey("UserId")]
    public string UserId { get; set; }

    [Key, Column(Order = 1), ForeignKey("FollowerId")]
    public string FollowerId { get; set; }

    public DateTime CreatedAt { get; set; }

    public DateTime UpdatedAt { get; set; }

    public bool IsFollowing { get; set; }
}

我有一个关注和取消关注功能,它们看起来像这样:

    public async Task<bool> Follow(string userId, string currentUserId)
            {
                var currentExist = await GetFollower(userId, currentUserId);
                // insert if new
                if (currentExist == null)
                {
                    var newFollower = new Following()
                    {
                        FollowerId = currentUserId,
                        UserId = userId,
                        CreatedAt = DateTime.UtcNow,
                        UpdatedAt = DateTime.UtcNow,
                        IsFollowing = true
                    };
                    InsertFollower(newFollower);
                    // update counters
                    updateFollow(userId, currentUserId);
                    return true;
                }

                if (currentExist.IsFollowing)
                    return false;

                currentExist.UpdatedAt = DateTime.UtcNow;
                currentExist.IsFollowing = true;
                context.Entry(currentExist);

                // update counters
                updateFollow(userId, currentUserId);

                return true;
            }

            public async Task<bool> UnFollow(string userId, string currentUserId)
            {
                // this I get user from db
                var exist = await GetFollower(userId, currentUserId);
                if (exist == null || !exist.IsFollowing) return false;

                exist.UpdatedAt = DateTime.UtcNow;
                exist.IsFollowing = false;
                context.Entry(exist).State = EntityState.Modified;

                updateUnFollow(userId, currentUserId);

                return true;
            }

接下来,我叫SaveChangesAsync()

此功能更新用户计数器:

  private async Task updateFollow(string userId, string currentUserId)
    {
        await context.Database.ExecuteSqlCommandAsync("UPDATE User SET FollowerCount = FollowerCount + 1 WHERE UserId = {0}", userId);
        await context.Database.ExecuteSqlCommandAsync("UPDATE User SET FollowingCount = FollowingCount + 1 WHERE UserId = {0}", currentUserId);
    }

    private async Task updateUnFollow(string userId, string currentUserId)
    {
        await context.Database.ExecuteSqlCommandAsync("UPDATE User SET FollowerCount = FollowerCount - 1 WHERE UserId = {0}", userId);
        await context.Database.ExecuteSqlCommandAsync("UPDATE User SET FollowingCount = FollowingCount - 1 WHERE UserId = {0}", currentUserId);
    }

问题在于,如果我多次单击“关注”按钮。一次又一次地取消订阅和订阅。我会得到不正确的计数器值,此外,有时还会发生“并发”错误。换句话说,计数器值有时小于1,有时大于1,很少是正确的1。 这行是从数据库中删除还是更新都没关系。

我希望此功能看起来像github一样的“星形”按钮。

在Internet上,我设法找到有关“行版本化”的信息。但我想听听这项任务的完美解决方案。

1 个答案:

答案 0 :(得分:1)

着眼于此-我认为解决此问题的最佳方法是更改​​模型。 您需要捕获关注者和关注者。

创建一个多对多表以存储关注者/关注者。

CREATE TABLE followingTable 
(
FollowedUser Varchar(255), FollowingUser Varchar(255)
)

然后您的关注按钮插入到此表中(如果尚未插入)

然后而不是增加/减少以下/跟随者的计数 您可以根据“多对多表”计算值。

EG:

UPDATE U

SET FollowerCount = Count(FollowingUser)
FROM
User u
join followingTable ft
on u.UserId = Ft.FollowedUser

这种方法的优点在于,如果用户多次按下按钮,则不会给出错误的值。