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
33 changes: 33 additions & 0 deletions docs/events/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,39 @@ public Dictionary<string, object>? Headers { get; set; }

The full event data is available on `EventStream` and `IEvent` objects immediately after committing a transaction that involves event capture. See [diagnostics and instrumentation](/diagnostics) for more information on capturing event data in the instrumentation hooks.

## Event Type Names <Badge type="tip" text="8.4" />

If you look into the `mt_events` table in your system you'll see a column named `type` that will
have an alias for the .NET type name that Marten keys off when reading events from the database
to "know" what .NET type to deserialize the JSON data to.

The original idea was that people should be able to easily move event types around in their
solution without breaking the storage as full type names changed, so we purposely used _only_ the
type name of the .NET type for the event alias. In real life usage though, sometimes people will
use completely different .NET types with the same type name like in this example:

```csharp
public class GroupEvents
{
public record Created(string Name);
}

public class UserEvents
{
public record Created(string Name);
}
```

In that case, the original naming scheme of "created" will not correctly disambiguate between the
two different `Created` types above. While you _could_ manually alias all of these event types
yourself to disambiguate, it's too easy to forget to do that. Instead, you can just switch to different
naming schemes like this:

snippet: sample_event_naming_style

Note that you will have to switch out of the "classic" naming mode to disambiguate between event types
with the same class name in different namespaces.

## Optional Indexes

As of Marten 7.0, Marten is omitting indexes that aren't universally necessary, but
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Threading.Tasks;
using JasperFx.Events;
using Marten.Testing.Harness;
using Shouldly;
using Xunit;

namespace EventSourcingTests.Bugs;

public class Bug_3880_able_to_use_different_event_types_with_same_name : BugIntegrationContext
{
[Theory]
[InlineData(EventNamingStyle.SmarterTypeName)]
[InlineData(EventNamingStyle.FullTypeName)]
public async Task append_and_load(EventNamingStyle namingStyle)
{
StoreOptions(opts =>
{
opts.Events.EventNamingStyle = namingStyle;
});

var streamId = Guid.NewGuid();
theSession.Events.StartStream(streamId, new GroupEvents.Created("foo"), new UserEvents.Created("bar"));
await theSession.SaveChangesAsync();

var events = await theSession.Events.FetchStreamAsync(streamId);

events[0].ShouldBeOfType<Event<GroupEvents.Created>>().Data.Name.ShouldBe("foo");
events[1].ShouldBeOfType<Event<UserEvents.Created>>().Data.Name.ShouldBe("bar");
}
}

public class GroupEvents
{
public record Created(string Name);
}

public class UserEvents
{
public record Created(string Name);
}
37 changes: 37 additions & 0 deletions src/EventSourcingTests/EventGraphTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,33 @@ public EventGraphTests()
theGraph = new StoreOptions().EventGraph;
}

[Fact]
public void default_naming_style_is_classic()
{
theGraph.EventNamingStyle.ShouldBe(EventNamingStyle.ClassicTypeName);
}

[Fact]
public void override_the_naming_style_1()
{
theGraph.EventNamingStyle = EventNamingStyle.ClassicTypeName;
theGraph.EventMappingFor<AEvent>().EventTypeName.ShouldBe("a_event");
}

[Fact]
public void override_the_naming_style_2()
{
theGraph.EventNamingStyle = EventNamingStyle.SmarterTypeName;
theGraph.EventMappingFor<UserEvents.Created>().EventTypeName.ShouldBe("user_events.created");
}

[Fact]
public void override_the_naming_style_3()
{
theGraph.EventNamingStyle = EventNamingStyle.FullTypeName;
theGraph.EventMappingFor<UserEvents.Created>().EventTypeName.ShouldBe("EventSourcingTests.UserEvents.Created");
}

[Fact]
public void get_backwards_compatible_name_for_archived()
{
Expand Down Expand Up @@ -183,3 +210,13 @@ public void Apply(IssueAssigned e)
// Do stuff
}
}

public class GroupEvents
{
public record Created(string Name);
}

public class UserEvents
{
public record Created(string Name);
}
36 changes: 36 additions & 0 deletions src/EventSourcingTests/Examples/NamingStyles.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Threading.Tasks;
using JasperFx.Events;
using Marten;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

namespace EventSourcingTests.Examples;

public class NamingStyles
{
public static void configure_different_naming_styles()
{
#region sample_event_naming_style

var builder = Host.CreateApplicationBuilder();
builder.Services.AddMarten(opts =>
{
opts.Connection(builder.Configuration.GetConnectionString("marten"));


// This is the default behavior, but just showing you that
// this is an option
opts.Events.EventNamingStyle = EventNamingStyle.ClassicTypeName;

// This mode is "the classic style Marten has always used, except smart enough
// to disambiguate inner classes that have the same type name"
opts.Events.EventNamingStyle = EventNamingStyle.SmarterTypeName;

// Forget all the pretty naming aliases, just use the .NET full type name for
// the event type name
opts.Events.EventNamingStyle = EventNamingStyle.FullTypeName;
});

#endregion
}
}
5 changes: 5 additions & 0 deletions src/Marten/Events/EventGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ internal EventGraph(StoreOptions options)

}

/// <summary>
/// Opt into different aliasing styles for .NET event types
/// </summary>
public EventNamingStyle EventNamingStyle { get; set; } = EventNamingStyle.ClassicTypeName;

internal NpgsqlDbType StreamIdDbType { get; private set; }

internal StoreOptions Options { get; }
Expand Down
2 changes: 2 additions & 0 deletions src/Marten/Events/EventMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ protected EventMapping(EventGraph parent, Type eventType) : base(eventType)
{
TenancyStyle = parent.TenancyStyle;

EventTypeName = eventType.GetEventTypeName(parent.EventNamingStyle);

_parent = parent;
DocumentType = eventType;

Expand Down
5 changes: 5 additions & 0 deletions src/Marten/Events/IEventStoreOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ public interface IEventStoreOptions
/// </summary>
public bool UseMandatoryStreamTypeDeclaration { get; set; }

/// <summary>
/// Opt into different aliasing styles for .NET event types
/// </summary>
public EventNamingStyle EventNamingStyle { get; set; }

/// <summary>
/// Register an event type with Marten. This isn't strictly necessary for normal usage,
/// but can help Marten with asynchronous projections where Marten hasn't yet encountered
Expand Down
5 changes: 5 additions & 0 deletions src/Marten/Events/IReadOnlyEventStoreOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,9 @@ public interface IReadOnlyEventStoreOptions
/// but this will be true in 8.0
/// </summary>
bool UseMandatoryStreamTypeDeclaration { get; set; }

/// <summary>
/// Opt into different aliasing styles for .NET event types
/// </summary>
EventNamingStyle EventNamingStyle { get; set; }
}
2 changes: 1 addition & 1 deletion src/Marten/Marten.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<ItemGroup>
<PackageReference Include="FSharp.Core" Version="9.0.100" />
<PackageReference Include="JasperFx" Version="1.4.0" />
<PackageReference Include="JasperFx.Events" Version="1.4.0" />
<PackageReference Include="JasperFx.Events" Version="1.5.0" />
<PackageReference Include="JasperFx.RuntimeCompiler" Version="4.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<!-- This is forced by Npgsql peer dependency -->
Expand Down
Loading