
Polecat is now completely supported by JasperFx Software and automatically part of any existing and future support agreements through our existing plans.
Polecat was released as 1.0 this past week (with 1.1 & now 1.2 coming soon). Let’s call it what it is, Polecat is a port of (most of) Marten to target SQL Server 2025 and SQL Server’s new JSON data type. For folks not familiar with Marten, Polecat is in one library:
- A very full fledged Event Store library for SQL Server that includes event projection and subscriptions, Dynamic Consistency Boundary support, a large amount of functionality for Event Sourcing basics, rich event metadata tracking capabilities, and even rich multi-tenancy support.
- A feature rich set of Document Database capabilities backed by SQL Server including LINQ querying support
And while Polecat is brand spanking new, it comes out of the gate with the decade old Marten pedigree and its own Wolverine integration for CQRS usage. I’m confident in saying Polecat is now the best technical option for using Event Sourcing with SQL Server in the .NET ecosystem.
And of course, if you’re a shop with deep existing roots into EF Core usage, Polecat also comes with projection support to EF Core, so Polecat can happily coexist with EF Core in the same systems.
Alright, let’s just into a quick start. First, let’s say you’ve started a brand new .NET project through dotnet run webapi and you’ve added a reference to Polecat through Nuget (and you have a running SQL Server 2025 instance handy too of course!). Next, let’s start with the inevitable AddPolecat() usage in your Program file:
builder.Services.AddPolecat(options =>{ // Connection string to your SQL Server 2025 database options.Connection("Server=localhost;Database=myapp;User Id=sa;Password=YourStrong!Password;TrustServerCertificate=True"); // Optionally change the default schema (default is "dbo") options.DatabaseSchemaName = "myschema";});
Polecat can be used without IHost or IServiceCollection registrations by just directly building a DocumentStore object.
Next, let’s say you’ve got this simplistic document type (entity in Polecat parlance):
public class User{ public Guid Id { get; set; } public required string FirstName { get; set; } public required string LastName { get; set; } public bool Internal { get; set; }}
And now, let’s use Polecat within some Minimal API endpoints to capture and query User documents:
// Store a documentapp.MapPost("/user", async (CreateUserRequest create, IDocumentSession session) =>{ var user = new User { FirstName = create.FirstName, LastName = create.LastName, Internal = create.Internal }; session.Store(user); await session.SaveChangesAsync();});// Query with LINQapp.MapGet("/users", async (bool internalOnly, IDocumentSession session, CancellationToken ct) =>{ return await session.Query<User>() .Where(x => x.Internal == internalOnly) .ToListAsync(ct);});// Load by IDapp.MapGet("/user/{id:guid}", async (Guid id, IQuerySession session, CancellationToken ct) =>{ return await session.LoadAsync<User>(id, ct);});
For folks used to EF Core, I should point out that Polecat has its own “it just works” database migration subsystem that in the default development mode will happily make sure that all necessary database tables, views, and functions are exactly as they should be at runtime so you don’t have to fiddle with database migrations when all you want to do is just get things done.
While I initially thought that we’d mainly focus on the event sourcing support, we were also able to recreate the mass majority of Marten’s document database capabilities (including the “partial update” model, LINQ support, soft deletes, multi-tenancy, and batch updates for starters) as well if you’d only be interested in that feature set by itself.
Moving over to event sourcing instead, let’s say you’re into fantasy books like I am and you want to build a system to model the journeys and adventures of a quest in your favorite fantasy series. You might model some of the events in that system like:
public record QuestStarted(string Name);public record MembersJoined(string Location, string[] Members);public record MembersDeparted(string Location, string[] Members);public record QuestEnded(string Name);
And you model the current state of the quest party like this:
public class QuestParty{ public Guid Id { get; set; } public string Name { get; set; } = ""; public List<string> Members { get; set; } = new(); public void Apply(QuestStarted started) { Name = started.Name; } public void Apply(MembersJoined joined) { Members.AddRange(joined.Members); } public void Apply(MembersDeparted departed) { foreach (var member in departed.Members) Members.Remove(member); }}
The step above isn’t strictly necessary for event sourcing, but you usually need a projection of some sort sooner or later.
And finally, we can add events by starting a new event stream:
var store = DocumentStore.For(opts =>{ opts.Connection("Server=localhost,1433;Database=myapp;User Id=sa;Password=YourStrong!Password;TrustServerCertificate=True");});await using var session = store.LightweightSession();// Start a new stream with initial eventsvar questId = session.Events.StartStream<QuestParty>( new QuestStarted("Destroy the Ring"), new MembersJoined("Rivendell", ["Frodo", "Sam", "Aragorn", "Gandalf"]));await session.SaveChangesAsync();
And even append some new ones to the same stream later:
await using var session = store.LightweightSession();session.Events.Append(questId, new MembersJoined("Moria", ["Gimli", "Legolas"]), new MembersDeparted("Moria", ["Gandalf"]));await session.SaveChangesAsync();
And derive the current state of our quest:
var party = await session.Events.AggregateStreamAsync<QuestParty>(questId);// party.Name == "Destroy the Ring"// party.Members == ["Frodo", "Sam", "Aragorn", "Gimli", "Legolas"]
And there’s much, much more of course, including everything you’d need to build real systems based on our 10 years and counting supporting Marten with PostgreSQL.
How is Polecat Different than Marten?
There are of course some differences besides just the database engine:
- Polecat is using source generators instead of the runtime code generation that Marten does today
- Polecat will only support System.Text.Json for now as a serialization engine
- Polecat only supports the “Quick Append” option from Marten
- There is no automatic dirty checking
- No “duplicate fields” support so far, we’re going to reevaluate that though
- Plenty of other technical baggage features I flat out didn’t want to support in Marten didn’t make the cut, but I can’t imagine anyone will miss any of that!
Summary
For over a decade people have been telling me that Marten would be more successful and adopted by more .NET shops if it only supported SQL Server in addition to or instead of PostgreSQL. While I’ve never really disagreed with that idea — and it’s impossible to really prove the counter factual anyway — there have always been real blockers in both SQL Server’s JSON support lagging far behind PostgreSQL and frankly the time commitment on my part to be able to attempt that work in the first place.
So what changed to enable this?
- SQL Server 2025 added much better JSON support rivaling PostgreSQL’s JSONB type
- We had already invested in pulling the basic event abstractions and projection support out of Marten and into a common library called JasperFx.Events as part of the Marten 8.0 release cycle and that work was always meant to be an enabler for what is now Polecat
- Claude & Opus 4.5/4.6 turned out to be very, very good at grunt work
That second item had to this point been a near disaster in my mind because of how much work and time that took compared to the benefits and was the single most time consuming part of Polecat development. Let’s just say that I’m very relieved that that effort didn’t turn out to be a very expensive sunk cost for JasperFx!
I have no earthly idea how much traction Polecat will really get, but we’ve already had some interest from folks who have wanted to use Marten, but couldn’t get their .NET shop to adopt PostgreSQL. I’m hopeful!