Skip to content

Commit f0644cf

Browse files
Catch practices: avoid losing catched exception information. (#1555)
* Catch practices: avoid losing catched exception information. All catches have been reviewed for avoiding losing original exception information: * When another exception is raised, it should embed the original one as an inner exception. * Otherwise when re-thrown, it should be done without altering its original stack-trace. Preferred way is of course to use "throw;" in the catch clause without specifying the exception, but otherwise ReflectHelper provides a way to preserve the stack-trace. * Otherwise it should be transmitted to the logger. Some cases still do not follow this pattern for avoiding being too noisy, like "Try" utilities, failure in closing an already failed object, some "implemented by exception" features, ...
1 parent 8d2b123 commit f0644cf

24 files changed

+109
-70
lines changed

src/NHibernate/Async/Cache/StandardQueryCache.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,14 @@ public async Task<IList> GetAsync(QueryKey key, ICacheAssembler[] returnTypes, b
115115
result.Add(await (TypeHelper.AssembleAsync((object[])cacheable[i], returnTypes, session, null, cancellationToken)).ConfigureAwait(false));
116116
}
117117
}
118-
catch (UnresolvableObjectException)
118+
catch (UnresolvableObjectException ex)
119119
{
120120
if (isNaturalKeyLookup)
121121
{
122122
//TODO: not really completely correct, since
123123
// the UnresolvableObjectException could occur while resolving
124124
// associations, leaving the PC in an inconsistent state
125-
Log.Debug("could not reassemble cached result set");
125+
Log.Debug(ex, "could not reassemble cached result set");
126126
await (_queryCache.RemoveAsync(key, cancellationToken)).ConfigureAwait(false);
127127
return null;
128128
}

src/NHibernate/Async/Event/Default/DefaultMergeEventListener.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -263,11 +263,11 @@ private async Task<object> MergeTransientEntityAsync(object entity, string entit
263263

264264
if (((EventCache)copyCache).IsOperatedOn(propertyFromEntity))
265265
{
266-
log.Info("property '{0}.{1}' from original entity is in copyCache and is in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
266+
log.Info(ex, "property '{0}.{1}' from original entity is in copyCache and is in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
267267
}
268268
else
269269
{
270-
log.Info("property '{0}.{1}' from original entity is in copyCache and is not in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
270+
log.Info(ex, "property '{0}.{1}' from original entity is in copyCache and is not in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
271271
}
272272

273273
// continue...; we'll find out if it ends up not getting saved later

src/NHibernate/Async/Impl/SessionFactoryImpl.cs

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using System;
1212
using System.Collections.Concurrent;
1313
using System.Collections.Generic;
14+
using System.Collections.ObjectModel;
1415
using System.Data.Common;
1516
using System.Linq;
1617
using System.Runtime.Serialization;

src/NHibernate/Cache/StandardQueryCache.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,14 @@ public IList Get(QueryKey key, ICacheAssembler[] returnTypes, bool isNaturalKeyL
133133
result.Add(TypeHelper.Assemble((object[])cacheable[i], returnTypes, session, null));
134134
}
135135
}
136-
catch (UnresolvableObjectException)
136+
catch (UnresolvableObjectException ex)
137137
{
138138
if (isNaturalKeyLookup)
139139
{
140140
//TODO: not really completely correct, since
141141
// the UnresolvableObjectException could occur while resolving
142142
// associations, leaving the PC in an inconsistent state
143-
Log.Debug("could not reassemble cached result set");
143+
Log.Debug(ex, "could not reassemble cached result set");
144144
_queryCache.Remove(key);
145145
return null;
146146
}

src/NHibernate/Cfg/SettingsFactory.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ public Settings BuildSettings(IDictionary<string, string> properties)
6262
{
6363
sqlExceptionConverter = SQLExceptionConverterFactory.BuildSQLExceptionConverter(dialect, properties);
6464
}
65-
catch (HibernateException)
65+
catch (HibernateException he)
6666
{
67-
log.Warn("Error building SQLExceptionConverter; using minimal converter");
67+
log.Warn(he, "Error building SQLExceptionConverter; using minimal converter");
6868
sqlExceptionConverter = SQLExceptionConverterFactory.BuildMinimalSQLExceptionConverter();
6969
}
7070
settings.SqlExceptionConverter = sqlExceptionConverter;

src/NHibernate/Cfg/XmlHbmBinding/ClassBinder.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,9 @@ protected void BindAnyMeta(IAnyMapping anyMapping, Any model)
371371
string entityName = GetClassName(metaValue.@class, mappings);
372372
values[value] = entityName;
373373
}
374-
catch (InvalidCastException)
374+
catch (InvalidCastException ice)
375375
{
376-
throw new MappingException("meta-type was not an IDiscriminatorType: " + metaType.Name);
376+
throw new MappingException("meta-type was not an IDiscriminatorType: " + metaType.Name, ice);
377377
}
378378
catch (HibernateException he)
379379
{

src/NHibernate/Collection/AbstractPersistentCollection.cs

+4-2
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ public object Current
6161
{
6262
return enclosingInstance.operationQueue[position].AddedInstance;
6363
}
64-
catch (IndexOutOfRangeException)
64+
catch (IndexOutOfRangeException ex)
6565
{
66-
throw new InvalidOperationException();
66+
throw new InvalidOperationException(
67+
"MoveNext as not been called or its last call has yielded false (meaning the enumerator is beyond the end of the enumeration).",
68+
ex);
6769
}
6870
}
6971
}

src/NHibernate/Engine/StatefulPersistenceContext.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1439,7 +1439,7 @@ void IDeserializationCallback.OnDeserialization(object sender)
14391439
}
14401440
catch (HibernateException he)
14411441
{
1442-
throw new InvalidOperationException(he.Message);
1442+
throw new InvalidOperationException(he.Message, he);
14431443
}
14441444
}
14451445

@@ -1467,7 +1467,7 @@ void IDeserializationCallback.OnDeserialization(object sender)
14671467
}
14681468
catch (MappingException me)
14691469
{
1470-
throw new InvalidOperationException(me.Message);
1470+
throw new InvalidOperationException(me.Message, me);
14711471
}
14721472
}
14731473
}

src/NHibernate/Event/Default/DefaultMergeEventListener.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -266,11 +266,11 @@ private object MergeTransientEntity(object entity, string entityName, object req
266266

267267
if (((EventCache)copyCache).IsOperatedOn(propertyFromEntity))
268268
{
269-
log.Info("property '{0}.{1}' from original entity is in copyCache and is in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
269+
log.Info(ex, "property '{0}.{1}' from original entity is in copyCache and is in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
270270
}
271271
else
272272
{
273-
log.Info("property '{0}.{1}' from original entity is in copyCache and is not in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
273+
log.Info(ex, "property '{0}.{1}' from original entity is in copyCache and is not in the process of being merged; {1} =[{2}]", copyEntry.EntityName, propertyName, propertyFromEntity);
274274
}
275275

276276
// continue...; we'll find out if it ends up not getting saved later

src/NHibernate/Hql/Ast/ANTLR/HqlParser.cs

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Antlr.Runtime;
33

44
using NHibernate.Hql.Ast.ANTLR.Tree;
5+
using NHibernate.Util;
56
using IToken = Antlr.Runtime.IToken;
67
using RecognitionException = Antlr.Runtime.RecognitionException;
78

@@ -418,6 +419,7 @@ public IASTNode HandleIdentifierError(IToken token, RecognitionException ex)
418419
}
419420

420421
// Otherwise, handle the error normally.
422+
ReflectHelper.PreserveStackTrace(ex);
421423
throw ex;
422424
}
423425
}

src/NHibernate/Hql/Ast/ANTLR/HqlSqlWalker.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1097,7 +1097,7 @@ private void HandleWithFragment(FromElement fromElement, IASTNode hqlWithNode)
10971097
}
10981098
catch (Exception e)
10991099
{
1100-
throw new SemanticException(e.Message);
1100+
throw new SemanticException(e.Message, e);
11011101
}
11021102
}
11031103
}

src/NHibernate/Hql/Ast/ANTLR/QuerySyntaxException.cs

+5-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ public class QuerySyntaxException : QueryException
1010
{
1111
protected QuerySyntaxException() {}
1212
public QuerySyntaxException(string message, string hql) : base(message, hql) {}
13+
public QuerySyntaxException(string message, string hql, Exception inner) : base(message, hql, inner) {}
1314

1415
public QuerySyntaxException(string message) : base(message) {}
1516
public QuerySyntaxException(string message, Exception inner) : base(message, inner) {}
@@ -24,9 +25,9 @@ public static QuerySyntaxException Convert(RecognitionException e)
2425
public static QuerySyntaxException Convert(RecognitionException e, string hql)
2526
{
2627
string positionInfo = e.Line > 0 && e.CharPositionInLine > 0
27-
? " near line " + e.Line + ", column " + e.CharPositionInLine
28-
: "";
29-
return new QuerySyntaxException(e.Message + positionInfo, hql);
28+
? " near line " + e.Line + ", column " + e.CharPositionInLine
29+
: "";
30+
return new QuerySyntaxException(e.Message + positionInfo, hql, e);
3031
}
3132
}
32-
}
33+
}

src/NHibernate/Hql/Ast/ANTLR/QueryTranslatorImpl.cs

-2
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,6 @@ private void DoCompile(IDictionary<string, string> replacements, bool shallow, S
362362
}
363363
catch ( RecognitionException e )
364364
{
365-
// we do not actually propogate ANTLRExceptions as a cause, so
366-
// log it here for diagnostic purposes
367365
if ( log.IsInfoEnabled() )
368366
{
369367
log.Info(e, "converted antlr.RecognitionException");

src/NHibernate/Hql/Ast/ANTLR/Tree/FromElement.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -544,11 +544,12 @@ public void HandlePropertyBeingDereferenced(IType propertySource, string propert
544544
_dereferencedBySuperclassProperty = true;
545545
}
546546
}
547-
catch (QueryException)
547+
catch (QueryException ex)
548548
{
549549
// ignore it; the incoming property could not be found so we
550550
// cannot be sure what to do here. At the very least, the
551551
// safest is to simply not apply any dereference toggling...
552+
Log.Debug(ex, "Unable to find property {0}, no dereference will be handled for it.", propertyName);
552553
}
553554
}
554555
}

src/NHibernate/Hql/Util/SessionFactoryHelper.cs

+8-8
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,13 @@ public IQueryableCollection GetCollectionPersister(String role)
8787
{
8888
return (IQueryableCollection)sfi.GetCollectionPersister(role);
8989
}
90-
catch (InvalidCastException)
90+
catch (InvalidCastException ice)
9191
{
92-
throw new QueryException("collection is not queryable: " + role);
92+
throw new QueryException("collection is not queryable: " + role, ice);
9393
}
94-
catch (Exception)
94+
catch (Exception ex)
9595
{
96-
throw new QueryException("collection not found: " + role);
96+
throw new QueryException("collection not found: " + role, ex);
9797
}
9898
}
9999

@@ -186,15 +186,15 @@ public IQueryableCollection RequireQueryableCollection(String role)
186186
}
187187
return queryableCollection;
188188
}
189-
catch (InvalidCastException)
189+
catch (InvalidCastException ice)
190190
{
191191
throw new QueryException(
192-
"collection role is not queryable: " + role);
192+
"collection role is not queryable: " + role, ice);
193193
}
194-
catch (Exception)
194+
catch (Exception ex)
195195
{
196196
throw new QueryException("collection role not found: "
197-
+ role);
197+
+ role, ex);
198198
}
199199
}
200200
}

src/NHibernate/Id/IdentifierGeneratorFactory.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,9 @@ public static System.Type GetIdentifierGeneratorClass(string strategy, Dialect.D
309309
clazz = ReflectHelper.ClassForName(strategy);
310310
}
311311
}
312-
catch (Exception)
312+
catch (Exception ex)
313313
{
314-
throw new IdentifierGenerationException("Could not interpret id generator strategy: " + strategy);
314+
throw new IdentifierGenerationException("Could not interpret id generator strategy: " + strategy, ex);
315315
}
316316
return clazz;
317317
}

src/NHibernate/Impl/SessionFactoryImpl.cs

+5-4
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,10 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
205205
SchemaMetadataUpdater.QuoteTableAndColumns(cfg, Dialect);
206206
}
207207
}
208-
catch (NotSupportedException)
208+
catch (NotSupportedException ex)
209209
{
210-
// Ignore if the Dialect does not provide DataBaseSchema
210+
// Ignore if the Dialect does not provide DataBaseSchema
211+
log.Warn(ex, "Dialect does not provide DataBaseSchema, but keywords import or auto quoting is enabled.");
211212
}
212213

213214
#region Caches
@@ -339,9 +340,9 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
339340
{
340341
uuid = (string)UuidGenerator.Generate(null, null);
341342
}
342-
catch (Exception)
343+
catch (Exception ex)
343344
{
344-
throw new AssertionFailure("Could not generate UUID");
345+
throw new AssertionFailure("Could not generate UUID", ex);
345346
}
346347

347348
SessionFactoryObjectFactory.AddInstance(uuid, name, this, properties);

src/NHibernate/Mapping/Collection.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ public object Comparer
154154
{
155155
comparer = Cfg.Environment.BytecodeProvider.ObjectsFactory.CreateInstance(ReflectHelper.ClassForName(ComparerClassName));
156156
}
157-
catch
157+
catch (Exception ex)
158158
{
159-
throw new MappingException("Could not instantiate comparator class [" + ComparerClassName + "] for collection " + Role);
159+
throw new MappingException("Could not instantiate comparator class [" + ComparerClassName + "] for collection " + Role, ex);
160160
}
161161
}
162162
return comparer;

src/NHibernate/Mapping/PersistentClass.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -890,9 +890,9 @@ private Property GetRecursiveProperty(string propertyPath, IEnumerable<Property>
890890
}
891891
}
892892
}
893-
catch (MappingException)
893+
catch (MappingException ex)
894894
{
895-
throw new MappingException("property [" + propertyPath + "] not found on entity [" + EntityName + "]");
895+
throw new MappingException("property [" + propertyPath + "] not found on entity [" + EntityName + "]", ex);
896896
}
897897

898898
return property;

src/NHibernate/Persister/Entity/AbstractEntityPersister.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2206,14 +2206,14 @@ protected bool Check(int rows, object id, int tableNumber, IExpectation expectat
22062206
{
22072207
expectation.VerifyOutcomeNonBatched(rows, statement);
22082208
}
2209-
catch (StaleStateException)
2209+
catch (StaleStateException sse)
22102210
{
22112211
if (!IsNullableTable(tableNumber))
22122212
{
22132213
if (Factory.Statistics.IsStatisticsEnabled)
22142214
Factory.StatisticsImplementor.OptimisticFailure(EntityName);
22152215

2216-
throw new StaleObjectStateException(EntityName, id);
2216+
throw new StaleObjectStateException(EntityName, id, sse);
22172217
}
22182218
}
22192219
catch (TooManyRowsAffectedException ex)

src/NHibernate/QueryException.cs

+15
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,21 @@ public QueryException(string message, string queryString) : base(message)
4646
this.queryString = queryString;
4747
}
4848

49+
/// <summary>
50+
/// Initializes a new instance of the <see cref="QueryException"/> class.
51+
/// </summary>
52+
/// <param name="message">The message that describes the error. </param>
53+
/// <param name="queryString">The query that contains the error.</param>
54+
/// <param name="innerException">
55+
/// The exception that is the cause of the current exception. If the innerException parameter
56+
/// is not a null reference, the current exception is raised in a catch block that handles
57+
/// the inner exception.
58+
/// </param>
59+
public QueryException(string message, string queryString, Exception innerException) : base(message, innerException)
60+
{
61+
this.queryString = queryString;
62+
}
63+
4964
/// <summary>
5065
/// Initializes a new instance of the <see cref="QueryException"/> class.
5166
/// </summary>

src/NHibernate/StaleObjectStateException.cs

+11-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,17 @@ public class StaleObjectStateException : StaleStateException
2323
/// <param name="entityName">The EntityName that NHibernate was trying to update in the database.</param>
2424
/// <param name="identifier">The identifier of the object that is stale.</param>
2525
public StaleObjectStateException(string entityName, object identifier)
26-
: base("Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)")
26+
: this(entityName, identifier, null)
27+
{
28+
}
29+
/// <summary>
30+
/// Initializes a new instance of the <see cref="StaleObjectStateException"/> class.
31+
/// </summary>
32+
/// <param name="entityName">The EntityName that NHibernate was trying to update in the database.</param>
33+
/// <param name="identifier">The identifier of the object that is stale.</param>
34+
/// <param name="innerException">The original exception having triggered this exception.</param>
35+
public StaleObjectStateException(string entityName, object identifier, Exception innerException)
36+
: base("Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)", innerException)
2737
{
2838
this.entityName = entityName;
2939
this.identifier = identifier;

src/NHibernate/StaleStateException.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ public class StaleStateException : HibernateException
99
public StaleStateException(string message) : base(message)
1010
{
1111
}
12+
public StaleStateException(string message, Exception innerException) : base(message, innerException)
13+
{
14+
}
1215

1316
protected StaleStateException(SerializationInfo info, StreamingContext context)
1417
: base(info, context)
1518
{
1619
}
1720
}
18-
}
21+
}

0 commit comments

Comments
 (0)