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
Kyte
Nov 19, 2013

Never quacked for this

distortion park posted:

Instead of depending directly on those transient view models, you could depend on a view model factory (or one per VM depending on if the dependencies have much overlap). There are a few different ways to set this up depending on exact requirements and preferences, there are a bunch explained here:
https://stackoverflow.com/a/2280289


The Microsoft DI framework has explicit support for scoped dependencies but idk if it's easy to use outside of asp.net

I use MS DI all the time for small console applications. All you need to do is use a .NET Generic Host.
https://docs.microsoft.com/en-us/dotnet/core/extensions/generic-host

A lot of aspnet core's basic mechanisms are actually baked into the generic host, like configuration, DI and logging. It makes it really easy to bootstrap the app and lets you share configuration files, use secrets.json, etc. The defaults even plug into the windows event log so I can easily follow up errors in, say, a scheduled task.

In theory you're expected to use the generic host to launch a IHostedService, but nothing stops you from using AddTransient to register your main class and then use a single service locator to load it and execute it manually. You can even open a new scope to do prelaunch tasks in their own scope before you move onto the actual program. I use the latter to do "on boot" tasks in asp.net programs, too. (Stuff like rolling back uncommitted file uploads/deletions in case the server shut down or crashed midway)

Kyte fucked around with this message at 22:19 on Jan 23, 2022

Adbot
ADBOT LOVES YOU

zokie
Feb 13, 2006

Out of many, Sweden
Dependency Injection can absolutely have the same problems, what I look for is being able to use Find all References and similar stuff to figure out the dependency graph if that makes sense.

And the biggest culprit there is a bazillion internal nugets without symbols and using reflection/“conventions”/“magic” to wire up the DI-container. (I’m OK with stuff like Controllers, but please don’t go overboard”

I do not want to end up at a constructor that has zero usages! It’s not that hard to have a Bootstrap.cs with a method wiring everything up…

LongSack
Jan 17, 2003

Thanks for all the responses.

quote:

you could depend on a view model factory

Yep. Here's the final implementation of the factory:
C# code:
public class ViewModelFactory : IViewModelFactory
{
    private readonly IServiceProvider? _serviceProvider;
    public ViewModelFactory()
    {
        var app = Application.Current as App;
        _serviceProvider = app?.ServiceProvider;
    }
    public T? Create<T>() where T : ViewModelBase => _serviceProvider?.GetRequiredService<T>();
}

quote:

your ViewModelFactory should either have the ViewModel's dependencies as its own dependencies, and then new one up as required

Not necessary. GetRequiredService instantiates the class with all of its (constructor-injected) dependencies. If you want to know the dependencies of a particular VM, check its constructor (MainViewModel (sorta) excepted).

quote:

All you need to do is use a .NET Generic Host

Interesting, I'll definitely check it out. I currently use this to generate a configuration:
C# code:
public class ConfigurationFactory : IConfigurationFactory
{
    public IConfiguration Create(string filename, bool isOptional = true, string? directory = null)
    {
        var ret = new ConfigurationBuilder()
            .SetBasePath(string.IsNullOrWhiteSpace(directory) ? Directory.GetCurrentDirectory() : directory)
            .AddJsonFile(filename, optional: isOptional, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();
        return ret;
    }
}
Which works, but I don't see a way to get user secrets in there.

distortion park
Apr 25, 2011


LongSack posted:

Thanks for all the responses.

Yep. Here's the final implementation of the factory:
C# code:
public class ViewModelFactory : IViewModelFactory
{
    private readonly IServiceProvider? _serviceProvider;
    public ViewModelFactory()
    {
        var app = Application.Current as App;
        _serviceProvider = app?.ServiceProvider;
    }
    public T? Create<T>() where T : ViewModelBase => _serviceProvider?.GetRequiredService<T>();
}

Not necessary. GetRequiredService instantiates the class with all of its (constructor-injected) dependencies. If you want to know the dependencies of a particular VM, check its constructor (MainViewModel (sorta) excepted).

Interesting, I'll definitely check it out. I currently use this to generate a configuration:
C# code:
public class ConfigurationFactory : IConfigurationFactory
{
    public IConfiguration Create(string filename, bool isOptional = true, string? directory = null)
    {
        var ret = new ConfigurationBuilder()
            .SetBasePath(string.IsNullOrWhiteSpace(directory) ? Directory.GetCurrentDirectory() : directory)
            .AddJsonFile(filename, optional: isOptional, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();
        return ret;
    }
}
Which works, but I don't see a way to get user secrets in there.

You can't see ViewModelFactory's dependencies from its constructor though, I thought that was what you were trying to resolve?

epswing
Nov 4, 2003

Soiled Meat

distortion park posted:

You can't see ViewModelFactory's dependencies from its constructor though, I thought that was what you were trying to resolve?

It looks like view model factory is just a wrapper around service locator. What s/he’s saying is you can look at the constructor of a view model to see what it depends on, which is what s/he was looking to achieve.

distortion park
Apr 25, 2011


epswing posted:

It looks like view model factory is just a wrapper around service locator. What s/he’s saying is you can look at the constructor of a view model to see what it depends on, which is what s/he was looking to achieve.

I don't really see the benefit in wrapping it like that in that case - just use it directly in your base Application

Kyte
Nov 19, 2013

Never quacked for this

LongSack posted:

Interesting, I'll definitely check it out. I currently use this to generate a configuration:
C# code:
public class ConfigurationFactory : IConfigurationFactory
{
    public IConfiguration Create(string filename, bool isOptional = true, string? directory = null)
    {
        var ret = new ConfigurationBuilder()
            .SetBasePath(string.IsNullOrWhiteSpace(directory) ? Directory.GetCurrentDirectory() : directory)
            .AddJsonFile(filename, optional: isOptional, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();
        return ret;
    }
}
Which works, but I don't see a way to get user secrets in there.

I usually let CreateDefaultBuilder() add it, but if you don't wanna use it you can manually install the Microsoft.Extensions.Configuration.UserSecrets nuget, add the AddUserSecrets() line and then refer to the asp.net docs.
(I think if you use the right click -> manage user secrets option in Visual Studio it'll actually wire up most of it for you)

Kyte fucked around with this message at 17:34 on Jan 24, 2022

LongSack
Jan 17, 2003

distortion park posted:

I don't really see the benefit in wrapping it like that in that case - just use it directly in your base Application

Honestly, you're right, but the factory method looks cleaner (to me)
C# code:
var fooViewModel = _factory.Create<FooViewModel>();
vs
C# code:
var fooViewModel = (Application.Current as App)?.ServiceProvider?.GetRequiredService<FooViewModel>();
:shrug:

LongSack
Jan 17, 2003

Kyte posted:

I usually let CreateDefaultBuilder() add it, but if you don't wanna use it you can manually install the Microsoft.Extensions.Configuration.UserSecrets nuget, add the AddUserSecrets() line and then refer to the asp.net docs.
(I think if you use the right click -> manage user secrets option in Visual Studio it'll actually wire up most of it for you)

Ahh, I'm missing the nuget package! Thanks.

Edit: User secrets are really not designed to be used outside of net projects. According to some google research, it can be done, but it involves a lot of manual steps. Simply right-clicking and choosing “Manage User Secrets” does not work.

LongSack fucked around with this message at 00:41 on Jan 26, 2022

fuf
Sep 12, 2004

haha
This might not make any sense but I'm watching this video on state management in Blazor:

https://www.youtube.com/watch?v=GIupo55GTro

He outlines three methods, but then infuriatingly he doesn't actually show enough code for the third method, which is a hybrid of the first two. I can't find the code online anywhere.

The first method uses a cascading parameter like this (cut down):

CascadingAppState.razor
C# code:
@code{
   public string Test {get; set;}
}
Component.razor
C# code:
<p>@CascadingAppState.Test</p>

@code{
 [CascadingParameter]
    public CascadingAppState CascadingAppState{ get; set; }
}
The second method uses a state service like this:

AppState.cs
C# code:
public class AppState{
   public string Test {get; set;}
}
Component.razor
C# code:
@inject AppState AppState

<p>@AppState.Test</p>
And then for the third hybrid method he says:
"Use AppState Service as Property of Cascading Component"
(he also uses LocalStorage to store the state, but I'm not interested in that)

I can see that CascadingAppState.razor now includes the AppState Service as a property like this:
C# code:
@code{
   public AppState AppState {get;set;}
}
I thought maybe the child component would now look like this:
Component.razor
C# code:

<p>@CascadingAppState.AppState.Test</p>

@code{
    [CascadingParameter]
    public CascadingAppState CascadingAppState{ get; set; }
}
But I get an "Object reference not set to an instance of an object" error for CascadingAppState.AppState.Test

I think I'm missing something from CascadingAppState.cs to initialize or hook it up to AppState somehow? I thought maybe I needed to inject AppState as a dependency but it doesn't look like he has an @inject statement...

Any ideas?

e: you know what I think maybe the hybrid approach isn't what I wanted anyway so maybe it doesn't matter. I thought the point was that the CascadingParameter would take care of UI updates automatically and then there would be separate events in AppState for special circumstances. But it looks like you need events just to update the UI anyway so it's easier just to use a single AppState service I think and forget the CascadingParameter.

fuf fucked around with this message at 15:59 on Jan 26, 2022

epswing
Nov 4, 2003

Soiled Meat
I've got an ASP.NET (Framework 4.8) MVC site, and blob storage, both running on Azure. I'm displaying a list of files in storage that the user can click on to download. When they do, I'm basically doing this (this code is spread across a few classes, but I've boiled it down to one method in a controller):

C# code:
public async Task<FileResult> Download(string name)
{
    using (var stream = new MemoryStream())
    {
        CloudBlobContainer container = GetBlobContainer();
        CloudBlockBlob blob = container.GetBlockBlobReference(name);
        await blob.DownloadToStreamAsync(stream);
        byte[] bytes = stream.ToArray();
        return File(bytes, "application/octet-stream", name);
    }
}
The problem is obviously that, especially for large files, I download/buffer the entire file first (MemoryStream), and then feed it to the user. What I want to do is just stream it directly from Azure storage.

I've been attempting various solutions, but the usual googling is resulting in lots of different answers, half of which are .NET Core and don't apply, because the Azure storage libs are quite different. Which Stream should I be giving DownloadToStreamAsync? And how do I feed that back to the client without buffering?

mystes
May 31, 2006

epswing posted:

I've got an ASP.NET (Framework 4.8) MVC site, and blob storage, both running on Azure. I'm displaying a list of files in storage that the user can click on to download. When they do, I'm basically doing this (this code is spread across a few classes, but I've boiled it down to one method in a controller):

C# code:
public async Task<FileResult> Download(string name)
{
    using (var stream = new MemoryStream())
    {
        CloudBlobContainer container = GetBlobContainer();
        CloudBlockBlob blob = container.GetBlockBlobReference(name);
        await blob.DownloadToStreamAsync(stream);
        byte[] bytes = stream.ToArray();
        return File(bytes, "application/octet-stream", name);
    }
}
The problem is obviously that, especially for large files, I download/buffer the entire file first (MemoryStream), and then feed it to the user. What I want to do is just stream it directly from Azure storage.

I've been attempting various solutions, but the usual googling is resulting in lots of different answers, half of which are .NET Core and don't apply, because the Azure storage libs are quite different. Which Stream should I be giving DownloadToStreamAsync? And how do I feed that back to the client without buffering?
Can't you just pass the HttpResponse.OutputStream stream directly to DownloadToStreamAsync?

epswing
Nov 4, 2003

Soiled Meat

mystes posted:

Can't you just pass the HttpResponse.OutputStream stream directly to DownloadToStreamAsync?

Right, I was playing with Response.OutputSteam, something like this. I'm sure return File(Response.OutputStream, ...) is wrong. I get "System.NotSupportedException: Specified method is not supported"
C# code:
public async Task<FileResult> Download(string name)
{
    var container = GetBlobContainer();
    var blob = container.GetBlockBlobReference(name);
    await blob.DownloadToStreamAsync(Response.OutputStream);
    return File(Response.OutputStream, "application/octet-stream", name);
}
And this just hangs and the browser never shows the Save As dialog, whether I close the stream or not.
C# code:
public async Task Download(string name)
{
    var container = GetBlobContainer();
    var blob = container.GetBlockBlobReference(name);
    await blob.DownloadToStreamAsync(Response.OutputStream);
    //Response.OutputStream.Close(); ?
}
I'm sure you can tell that I'm kind of shooting in the dark now, but I can't seem to find the right doc or example. In both cases above, when I step through in VS, await blob.DownloadToStreamAsync(Response.OutputStream); blocks for the length of time it takes to download the file, so it appears to still be buffering anyways?

Potassium Problems
Sep 28, 2001
David Fowler tweeted about this the other day, I think this is what you're looking for?

https://twitter.com/davidfowl/status/1484985441832951810?s=21

mystes
May 31, 2006

I think response.body is the .net core version of response.OutputStream

epswing
Nov 4, 2003

Soiled Meat

Potassium Problems posted:

David Fowler tweeted about this the other day, I think this is what you're looking for?

Yep, that's what I want. But I don't know much about the newfangled .NET Core stuff. Really my question is how does this fit into a .NET Framework ASP.NET MVC controller?

Potassium Problems
Sep 28, 2001
Whoops, I completely missed the .NET 4.8 part. Sorry!

Munkeymon
Aug 14, 2003

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



epswing posted:

Right, I was playing with Response.OutputSteam, something like this. I'm sure return File(Response.OutputStream, ...) is wrong. I get "System.NotSupportedException: Specified method is not supported"
C# code:
public async Task<FileResult> Download(string name)
{
    var container = GetBlobContainer();
    var blob = container.GetBlockBlobReference(name);
    await blob.DownloadToStreamAsync(Response.OutputStream);
    return File(Response.OutputStream, "application/octet-stream", name);
}
And this just hangs and the browser never shows the Save As dialog, whether I close the stream or not.
C# code:
public async Task Download(string name)
{
    var container = GetBlobContainer();
    var blob = container.GetBlockBlobReference(name);
    await blob.DownloadToStreamAsync(Response.OutputStream);
    //Response.OutputStream.Close(); ?
}
I'm sure you can tell that I'm kind of shooting in the dark now, but I can't seem to find the right doc or example. In both cases above, when I step through in VS, await blob.DownloadToStreamAsync(Response.OutputStream); blocks for the length of time it takes to download the file, so it appears to still be buffering anyways?

We do this in one of our controllers and it's basically just
C# code:
return File(blob.OpenRead(), "text/yolo");
And IIRC that'll stream without loading the whole thing into memory.

Note that you can also make blob storage give you a temporary access token to append to the blob's Uri by calling CloudBlobContainer.GetSharedAccessSignature and redirect the client to to get the resource directly from there without your server having to do anything else.

Kyte
Nov 19, 2013

Never quacked for this

epswing posted:

I've got an ASP.NET (Framework 4.8) MVC site, and blob storage, both running on Azure. I'm displaying a list of files in storage that the user can click on to download. When they do, I'm basically doing this (this code is spread across a few classes, but I've boiled it down to one method in a controller):

C# code:
public async Task<FileResult> Download(string name)
{
    using (var stream = new MemoryStream())
    {
        CloudBlobContainer container = GetBlobContainer();
        CloudBlockBlob blob = container.GetBlockBlobReference(name);
        await blob.DownloadToStreamAsync(stream);
        byte[] bytes = stream.ToArray();
        return File(bytes, "application/octet-stream", name);
    }
}
The problem is obviously that, especially for large files, I download/buffer the entire file first (MemoryStream), and then feed it to the user. What I want to do is just stream it directly from Azure storage.

I've been attempting various solutions, but the usual googling is resulting in lots of different answers, half of which are .NET Core and don't apply, because the Azure storage libs are quite different. Which Stream should I be giving DownloadToStreamAsync? And how do I feed that back to the client without buffering?
One option is to use OpenReadAsync and pull data using FileStreamResult with Response.BufferOutput set to false.

Another option is to use DownloadToFile() to a temp file and then use Response.TransmitFile.

Or return a redirect to a SAS URI and the browser then downloads directly off Azure servers.

Or use DownloadToStream and point it to Response.OutputStream? I'm not sure what'd you need to do there wrt buffering and headers and whatnot.
You'd basically have to manually set up the headers, then do a loop with stream.Read(), Response.OutputStream.Write(), Response.Flush(), etc.

Kyte fucked around with this message at 01:50 on Jan 28, 2022

epswing
Nov 4, 2003

Soiled Meat
I think the redirect to SAS URI is probably the Right Way (isn’t this what it’s for?), and I don’t have to bother streaming the file through my server at all.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
Is there something like Python's casefold for strings in C#? ToLower() doesn't appear to work. Casefold will lowercase characters in other languages:

code:
>>> 'der Fluß'.casefold()
'der fluss'

raminasi
Jan 25, 2005

a last drink with no ice

Rocko Bonaparte posted:

Is there something like Python's casefold for strings in C#? ToLower() doesn't appear to work. Casefold will lowercase characters in other languages:

code:
>>> 'der Fluß'.casefold()
'der fluss'

Does the culture-specific overload do what you need?

No Pants
Dec 10, 2000

If what's in System.Globalization isn't what you're after, you probably need to go directly to Normalizer2 from ICU. Here's an example using icu.net:
C# code:
using Icu;
using Icu.Normalization;

Wrapper.Init();
var input = "der Fluß";
var output = Normalizer2.GetNFKCCasefoldInstance().Normalize(input);
Wrapper.Cleanup();
Console.WriteLine(output);
// outputs: der fluss

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
You should never use the ToLower() or ToUpper() overloads that don't take a CultureInfo argument in C#. That is because their behaviour is influenced by the user's locale, but it is easy not to realise this when reading the code. You should use (as appropriate to the use case) ToLowerInvariant() or ToUpperInvariant(), or an overload of ToLower() or ToUpper() that takes a CultureInfo argument.

A few years ago we released software to an important customer based in Denmark that used those overloads in internal serialization code. Some features were broken in certain locales, including Danish. Since then we implemented static analysis that errors on use of those overloads.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Actually, I think that I am wrong, it was double.ToString() that was the problem on that occasion. In the aftermath of that, it was nevertheless decided that we would disallow the ToLower() and ToUpper() overloads without CultureInfo.

redleader
Aug 18, 2005

Engage according to operational parameters
it always tickles me that the "invariant culture" is america

rarbatrol
Apr 17, 2011

Hurt//maim//kill.
I once discovered an infinite loop in our code, responsible for de-duplicating slashes in file paths. The IndexOf call (our while loop condition) wasn't aware of the current locale but the replace operation was, so it failed to replace two slashes separated by... I want to say it was the Mongolian vowel separator character.

insta
Jan 28, 2009

redleader posted:

it always tickles me that the "invariant culture" is america

Is it, or is it the machine's current culture?

Hammerite
Mar 9, 2007

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

insta posted:

Is it, or is it the machine's current culture?

It is based on EN-US conventions and is the same everywhere. If it were based on the machine's current culture then it would not be "invariant". The whole point of it is that it's the same (doesn't vary) regardless of the context in which the software is running, hence invariant.

NihilCredo
Jun 6, 2011

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

redleader posted:

it always tickles me that the "invariant culture" is america

System.Globalization.CultureInfo.IdgafAboutCulture

Polio Vax Scene
Apr 5, 2009



NihilCredo posted:

System.Globalization.CultureInfo.IdgafAboutCulture

uncultured swine

mystes
May 31, 2006

NihilCredo posted:

System.Globalization.CultureInfo.WhitePrivilege

Munkeymon
Aug 14, 2003

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



redleader posted:

it always tickles me that the "invariant culture" is america

Not (entirely) when you're running Core on Linux where it uses https://en.wikipedia.org/wiki/Currency_sign_(typography) for currency instead of $

full point
Jan 21, 2006

fuf posted:

This might not make any sense but I'm watching this video on state management in Blazor:

I'm not 100% certain but his Blazortrain series might cover that topic with more complete examples.

https://www.youtube.com/watch?v=BB4lK2kfKf0

https://www.youtube.com/watch?v=ib_6mYbkL2s

toiletbrush
May 17, 2010
I was looking at some 3+ year old internal library code at work yesterday that is breaking horribly. One thing that stands out is some ambient context that is held in a Dictionary wrapped in an AsyncLocal<...>. I get what async local is for and why we are using that rather than static or thread local (I think), but the weird thing is that it is defined as...
code:
Dictionary<string, AsyncLocal<string>> context = ...
...rather than...
code:
AsyncLocal<Dictionary<string, string>> context = ...
...which is what I would have expected. If the keys aren't async local, how does the storage point to values that are? I don't get how the non-AsyncLocal<> dictionary doesn't end up maintaining references to data that is either out of scope (once the async context is disposed) or leak memory. How come it's done like this?

No Pants
Dec 10, 2000

I can see that being adequate if it's being passed around, especially if you aren't adding keys. The AsyncLocal<>.Value property's get/set methods retrieve and store the actual references to the values on the current ExecutionContext, so there shouldn't be a memory leak concern. (Maybe if you're doing something weird with the event handler delegate, as that's the only state stored by an AsyncLocal instance.)

fuf
Sep 12, 2004

haha
Does anyone have big thoughts on Rider vs Visual Studio, especially for blazor development?

I keep flipping between the two and can't really settle on one.

Rider seems to be a lot faster and smoother than VS.

But one thing I really like about VS is the code suggestions. Not just normal intellisense but when it will suggest an entire line for you based on the previous lines. Sometimes I'm amazed how accurate and helpful it is. I can't figure out if Rider has something similar?

I use the vim extensions for both which adds some complications. I sort of half-learned vim about 15 years ago and now I'm stuck with it even though I'm not that good at it.

Just-In-Timeberlake
Aug 18, 2003
Can anybody provide some insight as to why this FileSystemWatcher code won't fire on change or rename events, but works just fine for the created event? This folder is an FTP folder (network share, maybe that?) that customers upload files to, there's a delay before processing since some files are large and I want the files to be fully uploaded before trying to process them. I need the rename/change to fire because some FTP clients will upload with a .filepart extension, which then gets changed to the actual filetype extension, which then throws an error because the e.FullPath still has the .filepart extension on the raised create event but that file no longer exists so you get a System.IO.FileNotFoundException. Or maybe there's some other way to deal with that scenario I'm unaware of.

code:
        protected async void OnChanged(object source, FileSystemEventArgs e)
        {
            List<String> acceptedFileTypes = new List<string>() { ".csv", ".txt", ".json" };

            if (e.ChangeType == WatcherChangeTypes.Created				/* fires */
                || e.ChangeType == WatcherChangeTypes.Renamed			/* does not fire */
                || e.ChangeType == WatcherChangeTypes.Changed)			/* does not fire */
            {
                try
                {
                    int delay = 20000;
                    await Task.Delay(delay);

                    if (acceptedFileTypes.FindAll(x => x == System.IO.Path.GetExtension(e.FullPath)).Count > 0)
                    {
                        //do some things

                    }

                }
                catch (Exception ex)
                {
                    // something bad happened that stops the service from starting
                    Log.ForContext("MethodName", System.Reflection.MethodInfo.GetCurrentMethod()).Error("{ex}", ex);

                }

            }
            
        }
These are the notify filters I have assigned:

code:
		NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastWrite | NotifyFilters.FileName |
                                      NotifyFilters.DirectoryName | NotifyFilters.LastAccess | NotifyFilters.LastWrite
e: figured it out, wasn't binding the correct event handler

Just-In-Timeberlake fucked around with this message at 15:57 on Jan 31, 2022

Red Mike
Jul 11, 2011

fuf posted:

Does anyone have big thoughts on Rider vs Visual Studio, especially for blazor development?

I keep flipping between the two and can't really settle on one.

Rider seems to be a lot faster and smoother than VS.

But one thing I really like about VS is the code suggestions. Not just normal intellisense but when it will suggest an entire line for you based on the previous lines. Sometimes I'm amazed how accurate and helpful it is. I can't figure out if Rider has something similar?

I use the vim extensions for both which adds some complications. I sort of half-learned vim about 15 years ago and now I'm stuck with it even though I'm not that good at it.

Assuming there isn't licensing/cost issues, just pick one and stick with it. They're similar enough that you can work one if you know the other, but also once you're familiar enough with either of them you tend to be more productive in it than if you keep flipping back and forth and only know how to use the features they have in common.

Using Rider will let you be more productive with features like its DataGrip-like integration, the various tooling integrations, some specific plugins, as well as custom run config/launching/etc configurations, etc etc.
Using VS will let you be more productive with intellisense/roslyn-related integrations, better integration of source generators and some extensions around containers/etc, as well as better integration into certain platform you might be using (like Azure), etc etc.

The only "big" thing in my mind besides that would be if you're having to work with legacy/specific tech where VS works correctly and Rider sometimes breaks. You mentioned Blazor, but even with Razor I've had nothing but issues in bigger projects. For example, right now in Rider I've got a .cshtml without anything fancy except for a local function used for templating; that just breaks the parsing so Rider keeps insisting there's wrong symbols/it won't build, but it builds it fine. Apparently it's RIDER-34875 which has been open for 2 years and isn't any closer to a fix (and has an unsuitable workaround).

Adbot
ADBOT LOVES YOU

fps_nug
Feb 21, 2021

horsing around no longer
just spent my first serious 5 hours since high school learning programming, decided to start with c# for no other reason than accessibility. I learned on python and now that I have an incredibly rudimentary knowledge do you guys have any ideas of where to go from here?

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