AzureFunctions.Autofac threadsafe dependency injection issue - c#

I am using AzureFunctions.Autofac to inject into my Azure Functions web api. An example of the config:
public class DIConfig
{
public DIConfig()
{
DependencyInjection.Initialize(builder =>
{
// DAL
builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerLifetimeScope();
builder.RegisterType<SecretCompanyContext>().InstancePerLifetimeScope();
builder.RegisterType<SecretCompanyContext>().As<ICartContext>().InstancePerLifetimeScope();
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();
// Services
builder.RegisterType<InventoryServices>().As<IInventoryServices>().InstancePerLifetimeScope();
// Controllers ported from ASP.NET MVC Web API
builder.RegisterType<InventoryController>().InstancePerLifetimeScope();
});
}
Then my Azure functions, I have one class that defines all methods in the API
[DependencyInjectionConfig(typeof(DIConfig))]
public class InventoryFunctions : FunctionsApi
{
[FunctionName("GetProductsByCategory")]
// /inventory/categories/{id}/products
public static async Task<HttpResponseMessage> GetProductsByCategory(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "inventory/categories/{id}/products")]
HttpRequestMessage req,
TraceWriter log,
int id,
[Inject] InventoryController controller)
{
// do stuff
var result = await controller.GetProductsByCategory(id);
return JsonResponse(result, HttpStatusCode.OK);
}
[FunctionName("GetInventoryBySku")]
// /inventory/skus?sku=ASDF&sku=ASDG&sku=ASDH
public static async Task<HttpResponseMessage> GetInventoryBySku(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "inventory")]
HttpRequestMessage req,
TraceWriter log,
[Inject] InventoryController controller)
{
// do stuff
var result = await controller.QueryInventoryBySkuList(skuList);
return JsonResponse(result, HttpStatusCode.OK);
}
[FunctionName("UpdateProductsQuantity")]
// /inventory
// Post
public static async Task<HttpResponseMessage> UpdateProductsQuantity(
[HttpTrigger(AuthorizationLevel.Function, "put", Route = "inventory")]
HttpRequestMessage req,
TraceWriter log,
[Inject] InventoryController controller)
{
// do stuff
var inventoryProducts = await req.Content.ReadAsAsync<List<InvProductOperation>>();
var result = await controller.UpdateAvailableProductsQuantity(inventoryProducts);
return JsonResponse(result, HttpStatusCode.OK);
}
But I keep getting this error:
A second operation started on this context before a previous
asynchronous operation completed. Use 'await' to ensure that
any asynchronous operations have completed before calling
another method on this context. Any instance members are not
guaranteed to be thread safe.
I have verified that async and await are used properly, so following the error message's recommendation isn't fixing it. What appears to be the issue is that IDbContext is not honoring the InstancePerLifetimeScope as expected. Is this happening because I have more than one method in my InventoryFunctions class? Or is AzureFunctions.Autofac not threadsafe?

Change the registration of the DbContext to this:
builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerDependency();
You can find a deeper explanation of mine for why this is happening here.

I was going by this SO answer: Autofac - InstancePerHttpRequest vs InstancePerLifetimeScope which said that InstancePerLifetimeScope was the non-ASP.NET equivalent of InstancePerRequest.
I spoke to the developers and they said the truth is that getting one DbContext per HttpRequest was the default behavior when you simply register using builder.RegisterType<SecretCompanyContext>.As<IDbContext>() so there's some misinformation out there.
So the solution is, instead of using
builder.Register<IDbContext>(c => new SecretCompanyContext()).InstancePerDependency();
or
builder.RegisterType<SecretCompanyContext>().As<IDbContext>().InstancePerLifetimeScope();
one should just use
builder.RegisterType<SecretCompanyContext>().As<IDbContext>();
if the goal is one instance per HTTP request.

Related

Unit Testing Azure Functions With Dependency Injection

I haven't written any Azure functions in quite a long time, and thought I'd refresh myself today, but I've clearly forgotten how to write appropriate unit tests for them. I have the following Function - it picks a random quote from a list;
public class QuoteFunction
{
private readonly IQuoteBank _repository;
public QuoteFunction(IQuoteBank repository)
{
_repository = repository;
}
[FunctionName("GetQuote")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
Quote quote = _repository.getQuote();
return new OkObjectResult(quote);
}
}
and it uses dependency injection to obtain the list of quotes - I have the following in Startup;
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton<IQuoteBank, QuoteBank>();
builder.Services.AddLogging();
}
which is injected into the constructor of the Function. as shown in the first snippet.
What I am struggling with is how I can use Moq to force the quote (which is randomly selected) to be consistent. I know I can mock the Interface IQuoteBank - but there is no where I can pass this mock object into the Run method.
So what I want to know is how I can pass a mock object to make the same quote be produced for unit testing? Has anyone done anything like this before? any examples in github?
I'm pretty sure I did a few years ago, just cant remember at all.
Setup the mock and pass that into the subject under test via constructor injection.
public async Task MyTestMehod() {
// Arrange
Mock<IQuoteBank> mock = new Mock<IQuoteBank>();
mock.Setup(_ => _.getQuote()).Returns("my consistent quote here")
var subject = new QuoteFunction(mock.Object);
//Act
IActionResult result = await subject.Run(Mock.Of<HttpRequest>(), Mock.Of<ILogger>());
//Assert
// ... assert my expected behavior
}

Intercept HttpClient with third party extensions using state

Injecting state into your HttpRequest when using IHttpClientFactory is achievable by populating HttpRequestMessage.Properties see Using DelegatingHandler with custom data on HttpClient
Now if I have third party extensions on HttpClient (such as IdentityModel), how would I intercept these http requests using custom state?
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
var httpClient = factory.CreateClient();
// GetDiscoveryDocumentAsync is a third party extension method on HttpClient
// I therefore cannot inject or alter the request message to be handled by the InterceptorHandler
var discovery = await httpClient.GetDiscoveryDocumentAsync();
// I want id to be associated with any request / response GetDiscoveryDocumentAsync is making
}
The only plausible solution I currently have is to override HttpClient.
public class InspectorHttpClient: HttpClient
{
private readonly HttpClient _internal;
private readonly int _id;
public const string Key = "insepctor";
public InspectorHttpClient(HttpClient #internal, int id)
{
_internal = #internal;
_id = id;
}
public override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
// attach data into HttpRequestMessage for the delegate handler
request.Properties.Add(Key, _id);
return _internal.SendAsync(request, cancellationToken);
}
// override all methods forwarding to _internal
}
A then I'm able to intercept these requests.
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
var httpClient = new InspectorHttpClient(factory.CreateClient(), id);
var discovery = await httpClient.GetDiscoveryDocumentAsync();
}
Is that a plausible solution? Something tell me now not to override HttpClient. Quoting from https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=net-5.0
The HttpClient also acts as a base class for more specific HTTP clients. An example would be a FacebookHttpClient providing additional methods specific to a Facebook web service (a GetFriends method, for instance). Derived classes should not override the virtual methods on the class. Instead, use a constructor overload that accepts HttpMessageHandler to configure any pre- or post-request processing instead.
I almost included this in my other answer as an alternative solution, but I figured it was too long already. :)
The technique is practically the same, but instead of HttpRequestMessage.Properties, use AsyncLocal<T>. "Async local" is kind of like thread-local storage but for a specific asynchronous code block.
There are a few caveats to using AsyncLocal<T> that aren't particularly well-documented:
Use an immutable nullable type for T.
When setting the async local value, return an IDisposable that resets it.
If you don't do this, then only set the async local value from an async method.
You don't have to follow these guidelines, but they will make your life much easier.
With that out of the way, the solution is similar to the last one, except it just uses AsyncLocal<T> instead. Starting with the helper methods:
public static class AmbientContext
{
public static IDisposable SetId(int id)
{
var oldValue = AmbientId.Value;
AmbientId.Value = id;
// The following line uses Nito.Disposables; feel free to write your own.
return Disposable.Create(() => AmbientId.Value = oldValue);
}
public static int? TryGetId() => AmbientId.Value;
private static readonly AsyncLocal<int?> AmbientId = new AsyncLocal<int?>();
}
Then the calling code is updated to set the ambient value:
public async Task DoEnquiry(IHttpClientFactory factory)
{
var id = Database.InsertEnquiry();
using (AmbientContext.SetId(id))
{
var httpClient = factory.CreateClient();
var discovery = await httpClient.GetDiscoveryDocumentAsync();
}
}
Note that there is an explicit scope for that ambient id value. Any code within that scope can get the id by calling AmbientContext.TryGetId. Using this pattern ensures that this is true for any code: synchronous, async, ConfigureAwait(false), whatever - all code within that scope can get the id value. Including your custom handler:
public class HttpClientInterceptor : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var id = AmbientContext.TryGetId();
if (id == null)
throw new InvalidOperationException("The caller must set an ambient id.");
// associate the id with this request
Database.InsertEnquiry(id.Value, request);
return await base.SendAsync(request, cancellationToken);
}
}
Followup readings:
Blog post on "async local" - written before AsyncLocal<T> existed, but has details on how it works. This answers the questions "why should T be immutable?" and "if I don't use IDisposable, why do I have to set the value from an async method?".

How to write unit tests for Azure Functions that use wrapper?

I use a wrapper class on all of my Azure Functions:
public interface IFunctionWrapper
{
Task<IActionResult> Execute(HttpRequest req, ExecutionContext context, Func<Task<IActionResult>> azureFunction);
}
public class FunctionWrapper : IFunctionWrapper
{
private readonly ILogger _log;
public FunctionWrapper(ILogger<FunctionWrapper> log)
{
_log = log;
}
public async Task<IActionResult> Execute(HttpRequest req, ExecutionContext context, Func<Task<IActionResult>> azureFunction)
{
try
{
// Log few extra information to Application Insights
// Do authentication
return await azureFunction();
}
catch (Exception ex)
{
// Return a custom error response
}
}
}
And here is how it is used in a function:
public class MyFunctions
{
private readonly IFunctionWrapper _functionWrapper;
public MyFunctions(IFunctionWrapper functionWrapper)
{
_functionWrapper = functionWrapper;
}
public async Task<IActionResult> GetPost(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
ExecutionContext context,
ILogger log)
{
return await _functionWrapper.Execute(req, context, async () =>
{
// Function code...
return new JsonResult(post);
});
}
}
I am trying to write unit tests for this GetPost function. How can I mock the FunctionWrapper class in this situation?
Mock the desired behavior of the wrapper abstraction.
The following example uses MOQ to mock the wrapper. Pay attention to the setup of the mock
[TestClass]
public class MyFunctionsTests {
[TestMethod]
public async Task GetPost_Should_Execute_Wrapper() {
//Arrange
//mock the wrapper
IFunctionWrapper wrapper = Mock.Of<IFunctionWrapper>();
//configure the mocked wrapper to behave as expected when invoked
Mock.Get(wrapper)
.Setup(_ => _.Execute(It.IsAny<HttpRequest>(), It.IsAny<ExecutionContext>(), It.IsAny<Func<Task<IActionResult>>>()))
.Returns((HttpRequest r, ExecutionContext c, Func<Task<IActionResult>> azureFunction) =>
azureFunction()); //<-- invokes the delegate and returns its result
MyFunctions function = new MyFunctions(wrapper);
//these should be initialized as needed for the test
HttpRequest req = null;
ExecutionContext ctx = null;
ILogger log = Mock.Of<ILogger>();
//Act
IActionResult result = await function.GetPost(req, ctx, log);
//Assert
result.Should().NotBeNull();
//verify that mocked wrapper was called
Mock.Get(wrapper).Verify(_ => _.Execute(It.IsAny<HttpRequest>(), It.IsAny<ExecutionContext>(), It.IsAny<Func<Task<IActionResult>>>()));
//...perform other assertions here
}
}
The code in the original question omitted most of the body of the subject under test. That being said, this example is based on what was originally provided, which was used to create a reproducible example used to create the test above
You don't need to create a wrapper interface at all:
HttpRequest is mockable: https://mahmutcanga.com/2019/12/13/unit-testing-httprequest-in-c/
ExecutionContext can be mocked (or as its just a POCO used as-is)
ILogger can be mocked
Use dependency injection to inject the dependencies of the function (and mock those then).
Remember you really only want to test that parameter validation and possible parsing works correctly.

Mediatr IPipelineBehavior not triggered

I have implemented Mediatr in my .NET framework project and would like to use a IPipelineBehavior.
I have implemented and registered the container using the example from the project: https://github.com/jbogard/MediatR/blob/master/samples/MediatR.Examples.Unity/Program.cs
This is my Behavior
public class AuditPipelineBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILeaveAuditTrail _auditor;
public AuditPipelineBehavior(ILeaveAuditTrail auditor)
{
_auditor = auditor;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
if (AuditPrevented(request))
return await next();
var response = await next();
var auditLog = _auditor.CreateAuditLog(request, response);
//Dispatch audit
return response;
}
private static bool AuditPrevented<TInput>(TInput query)
{
return query.GetType().GetCustomAttributes(typeof(PreventAuditAttribute), true).Any();
}
}
And i register is like this together with my Mediatr
container.RegisterMediator(new HierarchicalLifetimeManager());
container.RegisterMediatorHandlers(applicationAssembly);
container.RegisterType(typeof(IPipelineBehavior<,>), typeof(AuditPipelineBehavior<,>));
container.RegisterType<ILeaveAuditTrail, DefaultAuditor>();
When i send the IRequest with Mediatr it is handled fine and i get the results but the AuditPipeline is not called.
If i remove the async keyword and just return next(); It works. But this way i cannot correctly intercept my response.
Also on the example Github the handle is implemented async: https://github.com/jbogard/MediatR/wiki/Behaviors
The issue was not related to the registration of Mediatror the PipelineBehavior.
When calling the mediator.Send()method it was not in an async method. .Resultwas used instead of await. Because of this the handling of the pipeline only worked when the asynckeyword was not present.
Make sure to never use the .Result to chain these async calls. Mark the controller method async and await the mediator.Send()

Can durable functions have multiple triggers?

I have a durable function that is triggered once a day by a Timer Trigger:
[FunctionName("MyDurableFunction")]
public static async Task Run(
[TimerTrigger("0 0 23 * * *", RunOnStartup = false)] TimerInfo myTimer,
[OrchestrationClient] DurableOrchestrationClient starter,
ILogger log)
{
await starter.StartNewAsync("OrchestrationFunction", null);
}
[FunctionName("OrchestrationFunction")]
public static async Task OrchestrationFunction(
[OrchestrationTrigger]DurableOrchestrationContext context,
ILogger log)
{
// do stuff
}
This works fine. For testing purposes I would also like to be able to trigger the durable function via a Http Trigger, so I added this:
[FunctionName("MyDurableFunctionHttpTrigger")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "demo")]HttpRequest req,
[OrchestrationClient] DurableOrchestrationClient starter,
ILogger log)
{
await starter.StartNewAsync("OrchestrationFunction", null);
return new OkObjectResult("blah");
}
Running these locally, including either the http trigger or the timer trigger will trigger the function, but including both in the class means that neither trigger events will occur. Is it possible to have multiple trigger types start an orchestration trigger?
I believe you can only have one trigger type per function but can suggest you write all your logic in to a separate project/assembly and then just reference the assembly and call the entry point via parameters, keeping your function implementation clean and simple and centralising the execution logic in another project (or classes within the same project).
On your code, you should have Orchestrator and Activity functions, so you could write one Activity function to do the work and call it from two orchestrators. The guidance on Durable Functions is to keep the orchestrator clean and simple managing just that - the orchestration, offloading the work to the Activities.
I recommend you look at the durable monitor pattern for your timer based requirement and look at the HTTP APIs for HTTP Triggers.
What you could do is create multiple normal functions, one for each type of trigger. A scheduled trigger, http trigger, blob trigger, or any other supported trigger.
Within that function you can start a new orchestration function. That orchestration function does not require a trigger in itself. You only need the DurableOrchestrationContext.
public static async Task<object> RunOrchestrator(
[OrchestrationTrigger] DurableOrchestrationContext context,
ILogger log)
{
// orchestration logic here
}
[FunctionName("Info_HttpStart1")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "starter1")]HttpRequestMessage req,
[OrchestrationClient]DurableOrchestrationClient starter,
ILogger log)
{
// Function input comes from the request content.
string instanceId = await starter.StartNewAsync("Info", null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
[FunctionName("Info_HttpStart2")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "starter2")]HttpRequestMessage req,
[OrchestrationClient]DurableOrchestrationClient starter,
ILogger log)
{
// Function input comes from the request content.
string instanceId = await starter.StartNewAsync("Info", null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}

Categories