我遇到了一个奇怪的问题,NServiceBus重复了X次消息,尽管处理程序中没有抛出异常。有一些信息涉及NHibernate会话和NSB ambiant事务。由于没有抛出任何错误,我不能100%确定问题,因此无法真正决定该怎么做。
我将NSB配置为Castle Windsor,如下所示:
IWindsorContainer container = new WindsorContainer(new XmlInterpreter());
container.Install(new ContainerInstaller());
container.Install(new UnitOfWorkInstaller(AppDomain.CurrentDomain.BaseDirectory, Castle.Core.LifestyleType.Scoped));
container.Install(new FactoryInstaller(AppDomain.CurrentDomain.BaseDirectory));
container.Install(new RepositoryInstaller(AppDomain.CurrentDomain.BaseDirectory));
Configure.With()
.CastleWindsorBuilder(container)
.FileShareDataBus(Properties.Settings.Default.DataBusFileSharePath)
.MsmqTransport()
.IsTransactional(true)
.PurgeOnStartup(false)
.UnicastBus()
.LoadMessageHandlers()
.ImpersonateSender(false)
.JsonSerializer();
UnitOfWorkInstaller
注册工作单元(NHibernate会话),如下所示:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
var fromAssemblyDescriptor = AllTypes.FromAssemblyInDirectory(new AssemblyFilter(_installationPath));
container.Register(fromAssemblyDescriptor
.IncludeNonPublicTypes()
.Pick()
.If(t => t.GetInterfaces().Any(i => i == typeof(IUnitOfWork)) && t.Namespace.StartsWith("Magma"))
.WithService.AllInterfaces()
.Configure(con => con.LifeStyle.Is(_lifeStyleType).UsingFactoryMethod(k => k.Resolve<IUnitOfWorkFactory>().Create())));
}
因此,每次消息到达时,所有存储库都使用相同的工作单元。我读到手动回滚当前事务导致错误(我真的不知道为什么)并且我也知道NSB为每个传输消息创建一个子容器,并且在处理消息之后处理该子容器。问题是当子容器被处理时,工作单元以这种方式处理:
public void Dispose()
{
if (!_isDisposed)
{
DiscardSession();
_isDisposed = true;
}
}
private void DiscardSession()
{
if (_transaction != null && _transaction.IsActive)
{
_transaction.Dispose();
}
if (Session != null)
{
Session.Dispose();
}
}
我的处理程序结构如下:( _unitOfWork作为构造函数依赖项传递)
public void Handle(<MessageType> message)
{
using (_unitOfWork)
{
try
{
// do stuff
_unitOfWork.Commit();
}
catch (Exception ex)
{
_unitOfWork.Rollback();
// rethrow so the message stays in the queue
throw;
}
}
}
我发现如果我不提交工作单元(刷新会话并提交事务),我会收到一条错误消息,指出该消息已经重试超过最大重试次数bla bla bla ...
所以它似乎与NHibernate会话以及它的创建和处理方式有关,但由于它是在工作单元内创建的,所以我无法真正使用会话工厂。我读到我可以使用IMessageModule来创建和处理会话,但我不知道这是否是正确的方法,因为我不知道是什么原因造成了错误。
所以回顾一下:
我正在使用一个范围的工作单元,以便使用它的所有处理程序依赖项将共享同一个实例(thx到子容器,BTW:我已将工作单元设置为瞬态思维,即子容器会将所有瞬态对象视为该容器中的单例,但我看到工作单元未被共享,因此这就是为什么它设置为作用域的原因)
我将处理程序包装在using(_unitOfWork) { }
语句中,以便在每次处理后处理工作单元。
当工作单位被处置时,NHibernate会话也被处理
如果我没有在Commit
上明确调用_unitOfWork
,则消息会重试超出最大重试次数,然后会引发错误。
导致此行为的原因是什么?并且IMessageModule是答案吗?
答案 0 :(得分:0)
我想我把它缩小了一点......我删除了所有using(_unitOfWork)
,_unitOfWork.Commit()
和_unitOfWork.Rollback()
并让NSB TransactionScope
完成工作提交或回滚事务,因为NHibernate的Session正在NSB事务范围中登记。
我也开始使用NHibernate会话的事务(Session.Transaction
)而不是通过Session.BeginTransaction()
获取对它的引用并使用它。我已经复制/粘贴了我的UoW实现,因此您可以看到差异(旧代码在评论中)。
我不知道我的更改是否占用了什么,除了使用Session的事务并删除了刷新,因为它在事务提交中处理似乎已经解决了问题...我没有必须明确调用Commit
方法,以便再次成功处理消息。这是我的UoW实现:
public class NHibernateUnitOfWork : INHibernateUnitOfWork
{
//private ITransaction _transaction;
private bool _isDisposed;
private bool _isInError;
public ISession Session { get; protected set; }
public NHibernateUnitOfWork(ISession session)
{
Contract.Requires(session != null, "session");
Session = session;
//_transaction = Session.BeginTransaction();
// create a new transaction as soon as the session is available
Session.BeginTransaction();
_isDisposed = false;
_isInError = false;
}
public void MarkCreated(Object entity)
{
// assert stuff
try
{
Session.SaveOrUpdate(entity);
//Session.Flush();
}
catch (HibernateException ex)
{
HandleError();
throw;
}
}
public void MarkUpdated(Object entity)
{
// assert stuff
try
{
Session.Update(entity);
//Session.Flush();
}
catch (HibernateException ex)
{
HandleError();
throw;
}
}
public void MarkSavedOrUpdated(Object entity)
{
// assert stuff
try
{
Session.SaveOrUpdate(entity);
//Session.Flush();
}
catch (HibernateException)
{
HandleError();
throw;
}
}
public void MarkDeleted(Object entity)
{
// assert stuff
try
{
Session.Delete(entity);
//Session.Flush();
}
catch (HibernateException ex)
{
HandleError();
throw;
}
}
public void Commit()
{
// assert stuff
try
{
//Session.Flush();
//_transaction.Commit();
Session.Transaction.Commit();
}
catch (HibernateException ex)
{
HandleError();
throw;
}
}
public void Rollback()
{
// assert stuff
try
{
//if (!_transaction.WasRolledBack)
//{
// _transaction.Rollback();
//}
Session.Transaction.Rollback();
}
catch (HibernateException ex)
{
HandleError();
throw;
}
}
public void Dispose()
{
if (!_isDisposed)
{
DiscardSession();
_isDisposed = true;
}
}
private void DiscardSession()
{
//if (_transaction != null && _transaction.IsActive)
//{
// _transaction.Dispose();
//}
if (Session != null)
{
try
{
// rollback all uncommitted changes
if (Session.Transaction != null && Session.Transaction.IsActive)
{
Session.Transaction.Rollback();
}
//Session.Clear();
Session.Close();
}
catch (Exception)
{ }
finally
{
Session.Dispose();
}
}
}
private void HandleError()
{
_isInError = true;
//if (_transaction != null && _transaction.IsActive)
//{
// _transaction.Rollback();
//}
if (Session.Transaction != null && Session.Transaction.IsActive)
{
Session.Transaction.Rollback();
}
}
// assert methods
}
这有什么意义吗?我仍然不知道是什么原因造成了错误,但它似乎与在事务范围完成之前处理NHibernate会话有关。