我已经开始使用NHibernate
作为数据库映射实现asp.net网页。
我正在关注this tutorial,但我无法让代码生效。
以下是我在抽象类中修改过的脚本。
如果修改是正确的,请告诉我吗?
我已将方法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
}
我在SQL事件探查器上只获得了这3个语句
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'
取消评论this.CommitChanges()关于删除Methode
之后的声明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
请问您的代码中有什么问题?我错过了什么?
答案 0 :(得分:0)
我会说:问题中显示的代码会导致错误的,不良后果。但问题不在于NHibernate端(例如实现中的一些错误) 。
问题是,每个操作集(网络请求,工作单元)应该封装在 transcation 中 - 使用显式Commit
或{ {1}}致电。
NHibernate有时必须执行一些WRITE操作语句*(引自9.6. Flush文档:)
使用本机ID生成的... 对象在保存时插入 ...
NHibernate必须发出该调用的事实并不意味着此类操作应该持续存在。在这种情况下,它只是不可避免的结果 - 获得DB生成的ID。它仍然可能发生,我们应该稍后回滚......
最重要的设置是Rollback
* FlushMode
(引自9.6. Flush文档:)
...除非您明确
ISession
,否则绝对无法保证Session何时执行ADO.NET调用,只保证它们执行的顺序。 但是,NHibernate确保Flush()
方法永远不会返回陈旧数据;他们也不会返回错误的数据。
因此,正是我们上面所经历的 - FlushMode设置为Auto ...没有显式事务提交或回滚,但数据是持久的。 但这很可能是事故...... 因为缺少交易
以下是FlushModes (请参阅下面的引用,代码段)
ISession.Find(..)
当我们从工厂创建会话时,我们可以指定其中一个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
}
或 - 使用FlusMode.Never并在事务Commit()之前调用Session.Flush()。
因为 - 我们应该成为FlushMode.Commit
显式调用的驱动程序。所以,不要依赖NHiberante发布INSERT的事实。这是后来处理的必要条件。总是在Transacton中包装这组操作,使用FlushMode.Commit ......这将是我的建议
注意:另请查看此Does criteria.List(Type) transaction management,以获取更多链接,以了解甚至是读取操作的交易优势。