Index: nhibernate/src/NHibernate.Test/ProxyInterface/CastleProxy.cs =================================================================== --- nhibernate/src/NHibernate.Test/ProxyInterface/CastleProxy.cs (revision 3262) +++ nhibernate/src/NHibernate.Test/ProxyInterface/CastleProxy.cs (working copy) @@ -10,5 +10,7 @@ int Id { get; set; } string Name { get; set; } + + void ThrowDeepException(); } } \ No newline at end of file Index: nhibernate/src/NHibernate.Test/ProxyInterface/CastleProxyFixture.cs =================================================================== --- nhibernate/src/NHibernate.Test/ProxyInterface/CastleProxyFixture.cs (revision 3262) +++ nhibernate/src/NHibernate.Test/ProxyInterface/CastleProxyFixture.cs (working copy) @@ -1,3 +1,4 @@ +using System; using System.Collections; using System.IO; using System.Runtime.Serialization; @@ -110,5 +111,39 @@ Assert.IsNotNull(s.Load(typeof(CastleProxyImpl), 5), "should be proxy - even though it doesn't exists in db"); s.Close(); } + + [Test] + public void ExceptionStackTrace() + { + ISession s = OpenSession(); + CastleProxy ap = new CastleProxyImpl(); + ap.Id = 1; + ap.Name = "first proxy"; + s.Save(ap); + s.Flush(); + s.Close(); + + s = OpenSession(); + ap = (CastleProxy) s.Load(typeof(CastleProxyImpl), ap.Id); + Assert.IsFalse(NHibernateUtil.IsInitialized(ap), "check we have a proxy"); + + try + { + ap.ThrowDeepException(); + Assert.Fail("Exception not thrown"); + } + catch (ArgumentException ae) + { + Assert.AreEqual("thrown from Level2", ae.Message); + + string[] stackTraceLines = ae.StackTrace.Split('\n'); + Assert.IsTrue(stackTraceLines[0].Contains("Level2"), "top of exception stack is Level2()"); + Assert.IsTrue(stackTraceLines[1].Contains("Level1"), "next on exception stack is Level1()"); + } + + s.Delete(ap); + s.Flush(); + s.Close(); + } } } \ No newline at end of file Index: nhibernate/src/NHibernate.Test/ProxyInterface/CastleProxyImpl.cs =================================================================== --- nhibernate/src/NHibernate.Test/ProxyInterface/CastleProxyImpl.cs (revision 3262) +++ nhibernate/src/NHibernate.Test/ProxyInterface/CastleProxyImpl.cs (working copy) @@ -11,6 +11,9 @@ private int _id; private string _name; + private void Level1() { Level2(); } + private void Level2() { throw new ArgumentException("thrown from Level2"); } + #region CastleProxy Members public int Id @@ -25,6 +28,11 @@ set { _name = value; } } + public void ThrowDeepException() + { + Level1(); + } + #endregion } } \ No newline at end of file Index: nhibernate/src/NHibernate/Proxy/Poco/Castle/CastleLazyInitializer.cs =================================================================== --- nhibernate/src/NHibernate/Proxy/Poco/Castle/CastleLazyInitializer.cs (revision 3262) +++ nhibernate/src/NHibernate/Proxy/Poco/Castle/CastleLazyInitializer.cs (working copy) @@ -73,7 +73,18 @@ catch (TargetInvocationException tie) { // Propagate the inner exception so that the proxy throws the same exception as - // the real object would (though of course the stack trace will be probably lost). + // the real object would. + + // Use private member _remoteStackTraceString to make the framework keep + // the original stack trace. + // Based on blog http://dotnetjunkies.com/WebLog/chris.taylor/archive/2004/03/03/8353.aspx + FieldInfo remoteStackTraceString = + typeof(Exception).GetField( "_remoteStackTraceString", + BindingFlags.Instance | BindingFlags.NonPublic); + + remoteStackTraceString.SetValue(tie.InnerException, + tie.InnerException.StackTrace + Environment.NewLine); + throw tie.InnerException; } }