Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Mr Shiny Pants
Nov 12, 2012

Sab669 posted:

Dumb Question: What exactly does Microsoft's Reference Source site show me?

I wanted to do some research for a question on stack overflow pertaining to an issue with the ZipArchive object, which exists in System.IO.Compression as stated here: https://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive(v=vs.110).aspx


If I pull up the online source reference: https://referencesource.microsoft.com/#q=ZipArchive

And search for "ZipArchive" I get one Class result with a different namespace and different members. Like MSDN lists a constructor which takes a Stream object but the Source site does not define any constructors, for example.

Clearly I must be misunderstanding something, but I don't know what. I very rarely use the online source reference so I'm not very familiar with it.

Where (if at all) can I find the source code for the object as defined on MSDN?

This one?

code:
/// <summary>
        ///  Static constructor Stream based constructor. all parameteres are obvious. 
        ///  This constructor wil not do any futher parsing.
        ///
        /// In the case of Create/CreateNew, it will also prebuild in cached in-memory 
        /// EndOfCentralDirectoryRecord (which is a minimal zip file requirement), so that if user 
        /// chooses to close right after he will get a file with just EndOfCentralDirectoryRecord.
        /// </summary>
        internal static ZipArchive OpenOnStream(Stream stream, FileMode mode, FileAccess access, bool streaming)

Adbot
ADBOT LOVES YOU

Sab669
Sep 24, 2009

I did see that but I didn't think it was the same thing. The constructor defined on MSDN I'm referring to only takes 1 parameter, whereas that defines 4.

So if you were to write:

var x = new ZipArchive(myStreamObj)

I don't really understand how that would end up calling the static OpenOnStream method. There's also a definition for private ZipArchive() which takes the same 4 args plus an additional bool.

But ok, then where is the implementation for the Entries property or CreateEntry method?

If internal static ZipArchive OpenOnStream is a constructor, then what is private ZipArchive? Why does the former include a return type as well as the name whereas the later only has the return type?

The private ZipArchive method is what I'm "used to" for a constructor; you give it a scope and it returns an instance of itself. Why does the former have scope, return type and a name if it's a constrictor?

Sab669 fucked around with this message at 17:44 on Jun 1, 2017

dougdrums
Feb 25, 2005
CLIENT REQUESTED ELECTRONIC FUNDING RECEIPT (FUNDS NOW)
That is in the namespace MS.Internal.IO.Zip

I have no idea why the ones in MSDN (System.IO.Compression) aren't in there though.

Sedro
Dec 31, 2008
The .NET Core sources should be close enough

https://github.com/dotnet/corefx/blob/master/src/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs

EssOEss
Oct 23, 2006
128-bit approved
I have always considered Reference Source to be a bit of an approximation. Interestingly enough, so is decompilation! There are methods that I have decompiled from .NET Framework assemblies themselves that end up differing in their runtime behavior, compared to the decompiled code. I can only gueess that there might be native-code variants of such functions switched in at runtime (or the decompiler in Reflector might just be buggy, I suppose?). For example, DateTimeOffset.ToUnixTimeMilliseconds() has a constant there to define the epoch. The constant visible in decompiled code is different from what is really used in practice. Thew me for a loop when I noticed my numbers were off!

CapnAndy
Feb 27, 2004

Some teeth long for ripping, gleaming wet from black dog gums. So you keep your eyes closed at the end. You don't want to see such a mouth up close. before the bite, before its oblivion in the goring of your soft parts, the speckled lips will curl back in a whinny of excitement. You just know it.
So LINQ to SQL can handle DateTime.AddHours just fine, but only AddHours I guess, because it throws errors on AddMinutes? I mean, fine, it's a one-line workaround. But what the gently caress? Why so oddly specific on only allowing AddHours?

redleader
Aug 18, 2005

Engage according to operational parameters
I've run into an weird/annoying problem trying to get a Winforms app working how (I think) it should.

I'm trying to write a Winforms program to run as a TortoiseSVN client-side post-commit hook. I've created a new Winforms project using the default project template. This runs just fine from VS, and shows the (empty, completely untouched Form1) as you'd expect.

If I set this same executable as a TortoiseSVN post-commit hook, the form simply doesn't show up. The program runs - I can see it in Task Manager (and have to kill it manually) - but the main form just doesn't display.

However, if I then add a call to MesageBox.Show() before Application.Run() and run the program as a post-commit hook, the message box pops up, then the main form displays.

Does anyone have any idea why showing the message box causes the form to show properly, or what I need to change to get the form to display when called from TortoiseSVN?

SirViver
Oct 22, 2008
This may be a stupid question, but did you make sure to have the "Hide the script while running" hook script option turned off? That's usually there to prevent the command window from popping up when executing a batch file or console application, but could also affect your WinForm. Showing the MessageBox might simply interfere with TortoiseSVN's attempt to hide the main process window, or reset some flag that caused it to be hidden before.

redleader
Aug 18, 2005

Engage according to operational parameters

SirViver posted:

This may be a stupid question, but did you make sure to have the "Hide the script while running" hook script option turned off? That's usually there to prevent the command window from popping up when executing a batch file or console application, but could also affect your WinForm. Showing the MessageBox might simply interfere with TortoiseSVN's attempt to hide the main process window, or reset some flag that caused it to be hidden before.

TortoiseSVN hangs with "hide script while running" turned on, presumably because it's waiting for the (invisible?) form to close. It works fine with the message box.

e: haha, gently caress. Figured it out - I needed to uncheck "hide this script". Annoyingly, I have another hook that uses Winforms that needs this option selected. God, how dumb.

redleader fucked around with this message at 22:33 on Jun 5, 2017

redleader
Aug 18, 2005

Engage according to operational parameters
So I asked another dev how their Winforms-based commit hook works. Turns out they hack it by having a timer call Form.Show() basically as soon as the program starts. Amazing. Beautiful. Elegant.

I suspect that it might have something to do with the way TSVN redirects standard input/output/error combined with some wonderful legacy behavior, but can't prove it - and I don't the time, inclination, knowledge, or energy to investigate further.

hailthefish
Oct 24, 2010

redleader posted:

some wonderful legacy behavior I don't the time, inclination, knowledge, or energy to investigate further.

Shame that's too long for a thread title.

tays revenge
Aug 29, 2009

redleader posted:

but can't prove it - and I don't the time, inclination, knowledge, or energy to investigate further.

This speaks directly to my soul.

Duct Tape
Sep 30, 2004

Huh?
Have an async question. When returning a value that doesn't require a long running operation (e.g. something local or in a cache or whatever), should I return the value directly or wrap it in an "await Task.FromResult"?

Best I can tell is that the IL for these two examples are exactly the same. Is this code just trivial enough for the compiler to be taking shortcuts, or is there really no difference between the two?

Directly
code:
public async Task<int> GetIntAsync(bool isLocal)
{
    if (isLocal)
    {
        return 10;
    }
    return await LongRunning.Operation();
}
await Task.FromResult
code:
public async Task<int> GetIntAsync(bool isLocal)
{
    if (isLocal)
    {
        return await Task.FromResult(10);
    }
    return await LongRunning.Operation();
}

EssOEss
Oct 23, 2006
128-bit approved

Duct Tape posted:

When returning a value that doesn't require a long running operation (e.g. something local or in a cache or whatever), should I return the value directly

Yes because why overcomplicate matters by adding some pointless level of indirection that may or may not get compiled away as the compiler goes "wtf bro".

Duct Tape
Sep 30, 2004

Huh?

EssOEss posted:

Yes because why overcomplicate matters by adding some pointless level of indirection that may or may not get compiled away as the compiler goes "wtf bro".

Yeah, this is my stance as well. I was just worried that I was unintentionally breaking the state machine for more-complex methods by not wrapping trivial results in Task.FromResult. Doesn't sound like that's the case, though.

Pixelboy
Sep 13, 2005

Now, I know what you're thinking...

Duct Tape posted:

code:
        return await Task.FromResult(10);

This just adds unnecessary complexity, and makes things harder to read.

Essential
Aug 14, 2003
I need to upgrade a dll from 4.0 framework to 4.6, however I have to keep backwards compatibility for 4.0. From what I'm reading this should work if I only use 4.0 stuff on the 4.0 machines. Can anyone confirm this? I don't need 4.6 installed on those machines to continue to use 4.0 features correct? The dll will load without the new framework installed?

So the dll moving forward will target 4.6. And I will put that dll on machines which only have 4.0 installed. Someone please tell me this will work!

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Essential posted:

I need to upgrade a dll from 4.0 framework to 4.6, however I have to keep backwards compatibility for 4.0. From what I'm reading this should work if I only use 4.0 stuff on the 4.0 machines. Can anyone confirm this? I don't need 4.6 installed on those machines to continue to use 4.0 features correct? The dll will load without the new framework installed?

So the dll moving forward will target 4.6. And I will put that dll on machines which only have 4.0 installed. Someone please tell me this will work!

I guess it might work, because the frameworks from 4.0 onwards were in-place upgrades, but this is a terrible idea and you are very likely to regret it.

You have a number of much safer options available, and if you give more details we could suggest the right one. Probably the easiest solution involving the least amount of work would be to split off the 4.6-exclusive features into their own library (which references the 4.0 library), and only install that additional library on the machines that can support it.

Essential
Aug 14, 2003

NihilCredo posted:

I guess it might work, because the frameworks from 4.0 onwards were in-place upgrades, but this is a terrible idea and you are very likely to regret it.

You have a number of much safer options available, and if you give more details we could suggest the right one. Probably the easiest solution involving the least amount of work would be to split off the 4.6-exclusive features into their own library (which references the 4.0 library), and only install that additional library on the machines that can support it.

Basically we have software that targets 4.0 distributed all across the country, so I don't have physical access to them. Moving forward, for async/await features (among other things) we need to upgrade to 4.6. We can't use the 4.0 async BCL library because of a few issues. The whole system is built on the same update framework, so as soon as we publish the 4.6 dll all the distributed systems will get it. I was hoping to avoid having 2 different frameworks to maintain.

Hopefully that explains it enough, I can provide more details if necessary.

GoodCleanFun
Jan 28, 2004

Essential posted:

I need to upgrade a dll from 4.0 framework to 4.6, however I have to keep backwards compatibility for 4.0. From what I'm reading this should work if I only use 4.0 stuff on the 4.0 machines. Can anyone confirm this? I don't need 4.6 installed on those machines to continue to use 4.0 features correct? The dll will load without the new framework installed?

So the dll moving forward will target 4.6. And I will put that dll on machines which only have 4.0 installed. Someone please tell me this will work!

If you move that library to 4.6 any machine without 4.6 will error on running the app even if you don't use features beyond the version installed.

I haven't done any serious async stuff yet so not sure if this would work, but could you build new async lib in 4.6 that includes/wraps the existing lib and then extends it? It seems this is what NihilCredo is suggesting as well.

Essential
Aug 14, 2003

GoodCleanFun posted:

If you move that library to 4.6 any machine without 4.6 will error on running the app even if you don't use features beyond the version installed.

I haven't done any serious async stuff yet so not sure if this would work, but could you build new async lib in 4.6 that includes/wraps the existing lib and then extends it? It seems this is what NihilCredo is suggesting as well.

Got it. I thought what I was reading told me otherwise, but makes sense.

Yeah I think extending the library and/or wrapping it is an option. I'm not quite sure how to go about this though, would appreciate any feedback anyone has.

Red Mike
Jul 11, 2011

Duct Tape posted:

Yeah, this is my stance as well. I was just worried that I was unintentionally breaking the state machine for more-complex methods by not wrapping trivial results in Task.FromResult. Doesn't sound like that's the case, though.

It will make it throw warnings though, which is mildly annoying. In most cases I've run into (return null because method shouldn't do anything, or because conditions aren't met, etc) since it's always the same value, I've used Task.FromResult. The created Task is reused so there's no downside other than readability. However, if you're not using await and are getting a Task from somewhere (including Task.FromResult), you can just remove the async modifier and return the Task directly without awaiting it.

So:

code:
Task<Thing> GetThingAsync()
{
  return GetThingAsync_Internal();
    or
  return Task.FromResult<object>(null);
}
Supposedly it has some effect on where exceptions would be thrown, but I've yet to run into a case where it actually matters myself.

Nevett
Aug 31, 2001

Duct Tape posted:

Have an async question. When returning a value that doesn't require a long running operation (e.g. something local or in a cache or whatever), should I return the value directly or wrap it in an "await Task.FromResult"?

The method doesn't need to be async since all it does is decide which task to return. You can simplify it like this:

code:
public Task<int> GetIntAsync(bool isLocal)
{
    if (isLocal)
    {
        return Task.FromResult(10);
    }
    return LongRunning.Operation();
}
This small change makes a big difference to the resulting IL since it doesn't need to build all the crazy async state machine stuff around the method.

Of course, you can't do this if you wanted to do something in this method with the result of the long running operation before returning it to the caller.


Edit: yeah what he said ^^

Night Shade
Jan 13, 2013

Old School

Red Mike posted:

Supposedly it has some effect on where exceptions would be thrown, but I've yet to run into a case where it actually matters myself.

It makes exceptions throw immediately, instead of faulting the returned Task. This normally isn't a huge deal if all you're doing is
code:
try {
  await thing();
} catch (...) {}
but if you're doing something like using .Select() to turn a list of urls into a list of parallel downloads, having exceptions propagate immediately will interrupt iterating the list. This prevents assigning the result of .Select(), which will leave you with orphaned tasks that iirc will crash your app if they fault and then get collected.

chippy
Aug 16, 2006

OK I DON'T GET IT
I'm getting some loving weird behaviour with Fluent Validation and MVC I hope you guys can help with.

I've got a very simple validator with only 5 rules:

code:
            RuleFor(x => x.ServiceId).Must(x => x.HasValue).WithMessage("Please select a service.");
            RuleFor(x => x.Animals).Must(x => x.Count > 0).WithMessage("Please select at least one animal.");
            RuleFor(x => x.StartTime).NotNull().WithMessage("Start time is required.");
            RuleFor(x => x.FinishTime).NotNull().WithMessage("Finish time is required.");
            RuleFor(x => x.StartDate).NotNull().WithMessage("Start date is required.");
The problem I'm having is that if the validation on any of the DateTime fields fails, then ServiceId and Animals don't get validated - specifically, ServiceId is listed in the ModelState with no errors, and Animal isn't listed at all. So if I just submit the form empty, I get it back with validation errors given only on those fields. However, once I fill in the date and times, the other two rules kick in and take effect.

Also, in the view, the validation messages for the date and times have the "text-danger" class, while the ones generated for ServiceId and Animals don't.

Clearly, those 2 sets of rules are being processed differently somehow, seemingly in 2 different passes. I presumed that the date and time fields were getting validated client side, and so the other rules weren't getting processed if they failed - however, in either case, the POST does actually hit the controller action before being validated and returned, which I believe doesn't happen if client side fails, as it prevents the POST from occuring? Also, I tried disabling client-slide validation completely, and saw the same behaviour.

Probably also worth mentioning that I was originally using NotNull for ServiceId (which did cause client side validation to occur), and NotEmpty for Animals, which gave the same behaviour, before I switched them to using Must().

Does anyone have any insight as to what's going on here?

e: I just changed them all to 'must' rules and got the exact same behaviour. What the actual gently caress.

chippy fucked around with this message at 21:24 on Jun 7, 2017

Red Mike
Jul 11, 2011

Night Shade posted:

It makes exceptions throw immediately, instead of faulting the returned Task. This normally isn't a huge deal if all you're doing is
code:
try {
  await thing();
} catch (...) {}
but if you're doing something like using .Select() to turn a list of urls into a list of parallel downloads, having exceptions propagate immediately will interrupt iterating the list. This prevents assigning the result of .Select(), which will leave you with orphaned tasks that iirc will crash your app if they fault and then get collected.

Yeah, except in all the practical cases I've seen during reviews, it wouldn't have mattered anyway because the code itself was badly designed and had major flaws besides this. The situation you mention would have been replaced at code review stage in all the codebases I've worked with, because you don't just turn a list of URLs into a list of parallel downloads like that, you call an async method of your own that does its own try/catching (and probably other functionality).

I'm sure there must be cases where it is a concern, which is why I mentioned it in the first place.

Also, in the 'orphaned tasks' situation, you normally just hit the default uncaught exception handler. Wouldn't crash your app, same as throwing in an async void wouldn't.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



Maybe it's because it's the end of the day and my brain is fried but I'm not seeing why a WebApi controller I added as a quick and dirty proxy for our weather data provider isn't getting routed:
C# code:
[RoutePrefix("weather")]
public class WeatherController : ApiController {
    [Route("{*url}"), AcceptVerbs("GET")]
    public async Task<HttpResponseMessage> Get(string url) {
I did add config.MapHttpAttributeRoutes(); to the top of Register in WebApiConfig.cs, so it's not that. It should be responding on localhost:1234/weather/ right?

Night Shade
Jan 13, 2013

Old School
Setting the contrivance of .Select(DownloadParallel) aside, if you're kicking off parallel Tasks at some point in the call chain you need to not be awaiting them immediately regardless of how smart the Task-returning method is. This makes the difference in exception propagation significant. e: because i can trust that an async method won't throw until the Task is observed and is therefore safe to pass directly to .Select / Seq.map / whatever. I can't say the same about a non-async method that happens to return Task.

Red Mike posted:

Also, in the 'orphaned tasks' situation, you normally just hit the default uncaught exception handler. Wouldn't crash your app, same as throwing in an async void wouldn't.

https://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception(v=vs.110).aspx posted:

This event provides notification of uncaught exceptions. It allows the application to log information about the exception before the system default handler reports the exception to the user and terminates the application.

:colbert:

Night Shade fucked around with this message at 05:14 on Jun 8, 2017

Red Mike
Jul 11, 2011

Night Shade posted:

Setting the contrivance of .Select(DownloadParallel) aside, if you're kicking off parallel Tasks at some point in the call chain you need to not be awaiting them immediately regardless of how smart the Task-returning method is. This makes the difference in exception propagation significant. e: because i can trust that an async method won't throw until the Task is observed and is therefore safe to pass directly to .Select / Seq.map / whatever. I can't say the same about a non-async method that happens to return Task.

That's fair enough, I guess. I've still yet to run into this case in the wild, so you can just mentally remove 'Supposedly' from my first post.

quote:

:colbert:

Huh, I stand corrected. I'm not sure what ASP.NET and the two other server techs we've used do then, because we've got those events for safety/logging, but uncaught exceptions never cause issues (unless they're actual hard faults).

e: And similarly in Unity on the client-side, actually, but that doesn't have async/await anyway.

Night Shade
Jan 13, 2013

Old School

Red Mike posted:

Huh, I stand corrected. I'm not sure what ASP.NET and the two other server techs we've used do then, because we've got those events for safety/logging, but uncaught exceptions never cause issues (unless they're actual hard faults).

e: And similarly in Unity on the client-side, actually, but that doesn't have async/await anyway.
ASP.Net and Unity probably use a different default handler. No idea about what Unity's would do, but ASP.Net app pools probably just let the web application app domain die and start a fresh one next request.

chippy
Aug 16, 2006

OK I DON'T GET IT

chippy posted:

I'm getting some loving weird behaviour with Fluent Validation and MVC I hope you guys can help with.

I've got a very simple validator with only 5 rules:

code:
            RuleFor(x => x.ServiceId).Must(x => x.HasValue).WithMessage("Please select a service.");
            RuleFor(x => x.Animals).Must(x => x.Count > 0).WithMessage("Please select at least one animal.");
            RuleFor(x => x.StartTime).NotNull().WithMessage("Start time is required.");
            RuleFor(x => x.FinishTime).NotNull().WithMessage("Finish time is required.");
            RuleFor(x => x.StartDate).NotNull().WithMessage("Start date is required.");
The problem I'm having is that if the validation on any of the DateTime fields fails, then ServiceId and Animals don't get validated - specifically, ServiceId is listed in the ModelState with no errors, and Animal isn't listed at all. So if I just submit the form empty, I get it back with validation errors given only on those fields. However, once I fill in the date and times, the other two rules kick in and take effect.

Also, in the view, the validation messages for the date and times have the "text-danger" class, while the ones generated for ServiceId and Animals don't.

Clearly, those 2 sets of rules are being processed differently somehow, seemingly in 2 different passes. I presumed that the date and time fields were getting validated client side, and so the other rules weren't getting processed if they failed - however, in either case, the POST does actually hit the controller action before being validated and returned, which I believe doesn't happen if client side fails, as it prevents the POST from occuring? Also, I tried disabling client-slide validation completely, and saw the same behaviour.

Probably also worth mentioning that I was originally using NotNull for ServiceId (which did cause client side validation to occur), and NotEmpty for Animals, which gave the same behaviour, before I switched them to using Must().

Does anyone have any insight as to what's going on here?

e: I just changed them all to 'must' rules and got the exact same behaviour. What the actual gently caress.

If anyone's interested, I think these two posts may hold the answers. The first one even has the same tone of confused frustration as I do.

https://github.com/JeremySkinner/FluentValidation/issues/127
https://github.com/JeremySkinner/FluentValidation/issues/130

ljw1004
Jan 18, 2005

rum

Red Mike posted:

Also, in the 'orphaned tasks' situation, you normally just hit the default uncaught exception handler. Wouldn't crash your app, same as throwing in an async void wouldn't.

I don't think that's right...

Up to VS2012 (.NET4), a task that ended in the "failed" state and which was never observed (i.e. no one ever did .ContinueWith on it) would raise the global unhandled exception handler.

But as of VS2012 (.NET4.5), once async/await was introduced, that behavior didn't make sense. So we removed it. We specifically wanted to support patterns like this, which never signs up anything for after JonesAsync.
code:
try {
  var task1 = FredAsync();
  var task2 = JonesAsync(); // kick them both off at the same time
  await task1; // but imagine if this throws!!!
  await task2;
} catch (Exception e) {
  Console.WriteLine("oops"); // at this stage JonesAsync has never been observed
}
That's entirely unrelated to the question of exceptions thrown inside async void....

code:
async void Button1Handler() {
  await Task.Delay(1);
  throw new Exception("oops");
}
An exception here will cause the exception to be rethrown inside the current SynchronizationContext: https://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs,223b5f320d791728

What does that do? It varies. I remember trying it inside WPF, Winforms and UWP (each have their own slightly different synchronization context). I remember that there were different behaviors between the three -- ignored? pop up an error dialog? terminate the program? -- but I can't remember which. I think they all involve the unhandled exception filter first.

What does it do when there's no synchronization context, i.e. it hits the threadpool? I think that goes down the unhandled exception filter route and then terminates the program, but can't remember.

What does it do in the ASP.NET synchronization context? I don't know.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



Munkeymon posted:

Maybe it's because it's the end of the day and my brain is fried but I'm not seeing why a WebApi controller I added as a quick and dirty proxy for our weather data provider isn't getting routed:
C# code:
[RoutePrefix("weather")]
public class WeatherController : ApiController {
    [Route("{*url}"), AcceptVerbs("GET")]
    public async Task<HttpResponseMessage> Get(string url) {
I did add config.MapHttpAttributeRoutes(); to the top of Register in WebApiConfig.cs, so it's not that. It should be responding on localhost:1234/weather/ right?

The problem was that when I pasted the API URL into Postman, it 'helpfully' prepended an extra [url]http://[/url] that I didn't notice until this morning :v:

ASP still won't route something that looks like a number to {*url} though. I hate the way ASP does routing :argh:

e: Really anything in .Net that's run-it-and-see-if-it-works is maddening because that's what a compiler is supposed to be for

Munkeymon fucked around with this message at 15:33 on Jun 8, 2017

SirViver
Oct 22, 2008

Munkeymon posted:

ASP still won't route something that looks like a number to {*url} though. I hate the way ASP does routing :argh:
Also quite fun if you want to route something that may have a "." in the URL content.

For example, if your route looks like api/myroute/{part1}/{part2}, with part1+2 being string variables, you get:
  • api/myroute/hello.world/foo
    Works as expected.

  • api/myroute/hello.world/foo.bar
    IIS static file handler intercepts the request (with a 404 response), because clearly you wanted a file due to the dot being there.
    Can be circumvented by modifying the web.config and adding another ExtensionlessUrlHandler with different path filter to the system.webServer handlers section.

  • api/myroute/hello.world.../foo.bar
    You're essentially hosed. The request goes through (assuming you have aforementioned web.config modification), but part1 will be "hello.world" with the trailing dots magically trimmed. Better hope these were not significant parts of an identifier or anything.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



SirViver posted:

Also quite fun if you want to route something that may have a "." in the URL content.

For example, if your route looks like api/myroute/{part1}/{part2}, with part1+2 being string variables, you get:
  • api/myroute/hello.world/foo
    Works as expected.

  • api/myroute/hello.world/foo.bar
    IIS static file handler intercepts the request (with a 404 response), because clearly you wanted a file due to the dot being there.
    Can be circumvented by modifying the web.config and adding another ExtensionlessUrlHandler with different path filter to the system.webServer handlers section.

  • api/myroute/hello.world.../foo.bar
    You're essentially hosed. The request goes through (assuming you have aforementioned web.config modification), but part1 will be "hello.world" with the trailing dots magically trimmed. Better hope these were not significant parts of an identifier or anything.

Tinkering with it, it appears to be the second one. I'm just going to make a slightly smarter proxy, I guess, because it's just not loving worth it to monkey with the whole site for this.

Red Mike
Jul 11, 2011

ljw1004 posted:

I don't think that's right...

Up to VS2012 (.NET4), a task that ended in the "failed" state and which was never observed (i.e. no one ever did .ContinueWith on it) would raise the global unhandled exception handler.

But as of VS2012 (.NET4.5), once async/await was introduced, that behavior didn't make sense. So we removed it. We specifically wanted to support patterns like this, which never signs up anything for after JonesAsync.
code:
try {
  var task1 = FredAsync();
  var task2 = JonesAsync(); // kick them both off at the same time
  await task1; // but imagine if this throws!!!
  await task2;
} catch (Exception e) {
  Console.WriteLine("oops"); // at this stage JonesAsync has never been observed
}
That's entirely unrelated to the question of exceptions thrown inside async void....

code:
async void Button1Handler() {
  await Task.Delay(1);
  throw new Exception("oops");
}
An exception here will cause the exception to be rethrown inside the current SynchronizationContext: https://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs,223b5f320d791728

What does that do? It varies. I remember trying it inside WPF, Winforms and UWP (each have their own slightly different synchronization context). I remember that there were different behaviors between the three -- ignored? pop up an error dialog? terminate the program? -- but I can't remember which. I think they all involve the unhandled exception filter first.

What does it do when there's no synchronization context, i.e. it hits the threadpool? I think that goes down the unhandled exception filter route and then terminates the program, but can't remember.

What does it do in the ASP.NET synchronization context? I don't know.

This is really good info, thanks. I'll forward it to my team and check with the server software we use so we can figure out what they do (if anything) so we're at least aware of what could happen. If nothing else, this entire exchange has convinced me that I should pay more attention to this and maybe add specifics to the guidelines instead of relying just on the code reviewer catching it.

Re: the initial bit though, does that mean that unobserved faulted tasks have no ill effects, so the original situation with the Select wouldn't have any issues (other than the exception being thrown where you might not expect)?

Night Shade
Jan 13, 2013

Old School

ljw1004 posted:

Up to VS2012 (.NET4), a task that ended in the "failed" state and which was never observed (i.e. no one ever did .ContinueWith on it) would raise the global unhandled exception handler.

But as of VS2012 (.NET4.5), once async/await was introduced, that behavior didn't make sense. So we removed it. We specifically wanted to support patterns like this, which never signs up anything for after JonesAsync.

Ah cool. I've been operating under the belief the .NET 4 behaviour was still the current behaviour. Cunningham's Law in action

Red Mike posted:

Re: the initial bit though, does that mean that unobserved faulted tasks have no ill effects, so the original situation with the Select wouldn't have any issues (other than the exception being thrown where you might not expect)?

Seems that way. That's a relief. You're still left with something blowing up Select itself instead of blowing up when the resulting Tasks are awaited, but that's something you can handle in code, not something that will kill your app at some indeterminate point in the future.


e: actually if Select blows up you may well be left with work happening inside Tasks that you no longer have any way of dealing with unless you've got a CancellationTokenSource you can use to abort them.

Night Shade fucked around with this message at 23:11 on Jun 8, 2017

Red Mike
Jul 11, 2011

Night Shade posted:

e: actually if Select blows up you may well be left with work happening inside Tasks that you no longer have any way of dealing with unless you've got a CancellationTokenSource you can use to abort them.

Honestly, edge cases like this are part of the reason why I said code like this wouldn't make it past review at my workplace. If you just wrap whatever you're calling into a task of your own with proper error handling then suddenly it's a null point because you've fixed both the exception case and the fact that the exception isn't thrown in the place you'd expect. And one of the guidelines already is "expect any async method to later turn into a sync one with no work done other than removing the await", so yeah.

I do now feel like this is yet another gotcha with regards to async/await that isn't highlighted enough even as an edge case.

itskage
Aug 26, 2003


What's up with C#'s list constructor returning void?

code:
var things = new List<ThingType>().Add(thing);

quote:

Cannot implicitly convert type 'void' to 'System.Collections.Generic.List<ThingType>'

Is it just a dumb pattern to try and initialize it and add something at the same time without using an initializer? It's not like I can pass something into the constructor.

Is this working pattern better? If so, why?
code:
var things = new List<ThingType>();
things.Add(thing);

Adbot
ADBOT LOVES YOU

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

itskage posted:

What's up with C#'s list constructor returning void?

code:
var things = new List<ThingType>().Add(thing);
Is it just a dumb pattern to try and initialize it and add something at the same time without using an initializer? It's not like I can pass something into the constructor.

Is this working pattern better? If so, why?
code:
var things = new List<ThingType>();
things.Add(thing);

You're trying to capture the return of the Add method, which is void.

Just do new List<ThingType> { thing };

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply