- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我想知道将 EF 上下文与 sp_getapplock
和 session 所有者类型一起使用的风险/安全性。基于docs ,我的理解是 session 所有者意味着如果在此之前未明确释放,则将在 session 结束时释放锁。我假设这也意味着连接结束。
我已经使用事务拥有的锁完成了所有这些工作,但这带来了其他问题和复杂性,所以我想知道如何使用 session 拥有的锁来做这件事,以及这将如何与DbContext
及其连接处理。
我不是 100% 确定 DbContext
默认情况下是如何工作的,无论是使用池还是每个上下文实例打开和关闭自己的连接(我读过的文档似乎建议后者) .对于这个问题,假设我没有对连接管理做任何事情(我很少做),所以 EF 正在处理它,或者任何处理它的人正在处理它。
如果我创建一个 DbContext
实例,获取连接,执行 SQL 以创建一个session-owned 锁,正常使用上下文,不要 释放锁并处理上下文,这能正常工作吗? (实际上,这将在 IDisposable
包装器中以防止出现这种情况,但问题仍然存在。)
说明不佳:
using (var ctx = new MyContext()) {
var conn = ctx.Database.Connection.Open();
conn.ExecuteSqlSomehow("sp_getapplock blahblah");
try {
// Lots of queries, savechanges, etc.
} finally {
// Oops I forgot to conn.ExecuteSql("sp_release the lock");
}
}
await WatchMovieAsync();
using (var ctx = new MyContext()) {
// Can this reuse the same connection, session and/or lock?
}
问题:
using
block 中时,另一个上下文(例如在另一个线程中)是否可以使用我发出锁的连接?using
block 中打开另一个连接(例如对于 SaveChanges
),从而释放锁定?最佳答案
答案似乎是否定的。显然,主要问题是每当从池中重新使用池连接(而不是释放回池时)时都会调用 sp_reset_connection ,这应该释放任何基于 session 的锁。 This social.msdn link声称甚至在 EF 命令之间调用了 sp_reset_connection。
最简单的解决方案是使用在锁定期间保持打开状态的专用连接,如果 API 可能会交错任何 sp_resest_connection 调用(分析可以证明这一点),则不要将其用于其他 SQL。
另见 SqlConnection vs Sql Session. Do their lifetimes coincide?
和what-does-exec-sp-reset-connection-mean-in-sql-server-profiler
这是我们正在尝试的 C# sp_GetAppLock() 包装器(免责声明——未经过全面测试):
用法:
using (new globalApplicationSqlServerLock(connectionString, "theUniqueLockName")
{
// protected code
}
实现:
using System;
using System.Data;
using System.Data.SqlClient;
namespace CompanyNamespace.Server.DataAdaptersCommon
{
/// <summary>
/// A wrapper around sp_GetAppLock (SQL Server), useful for global locking (by arbitrary name) across multiple machines.
/// For instance: Include the compatibility version number within the lock resource to create a version specific lock.
/// </summary>
public class GlobalApplicationSqlServerLock : SimplifiedDisposableBase
{
/// <summary>
/// Last returned value from sp_GetAppLock() or sp_ReleaseAppLock()
/// </summary>
public Int32 LastReturnCode { get; private set; }
/// <summary>
/// The SQL Connection to use.
/// </summary>
readonly SqlConnection _connection;
/// <summary>
/// The name of the lock chosen by the callse
/// </summary>
readonly string _lockName;
/// <summary>
/// The cumulative times that Lock() has been called.
/// </summary>
int _lockCount;
// Refer to sp_GetAppLock()
//
const string _lockOwner = "session";
const string _lockMode = "Exclusive";
const string _dbPrincipal = "public";
/// <summary>
/// Wait a maximum of this many seconds.
/// </summary>
Int32 _waitForLockMaxSeconds;
/// <summary>
/// Constructor accepting a Connection String
/// </summary>
/// <param name="connectionString">Connection string should include "...;AppName=AppType,WebPid" to improve DB side logging.</param>
/// <param name="lockName"></param>
/// <param name="waitForLockMaxSeconds">Throw an exception if the lock cannot be acquired within this time period.</param>
/// <param name="lockNow">True to obtain the lock via the contructor call. Lock is always released in Dipose()</param>
/// <param name="excludeFromOpenTransactionScope">True to exclude from any open TransactionScope</param>
public GlobalApplicationSqlServerLock(
string connectionString,
string lockName,
Int32 waitForLockMaxSeconds = 30,
bool lockNow = true,
bool excludeFromOpenTransactionScope = true)
{
SqlConnectionStringBuilder conStrBuilder = new SqlConnectionStringBuilder(connectionString);
if (excludeFromOpenTransactionScope)
conStrBuilder.Enlist = false;
_waitForLockMaxSeconds = waitForLockMaxSeconds;
// The lock must use a dedicated connection that stays open for the duration of the lock.
// Otherwise, since the lock owner is "session", when the connection is closed the lock "may" be
// released since connection pooling could have inconsistent side effects. So caller must ensure
// lock is released (using IDisposable, etc).
//
_connection = new SqlConnection(conStrBuilder.ConnectionString);
_connection.Open();
_lockName = lockName;
if (lockNow)
Lock();
}
/// <summary>
/// Lock
/// </summary>
public void Lock()
{
string errMsg;
if (!TryLock(out errMsg))
throw new Exception(errMsg);
}
/// <summary>
/// Try lock
/// </summary>
/// <param name="errMsg"></param>
/// <returns>True if lock obtained, false if not with error message.</returns>
public bool TryLock(out string errMsg)
{
using (SqlCommand command = new SqlCommand())
{
command.Connection = _connection;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "sp_GetAppLock";
command.Parameters.Add(new SqlParameter("@Resource", SqlDbType.NVarChar, 255) { Value = _lockName });
command.Parameters.Add(new SqlParameter("@LockMode", SqlDbType.NVarChar, 32) { Value = _lockMode });
command.Parameters.Add(new SqlParameter("@LockOwner", SqlDbType.NVarChar, 32) { Value = _lockOwner });
command.Parameters.Add(new SqlParameter("@LockTimeout", SqlDbType.Int) { Value = _waitForLockMaxSeconds });
command.Parameters.Add(new SqlParameter("@DBPrincipal", SqlDbType.NVarChar, 128) { Value = _dbPrincipal });
command.Parameters.Add(new SqlParameter("@Result", SqlDbType.Int) { Direction = ParameterDirection.ReturnValue });
command.ExecuteNonQuery();
LastReturnCode = (int)command.Parameters["@Result"].Value;
}
switch (LastReturnCode)
{
case 0:
case 1:
_lockCount++;
errMsg = null;
return true;
case -1:
errMsg = "The lock request timed out.";
break;
case -2:
errMsg = "The lock request was canceled.";
break;
case -3:
errMsg = "The lock request was chosen as a deadlock victim.";
break;
case -999:
errMsg = "Indicates a parameter validation or other call error.";
break;
default:
errMsg = "Unexpected return value";
break;
}
return false;
}
/// <summary>
/// Release the lock
/// </summary>
public void Release()
{
string errMsg;
using (SqlCommand command = new SqlCommand())
{
command.Connection = _connection;
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "sp_ReleaseAppLock";
command.Parameters.Add(new SqlParameter("@Resource", SqlDbType.NVarChar, 255) { Value = _lockName });
command.Parameters.Add(new SqlParameter("@LockOwner", SqlDbType.NVarChar, 32) { Value = _lockOwner });
command.Parameters.Add(new SqlParameter("@DBPrincipal", SqlDbType.NVarChar, 128) { Value = _dbPrincipal });
command.Parameters.Add(new SqlParameter("@Result", SqlDbType.Int) { Direction = ParameterDirection.ReturnValue });
command.ExecuteNonQuery();
LastReturnCode = (int)command.Parameters["@Result"].Value;
}
switch (LastReturnCode)
{
case 0:
_lockCount--;
return;
case -999:
errMsg = "Indicates a parameter validation or other call error.";
break;
default:
errMsg = "Unexpected return value";
break;
}
throw new Exception(errMsg);
}
/// <summary>
/// Disposable implmentation
/// </summary>
protected override void FreeManagedResources()
{
try
{
while (_lockCount > 0)
Release();
}
finally
{
try
{
if (_connection != null && _connection.State != ConnectionState.Closed)
_connection.Close();
}
finally
{
base.FreeManagedResources();
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace CompanyNamespace.Common
{
/// <summary>
/// To support IDisposable, pass true to constructor and call:
///
/// AutoDispose(IDisposable) for each disposable at time of creation,
///
/// Or override these as needed:
///
/// FreeManagedResources() and
/// FreeUnmanagedResources()
///
/// Multi-thread safe.
/// </summary>
public abstract class SimplifiedDisposableBase : IDisposable
{
/// <summary>
/// Flag for IDisposable
/// </summary>
protected bool _isDisposed = false;
/// <summary>
/// List of items that should be Dispose() when the instance is Disposed()
/// </summary>
private List<IDisposable> _autoDisposables = new List<IDisposable>();
/// <summary>
/// Constructor
/// </summary>
public SimplifiedDisposableBase()
{
}
/// <summary>
/// Finalizer (needed for freeing unmanaged resources and adds a check a Dispose() check for managed resources).
/// </summary>
~SimplifiedDisposableBase()
{
// Warning: An exception here will end the application.
// Do not attempt to lock to a possibly finalized object within finalizer
// http://stackoverflow.com/questions/4163603/why-garbage-collector-takes-objects-in-the-wrong-order
string errMessages = string.Empty;
try
{
errMessages = String.Format("Warning: Finalizer was called on class '{0}' (base class '{1}'). " +
"IDisposable's should usually call Dispose() to avoid this. (IsDisposed = {2})",
GetType().FullName,
typeof(SimplifiedDisposableBase).FullName,
_isDisposed);
Debug.WriteLine(errMessages);
Dispose(false); // free any unmanaged resources
}
#if DEBUG
catch (Exception ex)
{
errMessages = "Fatal: Exception occurred within Finalizer ~" + GetType().FullName + "()." + errMessages;
Debug.WriteLine(errMessages + " " + ex.Message);
// Verified that this exception appears in Windows Event Log and includes the originating class message and StackTrace[0]
throw new Exception(errMessages, ex);
}
#else
catch (Exception)
{
/* Don't exit the application */
}
#endif
}
/// <summary>
/// Add an managed item to be automatically disposed when the class is disposed.
/// </summary>
/// <param name="disposable"></param>
/// <returns>The argument</returns>
public T AutoDispose<T>(T disposable) where T : IDisposable
{
lock (_autoDisposables)
_autoDisposables.Add(disposable);
return disposable;
}
/// <summary>
/// Derived class can override and chain for support of IDisposable managed resources.
/// </summary>
protected virtual void FreeManagedResources()
{
lock (_autoDisposables)
{
_autoDisposables
.ForEach(d => d.Dispose());
_autoDisposables.Clear();
}
}
/// <summary>
/// Derived class can optionally override for support of IDisposable unmanaged resources.
/// </summary>
protected virtual void FreeUnmanagedResources() { }
/// <summary>
/// Standard IDisposable Implmentation
/// </summary>
public void Dispose()
{
Dispose(true); // calling multiple times is okay
GC.SuppressFinalize(this); // http://stackoverflow.com/questions/12436555/calling-suppressfinalize-multiple-times is okay
}
/// <summary>
/// Dispose
/// </summary>
protected virtual void Dispose(bool isDisposing)
{
if (!isDisposing) // if called from finalizer, do not use lock (causes exception)
{
if (!_isDisposed)
{
FreeUnmanagedResources(); // always free these
_isDisposed = true;
}
return;
}
// Remainder is called from IDisposable (not finalizer)
// Based on "Implemenent IDisposable Correctly"
// http://msdn.microsoft.com/en-us/library/ms244737.aspx
lock (_autoDisposables)
{
if (_isDisposed)
return; // the docs specifically state that Dispose() must be callable multiple times without raising an exception
try
{
try
{
FreeManagedResources();
}
finally
{
FreeUnmanagedResources(); // always free these
}
}
finally
{
_isDisposed = true;
}
}
}
}
}
关于c# - 将 session 拥有的 SQL Server sp_getapplock 与 EF6 DbContexts 一起使用是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42597192/
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!