Friday, April 16, 2021

Visual Stuido 16.10.0 Preview 2.0: New Git Features

I am always using Visual Studio latest preview and this morning I saw an update notification for Visual Studio 16.10.0 Preview 2.0. I have just installed and relaunched, and instantly noticed some change in the rightmost button in Visual Studio.
Enhanced rightmost button in Visual Studio
Now when you click on it, you are getting something similar to Git Changes window. And when I opened Git Changes window, I saw this.
Git Changes: Connect to Azure DevOps
And connecting this will allow you access Work Items and Builds for the project. And we can even create a PR (of course, that will open up the browser). As far as I can remember, this was available for Git for some time, but not for Azure DevOps.
Git Changes: Create PR
And then I looked for more information and saw these 2 blog posts, you should definitely check these out. There are more features to explore.


Hope this helps.

Happy Coding.

Regards,
Jaliya

Sunday, April 11, 2021

Early Support for .NET Hot Reload is Now Available with .NET 6 Preview 3

.NET 6 Preview 3 is released just a couple of days back with some nice features. One of the nicest features that got released with this preview is the early support for .NET Hot Reload. Currently, it's only available for ASP.NET Core and Blazor Projects. The command is dotnet watch.

So what's this? Let's have a look using a demo.

First things first. we need to install .NET 6 Preview 3. I have already installed and this is my dotnet version.
PS C:\Users\Jaliya\Desktop> dotnet --version
6.0.100-preview.3.21202.5
Now I am creating a new ASP.NET Core WebApi Project by running the following command.
dotnet new webapi -n webapi
Once the project is created, I am opening it up on VS Code, and let's go through some of the files. First the csproj file.
.csproj file
As expected it's targetting .NET 6. Now let's have a look at launchSettings.json file under Properties folder. It looks similar to what we have already seen and know, but you should notice something new.
launchSettings.json
There is a new property named hotReloadProfile and its value is set to aspnetcore. So this is what's going to enable hot loading. For Blazor WebAssembly projects, the profile value is blazorwasm.

Now I have got rid of the default WeatherForecastController and added a new TestController.
using Microsoft.AspNetCore.Mvc;

namespace webapi.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class TestController : ControllerBase
    {
        [HttpGet]
        public IActionResult Get()
        {
            return Ok("Hello World");
        }
    }
}
Nothing fancy here, it's just a single GET action returning some string. Not what I am going to do is run dotnet watch. And it's kind of hard to show hot reloading in writing, let me share a screencast.
Hot Reload
In the image above, I have snapped VS Code to the left and a browser to the right. I am doing code changes on VS Code and reloading the page on the right. Here I run the project with dotnet run and while it's running, I am changing the value of the string our endpoint is returning and saving. And sending another request, now the endpoint is returning the new value.

So is it restarting the application behind the scene? That's an interesting question. Let's look at the below sample.

Here I have this interface and its implementation.
public interface ISingletonService
{
    int GetCurrentNumber();
}

public class SingletonService : 
ISingletonService
{
    private int _someNumber;

    public int GetCurrentNumber()
    {
        return _someNumber++;
    }
}
It is registered as a Singleton meaning one instance per the whole lifetime of this application.
services.AddSingleton<ISingletonService, SingletonService>();
Now I am changing my test endpoint to something like below.
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
    private readonly ISingletonService _singletonService;

    public TestController(ISingletonService singletonService)
    {
        _singletonService = singletonService;
    }

    [HttpGet]
    public IActionResult Get()
    {
        return Ok($"Number: {_singletonService.GetCurrentNumber()}");
    }
}
So whenever I call this endpoint, it will return the current number calculated inside GetCurrentNumber method. Initially, it will start from 0, then per each call, it will increment by 1 and return the new value. Whenever I restart the application, it will again start from 0.
Hot Reload Preserving App State
Here again, I am doing a dotnet watch and while it's running, I am doing a change. You can see the output is getting changed, but most importantly the number keeping getting incremented from the last number. Basically, while the runtime is doing the hot reload, the app state is being preserved. It's not just a simple restart behind the scene.

Isn't it nice or what!

Do try it out! I am pretty sure this is going to be loved by all .NET devs around the world.

Happy Coding.

Regards,
Jaliya

Saturday, April 10, 2021

How to Delete an Azure Hybrid Connection through Azure Portal

This is a quick post on how we can delete an Azure Hybrid Connection through Azure Portal. Thought of writing a post since it took some time for me to figure out.

Once an Azure Hybrid Connection is added to Azure App Service/Function, unfortunately, there is no delete option on the page where we added it or on the list of available Hybrid Connections.
Available Hybrid Connections
Once a Hybrid Connection is added there is a Relay that gets created (this is essentially created with the same name as the Service Bus namespace you have given when you are creating a new Hybrid Connection). You need to click on the Relay and go inside there.
Relay
There on the left side menu, you will see a menu item called Hybrid Connections and once you click on it, you will see your list of Hybrid Connections. Now click on your Hybrid Connection to delete, then you will see the Delete option.
Delete Hybrid Connection
Hope this helps!

Happy Coding.

Regards,
Jaliya

Tuesday, March 23, 2021

ASP.NET Core and Swagger: Add Operations Programmatically

In this post, let's see how we can programmatically add Operations to Swagger document in an ASP.NET Core Application.

I had a requirement where an Angular application uses DevExpress Report Viewer and the reports are being rendered through an ASP.NET Core API. Front-end communicates with BE through an Azure API Gateway. So basically all the back-end services endpoints need to be exposed via Azure API Gateway and it's getting updated by discovering the endpoints in back-end services Swagger documents. But unfortunately Swagger is failing to discover the DevExpress default reporting endpoints.

So I have overridden the default endpoints and applied [ApiExplorerSettings(IgnoreApi = true)] attribute. Now those endpoints will be ignored from getting generated into the Swagger Document, but still, I needed to programmatically add those.

It's actually quite simple. I just needed to create a new IDocumentFilterIDocumentFilters are useful when we need to modify Swagger Documents after they're initially generated.

using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Collections.Generic;

public class AddReportViewerOperationsFilter : IDocumentFilter
{
    public void Apply(OpenApiDocument swaggerDocDocumentFilterContext context)
    {
        // Tags are for group the operations
        var openApiTags = new List<OpenApiTag> { new OpenApiTag { Name = "ReportViewer" } };

        // whatever the path
        swaggerDoc.Paths.Add($"/api/{swaggerDoc.Info.Version}/Reports/Viewer"new OpenApiPathItem
        {
            Operations = new Dictionary<OperationTypeOpenApiOperation>
            {
                // GET: /api/v1/Reports/Viewer
                {
                    OperationType.Get,
                    new OpenApiOperation
                    {
                        Tags = openApiTags,
                        Responses = new OpenApiResponses
                        {
                            { "200"new OpenApiResponse { Description = "Success" } }
                        }
                    }
                },
                // POST: /api/v1/Reports/Viewer
                {
                    OperationType.Post,
                    new OpenApiOperation
                    {
                        Tags = openApiTags,
                        Responses = new OpenApiResponses
                        {
                            { "200"new OpenApiResponse { Description = "Success" } }
                        }
                    }
                }
            }
        });
    }
}

So here I have just added 2 Operations, but you get the idea. You can define the Parameters for Operations if you want to, in my case, I didn't want to.

Finally, we need to register the AddReportViewerOperationsFilter when setting up Swagger. 

public void ConfigureServices(IServiceCollection services)
{
    // some code

    services.AddSwaggerGen(config =>
    {
        // some code

        config.DocumentFilter<AddReportViewerOperationsFilter>();
    }); // some code }

And now, when we spin up the service, we can see the new operations that got added in the Swagger document.

Swagger: Programmatically Added Operations

Hope this helps.

Happy Coding.

Regards,
Jaliya

Monday, March 15, 2021

WPF: External Login with Identity Server using Microsoft Edge WebView2

In this post, let's see how we can use Microsoft Edge WebView2 in a WPF Application to Sign In with an IdentityServer. This is inspired by the sample project available at IdentityModel/IdentityModel.OidcClient.Samples/WpfWebView where it's using WebBrowser. I wasn't happy with the appearance at all.

Not Responsive
Not Responsive
I thought of trying with WebView2 instead of WebBrowserWebView2 uses Microsoft Edge (Chromium) as the rendering engine whereas WebBrowser uses Internet Explorer.

So I have created a WPF Application targetting .NET 5 (please check WebView2 supported frameworks/platforms of here) and tried the code in IdentityModel/IdentityModel.OidcClient.Samples/WpfWebView project. But since I am going to be using WebView2, I have installed Microsoft.Web.WebView2 NuGet package and changed the WpfEmbeddedBrowser class as follows.

WpfEmbeddedBrowser.cs
public class WpfEmbeddedBrowser : IBrowser
{
    private BrowserOptions _options = null;

    public async Task<BrowserResultInvokeAsync(BrowserOptions optionsCancellationToken cancellationToken = default)
    {
        _options = options;

        var semaphoreSlim = new SemaphoreSlim(0, 1);
        var browserResult = new BrowserResult()
        {
            ResultType = BrowserResultType.UserCancel
        };

        var signinWindow = new Window()
        {
            Width = 800,
            Height = 600,
            Title = "Sign In",
            WindowStartupLocation = WindowStartupLocation.CenterScreen
        };
        signinWindow.Closing += (se) =>
        {
            semaphoreSlim.Release();
        };

        var webView = new WebView2();
        webView.NavigationStarting += (se) =>
        {
// Check whether we are navigating back to the RedirectUri specified in OidcClientOptions, // that means authentication process is completed
            if (IsBrowserNavigatingToRedirectUri(new Uri(e.Uri)))
            {
                e.Cancel = true;

                browserResult = new BrowserResult()
                {
                    ResultType = BrowserResultType.Success,
                    Response = new Uri(e.Uri).AbsoluteUri
                };

                semaphoreSlim.Release();
                signinWindow.Close();
            }
        };

        signinWindow.Content = webView;
        signinWindow.Show();

        // Explicit initialization
        await webView.EnsureCoreWebView2Async(null);

        // Delete existing Cookies so previous logins won't remembered
        webView.CoreWebView2.CookieManager.DeleteAllCookies();

        // Navigate
        webView.CoreWebView2.Navigate(_options.StartUrl);

        await semaphoreSlim.WaitAsync();

        return browserResult;
    }

    private bool IsBrowserNavigatingToRedirectUri(Uri uri)
    {
        return uri.AbsoluteUri.StartsWith(_options.EndUrl);
    }
}
Note: we need to have WebView2 Runtime installed on client machines for this to work.

And now when I ran the application, it's just beautiful. And works great with IdentityModel.OidcClient.
Responsive UI
Responsive UI
Seems Microsoft Edge WebView2 is the way to go if we are embedding web technologies (HTML, CSS, and JavaScript) in native apps.

Hope this helps.

Happy Coding.

Regards,
Jaliya

Tuesday, February 23, 2021

EF Core: Using HiLo Algorithm to Generate IDs

In this post, let's see how we can use Hi/Lo algorithm as the key generation strategy in EF Core. This can be achieved using UseHiLo method.

As always let's go by an example.

Consider the below code.
public class Category
{
    public int Id { getset; }

    public string Name { getset; }

    public ICollection<Product> Products { getset; }
}

public class Product
{
    public int Id { getset; }

    public string Name { getset; }

    public int CategoryId { getset; }
}
And MyDbContext is setup like this.
public class MyDbContext : DbContext
{
    public DbSet<Category> Categories { getset; }

    public DbSet<Product> Products { getset; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
            .UseSqlServer("ConnectionString")
            .LogTo(Console.WriteLineLogLevel.Information);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Category>(builder =>
        {
            builder
                .HasMany(x => x.Products)
                .WithOne()
                .HasForeignKey(x => x.CategoryId);
        });
    }
}
And now I am adding a Category and a Product.
using var context = new MyDbContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

var category = new Category() { Name = "Some Category" };
context.Categories.Add(category);

var product = new Product { Name =
"Some Product", CategoryId = category.Id };
context.Products.Add(product);

await context.SaveChangesAsync()
The SaveChanges here will fail here with the following error: "The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Products_Categories_CategoryId"". The reason is, we are setting the CategoryId of the Product, but it's still 0 because the Category record is still not saved to the database.

In this kind of a scenario, using HiLo algorithm to determine the Id before the record is actually saved in the database is quite handy.

Enabling HiLo is pretty easy. You can either enable it for all the entities or for a specific entity.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    // HiLo Id generation For all the entities
    //modelBuilder.UseHiLo();

    modelBuilder.Entity<Category>(builder =>
    {
        builder
            .HasMany(x => x.Products)
            .WithOne()
            .HasForeignKey(x => x.CategoryId);

        // HiLo Id generation only for Category
        builder
            .Property(x => x.Id)
            .UseHiLo("categoryseq");
    });
}
After this change, if you run the example, you are not going to see the previous error, instead, you should see Category now has a valid Id.
Valid CategoryId
If you have a second category added, it will have an Id of value 2.

Here something important to note is if you do UseHilo() for all the entities, the Ids are going to be kind of messed up, if you take the above example, the Id of the Product here will be 2.

Now let's try 2 simultaneous adding. While the control is waiting on SaveChanges, I am starting another instance of the application. 
Another Instance
Here the new category has the Id of value 11.

Now how is this working?

If you add in database migration, you will see EF is creating a Sequence, something like below.
migrationBuilder.CreateSequence(
    name"categoryseq",
    incrementBy: 10);
And once the database is updated, you can see the created sequence here.
Sequences
categoryseq
Now let's imagine so far we have only created the database and no data is inserted.

Now we are creating a new database context and adding a category to context (context.Categories.Add(category)), EF will run a query like this.
SELECT NEXT VALUE FOR [categoryseq]
This will return 1, and the category will be assigned the value of 1 and it has now blocked Ids from 1 to 10. If we add in another category in the same database context, EF won't run the above query again, it will increment the Id by 1 and when we have added 10 categories, EF will run the above query again to get the next value, which will be 11. But imagine while we are adding these 10 categories, some other user creates a new database context and starts adding categories, then that process will also call the above query, so this time he will get 11 (because so far we have only blocked from 1 to 10). And when we call for next value, we will get 21, because that process has now blocked 11 to 20.

Isn't it nice? But there is a kind of downside here. This can cause missing Ids (if you are worried about the order of the Ids), but generally, it shouldn't be an issue.

Hope this helps!

Happy Coding.

Regards,
Jaliya

Wednesday, February 10, 2021

EF Core: Owned Entity Types

In this post let's see what is Owned Entity Types in Entity Framework Core. This is a very nice feature when it comes to DDD.

Owned Entities are entities that can be only appeared on navigation properties of other entity types. Basically, they can't exist without the Owner.  

Let's go by an example. Consider the following two entities.
public class User
{
    public int Id { getset; }

    public string Name { getset; }

    public Address Address { getset; }
}
public class Address
{
    public string Street { getset; }

    public string City { getset; }
}
And my DbContext looks like this.
public class MyDbContext : DbContext
{
    public DbSet<User> Users { getset; }

    //...
}
So here, we can use OwnsOne to configure the Address Owned Entity to the Owner User as below (I am using Fluent API instead of annotations, you can annotations if you prefer, but I prefer Fluent API all the time).
public class UserEntityConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<Userbuilder)
    {
        builder.ToTable($"{nameof(User)}");

        builder.OwnsOne(x => x.Address);
    }
}
And this will create something like this.
dbo.User
If you don't like the default naming convention, you can always override the behavior like below.
public class UserEntityConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<Userbuilder)
    {
        builder.ToTable($"{nameof(User)}");

        builder.OwnsOne(x => x.Address, y =>
        {
            y.Property(y => y.City)
                    .HasColumnName("City");

            y.Property(y => y.Street)
                    .HasColumnName("Street");
        });
    }
}
And this would give something like below.
dbo.User

Now let's see consider the following code to insert some data and retrieval.
using var context = new MyDbContext();

User user = new User
{
    Name = "John Doe",
    Address = new Address { Street = "Some Street1", City = "Some City1" }
};

await context.Users.AddAsync(user);
await context.SaveChangesAsync();
using var anotherContext = new OrderDbContext();

user = await anotherContext.Users.FirstOrDefaultAsync();
Here for selection, EF Core will generate a query as below.
SELECT TOP(1) [u].[Id], [u].[Name], [u].[City], [u].[Street]
FROM [User] AS [u]
So here you should be able to see a very important thing. When we are getting User, we didn't have to explicitly Include Address to load Address details, it's loading its Owned Entities by default. This might not be a good example, since it's in the same table, we will see a clearer example when we are going through OwnsMany later in this post. 

Note: I am using anotherContext here for retrieval, because EF Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.

Above was kind of a one-to-one relationship. Now let's have a look at one-to-many type of relationship.
public class User
{
    public int Id { getset; }

    public string Name { getset; }

    public ICollection<Address> Addresses { getset; }
}
public class Address
{
    public string Street { getset; }

    public string City { getset; }

    public string Type { getset; }
}
Now I can use OwnsMany as below.
public class UserEntityConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<Userbuilder)
    {
        builder.ToTable($"{nameof(User)}");

        builder.OwnsMany(x => x.Addresses, y =>
        {
            y.ToTable("UserAddress");

            y.Property(y => y.City)
                .HasColumnName("City");

            y.Property(y => y.Street)
                .HasColumnName("Type");
            y.Property(y => y.Street)
                .HasColumnName("Type");
        });
    }
}
And this would give something like this.
dbo.User and dbo.UserAddress
Now let's have a look at below code to insert some data and retrieval.
using var context = new MyDbContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

var user = new User
{
    Name = "John Doe",
    Addresses = new List<Address>
    {
        new Address { Street = "Some Street1", City = "Some City1", Type= "Shipping" },
        new Address { Street = "Some Street2", City = "Some City2", Type= "Mailing" }
    }
};

await context.Users.AddAsync(user);
await context.SaveChangesAsync();

using var anotherContext = new MyDbContext();

user = await anotherContext.Users.FirstOrDefaultAsync();
Here for selection, the generated query would be the following. (you can basically ignore the subquery, that's because I am getting FirstOrDefault),
SELECT [t].[Id], [t].[Name], [u0].[UserId], [u0].[Id], [u0].[City], [u0].[Street], [u0].[Type]
FROM (
    SELECT TOP(1) [u].[Id], [u].[Name]
    FROM [User] AS [u]
) AS [t]
LEFT JOIN [UserAddress] AS [u0] ON [t].[Id] = [u0].[UserId]
ORDER BY [t].[Id], [u0].[UserId], [u0].[Id]
And here also, without doing .Include(x => x.Addresses), the addresses are being returned.

So that's about it. You can read more on Owned Entity Types by going to the below link.
There are some restrictions when it comes to Owned Entities which makes perfect sense.
  • You cannot create a DbSet<T> for an owned type.
  • You cannot call Entity<T>() with an owned type on ModelBuilder.
  • Instances of owned entity types cannot be shared by multiple owners (this is a well-known scenario for value objects that cannot be implemented using owned entity types).
Hope this helps.

Happy Coding.

Regards,
Jaliya

Wednesday, January 27, 2021

Azure Pipelines: Create/Update Azure API Management APIs

In this post, let's see how we can create/update Azure API Management APIs from both classic release pipelines and YAML based pipelines.

Here I am assuming, We have already created Azure API Management Service in Azure and we have set up Products.

The process is actually quite easy, thanks to this great free extension API Management Suite by Stephane Eyskens

First, let's have a look at how we can use this in classic release pipelines.

Once this extension is installed, you should be able to see a bunch of tasks starting with API Management - *. From there what we need is API Management - Create or Update API task. We just need to add this task after the deployment of the API.

API Management - Create or Update API

Then it's just a matter of configuring the task.
API Management - Create or Update API Settings
Most of the things here are pretty obvious, so I am not going to explain each one of them. The important ones are,
  • Products
    • Since I have already created a product in the APIM, I can just enter the name of the Product.
    • If you are creating the Product as part of the pipeline (using API Management - Create or update product task), then you need to select Product created by previous task.
  • OpenAPI Specification
    • The APIM needs to have access to OpenAPI specification in order to create the API. You have several options here. You need to select the version, it's format (json or yaml), any authorization needed to access the specification and the location (URL, Code or Build Artifact)
  • Configuration of the API
    • Here, the nice thing is you can specify the Policy. I prefer maintaining the policy in the code, so here I have just selected my policy by providing it's path.
So that's basically it. Once the API is deployed, this task will run, it will pull down the OpenAPI specification, create/update APIM API, apply the policy and it's all good to go.

If you prefer having deployment steps as part of YAML pipeline, we can easily set up this task there as well. Basically something like this.
task: stephane-eyskens.apim.apim.apim@5
    displayName: 'API Management - Create/Update API '
    inputs:
      ConnectedServiceNameARM: ''
      ResourceGroupName: ''
      ApiPortalName: ''
      UseProductCreatedByPreviousTask: false
      product1: ''
      OpenAPISpec: 'v3'
      swaggerlocation: ''
      targetapi: ''
      DisplayName: ''
      pathapi: ''
      subscriptionRequired: false
      TemplateSelector: Artifact
      policyArtifact: '$(Pipeline.Workspace)\scripts\apim\apim-policy.xml'
      MicrosoftApiManagementAPIVersion: '2018-01-01'
Don't worry, you have the UI to select the options just like classic releases.
API Management - Create or Update API Settings
So hope this helps. 

Huge thanks to Stephane Eyskens for coming up with this nice set of tasks.

Happy Coding.

Regards,
Jaliya