Closed woksin closed 8 months ago
Generated code when signature was public static (SomeSideEffect, OutgoingMessages) Upload(IFormFile file, ...)
here:
// <auto-generated/>
#pragma warning disable
using Microsoft.AspNetCore.Routing;
using System;
using System.Linq;
using Wolverine.Http;
using Wolverine.Marten.Publishing;
using Wolverine.Runtime;
namespace Internal.Generated.WolverineHandlers
{
// START: POST_api_signingrequests_id_rawdocument
public class POST_api_signingrequests_id_rawdocument : Wolverine.Http.HttpHandler
{
private readonly Wolverine.Http.WolverineHttpOptions _wolverineHttpOptions;
private readonly Wolverine.Runtime.IWolverineRuntime _wolverineRuntime;
private readonly Wolverine.Marten.Publishing.OutboxedSessionFactory _outboxedSessionFactory;
public POST_api_signingrequests_id_rawdocument(Wolverine.Http.WolverineHttpOptions wolverineHttpOptions, Wolverine.Runtime.IWolverineRuntime wolverineRuntime, Wolverine.Marten.Publishing.OutboxedSessionFactory outboxedSessionFactory) : base(wolverineHttpOptions)
{
_wolverineHttpOptions = wolverineHttpOptions;
_wolverineRuntime = wolverineRuntime;
_outboxedSessionFactory = outboxedSessionFactory;
}
public override async System.Threading.Tasks.Task Handle(Microsoft.AspNetCore.Http.HttpContext httpContext)
{
var object = new object();
var envelope = new Wolverine.Envelope(object);
if (!System.Guid.TryParse((string)httpContext.GetRouteValue("id"), out var id))
{
httpContext.Response.StatusCode = 404;
return;
}
// Tenant Id detection
// 1. Detecting tenant using Woksin.Extensions.Tenancy.Context.ITenantContextAccessor using different strategies to resolve the tenant
var tenantId = await TryDetectTenantId(httpContext);
if (string.IsNullOrEmpty(tenantId))
{
await WriteTenantIdNotFound(httpContext);
return;
}
var messageContext = new Wolverine.Runtime.MessageContext(_wolverineRuntime);
messageContext.TenantId = tenantId;
Wolverine.Http.Runtime.RequestIdMiddleware.Apply(httpContext, messageContext);
// Retrieve header value from the request
var file = ReadSingleFormFileValue(httpContext);
await using var documentSession = _outboxedSessionFactory.OpenSession(messageContext);
var eventStore = documentSession.Events;
// Loading Marten aggregate
var eventStream = await eventStore.FetchForWriting<DigitalSigning.Signing.SigningRequests.GettingDetails.SigningRequestDetails>(id, httpContext.RequestAborted).ConfigureAwait(false);
var result1 = Wolverine.Http.Marten.AggregateAttribute.ValidateAggregateExists<DigitalSigning.Signing.SigningRequests.GettingDetails.SigningRequestDetails>(eventStream);
// Evaluate whether or not the execution should be stopped based on the IResult value
if (!(result1 is Wolverine.Http.WolverineContinue))
{
await result1.ExecuteAsync(httpContext).ConfigureAwait(false);
return;
}
// The actual HTTP request handler execution
(var storeRawDocument, var outgoingMessages) = DigitalSigning.Signing.SigningRequests.Documents.DocumentsHandler.Upload(file, eventStream.Aggregate, messageContext, documentSession, messageContext);
// Placed by Wolverine's ISideEffect policy
await storeRawDocument.ExecuteAsync(documentSession, envelope, httpContext.RequestAborted).ConfigureAwait(false);
// Outgoing, cascaded message
await messageContext.EnqueueCascadingAsync(outgoingMessages).ConfigureAwait(false);
await documentSession.SaveChangesAsync(httpContext.RequestAborted).ConfigureAwait(false);
// Commit any outstanding Marten changes
await documentSession.SaveChangesAsync(httpContext.RequestAborted).ConfigureAwait(false);
// Have to flush outgoing messages just in case Marten did nothing because of https://github.com/JasperFx/wolverine/issues/536
await messageContext.FlushOutgoingMessagesAsync().ConfigureAwait(false);
// Wolverine automatically sets the status code to 204 for empty responses
if (!httpContext.Response.HasStarted) httpContext.Response.StatusCode = 204;
}
}
// END: POST_api_signingrequests_id_rawdocument
}
Generated code when just removing the side effect from the signature:
// <auto-generated/>
#pragma warning disable
using Microsoft.AspNetCore.Routing;
using System;
using System.Linq;
using Wolverine.Http;
using Wolverine.Marten.Publishing;
using Wolverine.Runtime;
namespace Internal.Generated.WolverineHandlers
{
// START: POST_api_signingrequests_id_rawdocument
public class POST_api_signingrequests_id_rawdocument : Wolverine.Http.HttpHandler
{
private readonly Wolverine.Http.WolverineHttpOptions _wolverineHttpOptions;
private readonly Wolverine.Marten.Publishing.OutboxedSessionFactory _outboxedSessionFactory;
private readonly Wolverine.Runtime.IWolverineRuntime _wolverineRuntime;
public POST_api_signingrequests_id_rawdocument(Wolverine.Http.WolverineHttpOptions wolverineHttpOptions, Wolverine.Marten.Publishing.OutboxedSessionFactory outboxedSessionFactory, Wolverine.Runtime.IWolverineRuntime wolverineRuntime) : base(wolverineHttpOptions)
{
_wolverineHttpOptions = wolverineHttpOptions;
_outboxedSessionFactory = outboxedSessionFactory;
_wolverineRuntime = wolverineRuntime;
}
public override async System.Threading.Tasks.Task Handle(Microsoft.AspNetCore.Http.HttpContext httpContext)
{
// Tenant Id detection
// 1. Detecting tenant using Woksin.Extensions.Tenancy.Context.ITenantContextAccessor using different strategies to resolve the tenant
var tenantId = await TryDetectTenantId(httpContext);
if (string.IsNullOrEmpty(tenantId))
{
await WriteTenantIdNotFound(httpContext);
return;
}
var messageContext = new Wolverine.Runtime.MessageContext(_wolverineRuntime);
messageContext.TenantId = tenantId;
if (!System.Guid.TryParse((string)httpContext.GetRouteValue("id"), out var id))
{
httpContext.Response.StatusCode = 404;
return;
}
Wolverine.Http.Runtime.RequestIdMiddleware.Apply(httpContext, messageContext);
// Retrieve header value from the request
var file = ReadSingleFormFileValue(httpContext);
await using var documentSession = _outboxedSessionFactory.OpenSession(messageContext);
var eventStore = documentSession.Events;
// Loading Marten aggregate
var eventStream = await eventStore.FetchForWriting<DigitalSigning.Signing.SigningRequests.GettingDetails.SigningRequestDetails>(id, httpContext.RequestAborted).ConfigureAwait(false);
var result1 = Wolverine.Http.Marten.AggregateAttribute.ValidateAggregateExists<DigitalSigning.Signing.SigningRequests.GettingDetails.SigningRequestDetails>(eventStream);
// Evaluate whether or not the execution should be stopped based on the IResult value
if (!(result1 is Wolverine.Http.WolverineContinue))
{
await result1.ExecuteAsync(httpContext).ConfigureAwait(false);
return;
}
// The actual HTTP request handler execution
var outgoingMessages = DigitalSigning.Signing.SigningRequests.Documents.DocumentsHandler.Upload(file, eventStream.Aggregate, messageContext, documentSession, messageContext);
// Outgoing, cascaded message
await messageContext.EnqueueCascadingAsync(outgoingMessages).ConfigureAwait(false);
await documentSession.SaveChangesAsync(httpContext.RequestAborted).ConfigureAwait(false);
// Commit any outstanding Marten changes
await documentSession.SaveChangesAsync(httpContext.RequestAborted).ConfigureAwait(false);
// Have to flush outgoing messages just in case Marten did nothing because of https://github.com/JasperFx/wolverine/issues/536
await messageContext.FlushOutgoingMessagesAsync().ConfigureAwait(false);
// Wolverine automatically sets the status code to 204 for empty responses
if (!httpContext.Response.HasStarted) httpContext.Response.StatusCode = 204;
}
}
// END: POST_api_signingrequests_id_rawdocument
}
I also noticed another, slightly related problem. As a work around just now I don't return the side effect but I just create the side effect object and execute it manually. However in the sideeffect I do use the document session to store som object to the document store. I also in that method called SaveChangesAsync on the document session. The HTTP endpoint also returns Events. What I noticed is that the events were not published / stored. I debugged a bit and found out that after the events has been appended to the stream and SaveChangesAsync is called then it returns prematurely because it hits the
if (!session._workTracker.HasOutstandingWork())
return;
and returns from the method without persisting the events.
This was fixed by simply removing the manual call to SaveChangesAsync in the side effect Execute method. I don't know if this is expected or not, but I can certainly not find documentation on that you shouldn't do this.
@jeremydmiller
@woksin Yeah, sorry, the answer to that one is "don't manually call SaveChangesAsync() if using the transactional middleware"
I think this was fixed by recent changes to Lamar and should be gone in Wolverine 2.0. I can't reproduce this today.
Wolverine 1.18.1 When using a HTTP endpoint for uploading a file using the
IFormFile
the generated wolverine code fails to compile when the return type is either a side effect or is a tuple containing a side effect. This seems to be consistent with having the return type include a side effect as I have tried many permutations of the method signature including IMessageContext, injected services, a projected aggregate, etc