问题
我的概念是,当用户创建带有标签的帖子时,服务器首先检查标签名称是否已经存在,如果存在,则计数器会递增,否则将创建一个新的标签。
问题在于,当多个用户同时创建一个带有新标签(比如
这段代码展示了我如何实现持久化:
PostRepository
这是我的ER图的一部分:
值得一提的是,如果一个用户首先创建了标签,那么其他用户只需递增计数器并使用相同的标签,即可按预期工作。
我的概念是,当用户创建带有标签的帖子时,服务器首先检查标签名称是否已经存在,如果存在,则计数器会递增,否则将创建一个新的标签。
问题在于,当多个用户同时创建一个带有新标签(比如
new_tag
)的帖子时,数据库中会持久化多个相同名称的标签,而不是带有计数器=#使用此标签的用户数量 的1个标签。可以看到,对于每个用户,在数据库中都会创建一个新的标签记录。
--------------------------------
| id | tagName | counter |
|------|-----------|-----------|
| 1 | new_tag | 1 |
| 2 | new_tag | 1 |
| 3 | new_tag | 1 |
| 4 | new_tag | 1 |
--------------------------------
What I expect:
--------------------------------
| id | tagName | counter |
|------|-----------|-----------|
| 1 | new_tag | 4 |
--------------------------------
这段代码展示了我如何实现持久化:
PostRepository
public async Task<bool> AddAsync(Post entity)
{
await AddNewTagsAsync(entity);
_context.Attach(entity.Event);
await _context.AddAsync(entity);
await _context.Database.BeginTransactionAsync();
var result = await _context.SaveChangesAsync();
_context.Database.CommitTransaction();
return result > 0;
}
public async Task AddNewTagsAsync(Post post)
{
// store tags name in lower case
if ((post.PostTags == null) || (post.PostTags.Count==0))
return;
post.PostTags.ForEach(pt => pt.Tag.TagName = pt.Tag.TagName.ToLower());
for(var i =0; i<post.PostTags.Count; i++)
{
var postTag = post.PostTags[i];
// here lays the main problem, when many concurrent users check for tag existence
// all get null and new tag will be created, workaround needed!
var existingTag = await _context.Tags.SingleOrDefaultAsync(x => x.TagName == postTag.Tag.TagName);
// if tag exists, increment counter
if (existingTag != null)
{
existingTag.Counter++;
postTag.Tag = existingTag;
continue;
}
// else the new Tag object will be peristed
}
}
这是我的ER图的一部分:
![This](https://istack.dev59.com/X1oPq.webp)
值得一提的是,如果一个用户首先创建了标签,那么其他用户只需递增计数器并使用相同的标签,即可按预期工作。
PostTags
条目的数量实际上不代表您正在寻找的计数器吗?但是,您可能需要查看使用EF的锁定技术,例如处理并发冲突。 - Gene_context.Tags.SingleOrDefaultAsync...
和CommitTransaction
之间存在一个时代。这种类型的冲突只能通过唯一的数据库索引和捕获异常来解决。 - Gert Arnold