The .NET Tools Blog : Essential productivity kit for .NET developers | The JetBrains Blog https://blog.jetbrains.com Developer Tools for Professionals and Teams Tue, 20 Jun 2023 09:10:42 +0000 en-US hourly 1 https://blog.jetbrains.com/wp-content/uploads/2023/02/cropped-icon-512-32x32.png The .NET Tools Blog : Essential productivity kit for .NET developers | The JetBrains Blog https://blog.jetbrains.com 32 32 Rider 2023.2 EAP 5: Improved Support for C#, Better Performance Profiling, and More. https://blog.jetbrains.com/dotnet/2023/06/16/rider-2023-2-eap-5/ Fri, 16 Jun 2023 15:11:51 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/06/Blog_Featured_image_1280x600-1.png https://blog.jetbrains.com/?post_type=dotnet&p=363862 The latest installment in our Early Access Program for Rider 2023.2 is here. But before you download this build, let’s go over the most important updates it contains.

C# support

Support for default parameter values in lambdas

As part of our work on supporting C# 12 language updates, we’re introducing support for default parameter values in lambda expressions in Rider 2023.2. In addition to the standard set of warning messages associated with recognizing this syntax, we’ve also tweaked an existing inspection, The parameter has the same default value, to account for default parameter values in lambdas. This inspection will also be triggered when an invocation has an argument value that is the same as the default value of the parameter in the invoked delegate, making the expression redundant.

New inspections for improved control over object disposal

Rider 2023.2 introduces two new code inspections designed to address scenarios where the state of a returned object may be negatively influenced by its early disposal or the early disposal of the object that spawned it.

When a variable is captured by a using statement, it ensures that the object is properly disposed of when it goes out of scope. Returning an object captured by a using statement can be problematic because it extends the lifespan of the returned object beyond the method scope, causing the object to be disposed of immediately after returning. This can lead to unexpected behavior and resource-related issues.

The Return of a variable captured by ‘using’ statement inspection alerts you in cases where the returned object is immediately disposed of.

Similarly, the Return of a task produced by ‘using’-captured object inspection identifies scenarios where a Task is produced by an object captured by a using statement and then immediately returned. To extend the lifetime of the disposable object enough for Task completion, a corresponding quick-fix will introduce asynchronous awaiting for that Task before its return. 

Terminal migration to ConPTY on Windows

JetBrains Rider includes an embedded terminal emulator to work with a command-line shell from inside the IDE. Previous versions of Rider relied on a third-party WinPTY library as a terminal emulation layer on Windows. For Rider 2023.2, we’re switching to the OS-provided ConPTY for versions of Windows that support it. While no visible changes are expected, the switch opens the door for further improvements to the terminal and the run console.

Performance profiling

Group by Thread for sampling, tracing, and line-by-line snapshots

The dotTrace Profiler inside Rider 2023.2 introduces the Group by Thread option and corresponding button in the Call Tree panel for performance snapshots. This option organizes sampling, tracing, and line-by-line snapshots based on individual threads, allowing for deeper insight into thread-specific performance issues. 

Using this grouping method can also give you a birds-eye view of the project’s performance, as the call trees with the most ancestors will end up at the top of the list, making any hotspots glaringly obvious. 

Once you’ve clicked on the Group by Thread toggle button, you will see the call trees organized by thread. You can navigate from one thread to another by using the keyboard, and the trees will expand at once. Red-colored percentage values to the left of the call tree indicate higher subtree power, helping you pinpoint potential performance bottlenecks at a glance. 

Notable fixes

  • We added support for the UE_INLINE_GENERATED_CPP_BY_NAME include, which improves compile time when added to your project’s .cpp file (RSCPP-34358).
  • We fixed a bug that caused the reformatting of project files after a user executed any project modification actions (RIDER-40468).

Known vulnerabilities

A vulnerability exists in .NET 6.0, .NET7.0, and NuGet where a potential race condition that can lead to a symlink attack on Linux. Non-Linux platforms are not affected.

Our team is aware of the issue, and we’re working on eliminating it.

For the full list of resolved issues, please refer to our issue tracker.

That’s it for now! Please share your feedback on the latest EAP builds of Rider in the comments below or on social media.

]]>
ReSharper 2023.2 EAP 5: Improved Control Over Object Disposal, Support for Default Lambda Parameters, and C++23 Standard Library Modules. https://blog.jetbrains.com/dotnet/2023/06/16/resharper-2023-2-eap-5/ Fri, 16 Jun 2023 15:11:40 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/06/Blog_Featured_image_1280x600_ReSharper-2x.png https://blog.jetbrains.com/?post_type=dotnet&p=363864 The latest build for the ReSharper 2023.2 Early Access Program is available for download from our website. 

Let’s take a look at what’s inside!

C# support

Support for default parameter values in lambdas

As part of our work on supporting C# 12 language updates, we’re introducing support for default parameter values in lambda expressions in ReSharper 2023.2. In addition to the standard set of warning messages associated with recognizing this syntax, we’ve also tweaked an existing inspection, The parameter has the same default value, to account for default parameter values in lambdas. This inspection will also be triggered when an invocation has an argument value that is the same as the default value of the parameter in the invoked delegate, making the expression redundant.

New inspections for improved control over object disposal

ReSharper 2023.2 introduces two new code inspections designed to address scenarios where the state of a returned object may be negatively influenced by its early disposal or the early disposal of the object that spawned it.

When a variable is captured by a using statement, it ensures that the object is properly disposed of when it goes out of scope. Returning an object captured by a using statement can be problematic because it extends the lifespan of the returned object beyond the method scope, causing the object to be disposed of immediately after returning. This can lead to unexpected behavior and resource-related issues.

The Return of a variable captured by ‘using’ statement inspection alerts you in cases where the returned object is immediately disposed of.

Similarly, the Return of a task produced by ‘using’-captured object inspection identifies scenarios where a Task is produced by an object captured by a using statement and then immediately returned. To extend the lifetime of the disposable object enough for Task completion, a corresponding quick-fix will introduce asynchronous awaiting for that Task before its return. 

C++ support

This EAP build brings support for the C++23 standard library modules and C++20’s [[no_unique_address]] attribute, new code formatter settings, and other updates. 

For more details, please refer to this blog post.

dotTrace

Group by Thread for sampling, tracing, and line-by-line snapshots

dotTrace 2023.2 introduces the Group by Thread option and corresponding button in the Call Tree panel for performance snapshots. This option organizes sampling, tracing, and line-by-line snapshots based on individual threads, allowing for deeper insight into thread-specific performance issues. 

Using this grouping method can also give you a birds-eye view of the project’s performance, as the call trees with the most ancestors will end up at the top of the list, making any hotspots glaringly obvious. 

Once you’ve clicked on the Group by Thread toggle button, you will see the call trees organized by thread. You can navigate from one thread to another by using the keyboard, and the trees will expand at once. Red-colored percentage values to the left of the call tree indicate higher subtree power, helping you pinpoint potential performance bottlenecks at a glance. 

That’s it for now! For the full list of improvements and fixes that made it into the latest EAP build, please check out our issue tracker. As always, we’d love to hear your opinions and suggestions in the comments below.

]]>
How to Implement a Soft Delete Strategy with Entity Framework Core https://blog.jetbrains.com/dotnet/2023/06/14/how-to-implement-a-soft-delete-strategy-with-entity-framework-core/ Wed, 14 Jun 2023 12:42:26 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/01/rd-how-tos-blog-featured-image-1280x600-1-6.png https://blog.jetbrains.com/?post_type=dotnet&p=314434 I’m sure we’ve all had a turbulent relationship with the definition of “delete” throughout our development careers. So what does the user mean when they say “delete my data”? Well, if you’re anything like me, you’ll quickly realize the user wants to logically delete information from cluttering the user interface and not permanently delete records out of existence… Oops 😬. 

With hard lessons learned, many developers will turn to a Soft Delete strategy allowing them to reverse accidental deletes, maintain data integrity, and general administrative oversight. You may also be required by law to retain data for a certain period, and this strategy can help you accomplish those requirements.

This post will explore how to implement a Soft Delete strategy with Entity Framework Core and how to use it during writing and reading to your database engine of choice.

What is a Soft Delete?

As alluded to in the introduction, there are two kinds of deletes in application development: physical and logical. 

A physical delete removes a record from a data store and is highly destructive. Data deleted through a physical delete is lost, and only system administrators can recover the data, typically using extreme measures or backups. Physical deletes commonly use a mechanism of the data storage engine to execute a non-reversible command. For example, SQL-based databases can run DELETE statements to remove records from a table (hands up if you’ve ever accidentally forgotten the WHERE clause).

DELETE FROM dbo.Movies
WHERE Movies.Id = '1'

Conversely, a soft delete is a logical decision made by the development team to mark records to ignore during queries. Root elements of a data model will have a flag of some kind, either a boolean flag or a timestamp indicating the time of deletion. Queries applied to root elements must explicitly specify whether to use the deletion indicator as a factor in producing a result set. 

For example, here’s a SQL query returning a record, but only if the IsDeleted bit column is set to 0 for “false”.

SELECT * FROM dbo.Movies
WHERE Movies.Id = '1' AND Movies.IsDeleted = 0

If you or your user would like to recover data, recovering deleted data is as straightforward as changing the value of the deletion indicator.

UPDATE Movies
SET Movies.IsDeleted = 0
WHERE Id = '1';

Soft delete markers are more challenging to implement into an existing system, as it takes some thought about when and where to apply deletion indicators. Additionally, there can be some overhead in the form of additional indexes and a growing record count. These are drawbacks worth considering if you have limited disk space or I/O limitations.

Now that you have a general idea of what constitutes a Soft Delete strategy let’s go ahead and implement it using Entity Framework Core.

Entity Framework soft deletes with Interceptors

Entity Framework Core includes a concept of interceptors – an approach to extending the execution pipeline. There are several types of interceptors, and standard implementations allow you to modify the SQL command, alter entities before you save any changes, and use auditing techniques.

In this example, you’ll use an interceptor to modify entities during the writing phase of the application. First, let’s define a Movie entity, which you will adjust to support soft deletes.

public class Movie 
{
    public int Id { get; set; }
    public string Title { get; set; } = "";
    public string Writer { get; set; } = "";
    public string Director { get; set; } = "";
    public int ReleaseYear { get; set; }
    
    public override string ToString()
        => $"{Id}: {Title} ({ReleaseYear})";
}

To allow for increased reuse, we’ll create an ISoftDelete interface, giving you shared properties and implementation to undo any delete. Any of the properties, IsDeleted or DeletedAt, is sufficient for a soft delete strategy, but I’ve added both in this example for maximum verbosity. If you want to adopt this soft-delete approach, you’’ probably only want to have one of these properties. 

public interface ISoftDelete
{
    public bool IsDeleted { get; set; }
    public DateTimeOffset? DeletedAt { get; set; }

    public void Undo()
    {
        IsDeleted = false;
        DeletedAt = null;
    }
}

Apply the interface to the Movie entity, and look at the final entity definition.

public class Movie : ISoftDelete
{
    public int Id { get; set; }
    public string Title { get; set; } = "";
    public string Writer { get; set; } = "";
    public string Director { get; set; } = "";
    public int ReleaseYear { get; set; }
    
    public override string ToString()
        => $"{Id}: {Title} ({ReleaseYear})";

    public bool IsDeleted { get; set; }
    public DateTimeOffset? DeletedAt { get; set; }
}

While you could set the deleted flag on every entity you want to delete, that would be tedious and not to mention error-prone. So, let’s take advantage of EF Core infrastructure and write a SoftDeleteInterceptor.

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;

public class SoftDeleteInterceptor : SaveChangesInterceptor
{
    public override InterceptionResult<int> SavingChanges(
        DbContextEventData eventData, 
        InterceptionResult<int> result)
    {
        if (eventData.Context is null) return result;
        
        foreach (var entry in eventData.Context.ChangeTracker.Entries())
        {
            if (entry is not { State: EntityState.Deleted, Entity: ISoftDelete delete }) continue;

            entry.State = EntityState.Modified;
            delete.IsDeleted = true;
            delete.DeletedAt = DateTimeOffset.UtcNow;
        }

        return result;
    }
}

As you call SaveChanges on a DbContext instance, this interceptor will check to see if any entry in the change tracker implements ISoftDelete. If so, the interceptor will change the entity state of Deleted to Modified and set all soft delete properties.

As you can see in the implementation, this interceptor works with EF Core constructs before invoking any database-specific functionality. This interceptor will work with any database provider supported by EF Core, including, but not limited to, SQL Server, PostgreSQL, SQLite, and MySQL. For this sample, I’ve used the Microsoft.EntityFrameworkCore.InMemory package, but feel free to substitute your favorite provider.

The final step to complete your writing phase modifications is registering the interceptor with a DbContext definition using the call to AddInterceptors during the OnConfiguring phase of initialization.

using Microsoft.EntityFrameworkCore;

public class Database : DbContext
{
    public DbSet<Movie> Movies => Set<Movie>();

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseInMemoryDatabase("test")
            .AddInterceptors(new SoftDeleteInterceptor());

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
    }
}

Any attempt to remove an entity from the database using the EF Core DbContext will switch from a delete to an update statement. Let’s go ahead and see it in action.

var db = new Database();
var firstMovie = db.Movies.First();

Console.WriteLine($"{firstMovie.Title} ({firstMovie.ReleaseYear})");

// delete operation (actually an update)
db.Movies.Remove(firstMovie);
db.SaveChanges();
Console.WriteLine($"Deleted \"{firstMovie}\"");

As you may have noticed, the code looks like regular old EF Core. What about reading data? How do you filter out deleted records? You’ll see how to do that in the next section.

Automatically filter soft-deleted records

Marking records to be deleted is only half the story. With a single configuration, you can tell EF Core to ignore soft-deleted records when executing queries, and you can do that using query filters on our entity definitions. For example, the modified DbContext definition with a query filter on the Movies collection is here.

public class Database : DbContext
{
    public DbSet<Movie> Movies => Set<Movie>();

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseInMemoryDatabase("test")
            .AddInterceptors(new SoftDeleteInterceptor());

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Automatically adding query filter to 
        // all LINQ queries that use Movie
        modelBuilder.Entity<Movie>()
            .HasQueryFilter(x => x.IsDeleted == false);
    }
}

You can apply as many query filters as you like, but I suggest limiting query filters to what’s necessary, as they are typically invisible in a LINQ query. The “invisible” nature of query filters means developers on your team have to understand and manage these concepts in their minds. With too many filters, it can get confusing and lead to unexpected bugs.

Putting it all together with reads and writes

I’ve created a quick sample application below, something you might expect to see given the DbContext from previous sections.

using Microsoft.EntityFrameworkCore;
using SoftDeletes.Models;

// save test data of movies
Movies.Initialize();

var db = new Database();
var firstMovie = db.Movies.First();

Console.WriteLine($"{firstMovie.Title} ({firstMovie.ReleaseYear})");

// delete operation
db.Movies.Remove(firstMovie);
db.SaveChanges();
Console.WriteLine($"Deleted \"{firstMovie}\"");

Console.WriteLine($"Total Movies: {db.Movies.Count()}");
Console.WriteLine($"Total Movies (including deleted): {db.Movies.IgnoreQueryFilters().Count()}");
Console.WriteLine($"Total Deleted: {db.Movies.IgnoreQueryFilters().Count(x => x.IsDeleted)}");

public static class Movies
{
    public static readonly IReadOnlyList<Movie> All = new List<Movie> {
        new() { Id = 1, Title = "Glass Onion", Director = "Rian Johnson", Writer = "Rian Johnson", ReleaseYear = 2022 },
        new() { Id = 2, Title = "Avatar: The Way of Water", Director ="James Cameron", Writer = "James Cameron", ReleaseYear = 2022 },
        new() { Id = 3, Title = "The Shawshank Redemption", Writer = "Stephen King", Director = "Frank Darabont", ReleaseYear = 1994 },
        new() { Id = 4, Title = "Pulp Fiction", Writer = "Quentin Tarantino", Director = "Quentin Tarantino", ReleaseYear = 1994 },
        new() { Id = 5, Title = "Seven Samurai", Writer = "Akira Kurosawa", Director = "Akira Kurosawa", ReleaseYear = 1954 },
        new() { Id = 6, Title = "Gladiator", Writer = "David Franzoni", Director = "Ridley Scott", ReleaseYear = 2000 },
        new() { Id = 7, Title = "Old Boy", Writer = "Garon Tsuchiya", Director = "Park Chan-wook", ReleaseYear = 2003 },
        new() { Id = 8, Title = "A Clockwork Orange", Director = "Stanley Kubrick", Writer = "Stanley Kubrick", ReleaseYear = 1971 },
        new() { Id = 9, Title = "Metroplis", Director = "Fritz Lang", Writer = "Thea von Harbou", ReleaseYear = 1927 },
        new() { Id = 10, Title = "The Thing", Director = "John Carpenter", Writer = "Bill Lancaster", ReleaseYear = 1982 }
    };
    
    public static void Initialize()
    {
        var db = new Database();
        db.Movies.AddRange(All);
        db.SaveChanges();
    }
}

You’ll notice no mention of the IsDeleted flag anywhere in the code. The lack of ISoftDelete properties in read/write usage is because the EF Core interceptor and query filter use the properties transparently.

Additionally, to negate query filters, you can use IgnoreQueryFilters on any LINQ query, and you’ll get unadulterated access to form a LINQ query.

// after a delete
db.Movies.Count(); // 9
db.Movies.IgnoreQueryFilters().Count(); // 10

Let’s take a look at the complete application in a single file.

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;

// save test data of movies
Movies.Initialize();

var db = new Database();
var firstMovie = db.Movies.First();

Console.WriteLine($"{firstMovie.Title} ({firstMovie.ReleaseYear})");

// delete operation
db.Movies.Remove(firstMovie);
db.SaveChanges();
Console.WriteLine($"Deleted \"{firstMovie}\"");

Console.WriteLine($"Total Movies: {db.Movies.Count()}");
Console.WriteLine($"Total Movies (including deleted): {db.Movies.IgnoreQueryFilters().Count()}");
Console.WriteLine($"Total Deleted: {db.Movies.IgnoreQueryFilters().Count(x => x.IsDeleted)}");

public static class Movies
{
    public static readonly IReadOnlyList<Movie> All = new List<Movie> {
        new() { Id = 1, Title = "Glass Onion", Director = "Rian Johnson", Writer = "Rian Johnson", ReleaseYear = 2022 },
        new() { Id = 2, Title = "Avatar: The Way of Water", Director ="James Cameron", Writer = "James Cameron", ReleaseYear = 2022 },
        new() { Id = 3, Title = "The Shawshank Redemption", Writer = "Stephen King", Director = "Frank Darabont", ReleaseYear = 1994 },
        new() { Id = 4, Title = "Pulp Fiction", Writer = "Quentin Tarantino", Director = "Quentin Tarantino", ReleaseYear = 1994 },
        new() { Id = 5, Title = "Seven Samurai", Writer = "Akira Kurosawa", Director = "Akira Kurosawa", ReleaseYear = 1954 },
        new() { Id = 6, Title = "Gladiator", Writer = "David Franzoni", Director = "Ridley Scott", ReleaseYear = 2000 },
        new() { Id = 7, Title = "Old Boy", Writer = "Garon Tsuchiya", Director = "Park Chan-wook", ReleaseYear = 2003 },
        new() { Id = 8, Title = "A Clockwork Orange", Director = "Stanley Kubrick", Writer = "Stanley Kubrick", ReleaseYear = 1971 },
        new() { Id = 9, Title = "Metroplis", Director = "Fritz Lang", Writer = "Thea von Harbou", ReleaseYear = 1927 },
        new() { Id = 10, Title = "The Thing", Director = "John Carpenter", Writer = "Bill Lancaster", ReleaseYear = 1982 }
    };
    
    public static void Initialize()
    {
        var db = new Database();
        db.Movies.AddRange(All);
        db.SaveChanges();
    }
}

public class SoftDeleteInterceptor : SaveChangesInterceptor
{
    public override InterceptionResult<int> SavingChanges(
        DbContextEventData eventData, 
        InterceptionResult<int> result)
    {
        if (eventData.Context is null) return result;
        
        foreach (var entry in eventData.Context.ChangeTracker.Entries())
        {
            if (entry is not { State: EntityState.Deleted, Entity: ISoftDelete delete }) continue;

            entry.State = EntityState.Modified;
            delete.IsDeleted = true;
            delete.DeletedAt = DateTimeOffset.UtcNow;
        }

        return result;
    }
}

public class Database : DbContext
{
    public DbSet<Movie> Movies => Set<Movie>();

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseInMemoryDatabase("test")
            .AddInterceptors(new SoftDeleteInterceptor());

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Movie>()
            .HasQueryFilter(x => x.IsDeleted == false);
    }
}

public class Movie : ISoftDelete
{
    public int Id { get; set; }
    public string Title { get; set; } = "";
    public string Writer { get; set; } = "";
    public string Director { get; set; } = "";
    public int ReleaseYear { get; set; }
    
    public override string ToString()
        => $"{Id}: {Title} ({ReleaseYear})";

    public bool IsDeleted { get; set; }
    public DateTimeOffset? DeletedAt { get; set; }
}

public interface ISoftDelete
{
    public bool IsDeleted { get; set; }
    public DateTimeOffset? DeletedAt { get; set; }

    public void Undo()
    {
        IsDeleted = false;
        DeletedAt = null;
    }
}

Executing the program above will give you the following output.

Glass Onion (2022)
Deleted "1: Glass Onion (2022)"
Total Movies: 9
Total Movies (including deleted): 10
Total Deleted: 1

So straightforward and much easier to accomplish now with EF Core than in previous iterations. This a reminder that no data is physically deleted, only logically “deleted”. The code to insert/delete entities remains the same, and querying also does not look any different from routine EF queries. All thanks to the power of EF interceptors.

Conclusion

The soft delete strategy can help you provide the user experiences you intend without the risk of catastrophically destroying data. While the technique has some overhead, you can overcome these challenges using EF Core infrastructure and a simple interface. You’ll likely want to add additional indexes to your tables for the deletion flags to speed up queries. In addition, you can apply query filters and interceptors to other problems. It is good knowledge when looking at different approaches to solving complex business tasks.

Thank you for reading, and if you have any comments or questions, please feel free to leave them in the comment section.

Image Credit: Alex Rybin

]]>
Blazor Essentials – New Guide Tutorial https://blog.jetbrains.com/dotnet/2023/06/13/blazor-essentials-new-guide-tutorial/ Tue, 13 Jun 2023 09:06:03 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/06/rs-how-tos-featured_blog_1280x720.png https://blog.jetbrains.com/?post_type=dotnet&p=359797 Want to learn Blazor? Then we have good news for you! On our JetBrains .NET Guide, we’ve just published the Blazor Essentials tutorial.

Blazor lets you to create progressive web apps using C#, having significantly less reliance on JavaScript that was necessary in previous versions of ASP.NET. This model is intended to make Blazor appealing to current C# developers, since they can focus less on JavaScript and write more in C# for full-stack development.

In the Blazor Essentials tutorial, we’ll explore Blazor, a Single Page Application (SPA) web framework that is part of ASP.NET Core. After covering some the basics, we’ll gradually look at working with forms, data, JavaScript interoperability, and more.

Tip: If you prefer regular ASP.NET Core with MVC or Razor pages, and a sprinkle of JavaScript, check out the HTMX for ASP.NET Core Developers.

Which SPA framework(s) are you using? Which do you prefer? Let us know in the comments!

]]>
Rider 2023.2 EAP 4 Is Out! https://blog.jetbrains.com/dotnet/2023/06/09/rider-2023-2-eap-4/ Fri, 09 Jun 2023 09:07:27 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/06/Blog_Featured_image_1280x600.png https://blog.jetbrains.com/?post_type=dotnet&p=362222 The latest EAP build for Rider 2023.2 has just been released and is available for download! Let’s take a look at what’s inside.

Improved navigation from var declarations

Rider 2023.2 EAP 4 introduces improved navigation from var keywords. 

It’s common for developers to have a variable with a wrapper type such as Nullable<T>, ValueTuple<T1, T2>, or KeyValuePair<TKey,TValue> in their code. Previously, navigating from a var keyword of such variables would immediately take a developer to declarations or usages of Nullable<T> instead of the underlying type they were looking for.

All navigation actions (Go to…, Find Usages, etc.) now suggest underlying types when navigating from var for common types used to wrap other types. For example, Rider will suggest navigating to Person when using the Go to declaration action from the var keyword of a variable with the ImmutableArray<Person>? Type.

User experience

Pinned run configurations in the Run widget

To make managing multiple run configurations easier, we’ve implemented the option to pin preferred configurations in the Run widget. To add a run configuration to the Pinned section, open the kebab menu (three dots) next to its name and select Pin. If you have multiple pinned configurations, you can easily rearrange them by dragging and dropping within the list. 

File sorting by modification time in the Solution Explorer 

Rider 2023.2 EAP 4 brings the long-awaited option to arrange your files in the Solution Explorer based on their modification time. This new functionality automatically reorders the files whenever the changes in your project are saved. To enable this feature, open the kebab menu (three dots) in the Solution Explorer and then select Tree Appearance | Sort by Modification Time.

Optimized Blueprint indexing for Unreal Engine

For Rider 2023.2, we’ve optimized the way Rider handles Blueprint indexing, leading to a drastic improvement in solution loading time.

By classifying Blueprints as secondary resources, Rider is now able to index all of your code before looking at your assets. This means you get access to the rich code editing experience sooner, while the assets are still being indexed in the background.

Click here to learn more about what Blueprints support in Rider has to offer. 

Docker Compose run configuration labels

Rider 2023.2 will make it easier for you to fine-tune the run configuration of Docker Compose through the introduction of labels. By adding these bits of code to the docker-compose.yml file, you can specify how and if you want to run and debug your applications.  

For example, if you want to disable the fast mode for some of your services, you can set a com.jetbrains.rider.fast.mode: "false" label for them. If you want to disable the debug mode, use with the label com.jetbrains.rider.debug: "false"

Web development

Volar support for Vue

Rider 2023.2 introduces Volar support for Vue to support the changes in TypeScript 5.0. This should provide more accurate error detection aligned with the Vue compiler. You can set the Vue service to use Volar integration on all TypeScript versions, under Settings / Preferences | Languages & Frameworks | TypeScript | Vue. By default, Volar will be used for TypeScript versions 5.0 and higher, and our own implementation will be used for TypeScript versions earlier than this.

Next.js custom documentation support

Next.js 13.1 now includes a plugin for the TypeScript Language Service specifically for the new app directory. This plugin offers suggestions for configuring pages and layouts, as well as helpful hints for using both Server and Client Components. It also comes with custom documentation, which means that it adds extra information to the output of the TypeScript Language Service. It’s now possible to view this custom documentation in Rider. 

CSS: Convert color to lch and oklch

Rider first introduced CSS color modification features back in version 2022.3. One of the applications for this is for changing rgb to hsl and vice versa. With our next release, we are expanding this support to include the conversion of lch and oklch with other color functions.

Working with databases

These are just a few of the updates available in the EAP 4 build:

  • More options for connecting with SSL certificates.
  • Use of HTTP proxy settings in the remote development process.
  • WSL support for dump tools.

Click here to learn more about these and all other updates for working with databases that will be coming to Rider 2023.2.  

Notable fixes

  • We’ve resolved the issue with the SSL certificate warning appearing on each startup when running Rider on macOS (RIDER-92026). 
  • We’ve fixed the Go to Symbol behavior where the action would sometimes open files without scrolling to the searched symbols (RIDER-9402).
  • The Separate Watches context action inside the debugger now correctly brings the Immediate Window into the view. Local variables and Immediate evaluation results are displayed in the panel to the left (RIDER-93888).

For the full list of resolved issues, please refer to our issue tracker.

That’s it for now! Please share your opinion on the latest Early Preview builds of Rider in the comments below or on social media.

]]>
How Docker Fast Mode Works in Rider https://blog.jetbrains.com/dotnet/2023/06/07/how-docker-fast-mode-works-in-rider/ Wed, 07 Jun 2023 12:21:54 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/05/rd-how-tos-blog-featured-image-1280x600-1-3.png https://blog.jetbrains.com/?post_type=dotnet&p=358605 JetBrains Rider’s Docker fast mode helps speed up the feedback loop when building, debugging, and running  containerized applications. With fast mode, Rider builds your application locally and then mounts it within a Docker container for a production-like experience.

In this post, we’ll look at what Rider is doing under the hood to make fast mode work, and how it launches your containers when you start a run configuration.

Faster feedback loops

Some developers prefer to run their container applications as regular .NET projects during development. A typical scenario would be to run their backing services (e.g. database, queue or something similar) with Docker Compose, open local ports, and then run the .NET project on their workstation.

The advantage of this approach is that it lets you apply changes to the application much faster. You don’t need to rebuild the application container image and wait for it. In other words, you get a much quicker response after making your changes.

The drawback is that it is inconvenient to have different Docker Compose files for different environments. For example, having the same configuration for all environments can lead to inadvertently opening development ports in production, exposing your application to malicious attackers. Keeping track of differences between environments can be a hassle. Ugh, so much to remember!

And this is where our Docker fast mode can help. We use the same Docker Compose and Dockerfiles that you use for all environments, but apply some overrides to start the container quicker during development. Let’s have a look.

Under the hood

JetBrains Rider’s Docker fast mode approach is based on Docker’s multi-stage build feature. In short, multi-stage builds create environment checkpoints that can be reused between rebuilds. Let’s look at an example of a common Dockerfile for an ASP.NET Core application:

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["WebApp/WebApp.csproj", "WebApp/"]
RUN dotnet restore "WebApp/WebApp.csproj"
COPY . .
WORKDIR "/src/WebApp"
RUN dotnet build "WebApp.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "WebApp.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApp.dll"]

As you can see, there are several FROM instructions, also known as stages, with different names of base, build, publish, and final. Each stage can copy artifacts from other stages (for example, COPY --from=publish /app/publish .).

Now, why would you use different stages? 

The benefit is that you can use different base images for different stages. You can use the mcr.microsoft.com/dotnet/sdk image (almost 800 MB) to build the application and then use the mcr.microsoft.com/dotnet/aspnet image (about 200 MB) to create the final image that runs your application – copying the prepared artifact from the build stage. Therefore, the final image size will be much smaller than if you had used the sdk base image.

This is the multi-stage build technique, and it is quite common nowadays.

For Docker fast mode, we use it differently. You can specify the --target option to stop the build at a particular stage.

docker build -t web-app --target base .

Only the base stage will be built here.

Docker heavily uses caches when building the image; if there are no changes, Docker won’t do anything. And if you look at the first stage, there’s nothing to change.

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

Thus, if we build this stage once, the following builds will be very fast.

But there is still one small problem… We don’t have our application inside the image. And we can’t build it inside because we’re using the base image mcr.microsoft.com/dotnet/aspnet rather than mcr.microsoft.com/dotnet/sdk, which comes with the full toolchain

The good news is that we can build the application on the host machine and then simply attach the folder to the container. Here you can see this volume attached to the container (under Volumes) from the Services tool window.

Finally, Rider modifies the entrypoint to run your application when the container starts.

You can check the entrypoint (["dotnet", "/app/bin/Debug/net7.0/WebApplication1.dll" ]) on the container’s Inspection tab.

That’s pretty much it. Rider also sets some useful environment variables in fast mode, and exports and attaches a certificate to the container. 

The final command line created in the background by Rider will look similar to this:

docker build -f .\WebApplication1\Dockerfile --target base -t webapplication1:dev . && docker run --name webapplication1 -v {Path to the WebApplication1 folder}:/app --entrypoint dotnet webapplication1:dev /app/bin/Debug/net7.0/WebApplication1.dll

Customizing Docker fast mode

If you need to include specific tools or configurations in your development container image without affecting your production image, you’re in luck! It is possible to manually specify the required stage using the MSBuild DockerfileFastModeStage property in the project file. 

Let’s look at an example. Imagine you want to include dotnet-counters to monitor some valuable metrics. No problem! You can add a new dev stage and install dotnet-counters.

FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM base as dev
RUN apt-get update && apt-get -y install curl
RUN mkdir /tools && cd /tools && curl -JLO https://aka.ms/dotnet-counters/linux-x64 && chmod +x ./dotnet-counters

FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src
COPY ["WebApplication1/WebApplication1.csproj", "WebApplication1/"]
RUN dotnet restore "WebApplication1/WebApplication1.csproj"
COPY . .
WORKDIR "/src/WebApplication1"
RUN dotnet build "WebApplication1.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "WebApplication1.csproj" -c Release -o /app/publish /p:UseAppHost=false

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WebApplication1.dll"]

Next, add the DockerfileFastModeStage property to your project file:

<DockerfileFastModeStage>dev</DockerfileFastModeStage>

Rider will now use this dev stage to create a fast mode image, so you can run dotnet-counters inside the container. And since no other stages depend on this dev stage, the final image won’t contain these changes.

Conclusion

In this post, we discussed Docker fast mode and how it helps speed up your development feedback loop when debugging and running your application in a container. We’ve also seen how Rider modifies the Docker deployment to support Docker fast mode and can provide a faster way to run containers.

Download Rider and give it a try, and let us know how you’re using Docker fast mode!

]]>
Rider 2023.2 EAP 3: Improvements for Raw Strings, GitLab Integration, Memory Snapshot Analysis, and More https://blog.jetbrains.com/dotnet/2023/06/06/rider-2023-2-eap-3/ Tue, 06 Jun 2023 11:17:35 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/05/Blog_Featured_image_1280x600-6.png https://blog.jetbrains.com/?post_type=dotnet&p=359214 The third installment in the Rider 2023.2 Early Access Program has just been released! But before you download the EAP 3 build, let’s go over the feature highlights included with it.

C# support

The C# language support features included in Rider 2023.2 EAP 3 are focused on improving your experience working with raw strings. The improvements include:

  • New code formatter options enabling you to align or indent the content inside of raw strings.
  • Typing assistance when using the Enter, Delete, or Backspace keys.
  • A new Use raw string inspection and a corresponding quick-fix to convert verbatim strings into their raw counterparts.
  • A new Raw string can be simplified inspection and a quick-fix to remove redundant quotes and dollar sign symbols.
  • Context actions to add or remove extra quotes and dollar sign symbols.
  • Context actions to switch between single-line and multiline representations of raw strings.

Since language support is shared between Rider and ReSharper, you can refer to the ReSharper 2023.2 EAP 3 blog post for more information.

UX/UI improvements

Reworked hamburger menu in the main toolbar on Windows and Linux

We’ve refined the behavior of the hamburger menu in the new UI that is located in the main toolbar for Windows and Linux. Once you click on the menu icon, the elements now appear horizontally over the toolbar.

Also, there’s now an option to turn this menu into a separate toolbar. For this, go to View | Appearance | Main menu as a Separate Toolbar.

Updated window controls on macOS 

When working on macOS in full screen mode using the new UI, the window controls are now displayed right in the main toolbar – not in the floating bar as before.

Light theme with light header in the new UI

For Rider version 2023.2, we’ve refined the user experience with the Light theme by introducing the alternate Light with Light Header option, featuring matching light colors for window headers, tooltips, and notification balloons.

Single-Click Navigation Between Project Directories

In the Solution Explorer tool window, there’s a new Open Directories with Single Click option that makes expanding and collapsing the project folders quicker and more responsive. The option is available from the drop down menu once you click on the three dots icon.

VCS

GitLab integration

Rider 2023.2 EAP 3 introduces initial integration with GitLab, allowing you to work with the Merge Request functionality right from the IDE and streamline your development workflow.

Memory snapshot analysis

dotMemory snapshot analysis is coming to Rider! Rider 2023.2 EAP 3 introduces several improvements and changes to the memory profiling workflow:

  • The Profiling Session view has been moved to the Document window in Rider, offering the same powerful features found in the standalone version of dotMemory. Now, you can manually capture snapshots or set conditions to automatically trigger snapshot collection.
  • Additionally, you can compare snapshots from the same profiling session. This enables you to easily identify changes and trends in memory usage over time, helping you pinpoint potential performance bottlenecks.
  • Rider 2023.2 EAP 3 introduces basic views to help you analyze Object sets and Object instances. While we continue to expand the range of available views, you can already explore the essential information inside the IDE.
  • The Memory Allocations view has been moved to the Document window. Meanwhile, the Memory Profiler tool window, where it originally could be found, has received some tweaks. You can now open, copy, and delete analysis tabs within the selected workspace. Also, if all analysis tabs are closed, you can stop the profiling session right from the tool window.

The features described above are available on Windows, Linux, and Mac OS, with the exception of the Import Process Dump command, which is currently exclusive to Windows. You can now import the process dump using the corresponding button in the Workspaces list within the Memory Profiler tool window.

And that’s it for now! For the full list of changes included in this EAP build, please refer to our issue tracker.

Just a quick reminder on how you can join the Rider 2022.3 Early Access Program:

  • Download and install the EAP build from our website.
  • Use the Toolbox App.
  • Install this snap package from the SnapCraft store if you are using a compatible Linux distribution.

We always appreciate your feedback. Please feel free to share it in the comments below, in our issue tracker, or on social media.

]]>
ReSharper 2023.2 EAP 3: Improvements for Working with Raw Strings and More C++ Features. https://blog.jetbrains.com/dotnet/2023/06/06/resharper-2023-2-eap-3/ Tue, 06 Jun 2023 11:14:09 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/05/Blog_Featured_image_1280x600_ReSharper-2x-1.png https://blog.jetbrains.com/?post_type=dotnet&p=359208 The third Early Access Program build for ReSharper version 2023.2 has just been published! Before you download it, let’s take a look at the most important updates you’ll find inside. 

New C# inspections for working with raw strings

Use raw string inspection + quick-fix

Before C# 11, using verbatim strings was the way to go if you wanted to have a multi-line representation. However, doing so meant that you had to sacrifice the regular indentation of your code. You would also have to use two sets of quotes or curly braces in order to avoid confusing them with string delimiters or interpolations.

To improve your code’s readability, ReSharper 2023.2 will suggest transforming these verbatim strings into their raw representations.

Use raw string inspection in ReSharper 2023.2

Raw string editing context actions

Raw strings have a flexible design that prevents characters from escaping by adjusting the number of quotes, dollar signs, or interpolation braces.

Making these changes manually is tedious, but the latest version of ReSharper can do it for you. In addition to adding or removing quotes and dollar sign symbols, we’ve also added context actions for switching between single-line and multi-line representation.

Raw string editing in ReSharper 2023.2

Simplify raw string inspection + quick-fix

After performing a series of changes on your raw strings, you may end up with a non-optimal string representation where some of the quotes and dollar sign symbols become redundant.

As always, ReSharper offers a new inspection and a corresponding quick-fix to simplify a raw string.

Raw string editing in ReSharper 2023.2

More C# improvements:

  • ReSharper 2023.2 EAP 2 introduces new code formatter options, allowing you to align or indent the content inside of raw strings.
  • We’ve also improved typing assistance for when you use the Enter, Delete, or Backspace keys.

Gutter marks for recursive calls in C++

If you have a recursive call, ReSharper C++ will mark it in the gutter, making it more visible:

Raw string editing in ReSharper 2023.2

This EAP build also brings support for the .cppm module interface file extension, several improvements for more consistent and straightforward code navigation, and smarter generation of documentation comments. For more details on the ReSharper C++ updates, please see the dedicated blog post.

For the full list of features and improvements included in this build, please see our issue tracker.

That’s it for now!

As always, we’d love to hear your opinions and suggestions in the comments below.

]]>
Cecil Phillip – Building payment flows with Stripe and Azure – Webinar Recording https://blog.jetbrains.com/dotnet/2023/06/02/cecil-phillip-building-payment-flows-with-stripe-and-azure-webinar-recording/ Fri, 02 Jun 2023 10:00:35 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/05/Blog_Featured_image_1280x600-5.png https://blog.jetbrains.com/?post_type=dotnet&p=359376 The webinar recording for Building payment flows with Stripe and Azure, with Stripe Staff Developer Advocate, Cecil Phillip, is now available on JetBrainsTV. Be sure to like and subscribe on YouTube for more great content like this.

Content creators, businesses, and even developers are trying to find ways to connect with their customers and maximize revenue. How can we convert your audience into eager paying customers? To reach this goal, accepting payments must be as smooth as possible on both the front and back end.

In this session, we’ll discuss some things you should consider when adding payments to your .NET applications. We’ll talk about the importance of PCI compliance and alternative payment methods you should consider. Lastly, we’ll see how to scale your fulfillment workflow using serverless functions.

Session Notes
Some (extra) things Cecil will cover in this session:

  • Modeling workflows using Durable Functions
  • Handling webhooks event with Azure Service Bus
  • Connecting customer account with its associated user (Using IdentityServer)

Get the demo at Cecil’s GitHub repository.

Agenda

About the presenter:

Cecil Phillip

Cecil Phillip

Cecil Phillip is currently a Staff Developer Advocate at Stripe, with experience with .NET, JavaScript, Python, and serverless computing.

]]>
.NET Annotated Monthly | June 2023 https://blog.jetbrains.com/dotnet/2023/06/01/net-annotated-monthly-june-2023/ Thu, 01 Jun 2023 10:21:01 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/05/dotnet-annotated-blog-featured-image-1280x600-1-1.png https://blog.jetbrains.com/?post_type=dotnet&p=358802 Did you know? More than 400 billion PDFs were opened in Adobe products in the last year. Wow! That’s a lot of PDFs! How many did you open last year?

.NET news

Featured content 

Hi folks, it’s me, Khalid, your neighborhood-friendly developer advocate. Rachel has kindly asked me to curate the June 2023 newsletter, as I’d like to have a dialogue with our JetBrains community. 

How’s your year been so far? Have you learned anything new? What do you think is the next big thing in the .NET community? Can we, JetBrains, improve your .NET development experience? Finally, how are you doing? Please leave replies in the comments section below, and let’s chat. 

How am I doing? Thanks for asking. My year so far has been a polyglot development kind of year, with detours into JavaScript/TypeScript, Ruby, mobile development, and Web Assembly. Looking at these other ecosystems can help us understand what the .NET community does well, what our community can draw inspiration from, and how we can contribute to the general health of our little patch of development grass. 

Here are some examples I’ve noticed in our community that have similarities to initiatives happening in other communities (let me know if you agree in the comments):

  • Blazor United – The multi-paradigm programming of Blazor United allows for three modes: static site generation, server-side, and client-side. This model is growing in popularity with JavaScript frameworks like Next.js, Remix, and Astro.
  • MAUI – Mobile development is a daunting problem, and there’s currently a lot of competition in this space. It’s easy to see the similarities in frameworks like Flutter, Kotlin Multiplatform, and JetPack compose, not to mention a strong alternative in our ecosystem in Avalonia.
  • Minimal APIs I just wrote a post about this, and it’s clear to see the inspiration in Minimal APIs from the Ruby ecosystem and the Sinatra library. I could also include a plethora of Node libraries with similar syntax.
  • Web Assembly and WASIWeb Assembly is the new frontier for every ecosystem, and while Rust is the undisputed front-runner in this space, .NET has a real chance to win mind share. WASI will change how we write and deploy code, and it’s one of the most significant transformations in development coming.

I’d also like to highlight some of the work folks are doing to build a bridge to these other communities:

  • DotNetIsolator – Steven Sanderson continues his fantastic work in the WASM space, which will help open up new options for .NET developers. 
  • Vite.AspNetCore – Quetzal Rivera is working on a Vite middleware that helps bring JavaScript’s premiere build tooling into your ASP.NET Core workflow.
  • Avalonia – The Avalonia team has built an incredible toolchain that helps you deliver solutions to macOS, Linux, Windows, and mobile platforms. This year might be the year of Linux on the desktop, finally!
  • Marten – The power of PostgreSQL as a document database and event store? Yes, please.

There’s much to be excited about in technology, especially in the .NET space. Or should I say dotnet? As I wrap up, let me wish you the best this year, and may your backlogs be filled with the best features and your codebases stay bug-free. Please enjoy the excellent community links below, and tell the authors how much you enjoyed their work; it goes a long way.

Programing tutorials and tips 

.NET tutorials and tips

Related programming tutorials and tips:

AI can be very “bird brained”

https://twitter.com/morningdatabrew/status/1661537703496753155

Hmmmmm. Light does attract bugs. Do you think switching to dark theme will help?

Interesting and cool stuff

I don’t think that’s how Python programming works.

And finally, the latest from JetBrains

Here’s a chance to catch up on JetBrains news that you might have missed:

⚒️ Check out our .NET Guide! Videos, tips, and tricks on .NET related topics. ⚒️

👶 Download our C# for Babies book and teach your little one how to code before they can walk! 🍼

Early Access Programs

Blog posts, webinars, etc..

Don’t miss this fantastic offer! CODE Magazine is offering a free subscription to JetBrains customers. Get your copy today!

Sharing is caring! So share content that you find useful with other readers. Don’t keep it to yourself! Send us an email with your suggestions for publication in future newsletters!

Subscribe to .NET Annotated

]]>
Level Up with Live Templates in JetBrains Rider https://blog.jetbrains.com/dotnet/2023/05/30/level-up-with-live-templates-in-jetbrains-rider/ Tue, 30 May 2023 12:39:17 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/02/rd-how-tos-blog-featured-image-1280x600-1-1.png https://blog.jetbrains.com/?post_type=dotnet&p=321815 Let’s face it. There are parts of programming that are tedious and repetitive. JetBrains products help reduce boilerplate by offering generalized quick fixes and templates that benefit the most developers. That said, you’re likely to have unique code structures and patterns for your projects that only you and your team utilize. 

Wouldn’t it be great to create a set of specialized templates for you and your team to use throughout the development of your applications? Well, with live templates, you can do just that.

This post will show you how to create a new live template with placeholder variables and share the templates with other project members.

What is a live template?

A live template is a block of code that you can expand into commonly-used constructs such as loops, conditions, declarations, or statements. In the context of JetBrains IDEs, live templates have a key associated with them to make expanding them memorable and accessible.

Additionally, live templates fall into two categories: Simple templates and Parameterized templates.

Simple templates contain only fixed plain text and are used to reduce keystrokes in generating boilerplate. Parameterized templates include variables that enable a secondary step of replacing input fields with calculated values based on file-type context or having you specify values manually.

All live templates can be limited to a specific context using categories like language or file extension. This flexibility ensures live templates are only available when and where you want them.

Create a new live template

For this example, you’ll author a new “rec” live template, allowing you to create a record definition and set the name. This template will only be available in a C# context.

First, open JetBrains Rider’s settings and navigate to Editor | Live Templates | C#. Here, you’ll see all the existing live templates.

the Editor | Live Templates | C# settings in JetBrains Rider

Click the New Template button on the top right-hand side, next to the list of existing templates. You’ll want to change the following values of Shortcut to “rec” and Description to “create a C# record” while leaving all other settings on their default.

You’ll also want the following template on the left-hand side.

// Created At $DATETIME$
$MODIFIER$ record $NAME$($END$);

Once you’ve pasted the template, let’s change the macros for each placeholder by clicking the Edit variables button on the bottom right. Be sure to match the following order and settings below.

PlaceholderEditableDescription
$DATETIME$ UncheckedCurrent date and time in specified format
$MODIFIER$CheckedExecute basic completion
$NAME$CheckedNo macro selected

These template variables can be used in your live template and will be either populated automatically or serve as a placeholder that you can Tab ⇥ into when using your live template later on.

You’ll notice $END$ is not in the list of variables. That’s because $END$ is a reserved variable that indicates the final place for your cursor.

The final look of the live template in settings

Now we can start using our new live template! In a C# file, start typing “rec”, and you’ll see the live template you created, at which point you can press enter to start expanding the template. Check out the video below to see it in action.

showing the live template in action within JetBrains Rider

Sharing live templates

Live templates can be shared with other team members using JetBrains Rider’s layer-based settings. Follow the same steps you used in the previous section, but before clicking the save button, be sure to use the dropdown to select the team-shared option, which will save your newly created live template to a <SolutionName>.sln.DotSettings file at the root of your solution.

Showing the save menu in JetBrains Rider and the team-shared option.

If you don’t see your live template content in the .DotSettings file, try deleting the live template and recreating it again. JetBrains Rider will only save differences between personal and team-shared environments. Be sure to check in the .DotSettings file into source control to share the template with team members.

Conclusion

Live templates are a powerful feature of JetBrains Rider, and while you’ve likely used our templates, you’ve probably never created your own. Live templates allow you to share knowledge across teams. You can also use them personally to improve webinars and live demos by cutting out all that unnecessary typing. We hope this short tutorial gives you the courage to try and experiment with live templates and the idea of sharing them with team members.

Please comment below if you have any live templates you’d like to share.

References

Image Credit: Thomas Despeyroux

]]>
https://blog.jetbrains.com/zh-hans/dotnet/2023/05/30/level-up-with-live-templates-in-jetbrains-rider/ https://blog.jetbrains.com/ja/dotnet/2023/05/30/level-up-with-live-templates-in-jetbrains-rider/
The API Verifier: A New Era for ReSharper Plugins https://blog.jetbrains.com/dotnet/2023/05/26/the-api-verifier/ Fri, 26 May 2023 11:26:59 +0000 https://blog.jetbrains.com/wp-content/uploads/2023/05/rs-how-tos-blog-featured-image-1280x600-1.png https://blog.jetbrains.com/?post_type=dotnet&p=357902 Despite being a plugin itself, ReSharper serves as an incredible platform for plugin creation. ReSharper’s development team uses the dependency injection approach to design every aspect of the product. That means that any component of ReSharper is like a building block that can be overridden with plugins, opening up virtually limitless possibilities for fine-tuning every aspect of ReSharper’s behavior. 

As a plugin author, you get to take advantage of each and every component that ReSharper developers themselves use. But there’s a flip side to such freedom.

Following the “each public component is an extension point” strategy limits our ability to refactor the existing code. It also makes huge refactorings like preparing ReSharper for the out-of-process mode or preparing our codebase for integration into Fleet much harder. For years, we’ve declared every new version of ReSharper as incompatible with the installed plugins, even if the set of APIs they reference hasn’t technically changed. Due to our internal refactorings, we could not guarantee seamless migration.  

Both ReSharper and Rider have major releases three times a year, which means that until recently, plugin developers were forced to recompile their plugins at least three times a year. It was not lost on us that sticking with this approach made the job of plugin writers maddening, and we knew things had to change. 

That is how we got started with the idea of getting ReSharper’s platform to automatically detect changes in the APIs its plugins reference.  

What is an API verifier? 

In ReSharper, we treat each plugin as an essential part of the product. During the installation process, all parts are combined to form one installation. It is at this stage that we now detect if a plugin uses an API that’s been altered or is no longer available in the latest version of ReSharper.

Should the API verifier raise any red flags, the plugin will be marked as Incompatible and won’t be included with the installed product. It will, however, remain on the list of installed plugins in the Extension manager inside ReSharper.

Those plugins that do pass the API verification will automatically migrate to the next version of the product. ReSharper 2023.1 is actually the second version of ReSharper to incorporate the API verifier, so all plugins that have been compatible prior to the 2023.1 release will be preserved. 

A new way to specify dependencies

With the introduction of the API verifier, we’re also changing our requirements for specifying dependencies pertaining to product version compatibility. This requires a bit of backstory.

Internally, we call our major releases “waves”. To name a wave, we take the last two digits of the year of the release and add a single digit indicating the number for the release in that year. For example, for the first major release of 2023, the wave name is “wave 231”, and the third one for 2022 is “wave 223”. 

Specifying the wave name as a dependency in your plugin can benefit you in two ways. First, it will help us verify that a package is indeed an extension for ReSharper. Secondly, specifying the range of compatible waves lets you indicate which versions of the products are compatible with the plugin. 

In the past, we recommended using interval notations for that. For example, if you were to specify the range in interval notation as [223-231) that would mean that the plugin was only compatible with the 2022.3 version. However, with the introduction of the API verifier, we now recommend specifying waves without using interval notation. You can now declare forward-compatibility just by specifying 231 as a wave dependency, like this:

<dependencies>
<dependency id="Wave" version="231.0.0"/>
</dependencies>

Marketplace integration

The code for the API verifier which executes during the installation was also implemented inside JetBrains Marketplace. This is particularly good news for plugin developers as the page for their plugin now displays detailed warnings whenever the APIs used are no longer present in the SDK. API verification is performed on all available plugins before each Early Access Preview or major release goes public. 

An example of how the API verification results look on the plugin web page of JetBrains Marketplace.

Plugin developers can also opt in to receive email notifications whenever their plugins fail to pass the verification process. The emails provide exhaustive information on which APIs became obsolete or were removed, altered, or updated, helping you zero in on the parts that won’t compile without having to open a project with the plugin code. 

Need help with writing a plugin for ReSharper or Rider? 

What’s next? 

The information on product compatibility that’s now visible in JetBrains Marketplace is only there for the publisher. It’s not possible for us to automatically unlist a plugin, even if we are sure that it is completely outdated and is definitely not compatible with the current version of ReSharper. At the moment, we’re showing those plugins as available in the Extension manager, even though any attempts to install them will fail because they cannot pass the embedded verification.

In the future, we are going to weed out such plugins from the feed. The feeds with the plugins are currently built based on wave versions, not the actual build number of the product. This means that the list of plugins for ReSharper 2023.1 EAP 1 is the same as for the release version. The Marketplace and ReSharper development teams are already working on making the switch from a wave-based feed to a build-based feed, so stay tuned for more updates. 

Kudos

We would like to thank Mike-E for his endless energy and for bringing the importance of implementing this functionality to our attention.

]]>
https://blog.jetbrains.com/zh-hans/dotnet/2023/05/26/the-api-verifier/