Skip to content

Commit 467aca3

Browse files
committed
Fix some more state cleanup cases
1 parent f96d6ad commit 467aca3

3 files changed

Lines changed: 22 additions & 3 deletions

File tree

src/Npgsql/Internal/Converters/StreamConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ protected override Size BindValue(in BindContext context, Stream value, ref obje
2727
return checked((int)(value.Length - value.Position));
2828

2929
var memoryStream = new MemoryStream();
30-
value.CopyTo(memoryStream);
3130
writeState = memoryStream;
31+
value.CopyTo(memoryStream);
3232
return checked((int)memoryStream.Length);
3333
}
3434

src/Npgsql/Internal/PgConverter.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,10 @@ public Size BindAsObject(in BindContext context, object? value, ref object? writ
203203
// writeState pointing at a half-disposed object — they'd dispose it again.
204204
(var current, writeState) = (writeState, null);
205205
(current as IDisposable)?.Dispose();
206-
if (!ReferenceEquals(current, originalWriteState))
206+
// current==null with original!=null means an inner safety net (transparent wrapper case)
207+
// already disposed the original. Disposing again would double-dispose. The non-null branch
208+
// handles the genuine lifecycle-violation swap where current and original differ.
209+
if (current is not null && !ReferenceEquals(current, originalWriteState))
207210
(originalWriteState as IDisposable)?.Dispose();
208211
throw;
209212
}
@@ -377,7 +380,10 @@ public Size Bind(in BindContext context,
377380
// writeState pointing at a half-disposed object — they'd dispose it again.
378381
(var current, writeState) = (writeState, null);
379382
(current as IDisposable)?.Dispose();
380-
if (!ReferenceEquals(current, originalWriteState))
383+
// current==null with original!=null means an inner safety net (transparent wrapper case)
384+
// already disposed the original. Disposing again would double-dispose. The non-null branch
385+
// handles the genuine lifecycle-violation swap where current and original differ.
386+
if (current is not null && !ReferenceEquals(current, originalWriteState))
381387
(originalWriteState as IDisposable)?.Dispose();
382388
throw;
383389
}

src/Npgsql/NpgsqlParameter.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,20 @@ internal void Bind(out DataFormat format, out Size size, DataFormat? requiredFor
752752
// the type info may still support the required format, just not the format the cached
753753
// binding chose. Re-running Bind with the new preference can produce a satisfying binding.
754754
if (_isBound && requiredFormat is not null && _binding.DataFormat != requiredFormat.GetValueOrDefault())
755+
{
755756
DisposeBindingState();
757+
// First Bind consumed _providerWriteState into _binding. Re-resolve so the rebuild has
758+
// the state late-bound paths (ObjectConverter.BindValue) require — without this the
759+
// re-bind would hit "Invalid state" before the format-mismatch error could surface.
760+
// Typed-concrete paths re-resolve to default state, harmless.
761+
if (TypeInfo is not null && _providerWriteState is null)
762+
{
763+
var resolveContext = new ProviderValueContext { NestedObjectDbNullHandling = ParameterDbNullHandling };
764+
_ = StaticValueType == typeof(object)
765+
? TypeInfo.MakeConcreteForValueAsObject(resolveContext, Value is DBNull ? null : Value, out _providerWriteState)
766+
: MakeConcreteForTypedValue(TypeInfo, out _providerWriteState);
767+
}
768+
}
756769

757770
// Idempotent — validation pass and WriteBind both call this; first wins.
758771
if (!_isBound)

0 commit comments

Comments
 (0)