私が理解している限り、EF(とEF Core)には、私が照会しているリソースを明示的にロックするオプションはありませんが、この機能はかなり頻繁に必要になります。私がそれを必要とするたびにselectステートメント。
Postgresにのみ必要なので、 FOR UPDATE
の仕様に従って、 FOR UPDATE
は最後の項目です。ここで説明するようにselect文を取得するのが最も簡単でした。Linq to Entitiesでは、IQueryableをSQLの文字列? FOR UPDATE
を追加して直接実行します。しかし、これは私にパラメータプレースホルダを持つクエリを提供するか、実行計画のキャッシュが実際にはポストグルでは機能しないことを意味する準備されたクエリではないので、どちらの方法においても問題ありません。
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();
}
この方法で、他の開発者は、他のすべての手順と同様のLINQを経由してEFを使用して、代わりに実行することができ.ToList()
は、実行したい.ForUpdate()
( FOR UPDATE
はpostgresでサポートされている最後のオプションであるため、Updateは実装を容易にするためにクエリを実行し、その後はもう何も存在しないはずです)
この作品は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
}
私の英語ですみません、私の例が助けてくれることを願っています。