【问题标题】:Business logic for "follow" functionality. Problem in concurrency“跟随”功能的业务逻辑。并发问题
【发布时间】:2018-10-18 10:54:36
【问题描述】:

我使用 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 之类的“星形”按钮。

在互联网上,我设法找到了有关“rowversion”的信息。但我想听听这项任务的完美解决方案。

【问题讨论】:

  • 我会先问自己,为什么要存储一个多余的计数值?在运行时进行计数真的是性能问题吗?
  • 但我想听听这个任务的完美解决方案。这有点……宽泛。
  • @GertArnold 为什么保留它?在用户页面上显示此数据,而不是在访问用户页面时不断统计用户
  • @GertArnold 就像这里的“赞”按钮一样。我想要同样的解决方案。
  • 您在查询当前计数时是否真的遇到过性能问题?数据库往往非常擅长计数。不要低估冗余带来的麻烦。

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


【解决方案1】:

看看这个 - 我认为最好的解决方法是改变模型。 您需要捕获关注者和关注者。

创建一个多对多表来存储follower/followed。

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

这种方法的美妙之处在于,如果用户多次点击按钮,则不会给出错误值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-09
    • 2010-12-18
    相关资源
    最近更新 更多