|
Disclaimer: I am a VB/F# first developer that only recently started writing C# in anger:Surprise T Rex posted:Are there any good resources on how people tend to use things like Records/Record Structs, Pattern Matching, and other newer language features in the real world? I always use records instead of POCOs unless some specific library I am using doesn't like them. Unless I want reference equality for some reason, I will also use records for what I call "DI funnels", i.e. classes that take in a bunch of arguments in the constructors, stick them as-is in read-only fields, and then only expose one or more pure methods that do not mutate the fields in any way. Works the same as a standard class, takes fewer lines. I never use tuples to tie data together, I use tuples to return multiple values, deprecating the hack that is out parameters. Eg. instead of "SomeFlag SomeFunction(arg, out ActualContent? out)" I will just make SomeFunction return a tuple of (SomeFlag, ActualContent?). (Note: this is what F# does automatically, if you call int32.Parse from F# it gets automatically sugared to return a (bool, Option<int>) tuple). For local datatype definitions, anonymous records are fine if and only if they're local to a single function scope. If the scope is more than one function or longer than a dozen lines, it gets promoted to a private nested record type. Pattern matching is kind of a "depends" - a lot o f times it makes the dispatching look really nice, sometimes it just hides some assumptions. For example, just yesterday a coworker showed me this code: C# code:
|
# ? Jun 14, 2023 12:30 |
|
|
# ? May 18, 2024 06:42 |
|
I think that's generally the one place pattern matching really breaks down, if you're using nullable types. If you have "thing" not be nullable, and have "fetchSome" throw (or return a Result<T> or whatever) instead of just returning null, then pattern matching will generally do what you expect it to.Kyte posted:
Personally I don't mind tuples even in production code as long as they're named, but I really dislike code that is built to use them as basically half-baked collections; that's not what tuples are. Same with anonymous types, although those are at least much more difficult to try to use like this (but not impossible as proven by code I've run into before). You're basically building new boilerplate logic on top of code that has particular behaviours, in order to take advantage of one minor part of its behaviour (tuple deconstruction). Worse, you usually end up not even able to take full advantage of it, because for example in this code above: it's trivial for someone to change the order of two of the lines in the task definition, then not change the order of the results in the awaiting line. If the two changed lines return the same type (like say a string), you won't even notice. What I tend to use tuples for is basically as a replacement for a struct/record while writing small bits of code that doesn't tend to move across layers of the application. I don't generally like returning tuples from public methods (and never from a library), but even returning from a public method is acceptable if the number of values is well defined and it's a one-off/rarely used request (e.g. you have a service that retrieves a bit of data, or the children of a bit of data, or both; if "both" is something that's only used in a minor place like a debug method/analytics tracker/etc, then I have no issue using a tuple instead of an actual type). e: I also use tuples to avoid out parameters...because of all the places where out parameters don't work anyway. But again if it's a public method/library method, then I'll use a type unless it's a one-off/minor request that I don't care enough about. Usually even then I'll only use a tuple if it's a compound version of existing types.
|
# ? Jun 14, 2023 13:03 |
|
NihilCredo posted:
I might be missing something, but that code block will throw a NullReferenceException if thing is null. A property pattern only matches an expression if the expression result is not null.
|
# ? Jun 14, 2023 13:34 |
|
No Pants posted:I might be missing something, but that code block will throw a NullReferenceException if thing is null. A property pattern only matches an expression if the expression result is not null. Possibly, I'm on vacation and just going by what my coworker posted on Slack (where he said the guard clause triggered).
|
# ? Jun 14, 2023 13:52 |
|
Property patterns won't throw if it's null, it'll just early quit and act as if it's not a match. quote:A property pattern checks that the input value is not null and recursively matches values extracted by the use of accessible properties or fields.
|
# ? Jun 14, 2023 14:19 |
|
Red Mike posted:Property patterns won't throw if it's null, it'll just early quit and act as if it's not a match. Yeah, that's what I posted. It'll throw an exception when it gets to DoSomething(thing.SomeProperty);
|
# ? Jun 14, 2023 20:06 |
|
Oh sorry, I misread and thought you meant the property match would throw. Yeah, this is the general pattern where I'd expect thing to be a non-nullable, and then the entire thing does make sense.
|
# ? Jun 14, 2023 20:08 |
|
Potassium Problems posted:I want to say that returning a type that inherits from ObjectResult will ensure you don't have to materialize the list & will stream it to the client, but I don't know if that's true or not and I can't seem to find any documentation on it Ok, so is there any practical difference between these two? Returning IActionResult is less explicit, because return Ok(); and return Ok(true); and return Ok(new List<Apple>() { new Apple() }); will all compile fine, but at runtime my endpoint could return basically anything. I guess that's desirable sometimes, though? C# code:
C# code:
epswing fucked around with this message at 15:26 on Jun 15, 2023 |
# ? Jun 15, 2023 14:50 |
|
I think IActionResult simply predates ActionResult<T>. Before ASP.NET Core... 2 or 3 or something, returning IActionResult was the only option if you had a code path returning Ok(apples) and another returning NotFound("missing tree"). That said, all it really does is saving you from maintaining a [Produces(typeof(IEnumerable<Apple>)] attribute, for the benefit of OpenAPI generators and the like. Anything that's wrapped inside an ObjectResult still bypasses typechecking: C# code:
|
# ? Jun 15, 2023 15:31 |
|
NihilCredo posted:I think IActionResult simply predates ActionResult<T>. Before ASP.NET Core... 2 or 3 or something, returning IActionResult was the only option if you had a code path returning Ok(apples) and another returning NotFound("missing tree"). Ok, makes sense. Is it considered bad form for different code paths to return different types? This honestly makes my skin crawl, for some reason. C# code:
|
# ? Jun 15, 2023 15:39 |
|
Huh, I've always just returned an IActionResult and never really thought about it before.
|
# ? Jun 15, 2023 15:45 |
|
epswing posted:Ok, makes sense. Is it considered bad form for different code paths to return different types? It's definitely both OK and idiomatic for HTTP endpoint functions specifically. It is the whole point of HTTP status codes, after all. Some error codes will be handled implicitly (eg. 401/403 for missing authn/authz, 400 for mangled JSON), but not always, and some others will typically have to be generated by your own code (404 most commonly, or 400 for formally-correct-but-still-invalid input). For regular C# functions, returning different types is suboptimal. Sometimes it makes sense to return different implementations of the base class / interface indicated in your signature, if the consumer is able to meaningfully work with the base class / interface alone. A classic example is returning IEnumerable, but if you know that the number of items in your source is small and there are no side effects, you just return a List instead of a lazy enumerator, because it's going to be more efficient. However, if you're returning a base class / interface and then expect the consumer to have to run a type test to figure out what to do with it, that's a bad pattern and you should look for a different approach. The optimal solution would be union types, but C# doesn't really have them, unless you use the LanguageExt library which is admirable but still a huge hack.
|
# ? Jun 15, 2023 16:02 |
|
Red Mike posted:Personally I don't mind tuples even in production code as long as they're named, but I really dislike code that is built to use them as basically half-baked collections; that's not what tuples are. Same with anonymous types, although those are at least much more difficult to try to use like this (but not impossible as proven by code I've run into before). I just didn't want to have a big block of saving task to variables followed with another big block of awaiting all the tasks. It's just a waste of screen space in a function that was busy doing business things. Honestly I don't care for tuples at all outside of deconstruction and I use the gently caress out of that. No collection fulfills that need. Especially not when they're all different types. Granted, a tuple of, say, (string, string) can be sus but those would be labeled in first place since the type isn't telling me anything.
|
# ? Jun 23, 2023 18:41 |
|
Question about generic classes and constructors. I have this (abbreviated) class: C# code:
I can work around it with factory methods, but this is definitely unexpected behavior (to me, anyway). Thoughts?
|
# ? Jul 5, 2023 16:50 |
|
You don't need factory methods, you can just use named arguments to bypass the default overload resolution rules:C# code:
|
# ? Jul 5, 2023 17:21 |
|
And, incidentally, don't design your ServiceResult class to have both a singular Result property and an separate multi-value Results property. The methods you're calling that will be returning this object will know if they're going to be returning a single value or multiple values. Let them handle it by returning ModelResult<IList<ModelType>, long> instead of making a lot of annoying confusion for developers who will work on your codebase in the years to come as to whether they should be looking at Result or Results in every single result object they ever interact with. Also, since I've seen this anti-pattern often enough to have an idea of what it might be used for: don't use ServiceResult objects internally between C# layers of your application. They're fine to shape Web API contract output, but a lot of developers insist on bringing over development practices from worse languages or have a massively misplaced fear of using exceptions for error reporting and end up making a huge mess of their internal codebases by building these sorts of bespoke result types rather than just returning the actual value you actually wanted from methods and using exceptions for failures.
|
# ? Jul 5, 2023 22:32 |
|
I can’t find it but I’ll edit in the emote for the guy pointing and nodding later.
|
# ? Jul 5, 2023 22:46 |
|
epswing posted:I can’t find it but I’ll edit in the emote for the guy pointing and nodding later.
|
# ? Jul 5, 2023 23:17 |
|
NihilCredo posted:You don't need factory methods, you can just use named arguments to bypass the default overload resolution rules: Thank you, I should have thought of that. biznatchio posted:And, incidentally, don't design your ServiceResult class to have both a singular Result property and an separate multi-value Results property. The methods you're calling that will be returning this object will know if they're going to be returning a single value or multiple values. Let them handle it by returning ModelResult<IList<ModelType>, long> instead of making a lot of annoying confusion for developers who will work on your codebase in the years to come as to whether they should be looking at Result or Results in every single result object they ever interact with. OK, that makes sense. quote:Also, since I've seen this anti-pattern often enough to have an idea of what it might be used for: don't use ServiceResult objects internally between C# layers of your application. They're fine to shape Web API contract output, but a lot of developers insist on bringing over development practices from worse languages or have a massively misplaced fear of using exceptions for error reporting and end up making a huge mess of their internal codebases by building these sorts of bespoke result types rather than just returning the actual value you actually wanted from methods and using exceptions for failures. I see what you’re saying. I’m consolidating results at the services layer when it would more properly be done at the API layer. Thanks.
|
# ? Jul 5, 2023 23:26 |
I have installed a new HTTPS certificate for the host in the Windows certificate store, and deleted the old certificate (with the same common name), but Kestrel keep serving with the old certificate, which has now expired. Where does Kestrel find an old certificate that has been deleted from the Windows certificate store, and how do I make it pick up the new cert? This is in production, dev certificates should not be involved in any way. Never mind all this, it turns out the answer was that the certificate had to be installed in the per-user certificate store for the service account running the service, and I was looking in the machine certificate store, where someone else had also placed the certificate. nielsm fucked around with this message at 14:27 on Jul 10, 2023 |
|
# ? Jul 10, 2023 12:01 |
|
I have a question about including an expression in an EF select operation. If I run this code against SQL Server:code:
code:
code:
code:
|
# ? Jul 11, 2023 15:06 |
|
Initially a caveat that it sounds like a pretty bad idea to do this at all, and even after finding something that works it'll be easy for normal code changes/refactors to accidentally break it in a way that means you're no longer running it server-side. If that isn't an issue, then I haven't tested this but I expect this would work: code:
code:
|
# ? Jul 11, 2023 15:33 |
|
You're on the right track but are veering off course when you're compiling the expression. The IQueryable LINQ methods take expressions so that they can be broken down and reinterpreted as SQL. When trying to do what you're doing, pull everything out into the expression for reuse:code:
As an aside, you'll notice too that it can take an expression now, but, once it goes from IQueryable to IEnumerable (.ToList, .ToArray, .AsEnumerable, etc), it can't anymore. Paying attention to when you lose the IQueryable when building LINQ queries is a really good hint as to what work will be done where (though as you've seen it all kind of gets thrown out when EF decides to just be helpful and make it work at a huge performance penalty, but at least it gives you an idea of what definitely won't be done in the DB). If you're not doing the exact same projection each time and truly want to just reuse just the part that creates FullName, you can do that too, but it's definitely more involved. As far as expression trees go, it's not a terribly complicated task, but there's a lot to learn to get there, kind of a whole new way of thinking to adopt, and it opens you up to some of the weirdness and nuances you get moving from the type safety of code you're writing by hand to late binding, plus some pretty rough, unhelpful error messages. If that's the route you want to go, let me know and I could scratch something up to give you an idea.
|
# ? Jul 11, 2023 16:16 |
|
Admiral Snackbar posted:I have a question about including an expression in an EF select operation. If I run this code against SQL Server: Like, code:
Kyte fucked around with this message at 19:05 on Jul 11, 2023 |
# ? Jul 11, 2023 19:00 |
|
I appreciate everyone's feedback on this. I wanted to see if I could make this work by manipulating expression trees, and I put together a visitor that actually works (at least for anonymous types):code:
code:
code:
code:
|
# ? Jul 12, 2023 14:06 |
|
I have to migrate from framework 4.6.1 to netcore 6 on a pretty complex bit of software. its uh. fun? Also its only one day a week that I work on it lol
|
# ? Jul 12, 2023 20:54 |
|
RC Cola posted:I have to migrate from framework 4.6.1 to netcore 6 on a pretty complex bit of software. its uh. fun? mystes fucked around with this message at 20:58 on Jul 12, 2023 |
# ? Jul 12, 2023 20:55 |
|
mystes posted:I think regardless of the complexity of the software it entirely just depends on whether you're using specific features/dependencies that don't work in .net 6 that you have to replace; otherwise there aren't a lot of backwards incompatible changes and it should be trivial there are so so so many specific features and dependencies that don't work anymore
|
# ? Jul 12, 2023 21:03 |
|
I mean to be fair if you're dealing with enterprise software that for some reason hasn't been migrated so far it's very likely to be using stuff that won't work too
|
# ? Jul 12, 2023 21:04 |
|
Admiral Snackbar posted:I appreciate everyone's feedback on this. I wanted to see if I could make this work by manipulating expression trees, and I put together a visitor that actually works (at least for anonymous types): ngl it's cool but I'd be cursing you the entire time I was asked to maintain it. Hopefully you can squeeze as much reuse from it as possible.
|
# ? Jul 12, 2023 23:15 |
|
RC Cola posted:there are so so so many specific features and dependencies that don't work anymore Haha, this hits home. My company had a contact at MS for helping us figure out some of the specifics, and... I guess it'll end up being a little over a year of working on those hard parts.
|
# ? Jul 13, 2023 04:05 |
|
RC Cola posted:there are so so so many specific features and dependencies that don't work anymore god help you if there's any asp.net in there at all
|
# ? Jul 13, 2023 07:10 |
|
RC Cola posted:I have to migrate from framework 4.6.1 to netcore 6 on a pretty complex bit of software. its uh. fun? I've been slowly hacking away at something like this for the past few months. Fun is definitely one word for it The only advice I can give is mapping out exactly what all the blockers are and how they depend on each other to help you prioritise poo poo to jettison, transition or rewrite. That and trying to focus on incremental, bankable progress so you don't have to make a massive jump/merge six months from now that everything depends on. I'd say use tests to make sure you don't break anything, but if your situation is anything like mine I'm guessing meaningful tests are a distant dream. My current hitlist is something like MSMQ, WCF, EF6, some in-house libraries that mess around with AppDomains. History is a nightmare we are trying to wake up from
|
# ? Jul 13, 2023 08:08 |
|
Moq introduced some poo poo called SponsorLink that, if I understand correctly, uses a closed-source obfuscated dll to scan your git.config for your email and sends it (hashed but who cares) to a server to figure out if you're a sponsor of the project and then nags you if you aren't. They have since reverted it but hard to say what their future plans are, the primary dev seems to just not loving get why this is a problem. Marc Gravell commented on it here Edit: Author opened another issue to discuss the issues w/ oss developer support here as well Cold on a Cob fucked around with this message at 16:22 on Aug 10, 2023 |
# ? Aug 10, 2023 16:13 |
|
Thank you, guess we'll be looking into an alternative tomorrow at work, what a shame.
|
# ? Aug 10, 2023 17:11 |
|
I've heard good things about NSubstitute and FakeItEasy, but haven't had a chance to test myself yet. For what it's worth, it looks like the change got rolled back for now due to an incompatibility with MacOS.
|
# ? Aug 10, 2023 17:53 |
|
I've used FakeItEasy, it's fine and never had issues with it.
|
# ? Aug 10, 2023 17:57 |
|
ChocolatePancake posted:I've heard good things about NSubstitute and FakeItEasy, but haven't had a chance to test myself yet. I've read all the threads, since one of the projects I maintain uses Moq. The guy is doubling down and actively looking for a way to add back Sponsorlink or some other form of nagware, despite everyone telling him that it's an awful idea. He rejects the idea of selling support contracts or any other form of monetisation that has been actually tried, and he's fixated on the idea of So my recommendation is to permanently pin Moq to 4.18, since it's fortunately not a runtime dependency. Should you ever run into an incompatibility or have some spare time, move to one of the alternatives.
|
# ? Aug 10, 2023 19:29 |
|
The idea of having employees pay for the software used by their employer’s product is insane to begin with. Yes on a fully voluntary basis it’s a nice gesture. But absolutely nowhere near a foundation for a business model. Putting aside the outrageous privacy issues.
|
# ? Aug 10, 2023 20:04 |
|
|
# ? May 18, 2024 06:42 |
|
I've used NSubstitute extensively and never really had to think about it.
|
# ? Aug 10, 2023 20:12 |