C# 如何在Nhibernate中进行版本控制?
我真不敢相信,要有人给我展示一个简单的工作示例是如此困难。这让我相信,每个人都只能像知道怎么做一样说话,但实际上他们不知道 我把文章缩短到我想让例子做的事情。也许这篇文章太长了,把人们吓跑了 为了得到这笔赏金,我正在寻找一个可以在VS2010中复制并运行的工作示例 示例需要做什么C# 如何在Nhibernate中进行版本控制?,c#,.net,asp.net-mvc,sql-server-2008,nhibernate,C#,.net,Asp.net Mvc,Sql Server 2008,Nhibernate,我真不敢相信,要有人给我展示一个简单的工作示例是如此困难。这让我相信,每个人都只能像知道怎么做一样说话,但实际上他们不知道 我把文章缩短到我想让例子做的事情。也许这篇文章太长了,把人们吓跑了 为了得到这笔赏金,我正在寻找一个可以在VS2010中复制并运行的工作示例 示例需要做什么 显示mssql 2008中版本为时间戳的my domain中的数据类型 显示nhibernate自动引发“StaleObjectException” 向我展示这3个场景的工作示例 情景1 用户A来到站点并编辑第1行。用
我尝试的 我试图构建我自己的,但我不能让它抛出StaleObjectException,也不能让版本号递增。我厌倦了打开两个单独的浏览器并加载索引页。两个浏览器显示相同的版本号
public class Default1Controller : Controller
{
//
// GET: /Default1/
public ActionResult Index()
{
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var firstRecord = session.Query<TableA>().FirstOrDefault();
transaction.Commit();
return View(firstRecord);
}
}
}
public ActionResult Save()
{
var sessionFactory = CreateSessionFactory();
using (var session = sessionFactory.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var firstRecord = session.Query<TableA>().FirstOrDefault();
firstRecord.Name = "test2";
transaction.Commit();
return View();
}
}
}
private static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(c => c.FromConnectionStringWithKey("Test")))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<TableA>())
// .ExposeConfiguration(BuidSchema)
.BuildSessionFactory();
}
private static void BuidSchema(NHibernate.Cfg.Configuration config)
{
new NHibernate.Tool.hbm2ddl.SchemaExport(config).Create(false, true);
}
}
public class TableA
{
public virtual Guid Id { get; set; }
public virtual string Name { get; set; }
// Not sure what data type this should be for timestamp.
// To eliminate changing to much started with int version
// but want in the end timestamp.
public virtual int Version { get; set; }
}
public class TableAMapping : ClassMap<TableA>
{
public TableAMapping()
{
Id(x => x.Id);
Map(x => x.Name);
Version(x => x.Version);
}
}
public类default1控制器:控制器
{
//
//获取:/Default1/
公共行动结果索引()
{
var sessionFactory=CreateSessionFactory();
使用(var session=sessionFactory.OpenSession())
{
使用(var transaction=session.BeginTransaction())
{
var firstRecord=session.Query().FirstOrDefault();
Commit();
返回视图(第一条记录);
}
}
}
公共操作结果保存()
{
var sessionFactory=CreateSessionFactory();
使用(var session=sessionFactory.OpenSession())
{
使用(var transaction=session.BeginTransaction())
{
var firstRecord=session.Query().FirstOrDefault();
firstRecord.Name=“test2”;
Commit();
返回视图();
}
}
}
私有静态ISessionFactory CreateSessionFactory()
{
流畅地返回。Configure()
.数据库(MsSqlConfiguration.MsSql2008
.ConnectionString(c=>c.FromConnectionStringWithKey(“测试”))
.Mappings(m=>m.FluentMappings.AddFromAssemblyOf())
//.ExposeConfiguration(内置模式)
.BuildSessionFactory();
}
私有静态void BuidSchema(NHibernate.Cfg.Configuration配置)
{
新建NHibernate.Tool.hbm2ddl.SchemaExport(config.Create)(false,true);
}
}
公共类表格
{
公共虚拟Guid Id{get;set;}
公共虚拟字符串名称{get;set;}
//不确定时间戳的数据类型。
//要消除对多个版本的更改,请从int版本开始
//但最后要的是时间戳。
公共虚拟整数版本{get;set;}
}
公共类表映射:类映射
{
公共表映射()
{
Id(x=>x.Id);
Map(x=>x.Name);
版本(x=>x.Version);
}
}
nhibernate会阻止检索该行吗
不会。锁定只针对事务的范围放置,在web应用程序中,事务在请求结束时结束。此外,事务隔离模式的默认类型是,这意味着一旦select语句终止,就会释放读锁。如果在同一请求和事务中读取和编辑,可以在手头的行上放置读写锁,这将阻止其他事务向该行写入或从该行读取。但是,这种类型的并发控制在web应用程序中无法很好地工作
或者用户B是否仍能看到该行,但如果他试图保存该行,该行将崩溃
如果使用[乐观并发],就会发生这种情况。在NHibernate中,乐观并发通过添加。Save/update命令与更新所基于的版本一起发出。如果与数据库表中的版本不同,则不会更新任何行,NHibernate将抛出
如果用户A说取消并且不编辑会发生什么。我一定要吗
自己释放锁,或者是否可以将超时设置为释放
锁
否,在请求结束时释放锁
总的来说,您最好选择与NHibernate管理的版本字段进行乐观并发。是的,可以使用NHibernate锁定一行,但如果我理解得很好,您的场景是在web环境中,那么这不是最佳实践 最佳实践是使用乐观锁定和您提到的自动版本控制。 在页面打开时锁定一行,在页面卸载时释放该行将很快导致该行死锁(javascript问题,页面未正确终止…)。 乐观锁定将使NHibernate在刷新包含另一个会话修改的对象的事务时引发异常。 如果您想要对相同的信息进行真正的并发修改,您可以尝试考虑将许多用户输入合并到同一文档中的系统,但它是一个独立的系统,不由ORM管理 您必须选择一种方式来处理web环境中的会话。 唯一可行的方法是
session.Update(test);
session.Commit(); it would not commit right?
BEGIN TRAN
DECLARE @now DATETIME
SET @now = '2012-09-28 14:00:00'
SELECT *, @now AS NewEditTimestamp, CHECKSUM(ID, [Description]) AS RowChecksum
FROM TestLocks
WITH (HOLDLOCK, ROWLOCK)
WHERE ID = 3 AND EditTimestamp < DATEADD(mi, -30, @now)
/* Do all your stuff here while the record is locked */
UPDATE TestLocks
SET EditTimestamp = @now
WHERE ID = 3 AND EditTimestamp < DATEADD(mi, -30, @now)
COMMIT TRAN
BEGIN TRAN
DECLARE @RowChecksum INT
SET @RowChecksum = -845335138
UPDATE TestLocks
SET [Description] = 'New Description'
WHERE ID = 3 AND CHECKSUM(ID, [Description]) = @RowChecksum
SELECT @@ROWCOUNT AS RowsUpdated
COMMIT TRAN
public class SaveListener : NHibernate.Event.ISaveOrUpdateEventListener
{
public void OnSaveOrUpdate(NHibernate.Event.SaveOrUpdateEvent e)
{
NHibernate.Persister.Entity.IEntityPersister p = e.Session.GetEntityPersister(null, e.Entity);
if (p.IsVersioned)
{
//TODO: check types etc...
MyEntity m = (MyEntity) e.Entity;
DateTime oldversion = (DateTime) p.GetVersion(m, e.Session.EntityMode);
DateTime currversion = (DateTime) p.GetCurrentVersion(m.ID, e.Session);
if (oldversion < currversion.AddMinutes(-30))
throw new StaleObjectStateException("MyEntity", m.ID);
}
}
private static void Configure(NHibernate.Cfg.Configuration cfg)
{
cfg.EventListeners.SaveOrUpdateEventListeners = new NHibernate.Event.ISaveOrUpdateEventListener[] {new SaveListener()};
}
public static ISessionFactory CreateSessionFactory()
{
return Fluently.Configure().Database(...).
.Mappings(...)
.ExposeConfiguration(Configure)
.BuildSessionFactory();
}
public class MyEntityMap: ClassMap<MyENtity>
{
public MyEntityMap()
{
Table("MyTable");
Id(x => x.ID);
Version(x => x.Timestamp);
Map(x => x.PropA);
Map(x => x.PropB);
}
}