This document compares a developer's experience with an ASP.NET Core 2.0 application in two development environments: JetBrains Rider and Microsoft Visual Studio. For the purposes of providing an all-around, detailed comparison, the document walks through the various coding demos available in Scott Allen's ASP.NET Core Fundamentals course (as of November 9, 2017) on Pluralsight.
The steps outlined were taken in Visual Studio 2017 15.7.4 (RTM) and various Rider 2018.2 pre-release builds.
- Solution Explorer:
- Multimonitor support: all UI related to creating a project is shown on a single display.
- In the New Solution wizard, I can't select a directory using the updated Open File dialog; I have to
enter the path to parent directory manually in the Solution directory text box, and no recent directories
are available:
- Multimonitor support: Rider opened on display 1, New Solution wizard opened on display 2, then Select Solution Directory (Open File) opened on display 1 again.
- Initial IDE layout:
All good. Built, ran using the default IIS Express launch profile. Output window shows output from ASP.NET Core Web Server, server says listening on port 17570, then requests going through a different port (54448) set in generated applicationhost.config, default browser (Chrome) automatically opens with the correct URL.
Links from the Output window can be Ctrl+clicked, which is a tiny bit worse than what Rider does.
Stopping is not clear: can't Ctrl+C in the Output window, and no Stop command is
available in the Debug menu. The application can be either rerun from Visual Studio, or stopped
using the separate IIS Express UI:
Accepted Default run configuration settings, built, ran.
Run tool window says listening on port 5000, further requests going through the same port.
Browser not automatically opened - however, clicking the link from the Run window works to open in default browser.
Stopping is clear: works with Ctrl+C in the Run tool window, as well as with the Stop Default command.
I assume that the differences in start/stop experience are due to Visual Studio using IIS Express as a proxy to Kestrel and Rider using Kestrel directly.
A project file can be opened in the text editor via right-click > Edit project file.
Quick Info tooltip is available on hover for valid .csproj elements:
A project file can be opened in the text editor via F4 or via right-click > Edit > Edit ....csproj.
No Quick Info is available on elements.
Right-click a project node > Add > New Item (or Ctrl+Shift+A) > ASP.NET Core
> ASP.NET Core Configuration File (renamed to App Settings File in later VS
versions). This provides a predefined name (appsettings.json), and the default file template contains a
ConnectionStrings: DefaultConnection
setting:
Right-click a project node > Add > JSON file. Just a generic JSON file template provided by WebStorm. No predefined name, empty content.
Good editing experience in JSON and C#. Complete Statement doesn't work in JSON though, and importing a
reference for IConfiguration
is less obvious/comfortable than in Rider.
Good editing experience in JSON and C#. Import popup more useful than explicitly calling Ctrl+.
in Visual Studio for importing a reference for IConfiguration
.
All fine. Visual Studio handles Create from Usage and implementing interface members in a derived class, although not exactly as polished as Rider - good enough though:
- Visual Studio does allow implementing
IGreeter
from usage when theGreeter
class has been declared; however, Rider does have the advantage of providing the Create derived type context action atIGreeter
declaration. - Visual Studio's Create from Usage isn't polished enough right now: given the undeclared
IGreeter
interface in method parameters and the below line that uses an undeclared method,- The created symbol doesn't get focus: both when it's created in a separate file and when it's created in the same file;
- Roslyn doesn't infer that the generated method should return a string, not an object.
All fine. Regular C# coding that involves Create from Usage and implementing interface members in a derived class.
Good enough. Regular C# editing with certain issues both in Rider and Visual Studio. For example, VS
completion breaks down after the await
keyword - it just no longer suggests anything. Workaround:
type a sync statement first, then add the await
keyword. Rider handles this just fine. See
commit for details and context.
Good enough. Regular C# editing with certain issues both in Rider and Visual Studio. For example, Rider's
completion doesn't expect an identifier after the async
keyword, so it starts to suggest
weird classes, and this is where completion on space hurts: I wanted to type an unresolved symbol,
context
, but completion triggered on Space and inserted the unwanted
ContextBoundObject
class. A similarly bad completion experience on unresolved symbol just below: I wanted to use the undeclared
logger
to add it as parameter later, but got Logger<>
completed instead.
See commit for details and context.
Additionally, Rider displays an annoying Parameter Info tooltip in random spots inside the delegate:
Good enough in terms of C# editing.
Launch settings are natively supported, as opposed to Rider.
appsettings.Development.json was automatically nested under appsettings.json and didn't require any manual configuration.
Good enough in terms of C# editing, although the Use string interpolation context action wasn't available when I needed it:
Rider doesn't pick environment or any other settings from launchSettings.json because it doesn't
support launch settings: neither are they created with the ASP.NET Core template nor are they reflected
in run configurations. Instead, environment variable settings in the default run configuration must be
edited to switch to a different environment:
Creating appsettings.Development.json:
- Creating via copy-paste of appsettings.json works a little better than in Visual Studio because Rider suggests entering a name for the copy before actually creating it, and then suggests adding the new file to Git.
- However, Rider doesn't by default nest appsettings.Development.json under appsettings.json
as Visual Studio does. I had to click File Nesting Settings in the toolbar and manually enter
a nesting rule: parent: .json, child: .Development.json.
Neither in Visual Studio nor in Rider was I able to replicate Scott's exercise that involved displaying a
Greeting
value from appsettings.Development.json - probably a configuration mishap by me.
All good. Involves regular C# editing + creating an HTML file.
All good. Involves regular C# editing + creating an HTML file.
Live template for HTML file in Rider is slightly better as it places a hotspot at the value of the <title>
tag, and then Rider suggests adding the new file to Git.
Along with creating an HTML file, Visual Studio removes a section that includes the wwwroot folder from the .csproj file. Rider doesn't do that. Unsure if this has any side effects so far.
Viewing NuGet dependencies in Solution Explorer works fine.
Adding a directory in Visual Studio is harder: for some reason, Visual Studio's New folder is only available as a
separate contextual command, and not as an item in Add New Item (Ctrl+Shift+A
).
Viewing NuGet dependencies in Solution Explorer works fine. Manage NuGet Packages command is available in contextual menu for more nodes of the Dependencies tree than in Visual Studio, which is fine.
Adding a directory is easier in Rider as it can be done via Alt+Insert
, along with adding
new files.
Rider's lowerCamelCase and CamelCase completion matching is inferior to Visual Studio in simple cases
like the below. Visual Studio suggests the expected UseStaticFiles()
method matching usf
;
Rider suggests UseDefaultFiles()
instead, both when matching with usf
and
USF
(!!!!):
vs
All fine with C# editing and adding a new controller.
All fine with C# editing and adding a new controller.
C# editing around attributes works as expected.
Editing C# attributes works fine; however, import completion for the [Route]
attribute is severely hanging:
Action results: deriving from the Controller
base class, modifying an
action to return IActionResult
, creating a Restaurant
model, instantiating and
returning a model instance from the controller
All good in terms of editing C# and creating a new directory and a new model (except that creating a new directory is a separate action - see one of the above Visual Studio notes).
All good in terms of editing C# in the controller and creating a new directory for models and a new model in it (tested using Move context action and refactoring).
Visual Studio is lagging behind severely in this scenario:
- You can't create a view from usage, at all.
- This means if you're creating the default initial folder structure for models, you have to create a directory twice (separate action), and then use Add New Item to create a view.
- In the view, Scott right away removes the default content and uses the html template to roll out a barebones HTML structure. (This template isn't available in Rider but it's not needed there because Rider's default template for a Razor view is way better and already contains an HTML skeleton.)
When manually typing a model (@model Restaurant
), Visual Studio doesn't suggest importing the model
namespace - you need to manually type in an FQN.
Visual Studio doesn't provide Expand/Contract Selection commands in Razor views.
Visual Studio's completion only suggests the uppercase @Model
property outside of the imports; however,
it doesn't suggest anything when you type the lowercase @model
, which means Rider's problem with
suggested casing isn't as serious as it looks, because there's no typing habit for Rider completion to break when
dealing with Visual Studio typing patterns.
Rider is superior in this scenario: creating a view from usage (return View(model);
) in the
controller:
- Creates the entire folder structure for views along with the actual view.
- The view is auto-typed with the model (if the model is provided) via a
@model
directive. @using
directives are automatically inserted, and a hotspot is placed at the bare HTML's<title>
tag value.
One small hiccup is that when importing the model, FQN is used both in the model and in the using statement whereas it's only required in the using statement.
If a Razor view is created using a file template, the resulting view is more complete as well.
When manually typing a model (@model Restaurant
), Rider auto-imports the model namespace;
Visual Studio doesn't do this.
Rider makes Extend/Shrink Selection available in Razor views whereas Visual Studio doesn't.
Other than that, when using the imported model in Razor markup, it's quite annoying that completion
suggests @model
when model is typed, even though the lower-case @model
is only
applicable as the import statement, and @Model
should be provided instead.
Moving IGreeter.cs to the new Services directory by drag-n-drop in Solution Explorer.
Requires updating the namespace in IGreeter
, then compiling and going through several CS0246
build errors to add a missing using directive in Startup.cs.
Subpar manual experience as Visual Studio doesn't have a Move to Folder refactoring (in fact, no Move refactorings at all, and no refactorings available on Solution Explorer nodes.)
Creating a new directory, again, is a bit easier. Rider modifies the .csproj file with a new folder include but Visual Studio doesn't do that: again, unclear differences in project model handling although it looks like Rider uses Visual Studio 2017's MSBuild distribution.
Moving IGreeter.cs to the new directory can be done with drag-n-drop, in which case Rider's VCS integration interprets the operation as a move (whereas the Visual Studio's drag-n-drop is seen as a file deletion + file addition). However, moving with drag-and-drop in Rider seems to introduce differences in line endings!
The better way to move IGreeter.cs to the new directory is certainly via Refactor This > Move
to Folder on the file node in Solution Explorer. Invoking the refactoring with default settings (that
include fixing namespaces) just works. Good job:
Creating new services: IRestaurantData
,
InMemoryRestaurantData
, registering one of them in Startup.cs, modifying the Home
controller to receive restaurant data from an IRestaurantData
service, updating the view to
accept an enumerable model and iterate through the collection of restaurants
Visual Studio does it all fine actually; yellow here just means that Rider is significantly better during this coding segment.
- Creating
IRestaurantData
via Ctrl+Shift+A: fine. - Creating the derived
InMemoryRestaurantData
class: also has to be done with Ctrl+Shift+A because there's no action to create a derived type. - Writing code in
InMemoryRestaurantData
: all fine, with a few notes:- When modifying the class declaration to implement
IRestaurant
, Visual Studio provides Implement interface and Implement interface explicitly quick actions. Nice. - Scott creates an
InMemoryRestaurantData()
constructor with thector
code snippet - nice, but that's the only option with Visual Studio as there's no context action on a field to initialize the field from constructor.
- When modifying the class declaration to implement
- Modifying the Home controller: good. Notes:
- Visual Studio doesn't provide import items in completion, which means that when referencing a
non-imported type, you have to make sure to spell and capitalize it correctly, and then use a
quick action to add an import. In Rider, import items are available in completion, which
allows using camelHumps and abbreviations without being precise with naming, and
additionally, accepting an import symbol suggestion adds the necessary
using
statement without the need to explicitly invoke a quick action. - Visual Studio provides a set of quick actions to generate
_restaurantData
(as a full or read-only field, full or read-only property, local variable), as well as explicit actions to change_restaurantData
toIRestaurantData
orrestaurantData
:
- Visual Studio doesn't provide import items in completion, which means that when referencing a
non-imported type, you have to make sure to spell and capitalize it correctly, and then use a
quick action to add an import. In Rider, import items are available in completion, which
allows using camelHumps and abbreviations without being precise with naming, and
additionally, accepting an import symbol suggestion adds the necessary
- Modifying the Home view: good. Notes:
- There's a table code snippet to generate an HTML table in Razor markup, nice.
- However, there's no foreach code snippet, just keyword completion.
- Creating
IRestaurantData
via Alt+Ins: good, and again, Rider suggests to add the new file to Git right away. - Declaring an interface member: Complete Statement at
GetAll{caret}
generates both the parentheses and the semicolon, Visual Studio doesn't do this. - Creating
InMemoryRestaurantData
class: can be done with Alt+Ins but can also be done easier with a context action onIRestaurantData
declaration to create a derived type:
Then, there's a quick-fix to implement the interface, and a context action to move to a separate file. - Writing code in
InMemoryRestaurantData
: good. Notes:- Quick-fix Initialize field from constructor is available after declaring the
_restaurants
field:
However, the created constructor takes a list of restaurants as a parameter; what we need instead is a parameterless constructor with a field inside that is initialized with a new list. No context action or refactoring to convert parameter to field initialization, and the Change Signature refactoring doesn't do that, too. No big deal to do this by hand though. - Rider's code completion after the
new
keyword in collection initializer results innew Restaurant()
, but the parentheses become redundant once braces are added for initializing properties. A typing assistant that removes redundant parentheses wouldn't hurt here.
- Quick-fix Initialize field from constructor is available after declaring the
- Modifying the Home controller: great! Notes:
- This time, after declaring a field and calling the Initialize field from
constructor quick-fix, it does exactly what we want: declares a constructor with a
IRestaurantData
parameter. Nice! - After modifying the parameter returned in
View()
to a collection of restaurants, Rider shows an error and suggests to modify the type of the view. Super nice!
- This time, after declaring a field and calling the Initialize field from
constructor quick-fix, it does exactly what we want: declares a constructor with a
- Modifying the Home view. Works, however:
- No live template for an HTML table in Razor views. As a side note, there is one in HTML
files handled by WebStorm, but it only generates a
<table>
tag pair, with no rows or cells inside - Visual Studio does better here. - No
foreach
live template in Razor, only keyword completion - same as in Visual Studio. Also, no way to surround markup with braces.
- No live template for an HTML table in Razor views. As a side note, there is one in HTML
files handled by WebStorm, but it only generates a
Again, Visual Studio does a good job, it's just Rider that does it better.
- Creating a directory and a file for the first view model: all fine.
- Modifying the controller: fine. Initialize field from constructor would be handy but it's not there. As
a side note, Visual Studio now provides a quick action to use object initializer - the action is available from
the constructor call only though, not from variable usages:
- Modifying the view: decent. Had to change model type by hand, after which it took Visual Studio ~30 seconds to
re-resolve the
Model
inforeach
, figure out it's now aHomeIndexViewModel
and finally start suggesting view model properties in code completion.
- Creating a directory and file for view model: good (both can be created from Alt+Ins and using a Git add suggestion.) Again, import items in completion rock!
- Modifying the controller: great. Initialize field from constructor rocks after declaring the
_greeter
field! Use object initializer is available both on constructor call and on further variable usages. The Change view model type quick-fix rocks again! - Modifying the view: considerably better than Visual Studio because the model type has already been
changed for us, and because
Model
is resolved quicker, with valid completion suggestions available instantly.
Creating a new Home controller action Details(int id)
, updating services
with a new Get(int id)
method to return details for a particular restaurant, creating a simple
Details view, updating the Index view to use tag helpers and creating a required _ViewImports.cshtml
along the way.
Visual Studio does the job but in a lot less intelligent way than Rider.
- Creating a
Details(int id)
action in the Home controller: decent.- Some code completion issues in Visual Studio when using a new method
Get(id)
before declaring it; creating the method in the interface from usage works (doesn't insert an implementation stub in the derivedInMemoryRestaurantData
though). - Returning
RedirectToAction("Index")
works but Visual Studio doesn't provide code completion for actions in the string literal, and Scott prefers to usenameof(Index)
instead; when returning aView(model)
. - Visual Studio doesn't see that the view doesn't exist and doesn't suggest to create one.
- Some code completion issues in Visual Studio when using a new method
- Updating services with a new
Get(int id)
method: fine. When going to the derivedInMemoryRestaurantData
, Visual Studio does provide a quick action to implement the new interface method. - Creating the Details view: OK. No completion for controller and action names in tag helpers though.
- Creating _ViewImports.cshtml: OK, using a specialized item via Ctrl+Shift+A. However, Visual Studio provides no completion for the assembly name refrenced from _ViewImports.cshtml.
- Modifying the Index view to render links to restaurant details using 3 different kinds of
syntax:
<a href>
,@Html.ActionLink
and a tag helper. Visual Studio provides completion for tag helper attributes but, again, there's no code completion for controller and view names in string literals - only for C# symbols after@
.
Mixed result: Rider shines with a few great features in this segment but there's a bunch of bugs as well.
- Creating a
Details(int id)
action in the Home controller: OK.- Similar code completion issues in Rider. Creating a method from usage works and also doesn't insert an
implementation stub in the derived class; however, provides a placeholder to change the default return
type from
object
to something else (Restaurant
) in our case. - A null check on model is easier to introduce with the Check variable for null
context action that is conveniently available at the end of the statement; a quick path to
null check pattern settings is provided, too - nice!
- When returning
RedirectToAction("Index")
, Rider provides code completion for actions in the string literal that is passed over as parameter as well as navigation to action, nice!
Usingnameof(Index)
is a bit problematic due to a ReSharper bug (RSRP-469876) but if it's just typed in without completion, Rider actually continues to provide navigation to the view!
- When returning a
View(model)
, Rider detects that the referenced view is missing and suggests to create it:
- Similar code completion issues in Rider. Creating a method from usage works and also doesn't insert an
implementation stub in the derived class; however, provides a placeholder to change the default return
type from
- Updating services with a new
Get(int id)
method: fine. There actually is a (poorly discoverable) context action to implement the new interface method in derived classes:
Good to know it's here but I'd expect a quick-fix instead. All in all, fine editing in both services. -
Creating the Details view: good, with a few quirks!
- Created with a quick-fix from controller (see above) - nice!
-
However, model completion suggests an unqualified
Restaurant
type, which then triggers an import action, and all that ends up with FQNs in both@using
and@model
directives - which, in turn, requires removing FQN from the@model
directive with a quick-fix. Also, the file template's active hotspot in the<title>
tag prevents from calling Alt+Enter on the@model
directive - you need to fill the hotspot first of all. ModelExpressionProvider
annoys as the first completion suggestion for both@mod
and@Mod
- should be@Model
instead!- Trying to write a tag helper but there's no completion for
asp-*
attributes and their values. This is because there's no _ViewImports.cshtml, and Rider doesn't provide a quick-fix to create one! - However, after creating _ViewImports.cshtml (see next step), Rider starts to provide completion
for both tag helper attributes (
asp-action
and such) and their values, nice!
- Creating _ViewImports.cshtml: with hiccups as there are no cshtml items in Alt+Ins on the Views
folder (only on nested folders). Workaround: use a generic File file template. After creating _ViewImports.cshtml,
there are no completion suggestions for assembly name in Rider, just like in Visual Studio :( - completion for
the
@addTagHelper
directive is available though. - Modifying the Index view to render links to restaurant details using 3 different kinds of syntax: fine,
with a few hiccups:
- When entering the regular anchor with relative paths, Rider complains it doesn't recognize the relative
path, suggests to set path mapping, then nothing happens but the inspection is gone. Trying to edit path
mappings fails silently:
- Action link syntax: again, action resolve in string parameter; however, completion can be
improved:
- Tag helper syntax: good! Rider even suggests
asp-route-id
that is derived from theDetails
action signature - something that Visual Studio doesn't do:
- When entering the regular anchor with relative paths, Rider complains it doesn't recognize the relative
path, suggests to set path mapping, then nothing happens but the inspection is gone. Trying to edit path
mappings fails silently:
Visual Studio does the job but Rider provides a lot more automation in this scenario.
- Link from Index.cshtml to a new
Create()
action: Visual Studio doesn't detect that there's no action yet. I need to create it manually in the next step. - Creating a new
Create()
action in the Home controller. When returningView()
, Visual Studio doesn't see there's no view yet. - Creating a new
CuisineType
enum in Models: adding a new class, then changing it toenum
and populating. OK. - Adding a
CuisineType
property to theRestaurant
model with the prop snippet: OK. - Creating a Create.cshtml view manually. Notes:
- There are no import suggestions for non-FQN types when declaring a model, have to type in the FQN by hand, although code completion does help with FQN.
- Snippets are available for
form
,select
, andinput
, nice! - Completion of model properties in tag helpers such as
asp-for
,asp-items
- nice!
- When referencing the
CuisineType
enum, Visual Studio doesn't provide an import suggestion, have to go up and type a@using
by hand.
Even though there are code analysis and code completion bugs when editing a view, the overall experience in this scenario is much richer than in Visual Studio.
- Link from Index.cshtml to a new action: great! Rider sees that there's no
Create()
action yet, and provides a quick-fix to create one:
- New
Create()
action in Home controller auto-created with the quick-fix. Now, adding a reference to a view that doesn't exist yet - Rider detects this, suggests to create one, and it's there now (and added to Git). Great! - Creating a new
CuisineType
enum in Models: can use the enum file template right away. -
Adding a
CuisineType
property to theRestaurant
model: OK. - Writing code in the Create.cshtml view (that was auto-created for us at step 2 above):
- No issue with model import as we referenced the model when the view was created, via a hotspot.
- No snippets for
form
,select
,input
are available. - Completion list for the
asp-for
attribute can be improved:
However, completion for model fields inasp-for
value is available! - Completion for
CuisineType
is available:
However! When using the@
prefix ahead of the expression in theasp-items
value (which compiles fine), Rider shows a bogus Cannot choose method from method group error and fails to resolve the type parameter. When the@
prefix is not used, Rider doesn't complain, and the application still compiles and runs (RSRP-469518) - Completion for
input
types is available:
Commit links:
Accepting form input: adding a restaurant edit model, modifying services, the Details view, and creating a new Home controller action
Visual Studio does the job with less automation but Rider's bugs set back its functional advantages. Tie in this scenario.
- Creating a
Create()
action overload in the Home controller to convert input data into a new restaurant item. - Creating a
RestaurantEditModel
input model in ViewModels via Add New Item - although this in fact could be created from usage in the Home controller, in which case the file would have been created in the Controllers folder and then should have been moved to ViewModels (and we know Visual Studio can't modify namespaces on drag-and-drop). - Modifying the new
Create()
action in the Home controller to receive aRestaurantEditModel
, all good. - Adding route constraints (attributes) to the two
Create()
actions. - Processing input model in the input
Create()
action. Using anAdd()
method fromIRestaurantData
that is not declared yet, and generating it from usage. - Implementing the
Add()
method inInMemoryRestaurantData
: Go to Implementation fromIRestaurantData
interface declaration (because there's no Go to Implementation on specific members), and using the Implement interface quick action to create a stub. Writing implementation code. - Modifying the Details.cshtml view to show more data on the restaurant entry that we've just created.
Rider's workflow is better automated but there are annoyances along the way. They were reported earlier, so let's say this is a tie with Visual Studio.
-
Creating a
Create()
action overload in the Home controller: OK. CreatingRestaurantEditModel
from usage. Created in the Home controller, then moved to a separate file with a context action (still under Controllers though), then Refactor This > Move to Folder to move the new file to ViewModels. Quite a complicated chain of actions but works, and Move to Folder does auto-adjust the namespace by default. - Creating properties in
RestaurantEditModel
- good. - Using Implement in derived classes CA in
IRestaurantData
to create an implementation stub inInMemoryRestaurantData
and navigate there - great! Writing implementation logic - good. - Adding route constraint attributes to the two
Create()
actions: hanging completion strikes again. - Can navigate from the Home controller to Details.cshtml to modify the view. Modifying the
view: OK, although, again, completion for
@Model
is annoying.
Just a one-line change to the return of the POST-specific Create()
action: OK. Used nameof
for better completion because Visual Studio doesn't suggest view names in string literals.
Just a one-line change to the return of the POST-specific Create()
action: OK. Used string
literal for view name instead of the nameof
approach in Visual Studio because of the
nameof
completion bug referenced somewhere above.
Adding model validation with data annotations in models, checking for valid model state in controller, and adding validation tag helpers in view
Visual Studio is less feature-rich but more stable. Tie again.
- Updating Create.cshtml to add labels to form items. Wrapping tags with a div: Shift+Alt+W in Visual Studio. However the action is not reformatting the resulting markup, and as Extend Selection doesn't work in Razor, I have to select manually, then invoke Ctrl+K,F to reformat the selection.
- Updating the
Restaurant
model and theRestaurantEditModel
view model with data annotations, using a quick action to importSystem.ComponentModel.DataAnnotations
. - Modifying the POST-specific
Create()
action to check if the model being passed is valid. Scott wraps a code selection with anif/else
manually, although there are code snippets in Visual Studio. - Updating Create.cshtml with placeholders for possible error messages using validation tag helpers.
- Adding the
ValidateAntiForgeryToken
attribute to the POST-specificCreate()
action.
Rider is better in terms of features, but the attribute completion tag is starting to annoy the hell out of me, thus a tie. Fix this, and Rider's going to be seriously better in this segment.
- Updating Create.cshtml to add labels to form items and placeholders for validation messages. Good. Completion for tag helpers and values works well; Surround with template lets me group label, input and span in a div. No auto-formatting on wrap but since Rider's Extend Selection works in Razor views, I can use that and then invoke Reformat Code on the selection.
- Updating the
Restaurant
model and theRestaurantEditModel
view model with data annotations. Hanging attribute completion strikes again in both classes! (RIDER-16880), apart from this all fine. Context actions to move attributes between sections are nice!
- Modifying the POST-specific
Create()
action to check if the model being passed is valid. All good. Extend Selection to the entire method body, then wrapping with an if statement using Surround With. After that, either use an else template to return a view if model state is invalid (in which case theelse
block will be highlighted as redundant), or use a quick-fix on the Return statement is missing inspection to add a return:
- Adding the
ValidateAntiForgeryToken
attribute to the POST-specificCreate()
action: all fine.
- The SQL Server Object Explorer view in Visual Studio shows available LocalDB instances:
- Although this is quite clumsy, you can get a connection string by opening properties of an instance:
- Go to Database > Add Data source > SQL Server.
- Try to set up a connection to SQL Server Local DB, give up, drop using Rider (DBE-6705).
(EF Core is already installed as part of Microsoft.AspNetCore.All.)
- Searching for installed packages in Solution Explorer > project > Dependencies: OK
- Installing new packages via Dependencies > Manage NuGet Packages: OK.
- Editing .csproj to install Microsoft.EntityFrameworkCore.Tools.DotNet as a
DotNetCliToolReference
: OK, although tag name completion is very basic (no camelHumps support). - Executing
dotnet ef
commands: via system shell or via bundled Package Manager Console.
(EF Core is already installed as part of Microsoft.AspNetCore.All.)
- Searching for installed packages in Solution Explorer > project > Dependencies: OK
- Installing new packages via Dependencies > Manage NuGet Packages: OK.
- Editing .csproj to install Microsoft.EntityFrameworkCore.Tools.DotNet as a
DotNetCliToolReference
: OK. Tag name completion is better (has camelHumps support). Highlighting for empty body of an XML tag is annoying (funnily, it goes away on introducing a line break):
- Executing
dotnet ef
commands: via system shell or via bundled Terminal window.
Visual Studio does the job despite different commands to create items, no import completion.
- Creating a new Data directory and an
OdeToFoodDbContext
class in it. Again, two different commands to create a directory and a class. Again, since there are no import items in completion, you have to be careful to type inDbContext
exactly and properly cased before Visual Studio suggests to add an import statement with a quick action. - Creating a new
SqlRestaurantData
service to use instead ofInMemoryRestaurantData
. New class is created via Ctrl+Shift+A as there's no action to create a new derived class form an existing interface. - Implementing
SqlRestaurantData
. The Implement Members action generates stubs, then it's regular code editing inside the stubs + creating a constructor with ctor, and a field to store the context with another quick action. All good.
Rider provides more automation and more convenience with its import items in completion; however, the Space completion behavior kind of counters the benefits.
- Creating a new Data directory and an
OdeToFoodDbContext
class in it. Both with Alt+Insert, Git addition suggested. Good.DbContext
is available as an import item in completion, imported seamlessly. ctor, prop live templates work well. -
Creating a new
SqlRestaurantData
service to use instead ofInMemoryRestaurantData
. Better because you can navigate toIRestaurantData
and invoke a context action Create derived type, then implement missing members, then use another context action to move to a separate file, and Rider suggests adding it to Git. Nice. - Implementing
SqlRestaurantData
. When adding a constructor and adding a parameter to it, you can Alt+Enter on the parameter to generate a read-only field, which is fine; but another approach would be to start typing an undeclared field to create from usage later, and this is where Rider's stupid space completion ruins the act: pressingSpace
at this point will complete the irrelevant library type instead of allowing me to type in a yet-to-be-declared field:
There's also a problem with Complete Statement with lambdas that I ran into (RSRP-470502) but it's unlikely to be a big deal for anyone unless they're using Complete Statement all the time.
Otherwise C# editing is just fine.
- Modifying appsettings.json to have a valid connection string (Visual Studio-specific DB): OK.
- Modifying Startup.cs to update available services and get access to connection string in appsettings.json. Fine. (Importing the unimported is a bit annoying, though, again.)
- Modifying appsettings.json to have a valid connection string (Rider-specific DB): OK.
- Modifying Startup.cs to update available services and get access to connection string: very fine. Import completion items help; creating a constructor with Alt+Ins, adding a parameter + a quick-fix to create a field works, unless I want to start with using an undeclared field (then I'd be hit by the Space completion problem described above.)
Commit links:
- Executing
dotnet ef migrations add InitialCreate
via Windows Command Prompt: OK. - Executing
dotnet ef database update
via Windows Command Prompt: OK. - Checking that the database has been created via SQL Server Object Explorer in Visual Studio: OK.
- Executing
dotnet ef migrations add InitialCreate
via the Terminal window: OK. - Executing
dotnet ef database update
via the Terminal window: OK. - Checking that the database has been created via the Database window. Spent an hour trying
to figure out why I don't see the right databases in the instance I'm connected to, then
accidentally clicked the DB filter (:facepalm:):
- SQL Server Object Explorer > DB > table > right-click > View Data - edit data in the editor, launch the application, see the data. Changes are committed to the DB on-the-fly. Intuitive Excel-like editor UI:
- Adding a new restaurant from application UI > refreshing DB editor in Visual Studio > new data is shown.
- Database view > find DB > table > press F4 to edit. Works fine if LocalDB is active; cryptic
error messages otherwise (had to refresh from Visual Studio to make the errors go away):
Editor is OK although Alt+Ins to add a row is less intuitive than in Visual Studio. To send added/modified data to database, got to click Submit.
- Adding a new restaurant from application UI > refreshing DB editor in Rider > new data is shown. Fine.
Visual Studio does the job without notable issues.
- Creating a Shared folder and a layout view (Add New Item -> Razor layout, named _Layout.cshtml by default) inside. Two different commands but OK, at least the layout item template is there.
- Removing redundant markup from Index.cshtml. Then adding a title via
ViewBag
, as well as a path to layout (no path completion). - Adding a
@RenderSection
block to _Layout.cshtml: OK. - Adding a footer section to the Index view: OK.
Rider does the job but there are issues in file templates and completion.
- Creating a Shared folder and a layout view inside. Both folder and file are created via
Alt+Ins, which is fine. However, there's no file template for a Razor layout! Workaround:
create a regular view, call it _Layout.cshtml, remove the
@model
directive, add@ViewBag.Title
and@RenderBody
. - Removing redundant markup from Index.cshtml.
Then adding a title via
ViewBag
, as well as a path to layout. For layout path, there's reference completion that suggests available views, and this looks fine; HOWEVER, if I go Scott's way and start entering a file path from application root, the reference completion will hinder and eat the entered path as soon as completion is committed:
- Adding a
@RenderSection
block to _Layout.cshtml: OK. - Adding a footer section to the Index view: OK.
Time and again, Rider seems to reformat .csproj files automatically on adding new files. Looks like RIDER-6450.
All goes as expected in Visual Studio
- Creating _ViewStart.cshtml in the Views folder: there's a special Razor View Start file template with the correct name and default content. All fine.
- Editing Create, Details views to define titles; editing the Index view to remove a layout reference because it's now in _ViewStart.cshtml: OK.
OK in Rider but a view start file template is missing.
- Creating _ViewStart.cshtml in the Views folder: there's no Razor view start file template available in the folder, nor are there any Razor templates. Workaround: use the generic file template.
- Editing Create, Details views to define titles and remove redundant HTML markup
(Rider's file templates for views contain more of it than Visual Studio's templates); editing the
Index view to remove a layout reference because it's now in _ViewStart.cshtml: OK.
Visual Studio does the job but requires too much manual work.
- Editing _ViewImports.cshtml: OK, although FQN completion is clumsy.
- Going through views to remove redundant using directives by hand: no quick actions to do this in Razor files.
Rider does a far better job, thanks to completion and batch QFs.
- Editing _ViewImports.cshtml: good, completion works well.
- Going to the first view, and batch-executing two quick-fixes: Remove unused directives and
Remove redundant qualifier. Nice!
Visual Studio does the job - nothing special though. A few code completion/code analysis issues along the way.
- Creating a Pages folder, adding a page via New Item > Razor Pages > Razor Page (additional templates include Razor Page using EF and Razor Page using EF (CRUD)) called Greeting.cshtml. All fine.
- Editing Greeting.cshtml: code completion for C# references is there.
- Copy/pasting _ViewImports and _ViewStart to the Pages folder, editing _ViewImports to use more namespaces.
- Editing Greeting.cshtml by adding an
@inject
directive (and Scott is having all kinds of issues with VS showing red code because of files not saved). - Editing ViewStart to use a _Layout.cshtml in the Pages folder (which is not there); copy-pasting existing _Layout.cshtml to the Pages folder.
- Editing Greeting.cshtml to use a
@model
instead of@inject
. This generates a code-behind file (Greeting.cshtml.cs, or was it there since adding the Razor page?) and also shows a nested and seemingly redundant_Pages_Greeting node:
- Editing code-behind: injecting service, getting message of the day within
OnGet()
. - Editing Greeting.cshtml to render a property on the code-behind, and adding a required
parameter via the
@page
directive; editing code-behind to make use of the parameter (adding string interpolation)
Visual Studio has at some point modified the .sln file to change project GUID. Made a similar change in Rider manually.
It all goes wrong as soon as Rider fails to properly create an item for a Razor page; if this is resolved, then the overall experience should be solid.
- Creating a Pages folder: fine. Creating a Razor Page: no file template for Razor Pages; workaround: create a regular MVC view.
- Editing Greeting.cshtml: OK, though no completion after the
@page
directive. - Copy/pasting _ViewImports and _ViewStart to the Pages folder, editing
_ViewImports to use more namespaces:
OdeToFoodRider.Pages
(the Razor page code-behind) is unresolved:
- Editing Greeting.cshtml by adding an
@inject
directive: good! Rider even suggests a name for the injected property. - Editing ViewStart to use a _Layout.cshtml in the Pages folder (which is not there). Rider highlights the reference _Layout.cshtml with red but doesn't suggest to create a page. Copy-pasting by hand doesn't remove red highlighting: Rider doesn't seem to understand the file system-based way to reference a layout in this case.
- Editing Greeting.cshtml to use a
@model
instead of@inject
. Model is unresolved (because code-behind wasn't generated); creating from usage creates a regular class in the Pages directory, instead of a nested Greeting.cshtml.cs. Have to rename, after which it's properly nested. Changing namespace in the code-behind to.Pages
fixes resolve from ViewImports.
- Editing code-behind: injecting service, creating
OnGet()
and displaying message of the day: all fine butOnGet()
is highlighted as never used. - Editing Greeting.cshtml to render a property on the code-behind, and adding a required
parameter via the
@page
directive; editing code-behind to make use of the parameter: OK.
VS gets the job done, with usual small annoyances (no import completion, shaky code analysis in Razor page view etc.)
- Adding a new Pages/Restaurants directory and Edit.cshtml Razor page (file template with code-behind).
- Adding a link to the new page from the Index view.
- Copying over a form from the Create view to Edit.cshtml and adapting (such as adding a hidden input for restaurant ID).
- Adding the
Update()
method toIRestaurantData
and derived types (just a stub inInMemoryRestaurantData
). - Editing Edit page code-behind with a constructor accepting an
IRestaurantData
and saving to a field, a bind property to hold and update restaurant information, implementingOnGet(int id)
and addingOnPost()
with model validation.
Rider provides a few nice features but again, there's no way to easily create a Razor page with code-behind + there's an array of minor annoying bugs. Yellow because the Red flag for the lack of the file template had already been given.
- Adding a new Pages/Restaurants directory and Edit.cshtml Razor page. Again, no Razor Page file
template, had to apply a workaround by creating a Razor view + codebehind file (which was created with a weird
ASP
namespace instead of the properOdeToFoodVisualStudio.Pages.Restaurants)
. After misplacing the new Razor page file, moved it to the right directory with Move to Folder, which introduced a line ending-only change in .csproj. Editing the Razor page triggered a flow of exceptions in Rangeable container, Rider protocol, typing assists etc. Unfortunately this is not easily reproducible, so just updated Rider to the latest nightly instead. - Adding a link to the new page from the Index view: OK.
- Copying over a form from the Create view to Edit.cshtml and adapting. Again,
completion at
asp-for
could be better. Otherwise fine, especially nice to be able to use the Change All quick-fix to correct copy-pasted model references:
- Adding the
Update()
method toIRestaurantData
and derived types. The Implement in derived types context action and its type selector: nice!
Shortcut completion for enum members is nice, too:
Overall a very solid editing experience. - Editing the Edit page's code-behind. Attribute completion strikes again! Introduce
readonly field quick-fix from constructor is useful here, again (although every time I call it I wonder
if the generated field should in fact be read-only).
OnGet()
,OnPost()
are highlighted as unused. Action and controller arguments ofRedirectToAction()
are highlighted as regular string literals, as opposed to their special treatment in MVC controllers:
The if live template continues to be deployed into a weirdif(b)
. Otherwise fine.
All fine in Visual Studio.
- Editing the Index view to use a partial view for each restaurant summary instead of a table.
- Creating a _Summary.cshtml partial view under Views/Home with Add New Item. Adding markup to display a restaurant summary. Copying links to Details and Edit views from Index.cshtml and adapting them.
Rider works well enough. Creating views from usage doesn't work here for whatever reason but Visual Studio doesn't have that, too. On par with Visual Studio although there are bugs to be reported.
- Editing the Index view to use a partial view for each restaurant summary. View completion in string
literals is nice! We need to reference a view that's not created yet though. Removing the table is easier with
Extend Selection (which in Visual Studio is only available in C#). Surrounding the Create
action link with a
<div>
via Surround With works but does so in a weird way, using the<a/>
tag by default, and putting a hotspot on the closing tag instead of the opening tag. - Creating a _Summary.cshtml partial view, and it looks like we can do that from
usage!
HOWEVER, it looks like no, we can't - for no particular reason:
OK, creating from live template then. - Editing _Summary.cshtml. No sync-editing of HTML tags, and on a mismatch between the opening and the
closing tag, the Insert closing tag quick-fix adds a new closing tag instead of replacing the
mismatching one. Otherwise fine; using Change All again to replace a variable with
Model
.
Visual Studio does the job at its usual standard although creating the directory hierarchy for view component view is particularly tricky.
- Creating a ViewComponents folder and a GreeterViewComponent.cs class inside.
Adding a constructor and the
Invoke()
method referencing a view that doesn't exist yet. - Removing the footer section from Index.cshtml as it's going to be a view component.
- Under Views/Shared, creating directory hierarchy Components/Greeter, and a new Razor view inside to be used by the view component: Default.cshtml. Creating these 3 items is a bit painful in VS. Very few edits to the view.
- Editing _Layout.cshtml to include 2 alternative ways of invoking the view component. One of them, the tag helper way, requires adding another directive to _ViewImports.cshtml - by hand.
Rider makes a statement to be better with the quick-fix to create view component views from usage - however, the quick-fix is unfortunately broken.
- Creating a ViewComponents folder and a GreeterViewComponent.cs class inside; adding logic to the class. All good, Create readonly field from constructor parameter helps again.
- Trying to create the Default view for the view component with a quick-fix:
It doesn't work unfortunately, throwing DEXP-361709 along the way. OK, creating manually, which is less cumbersome to do than in Visual Studio anyway. Adding simple view content, all fine. - Editing _Layout.cshtml to invoke the view component in two ways. Completion for view component class in
string literals is a nice surprise:
Neither Rider nor Visual Studio provide completion for the tag helper syntax. Adding a directive to _ViewImports.cshtml - OK although there's no completion for namespaces, much like in Visual Studio.
Visual Studio makes the process easy. Automating the generation of a self-signed certificate is especially appreciated.
- Switching to use SSL: Project properties > Debug > Enable SSL. A new URL is generated, copying it to the adjacent App URL setting. The actual settings are stored in launchSettings.json > iisExpress. When first trying to start an application that is configured to use SSL, Visual Studio suggests that a self-signed certificate is generated by IIS Express for development purposes, and then suggests to trust the certificate.
- Adding the
Rewriter()
middleware to Startup.cs with an option to permanently redirect all HTTP to HTTPS requests.
Poor experience. Running ASP.NET Core on IIS Express is not supported (RIDER-11638), thus you need to search for and go through a way to configure Kestrel to use SSL, which is not trivial. You also have to take care of generating a certificate with a PowerShell script on Windows or using a separate procedure on Mac/Unix.
- Switching to use SSL. Since Rider uses Kestrel directly, and even if it did use IIS Express as a proxy, it doesn't understand launchsettings.json, I need to set up SSL in Kestrel instead. Rider doesn't help at all with generating a certificate. Had to generate a certificate and configure Kestrel to use HTTPS and the certificate based on this guide. (Microsoft's own documentation is way less helpful.) Quite a painful and time-consuming process.
- Adding
Rewriter()
middleware - OK.
Commits:
Registering with an OpenID provider, adding OpenID configuration, configuring services and middleware, marking controllers, actions and page code-behind to use authorization
Visual Studio does the job, with some annoyances like no import items in completion (which bites in OAuth configuration).
- Scott creates an app registration using his Azure AD B2C user account:
I registered two OAuth apps (one for Rider and one for Visual Studio) with my GitHub account instead. - Adding OAuth-related settings, modifying Startup.cs and Program.cs - a mix of Scott's actions and this tutorial that is .NET Core/GitHub-specific. All regular C# and a bit of JSON editing. Fine but again, no import items in completion is a pain in Visual Studio. Also, copy-pasting isn't enjoyable as each unimported symbol must be imported with a separate quick action. Rider/ReSharper are way better in this regard.
- Marking the Home controller and its actions, as well as the Edit page
code-behind with
[Authorize]
and[AllowAnonymous]
attributes.
Rider does the job albeit with its own share of annoyances (import completion in attributes!) Copy-pasting code in Rider is enjoyable though, thanks to import suggestion that adds all imports at once.
- Using two GitHub app registrations (separate for Rider and Visual Studio).
- Adding OAuth-related JSON settings file, modifying Startup.cs and Program.cs to implement GitHub-based OAuth, using the same mix of Scott's video and a .NET Core/GitHub-focused tutorial as in the Visual Studio part. Copy-pasting parts from Visual Studio, and the import pop-up rocks in that it imports everything at once!
- Marking the Home controller and its actions, as well as the Edit page
code-behind with
[Authorize]
and[AllowAnonymous]
attributes. Import completion in attributes strikes again!
Adding markup and code to _Layout.cshtml that goes through user identity information and displays claim values. Regular Razor editing. All fine.
Adding markup and code to _Layout.cshtml that goes through user identity information and displays claim values. Regular Razor editing. All good.
Polished experience in Visual Studio. npm dependency indication in Solution Explorer is a nice bonus. The default content of package.json is a bit different from Rider.
- Adding package.json to the project via Add new item -> npm configuration file template. This introduces a new npm node in project dependencies. (node_folders isn't shown by default though, unless Show all files is on.
- Adding a bootstrap dependency in package.json > saving > Visual Studio auto-installs the package. Completion in package.json is very solid: both for property names (incl. packages available in npm) and values (versions of the packages).
Very solid experience in Rider, too. No dependency indication in Solution Explorer but overall smoother in terms of adding and editing package.json. Explicitly suggesting to start installing dependencies is probably a safer approach, too.
- Adding package.json to the project via WebStorm's file template, which suggests a name based on project name - nice touch!
- Adding a bootstrap dependency. Completion in package.json is on par with Visual Studio, it
looks like it even works faster. Rider doesn't auto-install but instead, suggests to install
dependencies, and does that if I allow it to:
The only downside is that there's no npm node in the project's Dependencies node in Solution Explorer, so you can only see there are installed dependencies if you choose to Show all files.
Writing an application builder extension method to enable serving files from node_modules (as an alternative to using something like gulp or Grunt)
Visual Studio: decent experience with ups and downs: drag-and-drop CSS to put a link reference to it is nice, but having to use an external namespace to enable code completion is not.
- Adding an undeclared application builder extension method to Startup.cs that would enable serving static files. Visual Studio can't create extension methods from usage.
- Adding a Middleware folder and a new class in it to implement the extension method. Putting
the class in the
Microsoft.AspNetCore.Builder
namespace to enable Visual Studio's code completion to see it :( - Editing _Layout.cshtml to refer to bootstrap.css in node_modules. Visual Studio can generate a link tag with the right path when you drag-and-drop bootstrap.css from Solution Explorer, quite a nice touch.
Rider: also up and down. Not being able to create extension methods from usage is sad. Completion issues in paths are annoying, too. C# editing is good though, so let's make it even.
- Adding an undeclared application builder extension method to Startup.cs that would enable serving
static files. Shocking fact: Rider can't create an extension method from usage, either:
- Adding a Middleware folder and a new class in it to implement the extension method. Smooth C# editing. Rider shows the extension method on call site without having to put it into an external namespace.
-
Editing _Layout.cshtml to refer to bootstrap.css in node_modules. Writing a link by
hand. Rider doesn't recognize
~
-prefixed paths and provides no sensible completion when you write the path. (A similar path problem occurred earlier in the course, see above.)
Adding Bootstrap classes to various elements across Razor views and layouts. Visual Studio's code completion isn't helpful at all (suggests certain Bootstrap classes but they aren't what I need every single time - this is probably hippie completion for class names that were already used).
Adding Bootstrap classes to various elements across Razor views and layouts. Completion is unhelpful, too, and as in Visual Studio, it's probably hippie completion. I assume that these problems are related to the way bootstrap.css is referenced in _Layout.cshtml.
All fine in Visual Studio. Well, maybe generating script/link references with drag-and-drop is not as nice as I thought, as it requires quite a bit of precise mouse manipulation and adding new lines in the editor beforehand.
- Adding a few more dependencies into package.json, they're then auto-installed.
- Drag-and-dropping each of the installed dependencies into _Layout.cshtml to generate script references to them.
Rider is doing great in terms of package.json editing but disappoints when editing script references (path completion).
- Adding a few more dependencies into package.json. The Update Dependencies popup is late to
show up this time but there's also a quick-fix to run
npm install
that is available earlier, nice!
- Typing script references by hand. Tried to go up to project folder in path completion but weirdly enough, it
doesn't see the node_modules directory at all:
It's a bit better when completing from root this time (node_modules can be seen on~/{caret}
) but after the next slash, path completion starts to look into project root instead of showing what's inside node_modules:
Wrapping script and link references in _Layout.cshtml with the <environment/>
tag and using various ASP.NET tag helpers to test if scripts and stylesheets have in fact been delivered
from a CDN.
Visual Studio does a very good job providing code completion for tag helper attributes in the <link/>
tag:
Wrapping markup with tags is a bit cumbersome though.
Wrapping with tags is more fluid with surround templates but there's a bug whereby after applying the surround template, a hotspot is put in the closing tag (this already occurred in one of the prior segments).
Unfortunately, Rider provides zero completion both for the <environment/>
tag, its
attributes and values, as well as for the tag helpers that are available inside the
<link/>
and <script/>
tags (asp-fallback-test-class
etc.).
This is missing functionality but let's go with yellow instead of red as this isn't a very mainstream scenario.