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
Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

raminasi posted:

I sure wish that whoever wrote that originally had cared about performance.

The fact that someone doesn't realise that repeated naive string concatenations will result in bad performance doesn't imply that they don't care about performance. It's surely a pretty common trap to fall into at least once in a programming career (I know I have, but in my case it was picked up before the code was being used in anger) - although surely it's more usual to run into it by writing a loop that updates the string, rather than just loads of concatenations, since then the problem can be missed when working with toy examples.

If someone is repeatedly making that mistake, as opposed to making it once and taking it as a learning experience, there's something a bit wrong.

Adbot
ADBOT LOVES YOU

ThePeavstenator
Dec 18, 2012

:burger::burger::burger::burger::burger:

Establish the Buns

:burger::burger::burger::burger::burger:
I've had the most success in keeping code reasonably performant and readable with the advice of "use the currently recommended standard library API first". Everyone has their own definition of "easy" and "simple" (and often that definition is "stuff I already know") but it's hard to argue with the 1st party .NET docs saying "use async/await, not .Wait() or .Result" or "use DateTimeOffset instead of DateTime".

In the case of string concatenation, I think you can make the case that all of these are fine if you follow "use the currently recommended standard library API first":
C# code:
void LogHourlyBackgroundJobDone(long executionTimeMillis)
{
    // This is fine
    logger.Log($"Finished background job that executes once per hour, execution time was {executionTimeMillis} ms.");

    // I'm not going to do more then drop a NIT if I thought using a different string format/concatenation/interpolation method would be better
    var logString = string.Format("Finished background job that executes once per hour, execution time was {0} ms.", executionTimeMillis);
    logger.Log(logString);

    // This is fine too, the primary factor for how this code is written should be consistency with how the rest of your codebase does log string construction
    logger.Log("Finished background job that executes once per hour, execution time was " + executionTimeMillis + " ms.");

    // This seems a little verbose for this situation and is probably the worst-looking example out of all of these,
    // but if someone really wanted to use it I could live with approving most PRs that had this as long as it wasn't obnoxious
    var logStringBuilder = new StringBuilder();
    logStringBuilder.Append("Finished background job that executes once per hour, execution time was ");
    logStringBuilder.Append(executionTimeMillis);
    logStringBuilder.Append(" ms.");
    logger.Log(logStringBuilder.ToString());
}
While clearly this is not:
C# code:
void LogSomeMetricsInHotPath(List<long> giantListOfExecutionTimesToLogInHotPath)
{
    if (giantListOfExecutionTimesToLogInHotPath.Count < 1000000)
    {
        throw new ArgumentException("Please only use this method to log batches of at least 1 million execution times.", nameof(giantListOfExecutionTimesToLogInHotPath));
    }

    // var logStringBuilder = new StringBuilder().AppendLine("Building a log string"); // just use the recommended API
    var logString = "Building a log string\n";

    foreach (var executionTimeMillis in giantListOfExecutionTimesToLogInHotPath)
    {
        // logStringBuilder.Append("Here's an Execution Time: ").Append(executionTimeMillis).AppendLine(" ms"); // this is just about as readable as the concatenation
        logString += "Here's an Execution Time getting logged in a really, really hot path: ";
        logString += executionTimeMillis;
        logString += "ms\n";
    }

    // logger.Log(logStringBuilder.ToString()); // that's all it takes
    logger.Log(logString);
}

ThePeavstenator fucked around with this message at 04:06 on Mar 3, 2022

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
The right answer is to use a log method that accepts a format string, so it doesn't even need to do the formatting if there's no log sink connected that would receive the message.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Someone needs to tell Microsoft that when someone types "Debug" into the Visual Studio C# text editor and then types a full stop, they do not in fact want the "Debug" they typed to be changed to "DebugSettingsReader". Nobody has ever wanted that.

insta
Jan 28, 2009

Jabor posted:

The right answer is to use a log method that accepts a format string, so it doesn't even need to do the formatting if there's no log sink connected that would receive the message.

Bingo -- string.Format has a place, and that's in resx files. If you are hardcoding your format string, that needs to be something else, because string.Format *is* slow.

code:
    [MemoryDiagnoser]
    public class StringFormatTest
    {
        private readonly int Digits = 8;

        [Benchmark]
        public string Concatted() => "Finished background job that executes once per hour, execution time was " + Digits + " ms.";

        [Benchmark]
        public string Interpolated() => $"Finished background job that executes once per hour, execution time was {Digits} ms.";

        [Benchmark]
        public string Formatted() => string.Format("Finished background job that executes once per hour, execution time was {0} ms.", Digits);

        [Benchmark]
        public string StringBuilding()
        {
            var logStringBuilder = new StringBuilder();
            logStringBuilder.Append("Finished background job that executes once per hour, execution time was ");
            logStringBuilder.Append(Digits);
            logStringBuilder.Append(" ms.");
            return logStringBuilder.ToString();
        }
    }

.NET5
|         Method |      Mean |    Error |   StdDev |    Median |  Gen 0 | Allocated |
|--------------- |----------:|---------:|---------:|----------:|-------:|----------:|
|      Concatted |  31.51 ns | 0.699 ns | 1.347 ns |  30.96 ns | 0.0421 |     176 B |
|   Interpolated | 221.54 ns | 3.846 ns | 5.001 ns | 222.79 ns | 0.0477 |     200 B |
|      Formatted | 228.65 ns | 4.607 ns | 8.765 ns | 226.90 ns | 0.0477 |     200 B |
| StringBuilding | 150.77 ns | 3.037 ns | 5.923 ns | 151.26 ns | 0.1624 |     680 B |

.NET6
|         Method |      Mean |    Error |   StdDev |  Gen 0 | Allocated |
|--------------- |----------:|---------:|---------:|-------:|----------:|
|      Concatted |  29.83 ns | 0.701 ns | 2.035 ns | 0.0421 |     176 B |
|   Interpolated | 211.89 ns | 4.284 ns | 7.941 ns | 0.0477 |     200 B |
|      Formatted | 195.37 ns | 2.401 ns | 2.005 ns | 0.0477 |     200 B |
| StringBuilding | 124.16 ns | 2.521 ns | 4.348 ns | 0.1624 |     680 B |

This was on .NET5 right now, which seems to treat all interpolation as string.Format. I know .NET6 changed that, so I'll download that SDK and retry.



edit: .NET6 added. I thought I was supposed to get a freebie :(

insta fucked around with this message at 18:45 on Mar 3, 2022

No Pants
Dec 10, 2000

The compiler (pre- and post-.NET 6) will convert an interpolated string to a string literal or a string.Concat call if the parameters line up, so it isn't all bad. The main thing in .NET 6 is fewer heap allocations for the non-trivial cases (edit: and exposing some types that allow methods to consume interpolated strings and do stuff themselves).

No Pants fucked around with this message at 19:47 on Mar 3, 2022

insta
Jan 28, 2009

No Pants posted:

The compiler (pre- and post-.NET 6) will convert an interpolated string to a string literal or a string.Concat call if the parameters line up, so it isn't all bad. The main thing in .NET 6 is fewer heap allocations for the non-trivial cases (edit: and exposing some types that allow methods to consume interpolated strings and do stuff themselves).

I know the compiler converts simple, compile-time string concats into a string.Concat call -- but I'm seriously surprised it can't figure out how to do that for the Interpolated example in my code.

redleader
Aug 18, 2005

Engage according to operational parameters

insta posted:

I know the compiler converts simple, compile-time string concats into a string.Concat call -- but I'm seriously surprised it can't figure out how to do that for the Interpolated example in my code.

iirc interpolation uses the current thread culture at runtime, or something like that

insta
Jan 28, 2009

redleader posted:

iirc interpolation uses the current thread culture at runtime, or something like that

Looks accurate -- changing my code above to use string Digits = "eight" gives the following:
code:
|         Method |      Mean |    Error |   StdDev |  Gen 0 | Allocated |
|--------------- |----------:|---------:|---------:|-------:|----------:|
|      Concatted |  24.57 ns | 0.496 ns | 0.591 ns | 0.0440 |     184 B |
|   Interpolated |  24.90 ns | 0.571 ns | 0.722 ns | 0.0440 |     184 B |
|      Formatted | 197.66 ns | 3.270 ns | 2.731 ns | 0.0439 |     184 B |
| StringBuilding | 117.73 ns | 2.318 ns | 4.735 ns | 0.1645 |     688 B |
I don't see how it could be inlined because it's a readonly field assigned at startup rather than a constant, and concat vs interpolated seems identical.

No Pants
Dec 10, 2000

insta posted:

I know the compiler converts simple, compile-time string concats into a string.Concat call -- but I'm seriously surprised it can't figure out how to do that for the Interpolated example in my code.

Yeah, I was talking about $""-type string interpolation. The earlier behavior where $"" is a shortcut for string.Format was more desirable because using string.Concat would allocate an extra string for the formatted int.

zokie
Feb 13, 2006

Out of many, Sweden
Now do the benchmarks with something more complex than that extremely trivial example.

StringBuilder is obviously the wrong solution for that use case.

insta
Jan 28, 2009

zokie posted:

Now do the benchmarks with something more complex than that extremely trivial example.

StringBuilder is obviously the wrong solution for that use case.

I'm happy to try them out if you have suggestions. This was the example from earlier in the thread.

Munkeymon
Aug 14, 2003

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



Hey, insta, what in the world are your workloads that you need to care so much about the minutiae of string concatenation?

brap
Aug 23, 2004

Grimey Drawer
.NET 6 can improve interpolated string perf only if the interpolated string is used as an argument for an "interpolated string builder" parameter. This compiles down into basically a sequence of append calls for all the literal and interpolated chunks of data in the interpolated string. A bunch of the standard logging/building methods take such arguments now in .NET 6, but if you convert the interpolated string to 'string' as in your benchmark, you won't see any of the perf improvement from it.

No Pants
Dec 10, 2000

The C# 10 compiler can lower an interpolated string to using the DefaultInterpolatedStringHandler type, too.

Like this
C# code:
using System;

var num = 100;
var thing = "butt";
var s = $"hello {num} {thing}s";
Console.WriteLine(s);
turns into this (unoptimized)
C# code:
[CompilerGenerated]
internal static class <Program>$
{
    private static void <Main>$(string[] args)
    {
        int value = 100;
        string value2 = "butt";
        DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(7, 2);
        defaultInterpolatedStringHandler.AppendLiteral("hello ");
        defaultInterpolatedStringHandler.AppendFormatted(value);
        defaultInterpolatedStringHandler.AppendLiteral(" ");
        defaultInterpolatedStringHandler.AppendFormatted(value2);
        defaultInterpolatedStringHandler.AppendLiteral("s");
        string value3 = defaultInterpolatedStringHandler.ToStringAndClear();
        Console.WriteLine(value3);
    }
}
The docs and this blog post are helpful if anyone's burning with curiosity about it.

insta
Jan 28, 2009

Munkeymon posted:

Hey, insta, what in the world are your workloads that you need to care so much about the minutiae of string concatenation?

I do a lot of backend data ETL processing stuff, but mostly in general wherever I go as a job I'm usually tasked with solving performance problems. There's a lot of developers who simply don't understand that similar constructs are wildly different in performance, especially once you scale it up to the millions or billions of records in production. If you work in a team who does understand that inside and out, I'm glad for you.

There's usually a couple things I see that are easy to change on the C# side that dramatically improve performance, List.Contains when they really wanted HashSet.Contains, string concatenation, .Any() vs .Count() > 1, etc. I guess I don't understand the call to wait until it's slow before you write the faster code? If you really want .Any(), but you see it as .Count() > 1 in the pull request, why would you not call it out right then? Why do you need to wait for the profiler to call that a hot path?

redleader
Aug 18, 2005

Engage according to operational parameters
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.

SirViver
Oct 22, 2008

insta posted:

.Any() vs .Count() > 1, etc.

Defect spotted :v:

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

insta posted:

If you really want .Any(), but you see it as .Count() > 1 in the pull request, why would you not call it out right then? Why do you need to wait for the profiler to call that a hot path?

Those aren't equivalent although I get that you mean > 0. However, IIRC it will be optimized to use the Count property if the underlying collection has it so it might not even be a problem in the first place.

insta
Jan 28, 2009

SirViver posted:

Defect spotted :v:

rageposting got me

rarbatrol
Apr 17, 2011

Hurt//maim//kill.
Man I wish we had performance problems as subtle as that. I'm dealing with legacy code continuously being glued together in new and terrifying ways, and we keep finding code that performs a database lookup per record, which itself usually comes from an initial database lookup.

rarbatrol fucked around with this message at 21:43 on Mar 6, 2022

Just-In-Timeberlake
Aug 18, 2003

rarbatrol posted:

Man I wish we had performance problems as subtle as that. I'm dealing with legacy code continuously being glued together in new and terrifying ways, and we keep finding code that performs a database lookup per record, which itself usually comes from an initial database lookup.

ah, you’re me, good to know.

LongSack
Jan 17, 2003

I’ve been using .Any() versus .Count > 1 (or .Count() > 1 for IEnumerable) since it was actually recommended that I do so by Intellisense (or Intellicode, some kind of “Intelli…”).

Should I not?

Also, I try not to hard code (some) strings preferring .resx files, so end up with things like
C# code:
if (userid <= 0)
{
    return new ApiError(string.Format(Strings.Invalid, “user id”));
}
where Invalid looks like ”The {0} is invalid.”

Should I be using something different? string.Concat for example?

TIA

Kyte
Nov 19, 2013

Never quacked for this

rarbatrol posted:

Man I wish we had performance problems as subtle as that. I'm dealing with legacy code continuously being glued together in new and terrifying ways, and we keep finding code that performs a database lookup per record, which itself usually comes from an initial database lookup.

Oh man that reminded me.
Some time ago we received a project made in C# that we were supposed to add new stuff to. To get around the natural limitations wrt cross-database queries and get nice simple LINQ queries, they'd tacked a ToList() to every DbSet call. Before any applicable Wheres, of course.

We didn't fix every instance, because it was out of scope and out of budget and frankly I'd've left it as-is as a "go gently caress yourself", but it was so drat slow to test I'd have to fix some anyways just to get things done in time.

Kyte fucked around with this message at 00:43 on Mar 7, 2022

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

LongSack posted:

I’ve been using .Any() versus .Count > 1 (or .Count() > 1 for IEnumerable) since it was actually recommended that I do so by Intellisense (or Intellicode, some kind of “Intelli…”).

Any() is the correct way to check if a collection is empty or not. Count() requires it to iterate the entire collection to perform the comparison, even though knowing there's a single thing in there is enough. This is very bad for lazy loaded collections. Count() will skip iterating and go check the Count property if it's a materialized collection like List or ICollection that has a Count property, but Any() is still safest and best to use.

And remember, Any() is equivalent to Count > 0, not 1.

Aldo Count is fine -- it's a property with no overhead. Count() is a LINQ extension method that will iterate the entire collection to get the count of items in it, except in the aforementioned scenario I mentioned above.

New Yorp New Yorp fucked around with this message at 01:38 on Mar 7, 2022

LongSack
Jan 17, 2003

New Yorp New Yorp posted:

And remember, Any() is equivalent to Count > 0, not 1.

:doh:

Of course it is, and I knew that

Also, thanks for your response. I never really considered the implications of Count(), but upon reflection (no pun intended) it makes sense.

LongSack fucked around with this message at 03:40 on Mar 7, 2022

insta
Jan 28, 2009

LongSack posted:

:doh:

Of course it is, and I knew that

Also, thanks for your response. I never really considered the implications of Count(), but upon reflection (no pun intended) it makes sense.

Yeah I got it wrong in the rant, and decided to keep it since it was quoted like 5 times. There are many things like this, that show up in other constructs you wouldn't think of:

str1.ToUpperInvariant() == str2.ToUpperInvariant() vs string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase),

I find this all the time, for random reasons. On the surface, they look functionally equivalent. The first one allocates 2 new strings and will traverse the length of both of them to build the culture-invariant uppercase version, then do an equality check. The second will first short-circuit by testing length, then will go character-by-character with an ordinal case-insensitive comparison (which itself is faster than invariant culture, if that's applicable), and fail as early as it can.

I'm going to stick with my method of converting the former to the latter, even if they're not on a hot-spot in a profiler. There is no case where the first one is better, and it will prolong the time until it does show up on a hot-spot in a profiler. It's something I can do on auto-pilot as I'm in the rest of the code, and if nothing else it helps me read it better since "string.Equals" is more appropriate to the logic instead of "ToUpperInvariant()"

worms butthole guy
Jan 29, 2021

by Fluffdaddy
I don't really deal with ASPNet much, but i'm having a simple issue that I'm hoping someone knows what i'm doing wrong. I'm using RestSharp to try to hit a API and then trying to serialize that data with the following:

code:
@page
@using RestSharp
@using System.Text.Json
@model IndexModel
@{
    ViewData["Title"] = "Home page";

    string url ="https://cat-fact.herokuapp.com";
    var client = new RestClient(url);

    var request = new RestRequest("facts");
    request.AddHeader("accept", "application/json");

    var response = await client.GetAsync(request);

    var data = response.Content;

    var cats = JsonSerializer.Deserialize<CatData>(response.Content);
    ViewData["data"] = cats;
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <pre>Response : @ViewData["data"]</pre>
</div>
The structure:
code:
namespace CityBreaks.Pages;
public class Status
{
    public bool Verified { get; set; }
    public int SentCount { get; set; }
}

public class CatData
{
    public Status Status { get; set; }
    public string Id { get; set; }
    public string User { get; set; }
    public string Text { get; set; }
    public int V { get; set; }
    public string Source { get; set; }
    public DateTime UpdatedAt { get; set; }
    public string Type { get; set; }
    public DateTime CreatedAt { get; set; }
    public bool Deleted { get; set; }
    public bool Used { get; set; }
}

The issue i'm running into though is that whn I try to run it, I get a Internal Server Error of the following:

code:
JsonException: The JSON value could not be converted to CityBreaks.Pages.CatData. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
So, it's clearly whatever i'm sending " var cats = JsonSerializer.Deserialize<CatData>(response.Content);" but I can't figure out the correct way to do it. Anyone have any ideas? :shrug:

Freaksaus
Jun 13, 2007

Grimey Drawer

worms butthole guy posted:

The issue i'm running into though is that whn I try to run it, I get a Internal Server Error of the following:

code:
JsonException: The JSON value could not be converted to CityBreaks.Pages.CatData. Path: $ | LineNumber: 0 | BytePositionInLine: 1.
So, it's clearly whatever i'm sending " var cats = JsonSerializer.Deserialize<CatData>(response.Content);" but I can't figure out the correct way to do it. Anyone have any ideas? :shrug:

You are currently casting the json data to a singular instance of Catdata.
code:
    var cats = JsonSerializer.Deserialize<CatData>(response.Content);
You should be casting it to a list of Catdata.
code:
    var cats = JsonSerializer.Deserialize<List<CatData>>(response.Content);
You can also remove the data variable as it's not being used anywhere.

Kyte
Nov 19, 2013

Never quacked for this

insta posted:

Yeah I got it wrong in the rant, and decided to keep it since it was quoted like 5 times. There are many things like this, that show up in other constructs you wouldn't think of:

str1.ToUpperInvariant() == str2.ToUpperInvariant() vs string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase),

I find this all the time, for random reasons. On the surface, they look functionally equivalent. The first one allocates 2 new strings and will traverse the length of both of them to build the culture-invariant uppercase version, then do an equality check. The second will first short-circuit by testing length, then will go character-by-character with an ordinal case-insensitive comparison (which itself is faster than invariant culture, if that's applicable), and fail as early as it can.

I'm going to stick with my method of converting the former to the latter, even if they're not on a hot-spot in a profiler. There is no case where the first one is better, and it will prolong the time until it does show up on a hot-spot in a profiler. It's something I can do on auto-pilot as I'm in the rest of the code, and if nothing else it helps me read it better since "string.Equals" is more appropriate to the logic instead of "ToUpperInvariant()"

The relative efficiency of the two isn't nearly as relevant as the fact they are not actually equivalent. Some languages do weird things with case conversions. ToUpper == ToUpper is less correct than Equals(IgnoreCase).

worms butthole guy
Jan 29, 2021

by Fluffdaddy

Freaksaus posted:

You are currently casting the json data to a singular instance of Catdata.
code:
    var cats = JsonSerializer.Deserialize<CatData>(response.Content);
You should be casting it to a list of Catdata.
code:
    var cats = JsonSerializer.Deserialize<List<CatData>>(response.Content);
You can also remove the data variable as it's not being used anywhere.

Thank you for this answer! It got my program running but now when I run it I get:

code:
Response : System.Collections.Generic.List`1[CityBreaks.Pages.CatData]
:suicide: so time to figure out how to destructure this lol.

Kyte
Nov 19, 2013

Never quacked for this

worms butthole guy posted:

Thank you for this answer! It got my program running but now when I run it I get:

code:
Response : System.Collections.Generic.List`1[CityBreaks.Pages.CatData]
:suicide: so time to figure out how to destructure this lol.

That's normal. A List<T>.ToString() gives you that, since it doesn't (want to) know how to convert the contents to a string.
Options:
1) Create a DisplayTemplate for CatData and then use @Html.DisplayFor(list) (it will automatically iterate across the list)
2) Create a DisplayTemplate for CatData, then foreach across the list and use @Html.DisplayFor(item) (if you want to customize the stuff around/between each item
3) Use JsonConverter.Serialize() to get a JSON representation you can throw to the screen.
4) Roll your own thing using foreach and whatnot.

insta
Jan 28, 2009

Kyte posted:

The relative efficiency of the two isn't nearly as relevant as the fact they are not actually equivalent. Some languages do weird things with case conversions. ToUpper == ToUpper is less correct than Equals(IgnoreCase).

That's why I said ToUpperInvariant(), and any case I've seen it used has been where they are just trying to compare strings like database column names or something.

I'm seriously jealous of all of you who get to work with nothing but absolute geniuses in C# and never inherit code with any of these issues.

Polio Vax Scene
Apr 5, 2009



Your team, a team of intellectuals: Have we considered doing a benchmark test of string concatenation methods to identify the most efficient way to generate our log files? We could increase performance by up to 4%!

My team:
code:
        private int GetEndTagPosition(string searchstring, string Tag)
        {
            int EndTagPosition = 0;
            int pos1 = 99999, pos2 = 99999, pos3 = 99999, pos4 = 99999, pos5 = 99999, pos6 = 99999, pos7 = 99999, pos8 = 99999;
            searchstring = searchstring + ' ';
            pos1 = searchstring.IndexOf(' ', searchstring.ToUpper().IndexOf(Tag.ToUpper()) + Tag.Length + 1);
            pos2 = EndTagPosition = searchstring.IndexOf("\t", searchstring.ToUpper().IndexOf(Tag.ToUpper()) + Tag.Length + 1);
            pos4 = searchstring.IndexOf("<br>", searchstring.ToUpper().IndexOf(Tag.ToUpper()) + Tag.Length + 1);
            pos3 = searchstring.IndexOf("\n", searchstring.ToUpper().IndexOf(Tag.ToUpper()) + Tag.Length + 1);
            pos5 = searchstring.IndexOf(",", searchstring.ToUpper().IndexOf(Tag.ToUpper()) + Tag.Length + 1);
            //pos6 = searchstring.IndexOf(".", searchstring.ToUpper().IndexOf(Tag.ToUpper()) + Tag.Length + 1); //removed since it causes issues with email addresses
            pos7 = searchstring.IndexOf(":", searchstring.ToUpper().IndexOf(Tag.ToUpper()) + Tag.Length + 1);
            pos8 = searchstring.IndexOf(";", searchstring.ToUpper().IndexOf(Tag.ToUpper()) + Tag.Length + 1);
            //EndTagPosition = Math.Min((pos1 == -1) ? 99999 : pos1, Math.Min((pos2 == -1) ? 99999 : pos2, Math.Min((pos3 == -1) ? 99999 : pos3, (pos4 == -1) ? 99999 : pos4))) - 1;
            EndTagPosition = Math.Min(
                (pos1 == -1) ? 99999 : pos1,
                    Math.Min((pos2 == -1) ? 99999 : pos2,
                        Math.Min((pos3 == -1) ? 99999 : pos3,
                            Math.Min((pos4 == -1) ? 99999 : pos4,
                                Math.Min((pos5 == -1) ? 99999 : pos5,
                                    Math.Min((pos6 == -1) ? 99999 : pos6,
                                        Math.Min((pos7 == -1) ? 99999 : pos7,
                                            (pos8 == -1) ? 99999 : pos8))))))) - 1;
            if (EndTagPosition == -99998)
            {
                EndTagPosition = 0;
            }

            return EndTagPosition;
        }

worms butthole guy
Jan 29, 2021

by Fluffdaddy

Kyte posted:

That's normal. A List<T>.ToString() gives you that, since it doesn't (want to) know how to convert the contents to a string.
Options:
1) Create a DisplayTemplate for CatData and then use @Html.DisplayFor(list) (it will automatically iterate across the list)
2) Create a DisplayTemplate for CatData, then foreach across the list and use @Html.DisplayFor(item) (if you want to customize the stuff around/between each item
3) Use JsonConverter.Serialize() to get a JSON representation you can throw to the screen.
4) Roll your own thing using foreach and whatnot.

Thank you. My complaint about ASP.NET is that this stuff isn't ever really explained well and you have to figure it out on your own or ask people. It's also feels like a different programming language at times from C# :shrug:

fuf
Sep 12, 2004

haha
What do people think of Umbraco?

After struggling so much with DB stuff in my last learning project I kinda like the idea of trying a CMS that will handle all that stuff for me for a while.

But it looks like it's very much geared towards old school blogs and ecommerce sites rather than anything "app"-like. Am I gonna be fighting against it if I want to do something that's a bit more like an SPA?

Is it worth trying out as part of a general .NET / C# learning journey, or is too much of its own little ecosystem to be widely applicable? Is it a good thing to have for job prospects? Any thoughts appreciated.

e: actually reading and thinking a bit more maybe what I actually want is a headless CMS? Then I can keep building on the Blazor frontend stuff I've been doing and maybe get less bogged down in DB stuff

e2: ok maybe I don't actually know what a headless CMS is...

fuf fucked around with this message at 18:55 on Mar 8, 2022

Munkeymon
Aug 14, 2003

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



insta posted:

I do a lot of backend data ETL processing stuff, but mostly in general wherever I go as a job I'm usually tasked with solving performance problems. There's a lot of developers who simply don't understand that similar constructs are wildly different in performance, especially once you scale it up to the millions or billions of records in production. If you work in a team who does understand that inside and out, I'm glad for you.

There's usually a couple things I see that are easy to change on the C# side that dramatically improve performance, List.Contains when they really wanted HashSet.Contains, string concatenation, .Any() vs .Count() > 1, etc. I guess I don't understand the call to wait until it's slow before you write the faster code? If you really want .Any(), but you see it as .Count() > 1 in the pull request, why would you not call it out right then? Why do you need to wait for the profiler to call that a hot path?

I was just curious because I figure if I profile the shambling monolith I support I'm sure it'd look like it's spending a lot of time doing string manipulation since we're rendering HTML and JSON in the Framework, but I know that most of the wall clock wait time is due to EF misuse and that's been how pretty much every web app I've worked on has been.

I thought maybe I'd find out you were stressing about the string concatenation performance in MVC or whatever.

Munkeymon fucked around with this message at 20:42 on Mar 8, 2022

distortion park
Apr 25, 2011


fuf posted:

What do people think of Umbraco?

After struggling so much with DB stuff in my last learning project I kinda like the idea of trying a CMS that will handle all that stuff for me for a while.

But it looks like it's very much geared towards old school blogs and ecommerce sites rather than anything "app"-like. Am I gonna be fighting against it if I want to do something that's a bit more like an SPA?

Is it worth trying out as part of a general .NET / C# learning journey, or is too much of its own little ecosystem to be widely applicable? Is it a good thing to have for job prospects? Any thoughts appreciated.

e: actually reading and thinking a bit more maybe what I actually want is a headless CMS? Then I can keep building on the Blazor frontend stuff I've been doing and maybe get less bogged down in DB stuff

e2: ok maybe I don't actually know what a headless CMS is...

What are you trying to do? I'd lean against learning too many things at once, there's probably something simple you can do in the first iteration

insta
Jan 28, 2009

Munkeymon posted:

I was just curious because I figure if I profile the shambling monolith I support I'm sure it'd look like it's spending a lot of time doing string manipulation since we're rendering HTML and JSON in the Framework, but I know that most of the wall clock wait time is due to EF misuse and that's been how pretty much every web app I've worked on has been.

I thought maybe I'd find out you were stressing about the string concatenation performance in MVC or whatever.

If I'm not doing backend ETL, I'm producing an API for JS-based SPAs, so all performance gains are good there. Surprisingly, the database code has usually been optimized pretty well by the time I get there, which might be why I see the goofy C# poo poo left over.

Adbot
ADBOT LOVES YOU

fuf
Sep 12, 2004

haha

distortion park posted:

What are you trying to do?

Honestly? I'm trying to learn enough .NET and C# so that I can switch jobs and get a mid-level developer role somewhere.

Which short term means trying to come up with random projects to keep up some motivation.

I think you're right about learning too many things at once. Umbraco is a branch too far at this stage.

I think the wall I hit with my Blazor app wasn't anything technical, but more to do with design patterns and just not really knowing how and where to structure all the business logic.

Blazor components / front end views? Fine with all that
Database operations? Difficult but I know what I'm trying to do
But the layer in between where, you know, the actual app is meant to do its thing? That's where I was really clueless about the best way to structure things. Everything got too mixed together and I would make changes that would cascade and break a bunch of other things. I hit a point where instead of being excited to keep hacking away my heart would sink at the mess of different files.

Because Blazor is new most of the learning resources are like "here is what is new and specific about Blazor components" but not much "here is how to structure an application in general". It also doesn't help that every tutorial course is like "how to add an employee to your list of employees". Or some of them get really adventurous and are like "how to add a car to your list of cars". But none of them really seem to do do anything with the data.

fuf fucked around with this message at 12:16 on Mar 9, 2022

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