我正在进行一个项目,以逐步淘汰遗留应用程序。在过程中,作为临时解决方案,我们使用数据库与遗留应用程序集成。
遗留应用程序使用具有可序列化隔离级别的事务。由于数据库与遗留应用程序的集成,我目前最好使用相同的悲观并发模型和可序列化的隔离级别。
这些序列化的事务不仅应该包装在SaveChanges语句的周围,而且还应该包含一些数据读取。
我这么做是靠
我的概念是,这将我的整个读写过程封装在序列化的事务中,然后提交。
我认为这是一种悲观的并发方式。
然而,阅读本文,https://learn.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application指出ef不支持悲观并发。
我的问题是:
发布于 2017-07-26 10:27:00
实现悲观并发的一种方法是使用如下一种方式:
var options = new TransactionOptions
{
IsolationLevel = System.Transactions.IsolationLevel.Serializable,
Timeout = new TimeSpan(0, 0, 0, 10)
};
using(var scope = new TransactionScope(TransactionScopeOption.RequiresNew, options))
{ ... stuff here ...}在Assemblies\Microsoft\Framework.NETFramework\v4.6.1\System.Transactions.dll中,您似乎必须右键单击TransactionScope,然后将其添加为: reference TransactionScope的引用
但是,如果有两个线程试图增加同一个计数器,则会发现一个成功,而另一个线程则在10秒内完成超时。这样做的原因是,当它们继续保存更改时,它们都需要将它们的锁升级到独占状态,但它们不能这样做,因为其他事务已经在同一行上持有共享锁。然后,Server将在一个事务失败以解决死锁后检测死锁。失败的一个事务将释放共享锁,而第二个事务将能够将其共享锁升级为独占锁并继续执行。
解决此死锁的方法是使用以下内容向数据库提供UPDLOCK提示:
private static TestEntity GetFirstEntity(Context context) {
return context.TestEntities
.SqlQuery("SELECT TOP 1 Id, Value FROM TestEntities WITH (UPDLOCK)")
.Single();
}这段代码来自Ladislav的博客,现在看起来是不可用的。另一种选择是求助于乐观锁定。
发布于 2017-05-11 19:19:59
该文档指出,EF没有内置的悲观并发支持。但这并不意味着你不能悲观地锁定EF。,这样您就可以对EF!进行悲观的锁定。
菜谱很简单:
我做了很多悲观的锁定,但乐观锁定更好。你不能这样做。--
悲观锁定无法帮助的一个典型示例是父级子关系,您可以锁定父级,并将其视为一个聚合(因此假设您也是唯一能够访问该子节点的)。因此,如果其他线程试图访问父对象,则在其他线程从父表释放锁之前,它将无法工作(将被阻塞)。但是使用ORM,任何其他编码器都可以独立地加载子程序-从那时起,2个线程将对子对象进行更改.使用悲观锁定,您可能会弄乱数据,如果乐观,您将得到异常,您可以重新加载有效数据并再次尝试保存.
所以代码::
public static class DbContextSqlExtensions
{
public static void LockTable<Entity>(this DbContext context) where Entity : class
{
var tableWithSchema = context.GetTableNameWithSchema<Entity>();
context.Database.ExecuteSqlCommand(string.Format("SELECT null as dummy FROM {0} WITH (tablockx, holdlock)", tableWithSchema));
}
}
public static class DbContextExtensions
{
public static string GetTableNameWithSchema<T>(this DbContext context)
where T : class
{
var entitySet = GetEntitySet<T>(context);
if (entitySet == null)
throw new Exception(string.Format("Unable to find entity set '{0}' in edm metadata", typeof(T).Name));
var tableName = GetStringProperty(entitySet, "Schema") + "." + GetStringProperty(entitySet, "Table");
return tableName;
}
private static EntitySet GetEntitySet<T>(DbContext context)
{
var type = typeof(T);
var entityName = type.Name;
var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
IEnumerable<EntitySet> entitySets;
entitySets = metadata.GetItemCollection(DataSpace.SSpace)
.GetItems<EntityContainer>()
.Single()
.BaseEntitySets
.OfType<EntitySet>()
.Where(s => !s.MetadataProperties.Contains("Type")
|| s.MetadataProperties["Type"].ToString() == "Tables");
var entitySet = entitySets.FirstOrDefault(t => t.Name == entityName);
return entitySet;
}
private static string GetStringProperty(MetadataItem entitySet, string propertyName)
{
MetadataProperty property;
if (entitySet == null)
throw new ArgumentNullException("entitySet");
if (entitySet.MetadataProperties.TryGetValue(propertyName, false, out property))
{
string str = null;
if (((property != null) &&
(property.Value != null)) &&
(((str = property.Value as string) != null) &&
!string.IsNullOrEmpty(str)))
{
return str;
}
}
return string.Empty;
}
}https://stackoverflow.com/questions/43912368
复制相似问题