Create a private NuGet package with GitHub and Azure

> 2024-01-15 Davide Benedetti

Let's say we made a class library project; now we'd like to make this NuGet package accessible from outside our local machine.

We can do so by hosting our package on a NuGet package feed - there are several options available: we'll pick GitHub here.

First, we want to host our project on GitHub if we haven't already done so: create new repo, push the code.

Then, we'll follow some steps to host the NuGet on GitHub so that it can be consumed by Visual Studio (with authentication, if we want a private feed) and, eventually, by an Azure web app.

From GitHub, let's create a Personal Access Token (Classic):

  • Go to Profile > Settings > Developer settings
    rte_image_23.png
  • Under Personal access tokens  > Tokens (classic) create a new token (classic) that has at least Read access to packages (make sure to save the token string in a safe place)
    rte_image_26.png
    rte_image_32.png
  • In our Project properties (back to Visual Studio), set Repository URL to https://github.com/<USER NAME>/<REPO NAME>
    rte_image_35.png
  • In our Project folder, create a nuget.config file with the following content:
<configuration>
  <packageSources>
    <clear />
    <add key="github" value="https://nuget.pkg.github.com/<USER NAME>/index.json" />
  </packageSources>
  <packageSourceCredentials>
    <github>
      <add key="Username" value="<USER NAME>" />
      <add key="ClearTextPassword" value="<TOKEN>" />
    </github>
  </packageSourceCredentials>
</configuration>

So we now have a nuget.config file that lives alongside our class library project's .csproj file.

In Visual Studio, Tools > NuGet Package Manager > Package Manager Console, cd (change directory) to the project directory and then run

dotnet pack --configuration Debug
dotnet nuget push ".\bin\Debug\<your build>.nupkg" --source "github"

This should be done every time we want to push out a new version.

Now we should be able to find our package at https://github.com/<USER NAME>?tab=packages

rte_image_38.png

That's it for GitHub. Now we want Visual Studio to be able to consume this private feed:

1) Go to Tools > NuGet Package Manager > Manage NuGet Packages for Solution
rte_image_62.png
2) Add https://nuget.pkg.github.com/<USER NAME>/index.json as a source:
3) On the top right corner, select the new package source
4) In the authentication dialog, use <USER NAME> and <TOKEN>
rte_image_68.png

Now we are subscribed to our new package feed and we can use our packages hosted there:

rte_image_41.png

This is it for Visual Studio. Now, what if we are hosting a web app on Azure, and this web app relies on an external NuGet feed? How does Azure know about it?

For an Azure App Service to use this private NuGet feed,

1) Go to Web App > Configuration > Application Settings > New Application setting
- Name: github (same as the key in nuget.config)
- Value: <TOKEN>
rte_image_44.png
2) The project being published might need a copy of the same nuget.config

That's it! Now when we publish our Azure Web App it'll be able to consume our new NuGet packages just like we would from Visual Studio.

References:

[1] https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-nuget-registry</p>

Create a NuGet package from Visual Studio

> 2024-01-15 Davide Benedetti

When the need arises to reuse the same codebase on different projects, we can create a library. NuGet is a way of doing so.

Let's think about naming and structure first.

Using the namespace naming convention, we can base the complete name of our library on three components:

1. The organization name, i.e. the owner of the libraries we make
2. A category name (optionally), i.e. what this library refers to
3. The name of our library

For instance, we made a Blazor component that shows a full-screen "please wait" spinner animation. It's related to Blazor and it's a wait screen, so we named it "WaitScreen" and the complete name is "SLACrew.Blazor.WaitScreen". This is also the namespace.

As for structure, our WaitScreen solution looks like this:

./SLACrew.Blazor.WaitScreen

-> .git/

-> src/

  -> SLACrew.Blazor.WaitScreen/

  -> SLACrew.Blazor.WaitScreen.Demo/

  -> SLACrew.Blazor.WaitScreen.sln

-> README.md

Instead of placing our solution in the SLACrew.BlazorWaitScreen folder, we place it in SLACrew.BlazorWaitScreen/src folder, allowing the base folder to host miscellaneous files. We can then create different projects where the main one hosts the library code and then optional demos with which we can test and improve our codebase.

We need to start by creating a Class Library project (Razor Class Library if we want to create Blazor components/services). In our case that's SLACrew.Blazor.WaitScreen:

rte_image_5.png

Now we can create a project that allows us to test out our features. In our case that's SLACrew.Blazor.WaitScreen.Demo, a Blazor Server project (WebAssembly would be fine too):

rte_image_8.png

In our case, the demo project shows a button that allows us to showcase the wait screen feature:

rte_image_11.png

The code structure will look something like this:

rte_image_14.png

Once we're done coding it's time to create a NuGet package out of our class library. Let's right-click our project and select Properties. Let's then make sure that
1) "Produce a package file during build operations" is checked
2) We increment the package version every time we want to push out a new version of our package

rte_image_17.png

When we build the class library we'll find the package in the debug/release folder:

rte_image_20.png

That's it. One next step would be to host this package to a NuGet feed on the web so that it can be consumed anywhere else and not just on our local computer. There's a blog article here outlining exactly how to do so.

Authentication in Entity Framework - Quick Deploy

> 2024-01-11 Davide Benedetti

Let's set up authentication in an ASP.NET Core web application. This is going to be the final result:

rte_image_14.png??

1) Install the following NuGet packages:

  • Microsoft.EntityFrameworkCore.Tools -> necessary to run migration
  • Microsoft.EntityFrameworkCore.SqlServer (if we use SqlServer dialect - adapt as needed)
  • Microsoft.AspNetCore.Identity.EntityFrameworkCore
  • Microsoft.AspNetCore.Identity.UI
  • Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore -> necessary to run Add-Migration CreateIdentitySChema

2) Create an ApplicationDbContext class:

ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
}

3) In Startup class,??

builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(OrmTest.ConnectionString));
builder.Services.AddDefaultIdentity<IdentityUser>(options =>
{
options.SignIn.RequireConfirmedAccount = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireDigit = false;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = false;
}).AddRoles<IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();


builder.Services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>, UserClaimsPrincipalFactory<IdentityUser, IdentityRole>>();

3.1) Make sure your connection string contains ";TrustServerCertificate=true"

4) In Package Manager Console, cd to the project directory and run these two commands:

Add-Migration CreateIdentitySchema

Update-Database

5) Run dotnet tool install -g dotnet-aspnet-codegenerator (just once) and then dotnet-aspnet-codegenerator identity --dbContext YOUR_CONTEXT_CLASS --files "Account.Login;Account.Logout;Account.Register" -tfm "net60" to get the default login/logout files. Adjust net60 to the version you need.

6) In App.razor, have this code

<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" >
            <NotAuthorized>
                You are not authorized to view this content
            </NotAuthorized>
        </AuthorizeRouteView>
    </Found>
    <NotFound>
        <CascadingAuthenticationState>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, this part is still under construction...</p>
            </LayoutView>
        </CascadingAuthenticationState>
    </NotFound>
</Router>

7) Make sure that your startup class has app.UseAuthentication(), app.UseAuthorization() and app.MapRazorPages()?? so you can navigate to Identity/Account/Login and all the other authentication pages we've just set up.

HIRE US!

Send us a message from the form below and we'll get back to you ASAP
>_
>_
>_
>_

Please wait...

An error has occurred. This application may no longer respond until reloaded. Reload 🗙