pet*_*ter 6 postgresql entity-framework npgsql entity-framework-core
据我所知,EF(和 EF Core)中没有选项可以显式锁定我正在查询的资源,但我会经常需要这个功能,并且真的不想回到写作每次我需要它时都选择语句。
因为我只需要它用于 postgres 并且根据规范 FOR UPDATE是查询中的最后一项,所以我想到实现它的最简单的方法是获取如下所述的 select 语句:在 Linq to Entities 中,您能否将 IQueryable 转换为字符串SQL?并附加FOR UPDATE并直接执行它。然而,这要么给我一个带有参数占位符的查询,要么不是一个准备好的查询,这意味着执行计划的缓存不会真正在 postgres 上工作,所以无论哪种方式都行不通。
Linq to SQL 有这个方法,DataContext.GetCommand但在 EF 和特别是 EF Core 中似乎没有任何等效的东西。我还查看了 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();
}
Run Code Online (Sandbox Code Playgroud)
这样,其他开发人员可以像所有其他过程一样通过 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);
}
}
Run Code Online (Sandbox Code Playgroud)
所以进入 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>
Run Code Online (Sandbox Code Playgroud)
现在我创建一个名为 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");
}
}
Run Code Online (Sandbox Code Playgroud)
这就是全部,我可以在数据库事务中使用,例如:
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
}
Run Code Online (Sandbox Code Playgroud)
对不起我的英语,我希望我的例子会有所帮助。
| 归档时间: |
|
| 查看次数: |
8348 次 |
| 最近记录: |