Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/shell/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在Nhibernate中进行版本控制?_C#_.net_Asp.net Mvc_Sql Server 2008_Nhibernate - Fatal编程技术网

C# 如何在Nhibernate中进行版本控制?

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行。用

我真不敢相信,要有人给我展示一个简单的工作示例是如此困难。这让我相信,每个人都只能像知道怎么做一样说话,但实际上他们不知道

我把文章缩短到我想让例子做的事情。也许这篇文章太长了,把人们吓跑了

为了得到这笔赏金,我正在寻找一个可以在VS2010中复制并运行的工作示例

示例需要做什么

  • 显示mssql 2008中版本为时间戳的my domain中的数据类型
  • 显示nhibernate自动引发“StaleObjectException”
  • 向我展示这3个场景的工作示例
  • 情景1

    用户A来到站点并编辑第1行。用户B来了(注意,他可以看到第1行)并单击以编辑第1行,在用户A完成之前,应拒绝用户B编辑该行

    场景2

    用户A来到站点并编辑第1行。用户B在30分钟后到达并单击以编辑第1行。用户B应该能够编辑此行并保存。这是因为用户A花了太长时间编辑行,并失去了编辑权限

    场景3

    用户A离开后回来。他点击了更新行按钮,他会收到StaleObjectException的问候

    我正在使用asp.NETMVC和FluentNHibernate。正在寻找在这些方面要完成的示例


    我尝试的

    我试图构建我自己的,但我不能让它抛出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);
    
        }
    }