Category Archives: ASP.NET

EF Core Relationships in ASP .NET Core 3.1

By Shahed C on February 3, 2020

This is the fifth of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

E is for EF Core Relationships

In my 2018 series, we covered EF Core Migrations to explain how to add, remove and apply Entity Framework Core Migrations in an ASP .NET Core web application project. In this article, we’ll continue to look at the newer 2020 NetLearner project, to identify entities represented by C# model classes and the relationships between them.

NOTE: Please note that NetLearner is a work in progress as of this writing, so its code is subject to change. The UI web apps still needs work (and will be updated at a later date) but the current version has the following models with the relationships shown below:

NetLearner database diagram
NetLearner database diagram

Classes and Relationships

The heart of the application is the LearningResource class. This represents any online learning resource, such as a blog post, single video, podcast episode, ebook, etc that can be accessed with a unique URL.

public class LearningResource
{
    public int Id { get; set; }

    [DisplayName("Resource")]
    public string Name { get; set; }


    [DisplayName("URL")]
    [DataType(DataType.Url)]
    public string Url { get; set; }

    public int ResourceListId { get; set; }
    [DisplayName("In List")]
    public ResourceList ResourceList { get; set; }

    public ContentFeed ContentFeed { get; set; }

    public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
} 

The ContentFeed class represents the RSS Feed (or channel information) for an online resource, a URL that can be used to retrieve more information about the online resource, if available.

public class ContentFeed
{
    public int Id { get; set; }

    [DisplayName("Feed URL")]
    public string FeedUrl { get; set; }

    public int LearningResourceId { get; set; }
    public LearningResource LearningResource { get; set; }
}

The ResourceList class represents a logical container for learning resources in the system. It is literally a list of items, where the items are your learning resources.

public class ResourceList
{
    public int Id { get; set; }

    public string Name { get; set; }

    public List<LearningResource> LearningResources { get; set; }
} 

The TopicTag class represents a single “tag” value that can be used to categorize online resources. Possibly values could be “.NET Core”, “ASP.NET Core” and so on.

public class TopicTag
{
    public int Id { get; set; }

    [DisplayName("Tag")]
    public string TagValue { get; set; }

    public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
} 

At this point, you may have noticed both the LearningResource and TopicTag classes contain a List<T> property of LearningResourceTopicTag. If you browse the database diagram, you will notice that this table appears as a connection between the two aforementioned tables, to establish a many-to-many relationship. (more on this later)

The following diagram shows an example of how the a LearningResource (e.g. link to a doc/video) is a part of a ResourceList, while each LearningResource also has a link back to its root site, channel or RSS feed (via ContentFeed).

One to One

Having looked through the above entities and relationships, we can see that each LearningResource has a ContentFeed. This is an example of a 1-to-1 relationship. For example:

  • Learning Resource = Wake Up and Code! blog site
  • Content Feed = RSS Feed for blog site

In the two classes, we see the following code:

public class LearningResource
{
public int Id { get; set; }

[DisplayName("Resource")]
public string Name { get; set; }


[DisplayName("URL")]
[DataType(DataType.Url)]
public string Url { get; set; }

public int ResourceListId { get; set; }
[DisplayName("In List")]
public ResourceList ResourceList { get; set; }

public ContentFeed ContentFeed { get; set; }

public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
}
public class ContentFeed
{
public int Id { get; set; }

[DisplayName("Feed URL")]
public string FeedUrl { get; set; }

public int LearningResourceId { get; set; }
public LearningResource LearningResource { get; set; }
}

Each Learning Resource has a corresponding Content Feed, so the LearningResource class has a property for ContentFeed. That’s pretty simple. But in the  ContentFeed class, you don’t necessarily need a property pointing back to the  LearningResource . In fact, all you need is a  LearningResourceId property. EF Core will ensure that LearningResource.Id points to ContentFeed.LearningResourceId in the database. But to help with object-property navigation in your code, it is useful to include an actual LearningResource object in the ContentFeed class to point back to LearningResource.

One to One Relationship
One to One Relationship

Another way of looking at the One-to-One relationship is to view the constraints of each database entity in the visuals below. Note that both tables have an Id field that is a Primary Key (inferred by EF Core) while the ContentFeeds table also has a Foreign Key for the LearningResourceId field used for the constraint in the relationship.

LearningResources table
LearningResources table
ContentFeeds table
ContentFeeds table
One to One Relationship
One to One Relationship

One to Many

Next, let’s take a look at the One-to-Many relationship for each ResourceList that has zero or more LearningResources. For example:

  • Resource List = ASP .NET Core Blogs (parent container)
  • Learning Resource = ASP .NET Core A-Z Blog Series (single URL)

In the two classes, we see the following code:

public class ResourceList
{
public int Id { get; set; }

public string Name { get; set; }

public List<LearningResource> LearningResources { get; set; }
}
public class LearningResource
{
public int Id { get; set; }

[DisplayName("Resource")]
public string Name { get; set; }


[DisplayName("URL")]
[DataType(DataType.Url)]
public string Url { get; set; }

public int ResourceListId { get; set; }
[DisplayName("In List")]
public ResourceList ResourceList { get; set; }

public ContentFeed ContentFeed { get; set; }

public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
}

Each Resource List has zero or more Learning Resources, so the ResourceList class has a List<T> property for LearningResources. This is even simpler than the previously described 1-to-1 relationship. In the LearningResource class, you don’t necessarily need a property pointing back to the ResourceList. But once again, to help with object-property navigation in your code, it is useful to include an actual ResourceList object in the LearningResource class to point back to ResourceList.

One to Many Relationship
One to Many Relationship

Another way of looking at the One-to-Many relationship is to view the constraints of each database entity in the visuals below. Note that both tables have an Id field that is a Primary Key (once again, inferred by EF Core) while the ResourceLists table also has a Foreign Key for the  ResourceListsId field used for the constraint in the relationship.

One to Many Constraint
One to Many Constraint

Many to Many

Finally, let’s also take a look at a Many-to-Many relationship, for each TopicTag and LearningResource, either of which can have many of the other. For example:

  • Topic Tag = “ASP .NET Core” (tag as a text description)
  • Learning Resource = Specific blog post on site (single URL)

This relationship is a little more complicated than all of the above, as we will need a “join table” to connect the two tables in question. Not only that, we will have to describe the entity in the C# code with connections to both tables we would like to connect with this relationship.

In the two classes we would like to connect, we see the following code:

public class TopicTag
{
    public int Id { get; set; }

    [DisplayName("Tag")]
    public string TagValue { get; set; }

    public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
} 
public class LearningResource
{
public int Id { get; set; }

[DisplayName("Resource")]
public string Name { get; set; }


[DisplayName("URL")]
[DataType(DataType.Url)]
public string Url { get; set; }

public int ResourceListId { get; set; }
[DisplayName("In List")]
public ResourceList ResourceList { get; set; }

public ContentFeed ContentFeed { get; set; }

public List<LearningResourceTopicTag> LearningResourceTopicTags { get; set; }
}

Next, we have the LearningResourceTopicTag class as a “join entity” to connect the above:

public class LearningResourceTopicTag
{
    public int LearningResourceId { get; set; }
    public LearningResource LearningResource { get; set; }

    public int TopicTagId { get; set; }
    public TopicTag TopicTag { get; set; }

}

This special class has the following properties:

  • LearningResourceId: integer value, pointing back to LearningResource.Id
  • LearningResource: optional “navigation” property, reference back to connected LearningResource entity
  • TopicTagId: integer value, pointing back to TopicTag.Id
  • TopicTag:  optional “navigation” property, reference back to connected TopicTag entity

To learn more about navigation properties, check out the official docs at:

Many to Many Relationship
Many to Many Relationship

Another way of looking at the Many-to-Many relationship is to view the constraints of each database entity in the visuals below. Note that the two connected tables both have an Id field that is a Primary Key (yes, inferred by EF Core!) while the LearningResourceTopicTag table has a Composite Key for the TopicTagId and LearningResourceId fields used for the constraints in the relationship.

Constraints for LearningResources
Constraints for LearningResources
 Constraints for TopicTags
Constraints for TopicTags

The composite key is described in the LibDbContext class inside the OnModelCreating() method:

public class LibDbContext : IdentityDbContext
{
    ...
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
       ...
       modelBuilder.Entity<LearningResourceTopicTag>()
        .HasKey(lrtt => new { lrtt.LearningResourceId, lrtt.TopicTagId });
    }
}

Here, the HasKey() method informs EF Core that the entity LearningResourceTopicTag has a composite key defined by both LearningResourceId and TopicTagId.

References

For more information, check out the list of references below.

For detailed tutorials that include both Razor Pages and MVC, check out the official tutorials below:

Deploying ASP .NET Core 3.1 to Azure App Service

By Shahed C on January 27, 2020

This is the fourth of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

D is for Deploying to Azure App Service

In this article, we’ll explore several options for deploying an ASP .NET Core web app to Azure App Service in the cloud. From the infamous Right-Click-Publish to fully automated CI/CD, you’ll learn about the latest Deployment Center option in the Azure Portal for App Service for web apps.

NOTE: If you’re looking for information on deploying to Docker or Kubernetes, please check out the following docs instead:

Right-Click Publish (aka Friends Don’t Let Friends Right-Click Publish)

If you’ve made it this far, you may be thinking one of the following:
a. “Hey, this is how I deploy my web apps right now!”
or
b. “Hey wait a minute, I’ve heard that you should never do this!”

Well, there is a time and place for right-click publish. There have been many debates on this, so I won’t go into the details, but here are some resources for you to see what others are saying:

So, what’s a web developer to do? To quote from the aforementioned MSDN article, “Continuing with the theme of prototyping and experimenting, right click publish is the perfect way for existing Visual Studio customers to evaluate Azure App Service (PAAS). By following the right click publish flow you get the opportunity to provision new instances in Azure and publish your application to them without leaving Visual Studio:”

In other words, you can use this approach for a quick test or demo, as shown in the screenshots below for Visual Studio.

  1. Right-click your ASP .NET Core web app project in Solution Explorer and select Publish.
  2. Click the Start button on the screen that appears and follow the onscreen instructions.
  3. Ensure that you’re logged in to the correct Azure subscription account you want to publish to.
Right-click, Publish from Solution Explorer
Right-click, Publish from Solution Explorer
Pick a Publish Target
Pick a Publish Target

Web Apps in the Azure Portal

In the screenshot above, you may notice an option to “Import Profile” using the button on the lower left. This allows you to import a Web App profile file that was generated by exporting it from an existing Azure Web App. To grab this profile file, simply navigate to your existing Web App in the Azure Portal and click on “Get publish profile” in the top toolbar of your Web App, shown below:

Get Publish Profile
Get Publish Profile

If you want to create a new Web App in Azure starting with the Azure Portal, follow the instructions below:

  1. Log in to the Azure at https://portal.azure.com
  2. On the top left, click + Create a resource
  3. Select “Web App” or search for it if you don’t see it.
  4. Enter/select the necessary values:
    • Subscription (select a subscription)
    • Resource Group (create or use existing to group resources logically)
    • Web App name (enter a unique name)
    • Publish (Code or Docker Image)
    • Runtime stack (.NET Core)
    • App Service Plan (create or use existing to set location and pricing tier)
    • OS (Windows or Linux)
    • Region (e.g. East US)
  5. Click Next through the screens and then click the Create button to complete the creation of your new Web App.
Create New Web App
Create New Web App

Now you can deploy to this Web App using any method you choose.

Runtime Options

If you like to stay ahead of ASP .NET Core releases, you may be using a pre-release version of the runtime. As of this writing, the latest stable version of ASP .NET Core is version 3.1, which is already available on Azure. If you’re looking for future preview releases, Azure App Service also has an option to install an Extension for preview runtimes.

To find the proper runtime:

  1. Navigate to your Web App in the Azure Portal.
  2. Click on Extensions under Development Tools.
  3. Click + Add to add a new extension.
  4. Choose an extension to configure required settings.
  5. Accept the legal terms and complete the installation.
Add Extension
Add Extension

Your options may include both 32-bit (x86) and 64-bit (x64) versions of the ASP .NET Core runtime and any preview versions of future releases. When planning ahead for multiple environments, you also have the option to deploy to Deployments Slots. This feature is available in StandardPremium or Isolated App Service Plan tiers and will covered in a future blog post in this series.

If you’re interested in Deployment Slots right now, check out the official docs at:

Deployment Center

In the list of features for your Web App, you will find an option to open up the new Deployment Center. Note that this has replaced the old Deployment Options. Let’s go over each of these options:

  1. Azure Repos
  2. Github
  3. Bitbucket
  4. Local Git
  5. OneDrive
  6. Dropbox
  7. External
  8. FTP
Deployment Options in Deployment Center
Deployment Options in Deployment Center
More Deployment Options
More Deployment Options

If you choose Azure Repos, you can set up your web app’s CI (Continuous Integration) system with an Azure Repo, which is part of Microsoft’s Azure DevOps services (formerly known as VSTS, aka Visual Studio Team Services). You will have options for using App Service as a Kudu build server or Azure Pipelines as your CI build system.

 Azure Repos choices: Kudu or Pipelines?
Azure Repos choices: Kudu or Pipelines?

If you choose Github or BitBucket or even a local Git account, you’ll have the ability to authorize that account to publish a specific repo, every time a developer pushes their code.

 Authorize Github/Bitbucket
Authorize Github/Bitbucket

If you choose OneDrive or DropBox, you’ll have ability to authorize your App Service to pick up files deployed to a shared folder in either location.

 Authorize OneDrive/DropBox
Authorize OneDrive/DropBox

You may also select an External repo or FTP source. To learn more about Azure Repos and Azure Pipelines, check out the official docs:

GitHub Repos (includes FREE option!)

If you’ve been using GitHub for public open-source projects or private projects on paid accounts, now is a great to time to create private repositories for free! In 2019, GitHub started offering free unlimited private repos, limited to 3 collaborators. This new free option comes with issue/bug tracking and project management as well.

For more information on GitHub pricing, check out their official pricing page:

 GitHub pricing: Free vs Pro, Team and Enterprise
GitHub pricing: Free vs Pro, Team and Enterprise

Now you can easily set up your starter projects in a private GitHub repository and take advantage of the aforementioned CI/CD setup without having to choose between paying a GitHub fee or making all your repos public.

CLI Commands

If you wish to publish to Azure App service using CLI (Command Line Interface) Commands, you may use the following commands, where you can choose a name for your Web App, Resource Group, App Sevice Plan, etc. Single-letter flags are usually preceded by a single hyphen, while flags spelled out with completed words are usually preceded by two hyphens.

First, install the Azure CLI in case you don’t have it already:

Authenticate yourself:

> az login

Create a new resource group:

> az group create -l <LOCATION> -n <RSG>
> az group create --location <LOCATION> --name <RSG>

Create a new App Service Plan, where <SKUCODE> sku may be F1 or FREE, etc

> az appservice plan create -g <RSG> -n <ASP> --sku <SKUCODE> 
> az appservice plan create --resource-group <RSG> --name <ASP> --sku <SKUCODE>

From the documentation, the SKU Codes include: F1(Free), D1(Shared), B1(Basic Small), B2(Basic Medium), B3(Basic Large), S1(Standard Small), P1V2(Premium V2 Small), PC2 (Premium Container Small), PC3 (Premium Container Medium), PC4 (Premium Container Large), I1 (Isolated Small), I2 (Isolated Medium), I3 (Isolated Large).

Create a new Web App within a Resource Group, attached to an App Service Plan:

> az webapp create -g <RSG> -p <ASP> -n <APP> 
> az webapp create --resource-group <RSG> --plan <ASP> --name <APP>

The above command should create a web app available at the following URL:

  • http://<APP>.azurewebsites.net

To push your commits to a Git Repo and configure for App Service Deployment, use the following CLI commands:

Create a new Git repo or reinitialize an existing one:

> git init

Add existing files to the index:

> git add .

Commit your changes with a commit message:

> git commit -m "<COMMIT MESSAGE>“

Set your FTP credentials and Git deployment credentials for your Web App:

> az webapp deployment user set --user-name <USER>

Configure an endpoint and add it as a git remote.

> az webapp deployment source config-local-git -g <RSG> -n <APP> --out tsv  
> az webapp deployment source config-local-git --resource-group <RSG> --name <APP> --out tsv
> git remote add azure <GIT URL>

The value for GIT URL is the value of the Git remote, e.g.

  • https://<USER>@<APP>.scm.azurewebsites.net/<APP>.git

Finally, push to the Azure remote to deploy your Web App:

> git push azure master

For more information on CLI Commands for Git and Azure App Service, check out the official docs:

Azure DevOps and YAML

Wait, what about Azure DevOps and YAML and Pipelines?

Since this is an A-Z series, you will have to wait for “Y is for YAML” to get more detailed information on constructing your build pipeline using YAML in Azure DevOps. If you can’t wait that long, feel free to check out the following .yml sample I uploaded for use with an ASP .NET Core 3.1:

If you’re reading this after June 2020, simply jump right to the “Y is for YAML” post in the 2020 A-Z series.

BONUS: for Azure SQL Database Deployment, watch the following video on MSDN Channel 9:

References

Cookies and Consent in ASP .NET Core 3.1

By Shahed C on January 20, 2020

This is the third of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

C is for Cookies and Consent

In this article, we’ll continue to look at the (in-progress) NetLearner application, which was generated using multiple ASP .NET Core web app project (3.1) templates. In previous releases, the template made it very easy for you to store cookies and display a cookie policy. However, the latest version doesn’t include cookie usage or a GDPR-compliant message out of the box.

Unless you’ve been living under a rock in the past couple of years, you’ve no doubt noticed all the GDPR-related emails and website popups since 2018. Whether or not you’re required by law to disclose your cookie policies, it’s good practice to reveal it to the end user so that they can choose to accept your cookies (or not).

Who Moved My Cookies?

In ASP .NET Core 2.x, the standard web app project templates provided by Microsoft included GDPR-friendly popup messages, that could be accepted by the end user to set a consent cookie. As of ASP .NET Core 3.x, this is no longer provided out of the box. However, you can still add this feature back into your project manually if needed.

Follow the instructions provided in the official docs to add this feature to your ASP .NET MVC or Razor Pages project:

But wait… how about Blazor web app projects? After asking a few other developers on Twitter, I decided to implement this feature myself, in my NetLearner repository. I even had the opportunity to answer a related question on Stack Overflow. Take a look at the following Blazor web project:

Browser Storage

As you probably know, cookies are attached to a specific browser installation and can be deleted by a user at an any time. Some  new developers may not be aware of where these cookies are actually stored.

Click F12 in your browser to view the Developer Tools to see cookies grouped by website/domain.

  • In (pre-Chromum) Edge/Firefox, expand Cookies under the Storage tab.
  • In (Chromium-based) Edge/Chrome, expand Storage | Cookies under the Application tab .

See screenshots below for a couple of examples how AspNet.Consent in stored, along with a boolean Yes/No value:

Cookies in Chromium-based Edge
Cookies in Chromium-based Edge
Cookies in Google Chrome
 Cookies in Mozilla Firefox
Cookies in Mozilla Firefox

Partial Views for your cookie message

The first time you launch a new template-generated ASP .NET Core 2.x web app, you should expect to see a cookie popup that appears on every page that can be dismissed by clicking Accept. Since we added it manually in our 3.x project, let’s explore the code to dig in a little further.

GDPR-compliant cookie message
GDPR-compliant cookie message

First, take a look at the _CookieConsentPartial.cshtml partial view in both the Razor Pages shared pages folder and the MVC shared views folder. The CSS class names and accessibility-friendly role attributes have been removed for brevity in the snippet below. For Razor Pages (in this example), this file should be in the /Pages/Shared/ folder by default. For MVC, this file should be in the /Views/Shared/ folder by default.

@using Microsoft.AspNetCore.Http.Features

@{
    var consentFeature = Context.Features.Get<ITrackingConsentFeature>();
    var showBanner = !consentFeature?.CanTrack ?? false;
    var cookieString = consentFeature?.CreateConsentCookie();
}

@if (showBanner)
{
    <div id="cookieConsent">
        <!-- CUSTOMIZED MESSAGE IN COOKIE POPUP --> 
        <button type="button" data-dismiss="alert" data-cookie-string="@cookieString">
            <span aria-hidden="true">Accept</span>
        </button>
    </div>
    <script>
        (function () {
            var button = document.querySelector("#cookieConsent button[data-cookie-string]");
            button.addEventListener("click", function (event) {
                document.cookie = button.dataset.cookieString;
            }, false);
        })();
    </script>
} 

This partial view has a combination of server-side C# code and client-side HTML/CSS/JavaScript code. First, let’s examine the C# code at the very top:

  1. The using statement at the top mentions the Microsoft.AspNetCore.Http.Features namespace, which is necessary to use ITrackingConsentFeature.
  2. The local variable consentFeature is used to get an instance ITrackingConsentFeature (or null if not present).
  3. The local variable showBanner is used to store the boolean result from the property consentFeature.CanTrack to check whether the user has consented or not.
  4. The local variable cookieString is used to store the “cookie string” value of the created cookie after a quick call to consentFeature.CreateConsentCookie().
  5. The @if block that follows only gets executed if showBanner is set to true.

Next, let’s examine the HTML that follows:

  1. The cookieConsent <div> is used to store and display a customized message for the end user.
  2. This <div> also displays an Accept <button> that dismisses the popup.
  3. The  data-dismiss attribute ensures that the modal popup is closed when you click on it. This feature is available because we are using Bootstrap in our project.
  4. The data- attribute for “data-cookie-string” is set using the server-side variable value for @cookieString.

The full value for cookieString may look something like this, beginning with the .AspNet.Consent boolean value, followed by an expiration date.

".AspNet.Consent=yes; expires=Mon, 18 Jan 2021 21:55:01 GMT; path=/; secure; samesite=none"

Finally, let’s examine the JavaScript that follows within a <script> tag:

  1. Within the <script> tag, an anonymous function is defined and invoked immediately, by ending it with (); after it’s defined.
  2. button variable is defined to represent the HTML button, by using an appropriate querySelector to retrieve it from the DOM.
  3. An eventListener is added to respond to the button’s onclick event.
  4. If accepted, a new cookie is created using the button’s aforementioned cookieString value.

To use the partial view in your application, simply insert it into the _Layout.cshtml page defined among both the Razor Pages shared pages folder and the MVC shared views folder. The partial view can be inserted above the call to RenderBody() as shown below.

<div class="container">
   <partial name="_CookieConsentPartial" />
   <main role="main" class="pb-3">
      @RenderBody()
   </main>
</div>

In an MVC application, the partial view can be inserted the same way, using the <partial> tag helper.

Blazor Implementation

Here are the steps I used in the Blazor project, tracked in the BlazorCookieExperiment branch:

  1. Update ConfigureServices() in Startup.cs to set up cookie usage
  2. Use JSInterop to set document.cookie in netLearnerJSInterop.js
  3. Update _Host.cshtml to include the .js file
  4. Observe code in _CookieConsentPartial.cshtml as reference
  5. Add _CookieConsentPartial.razor component in Shared folder
  6. Update MainLayout.razor to include above component
<div class="main">
    <div class="top-row px-4 auth">
        <LoginDisplay />
        <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
    </div>

    <_CookieConsentPartial />
    <div class="content px-4">
        @Body
    </div>
</div> 

Customizing your message

You may have noticed that there is only an Accept option in the default cookie popup generated by the template’s Partial View. This ensures that the only way to store a cookie with the user’s consent is to click Accept in the popup.

You may be wondering whether you should also display a Decline option in the cookie popup. But that would be a bad idea, because that would require you to store the user’s “No” response in the cookie itself, thus going against their wishes. If you wish to allow the user to withdraw consent at a later time, take a look at the GrantConsent() and WithdrawConsent() methods provided by ITrackingConsentFeature.

But you can still change the message in the cookie popup and your website’s privacy policy. To change the cookie’s displayed message, simply change the text that appears in the _CookieConsentPartial.cshtml partial view (or equivalent Razor component for ther Blazor project), within the <div> of the client-side HTML. In the excerpt shown in the previous section, this region is identified by the <!– CUSTOMIZED MESSAGE IN COOKIE POPUP –> placeholder comment.

   <div id="cookieConsent">
      <!-- CUSTOMIZED MESSAGE IN COOKIE POPUP -->
      <button type="button" data-dismiss="alert" data-cookie-string="@cookieString">
         <span aria-hidden="true">Accept</span>
      </button>
   </div>

Your message text is also a great place to provide a link to your website’s privacy policy. In the Razor Pages template, the <a> link is generated using a tag helper shown below. The /Privacy path points to the Privacy.cshtml Razor page in the /Pages folder.

<a asp-page="/Privacy">Learn More</a>

In a similar MVC application, you would find the Privacy.cshtml view within the /Views/Home/ folder, accessible via the Home controller’s Privacy() action method. In the MVC template, the <a> is link is generated using the following tag helper:

<a asp-area="" asp-controller="Home" asp-action="Privacy">Learn More</a>

Startup Configuration

None of the above would be possible without the necessary configuration. The cookie policy can be used by simply calling the extension method app.UseCookiePolicy() in the Configure() method of your Startup.cs file, in the root location of Razor Pages, MVC and Blazor projects.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
 {
    ...
    app.UseCookiePolicy();
    ...
 }

According to the official documentation, this “Adds the Microsoft.AspNetCore.CookiePolicy.CookiePolicyMiddleware handler to the specified Microsoft.AspNetCore.Builder.IApplicationBuilder, which enables cookie policy capabilities.
The cool thing about ASP .NET Core middleware is that there are many IApplicationBuilder extension methods for the necessary Middleware components you may need to use. Instead of hunting down each Middleware component, you can simply type app.Use in the Configure() method to discover what is available for you to use.

app.UseCookiePolicy() in Startup.cs
app.UseCookiePolicy() in Startup.cs

If you remove the call to app.UseCookiePolicy(), this will cause the aforementioned consentFeature value to be set to null in the C# code of your cookie popup.

var consentFeature = Context.Features.Get<ITrackingConsentFeature>(); 
cookieString is null if Cookie Policy disabled
cookieString is null if Cookie Policy disabled

There is also some minimal configuration that happens in the ConfigureServices() method which is called before the Configure() method in your Startup.cs file.

public void ConfigureServices(IServiceCollection services)
{
   services.Configure<CookiePolicyOptions>(options =>
   {
      // This lambda determines whether user consent for non-essential cookies is needed for a given request.
      options.CheckConsentNeeded = context => true;
      options.MinimumSameSitePolicy = SameSiteMode.None; 
   }); 
   ...
} 

The above code does a couple of things:

  1. As explained by the comment, the lambda (context => true) “determines whether user consent for non-essential cookies is needed for a given request” and then the CheckConsentNeeded boolean property for the options object is set to true or false.
  2. The property MinimumSameSitePolicy is set to SameSiteMode.None, which is an enumerator with the following possible values:
    • Unspecified = -1
    • None = 0
    • Lax = 1
    • Strict = 2

From the official documentation on cookie authentication,  The Cookie Policy Middleware setting for MinimumSameSitePolicy can affect the setting of Cookie.SameSite in CookieAuthenticationOptions settings. For more information, check out the documentation at:

References

Blazor Full-Stack Web Dev in ASP .NET Core 3.1

By Shahed C on January 13, 2020
ASP .NET Core A-Z Series

This is the second of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

Previous post:

NetLearner on GitHub:

In this Article:

B is for Blazor Full-Stack Web Dev

In my 2019 A-Z series, I covered Blazor for ASP .NET Core while it was still experimental. As of ASP .NET Core 3.1, server-side Blazor has now been released, while client-side Blazor (currently in preview) is expected to arrive in May 2020. This post will cover server-side Blazor, as seen in NetLearner.

To see the code in action, open the solution in Visual Studio 2019 and run the NetLearner.Blazor project. All modern web browsers should be able to run the project.

NetLearner.Blazor web app in action
NetLearner.Blazor web app in action

Entry Point and Configuration

Let’s start with Program.cs, the entry point of the application. Just like any ASP .NET Core web application, there is a Main method that sets up the entry point. A quick call to CreateHostBuilder() in the same file ensures that two things will happen: The Generic Host will call its own CreateDefaultBuilder() method (similar to how it works in a typical ASP .NET Core web application) and it will also call UseStartup() to identify the Startup class where the application is configured.

public class Program
{
   public static void Main(string[] args)
   {
      CreateHostBuilder(args).Build().Run();
   }
 
   public static IHostBuilder CreateHostBuilder(string[] args) =>
      Host.CreateDefaultBuilder(args)
      .ConfigureWebHostDefaults(webBuilder =>
      {
         webBuilder.UseStartup<Startup>();
      });
 }

Note that the Startup class doesn’t have to be called Startup, but you do have to tell your application what it’s called. In the Startup.cs file, you will see the familiar ConfigureServices() and Configure() methods, but you won’t need any of the regular MVC-related lines of code that set up the HTTP pipeline for an MVC (or Razor Pages) application. Instead, you just need a call to AddServerSideBlazor() in ConfigureServices() and a call to MapBlazorHub() in Configure() while setting up endpoints with UseEndPoints().

public class Startup
{
   public void ConfigureServices(IServiceCollection services)
   {
      ...
      services.AddServerSideBlazor();
      ...
   } 

   public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 
   {
      ...
      app.UseEndpoints(endpoints => 
      {
         endpoints.MapControllers();
         endpoints.MapBlazorHub();
         endpoints.MapFallbackToPage("/_Host");
      });
   }
}

Note that the Configure() method takes in an app object of type IApplicationBuilder, similar to the IApplicationBuilder we see in regular ASP .NET Core web apps.  A call to MapFallBackToPage() indicates the “/_Host” root page, which is defined in the _Host.cshtml page in the Pages subfolder.

Rendering the Application

This “app” (formerly defined in a static “index.html” in pre-release versions) is now defined in the aforementioned “_Host.cshtml” page. This page contains an <app> element containing the main App component.

<html>
... 
<body>
    <app>
        <component type="typeof(App)" render-mode="ServerPrerendered" />
    </app>
...
</body>
</html>

The HTML in this page has two things worth noting: an <app> element within the <body>, and a <component> element of type “App” with a render-mode attribute set to “ServerPrerendered”. This is one of 3 render modes for a Blazor component: Static, Server and ServerPrerendered. For more information on render modes, check out the official docs at:

According to the documentation, this setting “renders the component into static HTML and includes a marker for a Blazor Server app. When the user-agent starts, this marker is used to bootstrap a Blazor app.

The App component is defined in App.razor, at the root of the Blazor web app project. This App component contains nested authentication-enabled components, that make use of the MainLayout to display the single-page web application in a browser. If the user-requested routedata is found, the requested page (or root page) is displayed. If the user-requested routedata is invalid (not found), it displays a “sorry” message to the end user.

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>

The MainLayout.razor component (under /Shared) contains the following:

  • NavMenu: displays navigation menu in sidebar
  • LoginDisplay: displays links to register and log in/out
  • _CookieConsentPartial: displays GDPR-inspired cookie message
  • @Body keyword: replaced by content of layout when rendered

The _Layout.cshtml layout file (under /Pages/Shared) acts as a template and includes a call to @RenderBody to display its content. This content is determined by route info that is requested by the user, e.g. Index.razor when the root “/” is requested, LearningResources.razor when the route “/learningresources” is requested.

    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

The NavMenu.razor component contains NavLinks that point to various routes. For more information about routing in Blazor, check out the official docs at:

LifeCycle Methods

A Blazor application goes through several lifecycle methods, including both asynchronous and non-asynchronous versions where applicable. Some important ones are listed below:

  • OnInitializedAsync() and OnInitialized(): invoked after receiving initial params
  • OnParametersSetAsync() and OnParametersSet(): called after receiving params from its parent, after initialization
  • OnAfterRenderAsync() and OnAfterRender(): called after each render
  • ShouldRender(): used to suppress subsequent rendering of the component
  • StateHasChanged(): called to indicate that state has changed, can be triggered manually

These methods can be overridden and defined in the @code section (formerly @functions section) of a .razor page, e.g. LearningResources.razor.

 @code {
   ...
   protected override async Task OnInitializedAsync()
   {
      ...
   } 
   ...
}

Updating the UI

The C# code in LearningResources.razor includes methods to initialize the page, handle user interaction, and keep track of data changes. Every call to an event handler from an HTML element (e.g. OnClick event for an input button) can be bound to a C# method to handle that event. The StateHasChanged() method can be called manually to rerender the component, e.g. when an item is added/edited/deleted in the UI.

<div>
    <input type="button" class="btn btn-primary" value="All Resources" @onclick="(() => DataChanged())" />
</div>

...
private async void DataChanged()
{
    learningResources = await learningResourceService.Get();
    ResourceLists = await resourceListService.Get();
    StateHasChanged();
}

Note that the DataChanged() method includes some asynchronous calls to Get() methods from a service class. These are the service classes in the shared library that are also used by the MVC and Razor Pages web apps in NetLearner.

Parameters defined in the C# code can be used similar to HTML attributes when using the component, including RenderFragments can be used like nested HTML elements. These can be defined in sub-components such as ConfirmDialog.razor and ResourceDetail.razor that are inside the LearningResources.razor component.

<ResourceDetail LearningResourceObject="learningResourceObject"
                ResourceListValues="ResourceLists"
                DataChanged="@DataChanged">
    <CustomHeader>@customHeader</CustomHeader>
</ResourceDetail>

Inside the subcomponent, you would define the parameters as such:

@code {
    [Parameter]
    public LearningResource LearningResourceObject { get; set; }

    [Parameter]
    public List<ResourceList> ResourceListValues { get; set; }

    [Parameter]
    public Action DataChanged { get; set; }

    [Parameter]
    public RenderFragment CustomHeader { get; set; }
} 

For more information on the creation and use of Razor components in Blazor, check out the official documentation at:

Next Steps

Run the Blazor web app from the NetLearner repo and try using the UI to add, edit and delete items. Make sure you remove the restrictions mentioned in a previous post about NetLearner, which will allow you to register as a new user, log in and perform CRUD operations.

NetLearner.Blazor: Learning Resources

There is so much more to learn about this exciting new framework. Blazor’s reusable components can take various forms. In addition to server-side Blazor (released in late 2019 with .NET Core 3.1), you can also host Blazor apps on the client-side from within an ASP .NET Core web app. Client-side Blazor is currently in preview and is expected in a May 2020 release.

References

Authentication & Authorization in ASP .NET Core 3.1

By Shahed C on January 6, 2020
ASP .NET Core A-Z Series

This is the first of a new series of posts on ASP .NET Core 3.1 for 2020. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2020, titled ASP .NET Core A-Z! To differentiate from the 2019 series, the 2020 series will mostly focus on a growing single codebase (NetLearner!) instead of new unrelated code snippets week.

If you need some guidance before you get started with this series, check out my late 2019 posts, which serve as a prelude to the 2020 series:

NetLearner on GitHub:

In this Article:

A is for Authentication & Authorization

Authentication and Authorization are two different things, but they also go hand in hand. Think of Authentication as letting someone into your home and Authorization as allowing your guests to do specific things once they’re inside (e.g. wear their shoes indoors, eat your food, etc). In other words, Authentication lets your web app’s users identify themselves to get access to your app and Authorization allows them to get access to specific features and functionality.

In this article, we will take a look at the NetLearner app, on how specific pages can be restricted to users who are logged in to the application. Throughout the series, I will try to focus on new code added to NetLearner or build a smaller sample app if necessary.

Authentication in ASP .NET Core

The quickest way to add authentication to your ASP .NET Core app is to use one of the pre-built templates with one of the Authentication options. The examples below demonstrate both the CLI commands and Visual Studio UI.

Here are the CLI Commands for MVC, Razor Pages and Blazor (Server), respectively:

> dotnet new mvc --auth Individual -o mvcsample
> dotnet new webapp --auth Individual -o pagessample
> dotnet new blazorserver --auth Individual -o blazorsample 

Things to note:

  • The dotnet new command is followed by the template name (mvc, webapp, blazorserver).
  • The –auth option allows you to specify the authentication type, e.g. Individual
  • The -o option is an optional parameter that provides the output folder name for the project to be created in.

Here is the opening dialog in Visual Studio 2019, for creating a new project with Authentication:

Change Authentication upon creating a new project
Change Authentication upon creating a new project
 Select Authentication Type
Select Authentication Type

The above example uses “Individual” authentication, which offers a couple of options:

  • Store user accounts in-app: includes a local user accounts store
  • Connect to an existing user store in the cloud: connect to an existing Azure AD B2C application

Even if I choose to start with a local database, I can update the connection string to point to a SQL Server instance on my network or in the cloud, depending on which configuration is being loaded. If you’re wondering where your Identity code lives, check out my 2019 post on Razor UI Libraries, and jump to the last bonus section where Identity is mentioned.

From the documentation, the types of authentication are listed below.

  • None: No authentication
  • Individual: Individual authentication
  • IndividualB2C: Individual authentication with Azure AD B2C
  • SingleOrg: Organizational authentication for a single tenant
  • MultiOrg: Organizational authentication for multiple tenants
  • Windows: Windows authentication

To get help information about Authentication types, simply type ––help after the ––auth flag, e.g.

> dotnet new webapp --auth --help

Authentication in NetLearner

Within my NetLearner MVC app, the following snippets of code are added to the Startup.cs configuration:

public void ConfigureServices(IServiceCollection services)
{
...
   services.AddDbContext<LibDbContext>(options =>
   {
      options
         .UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
         assembly =>
         assembly.MigrationsAssembly
            (typeof(LibDbContext).Assembly.FullName));
   });

   services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
   .AddEntityFrameworkStores<LibDbContext>();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
 app.UseStaticFiles();
...
 app.UseAuthentication();
 app.UseAuthorization(); 
...
 app.Endpoints(...);
}

In the above, note that:

  • The ConfigureServices() method has calls to services.AddDbContext and server.AddDefaultIdentity. The call to add a DB Context will vary depending on which data store you choose for authentication. The call to AddDefaultIdentity ensures that your app calls AddIdentity, AddDefaultUI and AddDefaultTokenProviders to add common identity features and user Register/Login functionality.
  • The Configure() method has calls to app.UseAuthentication and app.UseAuthorization to ensure that authentication and authorization are used by your web app. Note that this appears after app.UseStaticFiles() but before app.UseEndpoints() to ensure that static files (html, css, js, etc) can be served without any authentication but MVC application-controlled routes and views/pages will follow authentication rules.
  • Similar to the MVC web project, you can also browse the Startup.cs file for the equivalent Razor Pages and Blazor web app projects.

Authorization in ASP.NET Core (MVC)

Even after adding authentication to a web app using the project template options, we can still access many parts of the application without having to log in. In order to restrict specific parts of the application, we will implement Authorization in our app.

If you’ve already worked with ASP .NET Core MVC apps before, you may be familiar with the [Authorize] attribute. This attribute can be added to a controller at the class level or even to specific action methods within a class.

[Authorize]
public class SomeController1: Controller
{
   // this controller class requires authentication
   // for all action methods
   public ActionResult SomeMethod()
   {
      //
   } 
}

public class SomeController2: Controller
{
   public ActionResult SomeOpenMethod()
   {
   }

   [Authorize]
   public ActionResult SomeSecureMethod()
   {
      // this action method requires authentication
   }
}

Well, what about Razor Pages in ASP .NET Core? If there are no controller classes, where would you add the [Authorize] attribute?

Authorization in ASP.NET Core (Razor Pages)

For Razor Pages, the quickest way to add Authorization for your pages (or entire folders of pages) is to update your ConfigureServices() method in your Startup.cs class, by calling AddRazorPagesOptions() after AddMvc(). The NetLearner configuration includes the following code:

services.AddRazorPages()
     .AddRazorPagesOptions(options =>
     {
         options.Conventions.AuthorizePage("/LearningResources/Create");
         options.Conventions.AuthorizePage("/LearningResources/Edit");
         options.Conventions.AuthorizePage("/LearningResources/Delete");
         options.Conventions.AuthorizePage("/ResourceLists/Create");
         options.Conventions.AuthorizePage("/ResourceLists/Edit");
         options.Conventions.AuthorizePage("/ResourceLists/Delete");
         options.Conventions.AllowAnonymousToPage("/Index");
     });

The above code ensures that the CRUD pages for Creating, Editing and Deleting any of the LearningResources are only accessible to someone who is currently logged in. Each call to AuthorizePage() includes a specific page name identified by a known route. In this case, the LearningResources folder exists within the Pages folder of the application.

Finally, the call to AllowAnonymousPage() ensures that the app’s index page (at the root of the Pages folder) is accessible to any user without requiring any login.

If you still wish to use the [Authorize] attribute for Razor Pages, you may apply this attribute in your PageModel classes for each Razor Page, as needed. If I were to add it to one of my Razor Pages in the LearningResources folder, it could look like this:

[Authorize]
public class CreateModel : PageModel
{
    ...
}

Authorization in Blazor

In a Blazor project, you can wrap elements in an <AuthorizeView> component, which may contain nested elements named <Authorized>, <NotAuthorized> and <Authorizing>.

<AuthorizeView>
   <Authorized Context="Auth">
     authorized elements go here
   </Authorized>
   <NotAuthorized>
     anonymous-accessed elements
   </NotAuthorized>
   <Authorizing>
     waiting message...
   </Authorizing>
</AuthorizeView>

The root element here has the following characteristics:

  • <AuthorizeView> element used as a container for nested elements
  • Optional role attribute, e.g. <AuthorizeView Roles=”Admin,RoleA”> used to set a comma-separated list roles that will be used to determine who can view the authorized nested elements
  • Optional context attribute, e.g. <AuthorizeView Context=”Auth”> to define a custom context, to avoid any conflicts with nested comments

The nested elements represent the following:

  • Authorized: shown to users who have been authorized to view and interact with these elements
  • NotAuthorized: shown to users who have not been authorized
  • Authorizing: temporary message shown while authorization is being processed

Testing Authorization in NetLearner

When I run my application, I can register and log in as a user to create new Learning Resources. On first launch, I have to apply migrations to create the database from scratch. Please refer to my 2018 post on EF Core Migrations to learn how you can do the same in your environment.

NOTE: the registration feature for each web app has been disabled by default. To enable registration, please do the following:

  1. Locate scaffolded Identity pages under /Areas/Identity/Pages/Account/
  2. In Register.cshtml, update the page to include environments in addition to Development, if desired.
  3. In Register.cshtml.cs, replace [Authorize] with [AllowAnonymous] to allow access to registration

Here are the screenshots of the Create page for a user who is logged in:

NetLearner MVC: Create New Resource
NetLearner MVC: Create New Resource
 NetLearner Razor Pages: Create New Resource
NetLearner Razor Pages: Create New Resource
  NetLearner Blazor: Create New Resource
NetLearner Blazor: Create New Resource

Here’s a screenshot of the page that an “anonymous” user sees when no one is logged in, indicating that the user has been redirected to the Login page. Note that all 3 web apps (MVC, Razor Pages and Blazor) have similar Identity pages. To allow manual customization, they were also auto-generated via scaffolding and included in all 3 projects.

NetLearner: Log in
NetLearner: Log in

Here are the screenshots showing the list of Learning Resources, visible to anyone whether they’re logged in or not:

 NetLearner MVC: List of Learning Resources
NetLearner MVC: List of Learning Resources
  NetLearner Razor Pages: List of Learning Resources
NetLearner Razor Pages: List of Learning Resources
   NetLearner Blazor: List of Learning Resources
NetLearner Blazor: List of Learning Resources

Other Authorization Options

Razor Pages have multiple ways of restricting access to pages and folders, including the following methods (as described in the official docs):

  • AuthorizePage: Require authorization to access a page
  • AuthorizeFolder: Require authorization to access a folder of pages
  • AuthorizeAreaPage: Require authorization to access an area page
  • AuthorizeAreaFolder: Require authorization to access a folder of areas
  • AllowAnonymousToPage: Allow anonymous access to a page
  • AllowAnonymousToFolder: Allow anonymous access to a folder of pages

You can get more information on all of the above methods at the following URL:

References

To learn more about Authentication, Authorization and other related topics (e.g. Roles and Claims), check out the official docs: