如何防止与EF同时执行SQL Server存储过程?

时间:2018-07-29 07:51:22

标签: c# sql sql-server entity-framework

我正在使用Entity Framework 6 Database.SqlQuery语句执行存储过程。我想防止两个用户同时执行相同的存储过程。

3 个答案:

答案 0 :(得分:4)

您可以通过两种方法解决此问题:

1)如果您只想锁定特殊的存储过程,则可以使用如下字典:

start   end    move
0       5      1   // the start value here is the lowest of the next 5     end values
5       10     1   // same as above
10      15     1   // same as above
15      25     1   // same as above
25      30     1   // same as above
30      35     0   // end goes up and down so 0 here
35      40     0   // same as above
40      30    -1   // next are all below 40 so -1 now 
30      20
20      15
15      10    
10      5

2)另外,您还可以确保每个存储过程都执行一次。添加以下代码以启动存储过程。

static Dictionary<string, object> _dictionary = new Dictionary<string, object>();
public List<TResult> ExecuteStoredProcedureWithLock<TResult>(string storedProcedureName, object parameters) where TResult : new()
{
    lock (_dictionary)
    {
        if (_dictionary.ContainsKey(storedProcedureName) == false)
        {
            _dictionary.Add(storedProcedureName, new object());
        }
    }

    lock (_dictionary[storedProcedureName])
    {
        return ExecuteStoredProcedure<TResult>(storedProcedureName, parameters);
    }
}

public async Task<List<TResult>> ExecuteStoredProcedureWithLockAsync<TResult>(string storedProcedureName, object parameters, CancellationToken cancellationToken) where TResult : new()
{
    lock (_dic)
    {
        if (_dic.ContainsKey(storedProcedureName) == false)
        {
            _dic.Add(storedProcedureName, new object());
        }
    }
    List<TResult> result = new List<TResult>();
    Monitor.Enter(_dic[storedProcedureName]);
    try
    {
        result = await ExecuteStoredProcedureAsync<TResult>(storedProcedureName, parameters, cancellationToken);
    }
    finally
    {
        Monitor.Exit(_dic[storedProcedureName]);
    }
    return result;
}

答案 1 :(得分:3)

此代码适用于同步版本和异步版本

    private static readonly SemaphoreLocker _semaphoreLocker = new SemaphoreLocker();
    public async Task<List<TResult>> ExecuteStoredProcedureWithLockAsync<TResult>(string storedProcedureName, object parameters, CancellationToken cancellationToken) where TResult : new()
    {
        List<TResult> result = new List<TResult>();
        await _semaphoreLocker.LockAsync(async () =>
        {
            result = await ExecuteStoredProcedureAsync<TResult>(storedProcedureName, parameters, cancellationToken);
        }, cancellationToken);
        return result;
    }
    private Object _locker = new Object();
    public List<TResult> ExecuteStoredProcedureWithLock<TResult>(string storedProcedureName, object parameters) where TResult : new()
    {
        lock (_locker)
        {
            return ExecuteStoredProcedure<TResult>(storedProcedureName, parameters);
        }
    }

private class SemaphoreLocker
{
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);

    public async Task LockAsync(Func<Task> worker, CancellationToken cancellationToken)
    {
        await _semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
        try
        {
            await worker();
        }
        finally
        {
            _semaphore.Release();
        }
    }
}

答案 2 :(得分:1)

如果所有用于调用存储过程的客户端仅调用ExecuteStoredProcedure Web服务,则以下代码将正常工作:

private Object thisLock = new Object(); 
public List<TResult> ExecuteStoredProcedure<TResult>(string storedProcedureName, object parameters) where TResult : new()
{
    lock (thisLock)  
    {  
        Type type = typeof(TResult);

        StringBuilder sb = new StringBuilder();
        sb.Append($"EXEC {storedProcedureName}");

        if (parameters == null)
            parameters = new { };

        var properties = parameters.GetType().GetProperties();
        object[] values = new object[properties.Length];

        for (int i = 0; i < properties.Length; i++)
        {
            sb.Append("@");
            sb.Append(properties[i].Name);
            sb.Append("=");
            sb.Append("@p");
            sb.Append(i);

            if (i < properties.Length - 1)
            {
                sb.Append(",");
            }

            values[i] = properties[i].GetValue(parameters);
        }

        if (type == typeof(ActionModel) || type.BaseType == typeof(ActionModel))
        {
            sb.AppendLine("");
            sb.AppendLine("WITH RESULT SETS ((IsValid BIT NULL,Id BIGINT NULL,Message NVARCHAR(MAX) NULL));");
        }

        return Database.SqlQuery<TResult>(sb.ToString(), values).ToList();
    }
}