diff --git a/Directory.Packages.props b/Directory.Packages.props
index ffe62e0d8b..e133e56a66 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -102,13 +102,13 @@
CancellationExceptions.IsCancellationLike instead of discarding every exception type while a
shard CTS is cancelled; jasperfx#505 — source-gen evolver two-statement fix for the
GetUninitializedObject cast (rides in the bundled JasperFx.Events.SourceGenerator analyzer). -->
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/docs/events/projections/event-projections.md b/docs/events/projections/event-projections.md
index e9cfbf3ee7..4a7f5ad918 100644
--- a/docs/events/projections/event-projections.md
+++ b/docs/events/projections/event-projections.md
@@ -137,6 +137,30 @@ The `Project()` methods can accept these arguments:
The return value must be either `void` or `Task` depending on whether or not the method needs to be asynchronous
+## Rebuilds and Document Teardown
+
+::: warning
+Because an `EventProjection` writes documents through *ad-hoc* `IDocumentOperations` calls (`ops.Insert`, `ops.Store`, etc.) inside its `Project()` / `Create()` methods, Marten **cannot infer which document types the projection produces**. Unlike a single-stream or multi-stream aggregation (whose view type is known), an `EventProjection` therefore does **not** automatically wipe its documents when the projection is rebuilt.
+
+If you rebuild an `EventProjection` and want the previously-written documents cleared first (the normal rebuild semantics), **declare each written document type explicitly** so it participates in the rebuild teardown:
+
+```cs
+public class MyProjection: EventProjection
+{
+ public MyProjection()
+ {
+ // Tell Marten this projection writes MyDoc, so a rebuild TRUNCATEs it first
+ Options.DeleteViewTypeOnTeardown();
+ }
+
+ public void Project(SomethingHappened e, IDocumentOperations ops)
+ => ops.Insert(new MyDoc { Id = e.Id /* ... */ });
+}
+```
+
+Without this declaration the old documents survive the rebuild. The per-row rebuild path tolerates that (re-inserting an existing id upserts), but the INSERT-only [binary `COPY` rebuild](/events/projections/rebuilding) (`Projections.RebuildWithBulkCopy`) requires an empty target table and will fail with a duplicate-key error. This is a long-standing `EventProjection` limitation, not specific to bulk-copy rebuilds.
+:::
+
## Identifying the Event Parameter
In both the `Create()` and `Project()` conventions above, the event parameter can be named anything — Marten identifies it **by type**, not by name. Given `Project(StopEvent1 e, IDocumentOperations ops)`, `StopEvent1` is the event because it's the only concrete event type in the signature (`IDocumentOperations` is an interface and is never treated as the event). The same applies to `IEvent`, which is always recognized as the event regardless of the parameter name.
diff --git a/src/DaemonTests/rebuild_with_bulk_copy_inserts.cs b/src/DaemonTests/rebuild_with_bulk_copy_inserts.cs
index b4271d4243..2dd3288c79 100644
--- a/src/DaemonTests/rebuild_with_bulk_copy_inserts.cs
+++ b/src/DaemonTests/rebuild_with_bulk_copy_inserts.cs
@@ -463,6 +463,12 @@ public partial class BulkCopyInsertProjection: EventProjection
public BulkCopyInsertProjection()
{
Name = ProjectionName;
+
+ // EventProjection cannot infer the document types it writes through ad-hoc ops.Insert/ops.Store,
+ // so it does not know to wipe them on rebuild. Declare the written type explicitly so the rebuild
+ // teardown TRUNCATEs it first — otherwise the INSERT-only binary COPY rebuild collides with docs a
+ // prior run left behind (duplicate key). This is a long-standing EventProjection limitation.
+ Options.DeleteViewTypeOnTeardown();
}
public void Project(ItemAdded e, IDocumentOperations ops)
@@ -480,6 +486,10 @@ public partial class InsertAndDeleteProjection: EventProjection
public InsertAndDeleteProjection()
{
Name = ProjectionName;
+
+ // See BulkCopyInsertProjection: an EventProjection must declare the doc types it writes ad-hoc so the
+ // rebuild teardown clears them.
+ Options.DeleteViewTypeOnTeardown();
}
public void Project(ItemAdded e, IDocumentOperations ops)