Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -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). -->
<PackageVersion Include="JasperFx" Version="2.27.0" />
<PackageVersion Include="JasperFx.Events" Version="2.27.0" />
<PackageVersion Include="JasperFx.Events.SourceGenerator" Version="2.27.0">
<PackageVersion Include="JasperFx" Version="2.28.0" />
<PackageVersion Include="JasperFx.Events" Version="2.28.0" />
<PackageVersion Include="JasperFx.Events.SourceGenerator" Version="2.28.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageVersion>
<PackageVersion Include="JasperFx.SourceGenerator" Version="2.27.0" />
<PackageVersion Include="JasperFx.SourceGenerator" Version="2.28.0" />
<PackageVersion Include="Jil" Version="3.0.0-alpha2" />
<PackageVersion Include="Lamar" Version="7.1.1" />
<PackageVersion Include="Lamar.Microsoft.DependencyInjection" Version="15.0.0" />
Expand Down
24 changes: 24 additions & 0 deletions docs/events/projections/event-projections.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<MyDoc>();
}

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<T>`, which is always recognized as the event regardless of the parameter name.
Expand Down
10 changes: 10 additions & 0 deletions src/DaemonTests/rebuild_with_bulk_copy_inserts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<BulkCopyDoc>();
}

public void Project(ItemAdded e, IDocumentOperations ops)
Expand All @@ -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<BulkCopyDoc>();
}

public void Project(ItemAdded e, IDocumentOperations ops)
Expand Down
Loading