GraphQL .NET: middleware problems with singleton schema - graphql-dotnet

I'm using GraphQL on a .NET core website/controller. The schema is quite large, such that the constructor takes about a second to run. I couldn't have that kind of overhead on every request, so I created the schema as a singleton, shared between all requests.
public async Task<IActionResult> Post([FromBody] GraphQLQuery query)
{
var executionOptions = new ExecutionOptions {
Schema = this.Schema, // dependency injected singleton
/* ... */
};
// ...
executionOptions.FieldMiddleware.Use(next => context =>
{
return next(context).ContinueWith(x=> {
var result = x.Result;
return doStuff(result);
});
});
var result = await new DocumentExecuter().ExecuteAsync(executionOptions).ConfigureAwait(false);
// ...
}
This worked most of the time, but it caused random problems with the middleware. Sometimes the middleware would start running twice for each element, which would usually cause an error the second time the middleware ran.

Looking at the source, it appears the middleware is being applied to the schema during the life cycle of a request, and then somehow rolled back at the end I guess? At least I'm assuming that's how the public void ApplyTo(ISchema schema) member is being used, although I'm not sure how the "rollback" part was happening.
This gave me an idea of how to solve the problem by pulling the middleware out of the view and put it in the schema constructor, like this:
public class MySchema : Schema
{
public MySchema()
{
this.Query = new MyQuery();
this.Mutation = new MyMutation();
var builder = new FieldMiddlewareBuilder();
builder.Use(next => context =>
{
return next(context).ContinueWith(x=> {
var result = x.Result;
return doStuff(result);
});
});
builder.ApplyTo(this);
}
}
So now the middleware is baked directly into the schema when the singleton is constructed, and the controller doesn't have to do anything.
This appears to have completely solved the problem. I'm not sure if there are other things in graphql-dotnet that mutate the schema during the request life cycle. If anyone knows of any other problems that might occur with a singleton schema I'd love to hear it!

Related

Unity to DryIoC conversion ParameterOverride

We are transitioning from Xamarin.Forms to .Net MAUI but our project uses Prism.Unity.Forms. We have a lot of code that basically uses the IContainer.Resolve() passing in a collection of ParameterOverrides with some primitives but some are interfaces/objects. The T we are resolving is usually a registered View which may or may not be the correct way of doing this but it's what I'm working with and we are doing it in backend code (sometimes a service). What is the correct way of doing this Unity thing in DryIoC? Note these parameters are being set at runtime and may only be part of the parameters a constructor takes in (some may be from already registered dependencies).
Example of the scenario:
//Called from service into custom resolver method
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", IObjectWithData)
};
//Custom resolver method example
var resolverOverrides = new List<ResolverOverride>();
foreach(var parameterOverride in parameterOverrides)
{
resolverOverrides.Add(parameterOverride);
}
return _container.Resolve<T>(resolverOverrides.ToArray());
You've found out why you don't use the container outside of the resolution root. I recommend not trying to replicate this error with another container but rather fixing it - use handcoded factories:
internal class SomeFactory : IProductViewFactory
{
public SomeFactory( IService dependency )
{
_dependency = dependency ?? throw new ArgumentNullException( nameof(dependency) );
}
#region IProductViewFactory
public IProductView Create( int productID, IObjectWithData objectWithData ) => new SomeProduct( productID, objectWithData, _dependency );
#endregion
#region private
private readonly IService _dependency;
#endregion
}
See this, too:
For dependencies that are independent of the instance you're creating, inject them into the factory and store them until needed.
For dependencies that are independent of the context of creation but need to be recreated for each created instance, inject factories into the factory and store them.
For dependencies that are dependent on the context of creation, pass them into the Create method of the factory.
Also, be aware of potential subtle differences in container behaviours: Unity's ResolverOverride works for the whole call to resolve, i.e. they override parameters of dependencies, too, whatever happens to match by name. This could very well be handled very differently by DryIOC.
First, I would agree with the #haukinger answer to rethink how do you pass the runtime information into the services. The most transparent and simple way in my opinion is by passing it via parameters into the consuming methods.
Second, here is a complete example in DryIoc to solve it head-on + the live code to play with.
using System;
using DryIoc;
public class Program
{
record ParameterOverride(string Name, object Value);
record Product(int productID);
public static void Main()
{
// get container somehow,
// if you don't have an access to it directly then you may resolve it from your service provider
IContainer c = new Container();
c.Register<Product>();
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", "blah"),
};
var parameterRules = Parameters.Of;
foreach (var po in parameterOverrides)
{
parameterRules = parameterRules.Details((_, x) => x.Name.Equals(po.Name) ? ServiceDetails.Of(po.Value) : null);
}
c = c.With(rules => rules.With(parameters: parameterRules));
var s = c.Resolve<Product>();
Console.WriteLine(s.productID);
}
}

How to use properly EntityRepository instances?

The documentation stresses that I should use a new EntityManager for each request and there's even a middleware for automatically generating it or alternatively I can use em.fork(). So far so good.
The EntityRepository is a great way to make the code readable. I could not find anything in the documentation about how they relate to EntityManager instances. The express-ts-example-app example uses single instances of repositories and the RequestContext middleware. This suggests that there is some under-the-hood magic that finds the correct EntityManager instances at least with the RequestContext. Is it really so?
Also, if I fork the EM manually can it still find the right one? Consider the following example:
(async () => {
DI.orm = await MikroORM.init();
DI.em = DI.orm.em;
DI.companyRepository = DI.orm.em.getRepository(Company);
DI.invoiceRepository = DI.orm.em.getRepository(Invoice);
...
fetchInvoices(em.fork());
}
async function fetchInvoices(em) {
for (const company of await DI.companyRepository.findAll()) {
fetchInvoicesOfACompany(company, em.fork())
}
}
async function fetchInvoicesOfACompany(company, em) {
let done = false;
while (!done) {
const invoice = await getNextInvoice(company.taxnumber, company.lastInvoice);
if ( invoice ) {
DI.invoiceRepository.persist(invoice);
company.lastInvoice = invoice.id;
em.flush();
} else {
done = true;
}
}
}
Does the DI.invoiceRepository.persist() in fetchInvoicesOfACompany() use the right EM instance? If not, what should I do?
Also, if I'm not mistaken, the em.flush() in fetchInvoicesOfACompany() does not update company, since that belongs to another EM - how should I handle situations like this?
First of all, repository is just a thin layer on top of EM (an extension point if you want), that bares the entity name so you don't have to pass it to the first parameter of EM method (e.g. em.find(Ent, ...) vs repo.find(...).
Then the contexts - you need a dedicated context for each request, so it has its own identity map. If you use RequestContext helper, the context is created and saved via domain API. Thanks to this, all the methods that are executed inside the domain handler will use the right instance automatically - this happens in the em.getContext() method, that first checks the RequestContext helper.
https://mikro-orm.io/docs/identity-map/#requestcontext-helper-for-di-containers
Check the tests for better understanding of how it works:
https://github.com/mikro-orm/mikro-orm/blob/master/tests/RequestContext.test.ts
So if you use repositories, with RequestContext helper it will work just fine as the singleton repository instance will use the singleton EM instance that will then use the right request based instance via em.getContext() where approapriate.
But if you use manual forking instead, you are responsible use the right repository instance - each EM fork will have its own one. So in this case you can't use a singleton, you need to do forkedEm.getRepository(Ent).
Btw alternatively you can also use AsyncLocalStorage which is faster (and not deprecated), if you are on node 12+. The RequestContext helper implementation will use ALS in v5, as node 12+ will be requried.
https://mikro-orm.io/docs/async-local-storage
Another thing you could do is to use the RequestContext helper manually instead of via middlewares - something like the following:
(async () => {
DI.orm = await MikroORM.init();
DI.em = DI.orm.em;
DI.companyRepository = DI.orm.em.getRepository(Company);
DI.invoiceRepository = DI.orm.em.getRepository(Invoice);
...
await RequestContext.createAsync(DI.em, async () => {
await fetchInvoices();
})
});
async function fetchInvoices() {
for (const company of await DI.companyRepository.findAll()) {
await fetchInvoicesOfACompany(company)
}
}
async function fetchInvoicesOfACompany(company) {
let done = false;
while (!done) {
const invoice = await getNextInvoice(company.taxnumber, company.lastInvoice);
if (invoice) {
company.lastInvoice = invoice; // passing entity instance, no need to persist as `company` is managed entity and this change will be cascaded
await DI.em.flush();
} else {
done = true;
}
}
}

ASP.NET HttpContext.Current inside Task.Run

I have a following code example that is used in ASP.NET MVC application.
The purpose of this code is to create "fire and forget" request for queuing some long running operation.
public JsonResult SomeAction() {
HttpContext ctx = HttpContext.Current;
Task.Run(() => {
HttpContext.Current = ctx;
//Other long running code here.
});
return Json("{ 'status': 'Work Queued' }");
}
I know this is not a good way for handling HttpContext.Current in asynchronous code, but currently our implementation not allows us to do something else.
I would like to understand how much this code is dangerous...
The question: Is it theoretically possible that setting the HttpContext inside Task.Run, will set the context to totally another request?
I think yes, but I'm not sure. How I understand it:
Request1 is handled with Thread1 from thread pool, then while Thread1 is handling absolutelly another request (Request2), the code inside Task.Run will set context from Request1 to Request2.
Maybe I am wrong, but my knowledge of ASP.NET internals not allows me to understand it correctly.
Thanks!
Let me bump a little internals on you:
public static HttpContext Current
{
get { return ContextBase.Current as HttpContext; }
set { ContextBase.Current = value; }
}
internal class ContextBase
{
internal static object Current
{
get { return CallContext.HostContext; }
set { CallContext.HostContext = value; }
}
}
public static object HostContext
{
get
{
var executionContextReader = Thread.CurrentThread.GetExecutionContextReader();
object hostContext = executionContextReader.IllogicalCallContext.HostContext;
if (hostContext == null)
{
hostContext = executionContextReader.LogicalCallContext.HostContext;
}
return hostContext;
}
set
{
var mutableExecutionContext = Thread.CurrentThread.GetMutableExecutionContext();
if (value is ILogicalThreadAffinative)
{
mutableExecutionContext.IllogicalCallContext.HostContext = null;
mutableExecutionContext.LogicalCallContext.HostContext = value;
return;
}
mutableExecutionContext.IllogicalCallContext.HostContext = value;
mutableExecutionContext.LogicalCallContext.HostContext = null;
}
}
So
var context = HttpContext.Current;
is equal to (pseudocode)
var context = CurrentThread.HttpContext;
and inside your Task.Run something like this happens
CurrentThread.HttpContext= context;
Task.Run will start new task with thread from thread pool. So you're telling that your new thread "HttpContext property" is reference to starter thread "HttpContext property" - so far so good (well with all the NullReference/Dispose exceptions you'll be facing after your starter thread finishes). Problem is if inside your
//Other long running code here.
You have statement like
var foo = await Bar();
Once you hit await, your current thread is returned to thread pool, and after IO finishes you grab new thread from thread pool - wonder what its "HttpContext property" is, right ? I don't know :) Most probably you'll end with NullReferenceException.
The issue you will run into here is that the HttpContext will dispose when the request is complete. Since you aren't awaiting the result of the Task.Run, you are essentially creating a race condition between the disposal of the HttpContext and it's usage within the task.
I'm pretty sure that the only issue your task will run into is a NullReferenceException or an ObjectDisposedException. I don't see any way where you could accidentally steal another request's context.
Also, unless you are handling & logging exceptions within your task, your fire and forget will throw and you'll never know about it.
Check out HangFire or consider using a message queue for processing backend jobs from a separate process.

Entity framework 6 async/await fail in parallel request

I'm working in ASP.NET 4.6 with EF 6 to a new Web Api 2 project, I've read a lot of articles that describes the async/await pattern and It's clear that for a lot of requests this pattern increase performance for a lot of reasons.
So I decided to use it for the first time.
In decided to create a Biz project and a DAL project here an example.
This is controller method:
[HttpGet]
public async Task<bool> CheckValueValidity(string value, string type)
{
return await _accountsBiz.CheckValueTypeValidity(value, type);
}
I instance biz object in controller constructor.
This is the BIZ:
public async Task<bool> CheckValueTypeValidity(string value, string type)
{
bool isValid = false;
switch (type.ToLower())
{
case "email":
isValid = await _accountsRepository.CheckEmailValidity(value);
break;
case "username":
isValid = await _accountsRepository.CheckUserNameValidity(value);
break;
}
return isValid;
}
I instance the repository in the BIZ constructor.
Finally this is the DAL method:
public async Task<bool> CheckEmailValidity(string email)
{
using(MyEntities db = new MyEntities())
{
return await db.AspNetUsers.CountAsync(u => u.Email == email) > 0 ? false : true;
}
}
And all it seems to work perfectly until I made two request at same time, one of these return the correct value the other say
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 made the requests from two different client so the context should be different. should....
I don't know how to fix it. I read a lot and my code seems to be correct.
The question is WHY throw this error? I can't understand
Thanks for help in advice.
Ok found the error in the execution flow there is a method that use DbContext without using block.... My mistake but now it's clear that context must be disposed each request!

FakeMessageContext throws NullReferenceException

I'm trying to write unit test for my saga. I have used SagaFixture to host my Saga and using FakeBus. I have also used FakeMessageContext because, in a Saga exectution I'm using MessageContext to get return address.
Now, when I'm trying to call Handle method on Fixture, it throws exception of type NullReferenceException.
Pasting code below:
SearchSaga
public class SearchSaga : Saga<SearchSagaData>, IAmInitiatedBy<PersonRequested>, IHandleMessages<PersonSearchCompleted>
{
public void Handle(PersonRequested message)
{
Data.Id = new Guid(message.MessageId);
Data.ReturnAddress = MessageContext.GetCurrent().ReturnAddress;
Bus.Publish(message);
}
}
SagaUnitTest
[Test]
public void PublishResponseOfTypeSearchPersonRequest()
{
var bus = new FakeBus();
var saga = new SearchSaga(bus);
var fixture = new SagaFixture<SearchSagaData>(saga);
FakeMessageContext.Reset();
var fakeContext = MockRepository.GenerateMock<IMessageContext>();
fakeContext.Stub(s => s.ReturnAddress).Return("queuename");
fakeContext.Stub(s => s.Headers).Return(new Dictionary<string, object>());
// act
using (FakeMessageContext.Establish(fakeContext))
{
fixture.Handle(new PersonRequested {MessageId = Guid.NewGuid().ToString(), Query = "Abc"});
}
var sentRequests = bus.PublishedMessages.OfType<SearchPersonRequest>().ToList();
Assert.That(sentRequests.Count, Is.EqualTo(1));
}
Error Stacktrace:
at Rebus.SagaContext..ctor(Guid id)
at Rebus.Bus.Dispatcher.DispatchToHandler[TMessage](TMessage message, IHandleMessages1 handler) at Rebus.Testing.SagaFixture1.Handle[TMessage](TMessage message)
The exception is caused by the fact that your IMessageContext mock has a null value on the Items property. The thrown exception is just pretty bad, but I'll make sure that the reported error gets better in the future.
For now, you can fix the situation by setting up a an items dictionary like so:
fakeContext.Stub(s => s.Items).Return(new Dictionary<string, object>());
Moreover, instead of using MessageContext.GetCurrent() to get to the message context inside your handler, you should take advantage of the fact that all Rebus' IoC container adapters ensure that you can have an IMessageContext injected into all of your handler instances.
This way, there's no need to use FakeMessageContext in your unit tests.

Resources