Skip to content
This repository has been archived by the owner on Jan 24, 2021. It is now read-only.

Legacy nancy module #2199

Merged
merged 13 commits into from
Jan 25, 2016
Merged

Legacy nancy module #2199

merged 13 commits into from
Jan 25, 2016

Conversation

grumpydev
Copy link
Member

TL;DR - Find/replace NancyModule with LegacyNancyModule for backwards compatibility.

The "old" NancyModule is now LegacyNancyModule and marked with an ObsoleteAttribute, this can be continued to be used for the immediate future, but the new NancyModule, which has async "only" routing, is the new standard moving forwards. The new NancyModule route definitions are the same as the old NancyModule "async" definitions, but without the pointless boolean on the end, e.g. this:

Get["/", true] = async (_, __) => "foo";

Becomes:

Get["/"] = async (_, __) => "foo";

The initial plan was to use some kind of inheritance to handle the LegacyNancyModule, but this isn't possible as it would hit the same issue of only differing on return type as the original async NancyModule work.

All the demos have been updated to use LegacyNancyModule rather than reworked to use the new one - this should serve as a reminder that we do need to remove it at some point :)

In addition to this work we could also do with Task<dynamic> versions of View, Negotiate etc., although some of those might be tricky as we may hit the "can't differ by return type" issue again so we need to have a think about that.

@grumpydev grumpydev added this to the 2.0 milestone Jan 24, 2016
@thecodejunkie
Copy link
Member

Looks good to me! Let's get a second pair of eyes on in and then pull it @NancyFx/most-valued-minions

@@ -81,6 +81,7 @@ public Task<dynamic> Invoke(DynamicDictionary parameters, CancellationToken canc
/// <param name="description"></param>
/// <param name="syncFunc">The action that should take place when the route is invoked.</param>
/// <returns>A Route instance</returns>
[Obsolete("Sync routes are deprecated (see LegacyNancyModule")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Missing ) at the end.

@khellang
Copy link
Member

LGTM (modulo one tiny nit) :shipit:

@grumpydev
Copy link
Member Author

Fixed the typo - also had a think about View etc. and I think we can potentially move all the current things that return Response to be async then add some code to either the conneg, or just before the connect, that does:

var taskResponse = response as Task;
if (taskResponse != null) // simplistic but you get the idea
{
    taskResponse.ConfigureAwait(continueOnCapturedContext: false);
    response = taskResponse.Result;
}

e.g. If View["foo"] was executed from a legacy module it would return Task as the dynamic response, which would automatically get "unwrapped" into ViewResult before going into conneg.

@jchannon
Copy link
Member

the new NancyModule, which has async "only" routing, - so even if I return "hello" from a route thats async by default or do I still have to add the async keyword

can it be non async?

@grumpydev
Copy link
Member Author

Yep, previously it would have been horrible because you'd have to return Task, but async handles all that for you so as long as you mark the delegate as async you can do:

return "Foo";

@jchannon
Copy link
Member

So I can still return "Foo" without the async keyword so it becomes
non-async?

On 25 January 2016 at 11:42, Steven Robbins [email protected]
wrote:

Yep, previously it would have been horrible because you'd have to return
Task, but async handles all that for you so as long as you mark the
delegate as async you can do:

return "Foo";


Reply to this email directly or view it on GitHub
#2199 (comment).

@khellang
Copy link
Member

I'm not sure I'd add async just for that, though. Simply because of the insane amount of generated code (see https://gist.github.com/khellang/6e7b3bd197657112714c). A Task.FromResult("Foo") would be better IMO.

@jchannon
Copy link
Member

What I'm getting at is I shouldn't mark return "foo" as async as it
serves no purpose making it async

On 25 January 2016 at 11:43, Kristian Hellang [email protected]
wrote:

I'm not sure I'd add async just for that, though. Simply because of the
insane amount of generated code (see
https://gist.github.com/khellang/6e7b3bd197657112714c). A
Task.FromResult("Foo") would be better IMO.


Reply to this email directly or view it on GitHub
#2199 (comment).

@grumpydev
Copy link
Member Author

Each to their own, Task.FromResult will work fine, but the generated state machines are heavily optimised for this use case with async/await (which they weren't with Tasks, hence Nancy's original Task helper stuff)

@khellang
Copy link
Member

@jchannon Right. So return Task.FromResult("Foo") then...

@grumpydev
Copy link
Member Author

@jchannon : it does serve a purpose, it changes the return type to Task and unwraps it for you, otherwise you have to return a Task yourself, you can't just return a string.

@khellang
Copy link
Member

response = taskResponse.Result

@grumpydev That could potentially block, right?

@grumpydev
Copy link
Member Author

Block or deadlock? It could block yes, but that's what the legacy stuff would do anyway, this is just another step towards everything being async under the hood (the response class should be next, but view engine generation etc)

@khellang
Copy link
Member

Oh, this is if a route in a legacy module is returning a Task? I guess it might work. We should just make sure it doesn't deadlock 😛

@grumpydev
Copy link
Member Author

Yeah, the configure await is there to hopefully avoid deadlocks, this idea was just to enable us to move things like Response and View to returning Task rather than just T without breaking the legacy stuff .

@jchannon
Copy link
Member

But isn't making something async when it isn't async causing unnecessary
overhead in terms of context switches etc?

On 25 January 2016 at 11:52, Steven Robbins [email protected]
wrote:

Yeah, the configure await is there to hopefully avoid deadlocks, this idea
was just to enable us to move things like Response and View to returning
Task rather than just T without breaking the legacy stuff .


Reply to this email directly or view it on GitHub
#2199 (comment).

@jchannon jchannon mentioned this pull request Jan 25, 2016
4 tasks
@phillip-haydon
Copy link
Member

👍

@grumpydev
Copy link
Member Author

@jchannon : no, that's where the optimisation comes in (previously hand rolled, now using async/await), if a Task is already complete (which it will be when using Task.FromResult or using an async method with no awaits) then it just executes the next code directly.

@jchannon
Copy link
Member

OK cool

On 25 January 2016 at 12:12, Steven Robbins [email protected]
wrote:

@jchannon https://github.com/jchannon : no, that's where the
optimisation comes in (previously hand rolled, now using async/await), if a
Task is already complete (which it will be when using Task.FromResult or
using an async method with no awaits) then it just executes the next code
directly.


Reply to this email directly or view it on GitHub
#2199 (comment).

jchannon added a commit that referenced this pull request Jan 25, 2016
@jchannon jchannon merged commit 31627ea into NancyFx:master Jan 25, 2016
@damianh
Copy link
Member

damianh commented Jan 31, 2016

taskResponse.Result

I think @khellang mentioned taskRepsonse.GetAwaiter().GetResult(); is better if you don't want AggregateException http://stackoverflow.com/a/17284612

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants