Skip to content

Commit

Permalink
Addressed code generation issue with transactional middleware applica…
Browse files Browse the repository at this point in the history
…tion against services in implied middleware. Closes GH-1118
  • Loading branch information
jeremydmiller committed Nov 4, 2024
1 parent 583b10a commit 4c7717f
Show file tree
Hide file tree
Showing 9 changed files with 75 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/Http/Wolverine.Http/HttpChain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public HttpChain(MethodCall method, HttpGraph parent)
applyAttributesAndConfigureMethods(_parent.Rules, _parent.Container);

// Add Before/After methods from the current handler
applyImpliedMiddlewareFromHandlers(_parent.Rules);
ApplyImpliedMiddlewareFromHandlers(_parent.Rules);

foreach (var call in Middleware.OfType<MethodCall>().ToArray())
{
Expand Down
2 changes: 2 additions & 0 deletions src/Persistence/EfCoreTests/EfCoreCompilationScenarios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,6 @@ public class Item
{
public Guid Id { get; set; }
public string Name { get; set; }

public bool Approved { get; set; }
}
1 change: 1 addition & 0 deletions src/Persistence/EfCoreTests/SampleDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
map.ToTable("items");
map.HasKey(x => x.Id);
map.Property(x => x.Name);
map.Property(x => x.Approved);
});
}
}
Expand Down
56 changes: 55 additions & 1 deletion src/Persistence/EfCoreTests/end_to_end_efcore_persistence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
using Weasel.SqlServer;
using Weasel.SqlServer.Tables;
using Wolverine;
using Wolverine.Attributes;
using Wolverine.EntityFrameworkCore;
using Wolverine.EntityFrameworkCore.Internals;
using Wolverine.Persistence.Durability;
using Wolverine.Runtime;
using Wolverine.SqlServer;
using Wolverine.Tracking;
using Wolverine.Transports;

namespace EfCoreTests;
Expand All @@ -35,14 +37,15 @@ public EFCorePersistenceContext() : base(true)
options.PersistMessagesWithSqlServer(Servers.SqlServerConnectionString);
options.Services.AddResourceSetupOnStartup(StartupAction.ResetState);
options.UseEntityFrameworkCoreTransactions();

options.Policies.ConfigureConventionalLocalRouting()
.CustomizeQueues((_, q) => q.UseDurableInbox());
});

ItemsTable = new Table("items");
ItemsTable.AddColumn<Guid>("Id").AsPrimaryKey();
ItemsTable.AddColumn<string>("Name");
ItemsTable.AddColumn<bool>("Approved");
}

public Table ItemsTable { get; }
Expand All @@ -60,6 +63,40 @@ public end_to_end_efcore_persistence(EFCorePersistenceContext context)
public Table ItemsTable { get; }

public IHost Host { get; }


[Fact]
public async Task using_dbcontext_in_middleware()
{
await withItemsTable();

var item = new Item { Id = Guid.NewGuid(), Name = "Hey"};
await saveItem(item);

await Host.InvokeMessageAndWaitAsync(new ApproveItem(item.Id));

var existing = await loadItem(item.Id);
existing.Approved.ShouldBeTrue();
}

private async Task<Item> loadItem(Guid id)
{
using var nested = Host.Services.CreateScope();

var context = nested.ServiceProvider.GetRequiredService<SampleDbContext>();
var item = await context.Items.FindAsync(id);

return item;
}

private async Task saveItem(Item item)
{
using var nested = Host.Services.CreateScope();

var context = nested.ServiceProvider.GetRequiredService<SampleDbContext>();
context.Items.Add(item);
await context.SaveChangesAsync();
}

[Fact]
public void service_registrations()
Expand Down Expand Up @@ -459,6 +496,7 @@ public async Task persist_an_incoming_envelope_mapped()
loadedEnvelope.OwnerId.ShouldBe(envelope.OwnerId);
loadedEnvelope.Attempts.ShouldBe(envelope.Attempts);
}

}

public class PassRecorder
Expand Down Expand Up @@ -492,4 +530,20 @@ public class Pass
{
public string From { get; set; }
public string To { get; set; }
}

public record ApproveItem(Guid Id);

public static class ApproveItemHandler
{
public static ValueTask<Item> LoadAsync(ApproveItem command, SampleDbContext dbContext)
{
return dbContext.Items.FindAsync(command.Id);
}

[Transactional]
public static void Handle(ApproveItem command, Item item)
{
item.Approved = true;
}
}
1 change: 1 addition & 0 deletions src/Wolverine/Attributes/TransactionalAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class TransactionalAttribute : ModifyChainAttribute
{
public override void Modify(IChain chain, GenerationRules rules, IServiceContainer container)
{
chain.ApplyImpliedMiddlewareFromHandlers(rules);
var transactionFrameProvider = rules.As<GenerationRules>().GetPersistenceProviders(chain, container);
transactionFrameProvider.ApplyTransactionSupport(chain, container);
}
Expand Down
9 changes: 8 additions & 1 deletion src/Wolverine/Configuration/Chain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,9 @@ protected void applyAttributesAndConfigureMethods(GenerationRules rules, IServic
var genericMethodAtts = handlers.SelectMany(x => x.Method.GetCustomAttributes<ModifyChainAttribute>());

foreach (var attribute in genericHandlerAtts.Concat(genericMethodAtts))
{
attribute.Modify(this, rules, container);
}
}

private static Type[] _typesToIgnore = new Type[]
Expand Down Expand Up @@ -223,8 +225,13 @@ private IEnumerable<Type> serviceDependencies(IServiceContainer container, IRead
}
}

protected void applyImpliedMiddlewareFromHandlers(GenerationRules generationRules)
private bool _appliedImpliedMiddleware;

public void ApplyImpliedMiddlewareFromHandlers(GenerationRules generationRules)
{
if (_appliedImpliedMiddleware) return;
_appliedImpliedMiddleware = true;

var handlerTypes = HandlerCalls().Select(x => x.HandlerType).Distinct();
foreach (var handlerType in handlerTypes)
{
Expand Down
3 changes: 3 additions & 0 deletions src/Wolverine/Configuration/IChain.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reflection;
using JasperFx.CodeGeneration;
using JasperFx.CodeGeneration.Frames;
using JasperFx.CodeGeneration.Model;
using JasperFx.Core.Reflection;
Expand Down Expand Up @@ -98,6 +99,8 @@ public IEnumerable<Variable> ReturnVariablesOfType<T>()
/// </summary>
/// <param name="type"></param>
public void AddDependencyType(Type type);

void ApplyImpliedMiddlewareFromHandlers(GenerationRules generationRules);
}

#endregion
1 change: 1 addition & 0 deletions src/Wolverine/Persistence/AutoApplyTransactions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public void Apply(IReadOnlyList<IChain> chains, GenerationRules rules, IServiceC

foreach (var chain in chains.Where(x => !x.HasAttribute<TransactionalAttribute>()))
{
chain.ApplyImpliedMiddlewareFromHandlers(rules);
var potentials = providers.Where(x => x.CanApply(chain, container)).ToArray();
if (potentials.Length == 1)
{
Expand Down
4 changes: 3 additions & 1 deletion src/Wolverine/Runtime/Handlers/HandlerChain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,8 @@ protected void applyCustomizations(GenerationRules rules, IServiceContainer cont
if (!_hasConfiguredFrames)
{
_hasConfiguredFrames = true;



applyAttributesAndConfigureMethods(rules, container);

Expand All @@ -427,7 +429,7 @@ protected void applyCustomizations(GenerationRules rules, IServiceContainer cont
.OfType<ModifyChainAttribute>()) attribute.Modify(this, rules, container);
}

applyImpliedMiddlewareFromHandlers(rules);
ApplyImpliedMiddlewareFromHandlers(rules);
}

protected IEnumerable<Frame> determineHandlerReturnValueFrames()
Expand Down

0 comments on commit 4c7717f

Please sign in to comment.