我需要在每个Save, SaveOrUpdate和Delete方法中添加CommitChange()吗?
本文关键字:添加 方法 CommitChange Delete SaveOrUpdate Save | 更新日期: 2023-09-27 18:07:32
我已经开始使用NHibernate
作为数据库映射实现asp.net网页。
我正在遵循本教程,但我有麻烦让代码工作。
下面是我在抽象类中修改的脚本。
请告诉我修改的是否正确?
我将this.CommitChanges();
方法添加到所有Save
, SaveOrUpdate
和Delete
方法中。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate;
using NHibernate.Criterion;
using Tec.Core.DataInterfaces;
using Tec.Data.SessionManagement;
namespace Tec.Data
{
public abstract class NHibernateRepository<T, IdT> : IRepository<T, IdT>
{
public T GetById(IdT id, bool shouldLock)
{
T entity;
if (shouldLock)
{
entity = (T)NHibernateSession.Load(persitentType, id, LockMode.Upgrade);
}
else
{
entity = (T)NHibernateSession.Load(persitentType, id);
}
return entity;
}
public List<T> GetAll()
{
return GetByCriteria();
}
public List<T> GetByCriteria(params ICriterion[] criterion)
{
ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);
foreach (ICriterion criterium in criterion)
{
criteria.Add(criterium);
}
return criteria.List<T>() as List<T>;
}
public List<T> GetByExample(T exampleInstance, params string[] propertiesToExclude)
{
ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);
Example example = Example.Create(exampleInstance);
foreach (string propertyToExclude in propertiesToExclude)
{
example.ExcludeProperty(propertyToExclude);
}
criteria.Add(example);
return criteria.List<T>() as List<T>;
}
public T GetUniqueByExample(T exampleInstance, params string[] propertiesToExclude)
{
List<T> foundList = GetByExample(exampleInstance, propertiesToExclude);
if (foundList.Count > 1)
{
throw new NonUniqueResultException(foundList.Count);
}
if (foundList.Count > 0)
{
return foundList[0];
}
else
{
return default(T);
}
}
public T Save(T entity) {
NHibernateSession.Save(entity);
//this.CommitChanges(); // manually added
return entity;
}
public T SaveOrUpdate(T entity)
{
NHibernateSession.SaveOrUpdate(entity);
//this.CommitChanges();
return entity;
}
public void Delete(T entity) {
NHibernateSession.Delete(entity);
//this.CommitChanges(); //Record is delete from database only if I enable this
statement
}
public void CommitChanges(){
if (NHibernateSessionFactory.Instance.HasOpenTransaction()) {
NHibernateSessionFactory.Instance.CommitTransaction();
} else {
// If there's no transaction, just flush the changes
NHibernateSessionFactory.Instance.GetSession().Flush();
}
}
private ISession NHibernateSession
{
get
{
return NHibernateSessionFactory.Instance.GetSession();
}
}
private Type persitentType = typeof(T);
}
//----------------------------------------------------------------------------------//
public class NHibernateSessionFactory
{
public static NHibernateSessionFactory Instance
{
get
{
return Nested.NHibernateSessionFactory;
}
}
private NHibernateSessionFactory()
{
InitSessionFactory();
}
private class Nested
{
static Nested() { }
internal static readonly NHibernateSessionFactory NHibernateSessionFactory = new NHibernateSessionFactory();
}
private void InitSessionFactory()
{
sessionFactory = new Configuration().Configure().BuildSessionFactory();
}
public void RegisterInterceptor(IInterceptor interceptor)
{
ISession session = ContextSession;
if (session != null && session.IsOpen)
{
throw new CacheException("You cannot register an interceptor once a session has already been opened");
}
GetSession(interceptor);
}
public ISession GetSession()
{
return GetSession(null);
}
private ISession GetSession(IInterceptor interceptor)
{
ISession session = ContextSession;
if (session == null)
{
if (interceptor != null)
{
session = sessionFactory.OpenSession(interceptor);
}
else
{
session = sessionFactory.OpenSession();
}
ContextSession = session;
}
return session;
}
public void CloseSession()
{
ISession session = ContextSession;
if (session != null && session.IsOpen)
{
session.Flush();
session.Close();
}
ContextSession = null;
}
public void BeginTransaction()
{
ITransaction transaction = ContextTransaction;
if (transaction == null)
{
transaction = GetSession().BeginTransaction();
ContextTransaction = transaction;
}
}
public void CommitTransaction()
{
ITransaction transaction = ContextTransaction;
try
{
if (HasOpenTransaction())
{
transaction.Commit();
ContextTransaction = null;
}
}
catch (HibernateException)
{
RollbackTransaction();
throw;
}
}
public bool HasOpenTransaction()
{
ITransaction transaction = ContextTransaction;
return transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack;
}
public void RollbackTransaction()
{
ITransaction transaction = ContextTransaction;
try
{
if (HasOpenTransaction())
{
transaction.Rollback();
}
ContextTransaction = null;
}
finally
{
CloseSession();
}
}
private ITransaction ContextTransaction
{
get
{
if (IsInWebContext())
{
return (ITransaction)HttpContext.Current.Items[TRANSACTION_KEY];
}
else
{
return (ITransaction)CallContext.GetData(TRANSACTION_KEY);
}
}
set
{
if (IsInWebContext())
{
HttpContext.Current.Items[TRANSACTION_KEY] = value;
}
else
{
CallContext.SetData(TRANSACTION_KEY, value);
}
}
}
private ISession ContextSession
{
get
{
if (IsInWebContext())
{
return (ISession)HttpContext.Current.Items[SESSION_KEY];
}
else
{
return (ISession)CallContext.GetData(SESSION_KEY);
}
}
set
{
if (IsInWebContext())
{
HttpContext.Current.Items[SESSION_KEY] = value;
}
else
{
CallContext.SetData(SESSION_KEY, value);
}
}
}
private bool IsInWebContext()
{
return HttpContext.Current != null;
}
private const string TRANSACTION_KEY = "CONTEXT_TRANSACTION";
private const string SESSION_KEY = "CONTEXT_SESSION";
private ISessionFactory sessionFactory;
}
}
下面是我的测试方法:
public void AddUser()
{
// create three people
User jose = new User();
jose.UserName = "Jose";
jose.UserLogin = "28";
mUser.SaveOrUpdate(jose);// Record is addded to database,
// some people said it's
// because on auto-increment identity
User maria = new User();
maria.UserName = "Maria";
maria.UserLogin = "29";
mUser.SaveOrUpdate(maria);
User mario = new User();
mario.UserName = "Mario";
mario.UserLogin = "27";
mUser.SaveOrUpdate(mario);
// delete Mario
mUser.Delete(mario); //Record is not deleted from database
}
,我只得到这3条语句在SQL分析器上执行
exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Jose',@p1=N'28'
exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Maria',@p1=N'29'
exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Mario',@p1=N'27'
Delete method上的uncomment this.CommitChanges()后的语句
exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Jose',@p1=N'28'
exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Maria',@p1=N'29'
exec sp_reset_connection
exec sp_executesql N'INSERT INTO TEC.dbo.tblSysUser (UserName, UserLogin) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar(4000),@p1 nvarchar(4000)',@p0=N'Mario',@p1=N'27'
exec sp_reset_connection
exec sp_executesql N'DELETE FROM TEC.dbo.tblSysUser WHERE UserID = @p0',N'@p0 bigint',@p0=64
你能告诉我我的代码是怎么回事吗?我错过什么了吗?
我会说:问题中显示的代码导致了错误的,不希望的结果。但问题不在NHibernate端(例如:实现中的一些bug)。
问题是,每个操作集(web请求,工作单元)应该封装在事务中-带有显式的Commit
或Rollback
调用。
NHibernate有时必须执行一些WRITE操作语句*(引自9.6)。刷新文档:)
…使用本地ID生成的对象在保存时插入…
事实上,NHibernate必须发出调用,并不意味着,这样的操作应该持续。在这种情况下,它的结果只是不可避免的——获取DB生成的ID。这仍然可能发生,我们应该稍后回滚操作…
最重要的设置是FlushMode
的ISession
*(引用自9.6)。刷新文档:)
…除非显式地使用
Flush()
,否则绝对不能保证Session何时执行ADO。. NET调用时,只显示它们执行的顺序。然而,NHibernate保证ISession.Find(..)
方法永远不会返回过时的数据;也不会返回错误的数据。
所以,正如我们上面所经历的- FlushMode设置为Auto…没有显式事务Commit或Rollback,但数据被持久化。但这很可能是事故…因为缺少事务
FlushMode
以下是可用的FlushModes (参见下面的引用,代码片段)
/// <summary>
/// Represents a flushing strategy.
/// </summary>
/// <remarks>
/// The flush process synchronizes database state with session state by detecting state
/// changes and executing SQL statements
/// </remarks>
[Serializable]
public enum FlushMode
{
/// <summary>
/// Special value for unspecified flush mode (like <see langword="null" /> in Java).
/// </summary>
Unspecified = -1,
/// <summary>
/// The <c>ISession</c> is never flushed unless <c>Flush()</c> is explicitly
/// called by the application. This mode is very efficient for read only
/// transactions
/// </summary>
Never = 0,
/// <summary>
/// The <c>ISession</c> is flushed when <c>Transaction.Commit()</c> is called
/// </summary>
Commit = 5,
/// <summary>
/// The <c>ISession</c> is sometimes flushed before query execution in order to
/// ensure that queries never return stale state. This is the default flush mode.
/// </summary>
Auto = 10,
/// <summary>
/// The <see cref="ISession"/> is flushed before every query. This is
/// almost always unnecessary and inefficient.
/// </summary>
Always = 20
}
当我们从工厂创建会话时,我们可以分配其中一个FlushModes。我强烈建议使用FlushMode.Commit
或-使用FlusMode。永远不要在事务Commit()之前调用Session.Flush()。
因为-我们应该是显式调用Flush()
的驱动程序。因此,不要依赖于nhibernate发出INSERT的事实。这是以后处理的必需品。总是在transaction中包装操作集,使用FlushMode.Commit…这就是我的建议
注意:还要检查Does criteria.List(Type)事务管理,以获得更多讨论事务甚至读操作的优势的链接。