EF Core可序列化事务在MySQL上每1000发生2-5次失败

时间:2019-06-26 10:14:52

标签: c# mysql entity-framework entity-framework-core ef-core-2.2

问题

我有以下代码:

var strategy =  _dbContext.Database.CreateExecutionStrategy();
return await strategy.ExecuteAsync(async () =>
{
    using (var transaction = _dbContext.Database.BeginTransaction(IsolationLevel.Serializable))
    {
        await (new Validator(_dbContext).ValidateRequestAndThrowAsync(request));

        var newUser = new Models.User
        {
            Name = request.Name,
            Surname = request.Surname,
            Username = request.Username,
        };
        _dbContext.Users.Add(newUser);
        await _dbContext.SaveChangesAsync();
        transaction.Commit();
        return newUser;
    }
});

其中_dbContext是MySQL数据库上下文,

await (new Validator(_dbContext).ValidateRequestAndThrowAsync(request));

使用相同的Username检查数据库中是否已使用_dbContext

这只是用于内部测试的代码。我知道您可以在数据库级别检查此约束。我正在尝试模拟的是检查数据库中不可放置的约束。因此,出于这个问题的目的,请考虑通过唯一索引检查Username的唯一性是不可行的。

如果我调用此函数10次或100次,则其中99.9%的时间将按预期执行:只有一个请求通过,其余的被停止,但Validator发送了一个很好的异常。 >

如果我调用它1000次,则每次运行我将开始可靠地获得2-5次重复。即使读取请求的日志,似乎只有1个请求通过,但我却插入了2-5个重复的行。

问题

我在做什么错?为什么每1000个请求中有2-5个重复?

额外

在各种尝试中,我还使用以下方法设置了数据库上下文:

services.AddDbContext<MainContext>(
    options => options.UseMySql(connectionString,
        mySqlOptions =>
        {
            mySqlOptions.EnableRetryOnFailure(
                maxRetryCount: 10, 
                maxRetryDelay: TimeSpan.FromSeconds(3), 
                errorNumbersToAdd: new int[] 
                {
                    1205, // Deadlock, see: https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/blob/ddf8b77e6f58f3915dd170ea1de73755a7b3cae6/upstream/EFCore.Upstream/Storage/Internal/MySqlTransientExceptionDetector.cs#L95
                });
            mySqlOptions.ServerVersion(new Version(5, 6, 41), ServerType.MySql);
        }
));

但是,鉴于所有请求在自身上使用都会失败,并显示一条消息,提示应改为使用CreateExecutionStrategy


此处要求的是ValidateRequestAndThrowAsync

        public static async Task ValidateRequestAndThrowAsync<T>(this IValidator<T> validator, T request)
        {
            var validationResult = await validator.ValidateAsync(request);
            if (validationResult.IsValid)
                return;

            throw new CustomError(validationResult.Errors);
        }

验证者:

private class Validator : AbstractValidator<Request>
{
    public Validator(MainContext dbContext)
    {
        RuleFor(x => x.Name)
            .NotEmpty();
        RuleFor(x => x.Surname)
            .NotEmpty();
        RuleFor(x => x.Username)
            .NotEmpty()
            .SetValidator(new UniqueUsernameValidator(dbContext));
    }
}

public class UniqueUsernameValidator : AbstractValidator<string>
{
    private readonly MainContext _dbContext;
    private readonly int? _userId;
    public UniqueUsernameValidator(MainContext dbContext, int? userId = null)
    {
        _dbContext = dbContext;
        _userId = userId;

        RuleFor(x => x)
            .MustAsync(NotExists);
    }

    private async Task<bool> NotExists(string username, CancellationToken cancellationToken)
    {
        return !await _dbContext.Users
            .Where(x => x.Username == username)
            .Where(x => !_userId.HasValue || x.Id != _userId.Value)
            .AnyAsync(cancellationToken);
    }
}

0 个答案:

没有答案
相关问题