Apply
and ShouldDelete
methods work for inherited events in inline projections but not async projections
#2632
-
Expectation: Given the interface Given the abstract class However, given the non-abstract class I am unsure why this is the case at this time. public class Bug__should_act_on_inherited_events: BugIntegrationContext
{
// passes
[Fact]
public async Task Test_deleting_inline_self_aggregate()
{
StoreOptions(opts =>
{
opts.Projections.Snapshot<UserAggregate>(SnapshotLifecycle.Inline);
});
theSession.Events.StartStream(Guid.NewGuid(),
new UserNameSetEvent("Alex"),
new UserDeletedEvent());
await theSession.SaveChangesAsync();
Assert.Equal(0, await theSession.Query<UserAggregate>().CountAsync());
}
// fails
[Fact]
public async Task Test_deleting_async_self_aggregate()
{
StoreOptions(opts =>
{
opts.Projections.Snapshot<UserAggregate>(SnapshotLifecycle.Async);
});
var daemon = await theStore.BuildProjectionDaemonAsync();
await daemon.StartAllShards();
theSession.Events.StartStream(Guid.NewGuid(),
new UserNameSetEvent("Alex"),
new UserDeletedEvent());
await theSession.SaveChangesAsync();
await daemon.WaitForNonStaleData(5.Seconds());
Assert.Equal(0, await theSession.Query<UserAggregate>().CountAsync());
}
// passes
[Fact]
public async Task Test_appending_inline_self_aggregate()
{
StoreOptions(opts =>
{
opts.Projections.Snapshot<UserAggregate>(SnapshotLifecycle.Inline);
});
theSession.Events.StartStream(Guid.NewGuid(),
new UserNameAndAgeSetEvent("Alex", 20));
await theSession.SaveChangesAsync();
Assert.Equal(1, await theSession.Query<UserAggregate>().CountAsync());
}
// fails
[Fact]
public async Task Test_appending_async_self_aggregate()
{
StoreOptions(opts =>
{
opts.Projections.Snapshot<UserAggregate>(SnapshotLifecycle.Async);
});
var daemon = await theStore.BuildProjectionDaemonAsync();
await daemon.StartAllShards();
theSession.Events.StartStream(Guid.NewGuid(),
new UserNameAndAgeSetEvent("Alex", 20));
await theSession.SaveChangesAsync();
await daemon.WaitForNonStaleData(5.Seconds());
Assert.Equal(1, await theSession.Query<UserAggregate>().CountAsync());
}
public record UserNameSetEvent(string Name);
public record UserNameAndAgeSetEvent(string Name, int Age) : UserNameSetEvent(Name);
public record DeletedEvent;
public record UserDeletedEvent : DeletedEvent;
public class UserAggregate
{
public Guid Id { get; set; }
public string Name { get; set; }
public void Apply(UserNameSetEvent @event)
{
Name = @event.Name;
}
public bool ShouldDelete(DeletedEvent @event) => true;
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
@elexisvenator, Marten can't "know" that the concrete // I introduced this
public interface IUserName
{
string Name { get; }
}
public record UserNameSetEvent(string Name) : IUserName;
public record UserNameAndAgeSetEvent(string Name, int Age) : UserNameSetEvent(Name);
public record DeletedEvent;
public record UserDeletedEvent : DeletedEvent;
public class UserAggregate
{
public Guid Id { get; set; }
public string Name { get; set; }
// I changed this to use the interface
public void Apply(IUserName @event)
{
Name = @event.Name;
}
// You'd need to do the same thing here
public bool ShouldDelete(DeletedEvent @event) => true;
} If you don't want to do that with the interfaces, I think you're best off doing a custom aggregate and doing things more programmatically. The abstract or interface event type thing is first come, first serve and not really combinatorial as I recall. So use that with caution. You. might be getting into a little more magic than what is easy to unravel later, and a little bit of code duplication may make things easier. I'm converting this to a discussion for now as I don't think there's anything to do but maybe improve docs. |
Beta Was this translation helpful? Give feedback.
@elexisvenator, Marten can't "know" that the concrete
UserNameSetEvent
is meant to be a base class, so it can't set up the generic event handler, plus Marten is building a filter on specific event types as an optimization. The quick fix is like so: