如何在EF Core中实现Select For Update[英] How to implement Select For Update in EF Core

本文是小编为大家收集整理的关于如何在EF Core中实现Select For Update的处理方法,想解了如何在EF Core中实现Select For Update的问题怎么解决?如何在EF Core中实现Select For Update问题的解决办法?那么可以参考本文帮助大家快速定位并解决问题。

问题描述

据我所知,EF(和EF核心)中没有选择要明确锁定我要查询的资源,但是我会经常需要此功能,并且并不是真的想跌倒返回写作选择语句,每次我都需要它.

因为我只需要postgres和根据规格 FOR UPDATE是查询中的最后一项,我想实现的最简单是在此处描述的选择语句: to-enties-can-can-you-convert-anvert-a-into-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-a-string">在实体中,您可以将iQueryable转换为sql的字符串吗?并附加FOR UPDATE并直接执行它.但是,这要么会给我一个带有参数占位符的查询,要么不是准备好的查询,这意味着执行计划的缓存实际上对Postgres都无法正常工作,因此无论哪种方式,这都是不行的.

linq to sql具有方法 DataContext.GetCommand ,但是EF中似乎没有任何等效的EF核心.我还查看了EntityFrameWork.Extended及其批处理/删除,但是由于他们必须将Select语句转换为其他语句,因此他们需要比我更复杂得多,因此我希望提供更简单的解决方案.

更新:

如果从描述中不清楚,我想创建一个这样的扩展方法:

public static IList<T> ForUpdate (this IQueryable<T> me)
{
    // this line is obviously what is missing for me :)
    var theUnderlyingCommand = me.GetTheUnderlyingDbCommandOrSimilar();

    theUnderlyingCommand.Text += "FOR UPDATE";
    return me.ToList();
}

这样,其他开发人员可以像所有其他过程一样通过LINQ使用EF,而不是运行.ToList()他们运行.ForUpdate(). (对于更新,目的是执行查询以使实施更轻松,并且因为FOR UPDATE是Postgres支持的最后一个选项,之后不再有其他内容)

)

推荐答案

使用SQLServer(无测试异步方法)对我来说这是我的工作:

首先,创建一个dbcommandinterceptor(我称为hintinterceptor.cs)

using System;
using System.Data.Common;
using System.Data.Entity.Infrastructure.Interception;
using System.Text.RegularExpressions;

public class HintInterceptor : DbCommandInterceptor
{
    private static readonly Regex _tableAliasRegex = new Regex(@"(?<tableAlias>FROM +(\[.*\]\.)?(\[.*\]) AS (\[.*\])(?! WITH \(\*HINT\*\)))", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Compiled);

    [ThreadStatic]
    public static string HintValue;

    private static string Replace(string input)
    {
        if (!String.IsNullOrWhiteSpace(HintValue))
        {
            if (!_tableAliasRegex.IsMatch(input))
            {
                throw new InvalidProgramException("Não foi possível identificar uma tabela para ser marcada para atualização(forupdate)!", new Exception(input));
            }
            input = _tableAliasRegex.Replace(input, "${tableAlias} WITH (*HINT*)");
            input = input.Replace("*HINT*", HintValue);
        }
        HintValue = String.Empty;
        return input;
    }

    public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
    {
        command.CommandText = Replace(command.CommandText);
    }

    public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
    {
        command.CommandText = Replace(command.CommandText);
    }
}

所以进入web.config注册您的拦截器类

<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
<providers>
  <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
<interceptors> 
  <interceptor type="Full.Path.Of.Class.HintInterceptor, Dll.Name" />
</interceptors>
</entityFramework>

现在我创建了一个名为Hintextension

的静态类
public static class HintExtension
{
    public static IQueryable<T> WithHint<T>(this IQueryable<T> set, string hint) where T : class
    {
        HintInterceptor.HintValue = hint;
        return set;
    }
    public static IQueryable<T> ForUpdate<T>(this IQueryable<T> set) where T : class
    {
        return set.WithHint("UPDLOCK");
    }
}

仅此而已,我可以在数据库交易中使用:

using(var trans = context.Database.BeginTransaction())
{
        var query = context.mydbset.Where(a => a.name == "asd").ForUpdate();
        // not locked yet
        var mylist = query.ToList();
        // now are locked for update
        // update the props, call saveChanges() and finally call commit ( or rollback)
        trans.Commit();
        // now are unlocked
}

对不起我的英语,希望我的榜样会有所帮助.

其他推荐答案

根据 EF Core

中的提示和其他面向数据库的调用

我在我的项目中使用MSSQL和EF核心实现了updlock:

public static class DbContextExtensions
{
    public static string GetUpdLockSqlForEntity<T>(this DbContext dbContext, int entityPk, bool pkContainsTableName = true) where T : class
    {
        var mapping = dbContext.Model.FindEntityType(typeof(T)).Relational();
        var tableName = mapping.TableName;
        var entityPkString = entityPk.ToString();
        string idPrefix = pkContainsTableName ? tableName.Substring(0, tableName.Length - 1) : string.Empty;
        return $"Select 1 from {tableName} with (UPDLOCK) where {idPrefix}Id = {entityPkString}";
    }
}

我们在数据库事务中使用此方法作为RAW SQL调用(锁定将在提交或回滚后发布):

using (var dbTran = await DataContext.Database.BeginTransactionAsync(IsolationLevel.ReadCommitted))
{
    try
    {
        await DataContext.Database.ExecuteSqlCommandAsync(DataContext.GetUpdLockSqlForEntity<Deposit>(entityId));
        dbTran.Commit();
    }
    catch (Exception e)
    {
        dbTran.Rollback();
        throw;
    }
}

本文地址:https://www.itbaoku.cn/post/1763936.html