Category Archives: ASP.NET

Forms and Fields in ASP .NET Core

By Shahed C on February 13, 2019

This is the sixth of a series of posts on ASP .NET Core for 2019. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2019, titled A-Z of ASP .NET Core!

ASPNETCoreLogo-300x267 A – Z of ASP .NET Core!

In this Article:

This article will refer to the following sample code on GitHub:

Web Forms And Bindings: https://github.com/shahedc/FormsAndBindings

F is for Forms (and Fields)

Before Tag Helpers were available, you would have to use HTML Helper methods to create forms and their elements in a ASP .NET Core views. This meant that your form could look something like this:

@using (Html.BeginForm())
{
   <input />
}

With the introduction of Tag Helpers, you can now make your web pages much more cleaner. In fact, Tag Helpers work with both MVC Views and Razor Pages. The syntax is much simpler:

<form method="post">

This looks like HTML because it is HTML. You can add additional server-side attributes within the <form> tag for additional features.

<form asp-controller="ControllerName" asp-action="ActionMethod" method="post">

In this above example, you can see how the attributes asp-controller and asp-action can be used to specify a specific controller name and action method. When these optional attributes are omitted, the current controller and default action method will be used.

Optionally, you can also use a named route, e.g.

<form asp-route="NamedRoute" method="post">

The asp-route attribute will look for a specific route with the name specified. When the form is submitted via HTTP POST, the action method will then attempt to read the form values via a passed values or bound properties.

In a Controller’s class file within an MVC app, you can set an optional Name for your action method’s Route attribute, as shown below:

[Route("/ControllerName/ActionMethod", Name = "NamedRoute")]
public IActionResult ActionMethod()
{
}

While you won’t find new Tag Helper equivalents for each and every HTML Helper you may have used in the past, you should consider using a Tag Helper wherever possible. You can even create your own custom Tag Helpers as well. For more information on custom Tag Helpers, check out the official documentation:

Tag Helpers for HTML form elements

Below is a list of Tag Helpers with their corresponding HTML form elements:

Input Tag Helper

Let’s say you have a model with a couple of fields:

public class MyModel
{
   public string MyProperty1 { get; set; }
   public string MyProperty2 { get; set; }
}

You can use the following syntax to use an Input Tag Helper with an expression name assigned to the asp-for attribute. This allows you to refer to the properties without requiring the “Model.” prefix in your Views and Pages.

@model MyModel 
...
<!-- Syntax -->
<input asp-for="<Expression Name>" />
...
<!-- Examples -->
<input asp-for="MyProperty1" />
<input asp-for="MyProperty2" />

Corresponding to the Input Tag Helper, there are existing HTML Helpers, with some differences:

  • Html.TextBox: doesn’t automatically set the type attribute
  • Html.TextBoxFor: also doesn’t automatically set the type attribute; strongly typed
  • Html.Editor: suitable for collections, complex objects and templates (while Input Tag Helper is not).
  • Html.EditorFor: also suitable for collections, complex objects and templates; strongly typed

Since Input Tag Helpers use an inline variable or expression in your .cshtml files, you can assign the value using the @ syntax as shown below:

@{
   var myValue = "Some Value";
 }
 <input asp-for="@myValue" />

This will generate the following textbox input field:

<input type="text" id="myValue" name="myValue" value="Some Value" />

To create more specific fields for email addresses, passwords, etc, you may use data-type attributes on your models to auto-generate the necessary fields. These may include one of the following enum values:

  • CreditCard
  • Currency
  • Custom
  • Date
  • DateTime
  • Duration
  • EmailAddress
  • Html
  • ImageUrl
  • MultilineText
  • Password
  • PhoneNumber
  • PostalCode
  • Text
  • Time
  • Upload
  • Url
// For example: 
[DataType(DataType.Date)]
public DateTime DateOfBirth { get; set; }

Note that each attribute can be applied on a field for the view/page generator to infer the data type, but is not used for data validation. For validation, you should use the appropriate validation techniques in your code. We will cover validation in a future blog post, but you can refer to the official docs for now:

Checkboxes

Any boolean field in your model will automatically be turned into a checkbox in the HTML form. There is no extra work necessary to specify that the input type is a “checkbox”. In fact, the generated HTML includes the “checkbox” type automatically, sets the “checked” property if checked and wraps it in a label with the appropriate caption. For example, imagine a boolean field named “IsActive”:

// boolean field in a model class
public bool IsActive { get; set; }
<!-- input field in page/view wrapped in label -->
<label class="form-check-label">
   <input class="form-check-input" asp-for="IsActive" /> 
   @Html.DisplayNameFor(model => model.IsActive)
</label>
<!-- HTML generated for boolean field -->
<label class="form-check-label">
<input 
   class="form-check-input" 
   type="checkbox" 
   checked="checked" 
   data-val="true" 
   data-val-required="The IsActive field is required." 
   id="IsActive" 
   name="IsActive" 
   value="true"> IsActive
</label>

Hidden Fields

In case you’re wondering how you can generate a hidden <input> field, you can simply use the [HiddenInput] attribute on your hidden field’s property, as shown below. If you wish, you can explicitly set “type=hidden” in your Page/View, but I prefer to set the attribute in the model itself.

// hidden property in model class
[HiddenInput] 
public string SomeHiddenField { get; set; } = "Some Value";
<!-- hidden field in page/view -->
<input asp-for="SomeHiddenField" /> 
<!-- HTML generated for hidden field --> 
<input type="hidden" id="SomeHiddenField" name="SomeHiddenField" value="Some Value">

Radio Buttons

For radio buttons, you can create one <input> tag for each radio button option, with a reference to a common field, and a unique value for each radio button. Each input element can be wrapped in a label to include a proper (clickable) text caption . You can generate these in a loop or from a collection from dynamically generated radio buttons.

// string property in model class
public string ExperienceLevel { get; set; }
<!-- input fields for radio buttons in page/view -->
<label><input asp-for="ExperienceLevel" value="N" type="radio" />Novice</label> 
<label><input asp-for="ExperienceLevel" value="B" type="radio"/>Beginner</label> 
<label><input asp-for="ExperienceLevel" value="I" type="radio" />Intermediate</label> 
<label><input asp-for="ExperienceLevel" value="A" type="radio"/>Advanced</label>
<!-- HTML generated for radio buttons --> 
<label><input value="N" type="radio" id="ExperienceLevel" name="ExperienceLevel">Novice</label>
<label><input value="B" type="radio" id="ExperienceLevel" name="ExperienceLevel">Beginner</label>
<label><input value="I" type="radio" id="ExperienceLevel" name="ExperienceLevel">Intermediate</label>
<label><input value="A" type="radio" id="ExperienceLevel" name="ExperienceLevel">Advanced</label>

Textarea Tag Helper

The multiline <textarea> field can be easily represented by a Textarea Tag Helper. This is useful for longer strings of text that need to be seen and edited across multiple lines.

public class MyModel
{
   [MinLength(5)]
   [MaxLength(1024)]
   public string MyLongTextProperty { get; set; }
}

As you would expect, you can use the following syntax to use a Textarea Tag Helper with an expression name assigned to the asp-for attribute.

@model MyModel 
...
<textarea asp-for="MyLongTextProperty"></textarea>

This will generate the following textarea input field:

<textarea 
   data-val="true" 
   data-val-maxlength="The field ... maximum length of '1024'." 
   data-val-maxlength-max="1024" 
   data-val-minlength="The field ... minimum length of '5'." 
   data-val-minlength-min="5" 
   id="MyLongTextProperty" 
   maxlength="1024" 
   name="MyLongTextProperty"
></textarea>

Note that the property name and its attributes are used to create that textarea with the necessary id, name, maxlength and data validation settings.

Corresponding to the Textarea Tag Helper, the existing HTML Helper is shown below:

  • Html.TextAreaFor

Label Tag Helper

The <label> field can be represented by a Label Tag Helper. A label usually goes hand-in-hand with a specific <input> field, and is essential in creating text captions for more accessible web applications. The Display attribute from your model’s fields are used for the label’s displayed text values. (You could use the DisplayName attribute instead and omit the Name parameter, but it limits your ability to use localized resources.)

public class MyModel
{
   [Display(Name = "Long Text")]
   public string MyLongTextProperty { get; set; }
}

You can use the following syntax to use a Label Tag Helper along with an Input Tag Helper.

@model MyModel 
...
<label asp-for="MyLongTextProperty"></label>
<input asp-for="MyLongTextProperty" />

This will generate the following HTML elements:

<label for="MyLongTextProperty">Long Text</label>
<input type="text" id="MyLongTextProperty" name="MyLongTextProperty" value="">

Note that the property name and its attributes are used to create both the label with its descriptive caption and also the input textbox with the necessary id and name.

Corresponding to the Label Tag Helper, the existing HTML Helper is shown below:

  • Html.LabelFor

Select Tag Helper

The <select> field (with its nested <option> fields) can be represented by a Select Tag Helper. This visually represents a dropdown or listbox, from which the user may select one or more options. In your model, you can represent this with a List<SelectListItem> of items, made possible by the namespace Microsoft.AspNetCore.Mvc.Rendering.

...
using Microsoft.AspNetCore.Mvc.Rendering;

public class MyModel
{
   public string MyItem { get; set; }
   
   public List<SelectListItem> MyItems { get; } = new List<SelectListItem>
   {
      new SelectListItem { Value = "Item1", Text = "Item One" },
      new SelectListItem { Value = "Item2", Text = "Item Two" },
      new SelectListItem { Value = "Item3", Text = "Item Three" },
   };
}

You can use the following syntax to use a Select Tag Helper.

@model MyModel 
...
<select asp-for="MyItem" asp-items="Model.MyItems"></select>

Note that the asp-items attribute does require a “Model.” prefix, unlike the asp-for attribute that we have been using so far. This will generate the following HTML:

<select id="MyItem" name="MyItem">
   <option value="Item1">Item One</option>
   <option value="Item2">Item Two</option>
   <option value="Item3">Item Three</option>
</select>

Note that the property name and its attributes are used to create both the dropdown list and also the nested options available for selection. For more customization, optgroups and multiple selections, check out the “Select Tag Helper” section in the Tag Helpers documentation at:

Corresponding to the Select Tag Helper, the existing HTML Helpers are shown below:

  • Html.DropDownListFor
  • Html.ListBoxFor

MVC Sample

In the sample repo, you’ll find an MVC web project with various models, views and controllers.

  • Models: In the “Models” folder, you’ll find a Human.cs class (shown below) with some fields we will use to display HTML form elements.
  • Views: Within the “Views” subfolder, the “Human” subfolder contains auto-generated views for the HumanController’s methods, while the “Attr” subfolder contains simple Index and Detail views to be used by the AttrController class.
  • Controllers: The HumanController class was auto-generated for the Human model, while the AttrController class was manually written to illustrate the use of various attributes (e.g. FromQuery, FromRoute, FromForm) within the action methods.
public class Human
{
   public int Id { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string Address { get; set; }
   public DateTime DateOfBirth { get; set; }
   public int FavoriteNumber { get; set; }
   public bool IsActive { get; set; }
}

Take a look at the Create and Edit views for the Human class, and you’ll recognize familiar sets of <label> and <input> fields that we discussed earlier.

...
<div class="form-group">
   <label asp-for="FirstName" class="control-label"></label>
   <input asp-for="FirstName" class="form-control" />
   <span asp-validation-for="FirstName" class="text-danger"></span>
</div>
...

To bind these fields, the Create and Edit methods that respond to HTTP POST both use a [Bind] attribute for the Human parameter with specific fields to bind from the model:

[HttpPost]
public async Task<IActionResult> Create(
   [Bind("Id,FirstName,LastName,Address,DateOfBirth,FavoriteNumber,IsActive")] Human human)

... 
[HttpPost]
public async Task<IActionResult> Edit(
   int id, 
   [Bind("Id,FirstName,LastName,Address,DateOfBirth,FavoriteNumber,IsActive")] Human human)

In the AttrController class, you’ll find a different approach to gathering information submitted in an HTML form.

  • FromQuery: The Index() method uses a [FromQuery] attribute which can obtain information from the URL’s QueryString parameters
  • FromRoute: The first Details() method uses a [FromRoute] attribute which can obtain information from route values.
  • FromForm: The second Details() method uses a [FromForm] attribute which links the submitted form fields to the fields of the corresponding model passed to the method.
[HttpGet]
public IActionResult Index([FromQuery] string humanInfo) { }
...
[HttpGet]
public IActionResult Details([FromRoute] int id) { }
...
[HttpPost]
public IActionResult Details([FromForm] Human human) { }
...

The first two methods above can be reached via HTTP GET requests by accessing a URL, while the third one can be reached when submitting the Details form with a submit button. Run the sample application to click around and view its behavior.

Forms-Bindings

 

<!-- From shared _Layout.cshtml, link to Index page -->
<a 
   class="nav-link text-dark" 
   asp-area="" 
   asp-controller="Attr" 
   asp-action="Index" 
   asp-route-humanInfo="John"
>Attributes</a>

<!-- From Index.cshtml, link to Details page -->
<a 
   class="nav-link text-dark" 
   asp-area="" 
   asp-controller="Attr" 
   asp-action="Details" 
   asp-route-id="1"
>View Details</a>
<!-- From Details.cshtml, self-submitting form -->
<form asp-action="Details">
...
<input type="submit" value="Submit" class="btn btn-primary" />
...
</form>

Note that <a> tags have an asp-route-xx attribute that can have any text value appended to the end of it. These route parameters, e.g. id and humanInfo, correspond directly to action method parameters seen in Index() and Details() methods of the AttrController class.

Razor Pages with BindProperty

Compared to MVC views, the newer Razor Pages make it a lot easier to bind your model properties to your HTML forms. The [BindProperty] attribute can be applied to MVC Controllers as well, but is much more effective within Razor Pages.

In the sample repo, you’ll find a Razor web project with multiple subfolders, including Models and Pages.

  • Models: In the “Models” folder of the Razor web project, you’ll find a Human.cs class (shown below) with some fields we will use to display HTML form elements.
  • Pages: Within the “Pages” subfolder, the “Human” subfolder contains auto-generated Razor Pages along with corresponding .cs classes that contain the necessary Get/Post methods.

Here, the HumanModel class is named slightly differently from the aforementioned (MVC) example’s Human class, since the subfolder within Pages is also called Human. (We would have to rename one or the other to avoid a naming conflict.)

 public class HumanModel
 {
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { get; set; }
    public DateTime DateOfBirth { get; set; }
    public int FavoriteNumber { get; set; }
    public bool IsActive { get; set; }
 }

This time, take a look at the Create and Edit pages for the Human class, and you’ll once again recognize familiar sets of <label> and <input> fields that we discussed earlier.

 <div class="form-group">
    <label asp-for="HumanModel.FirstName" class="control-label"></label>
    <input asp-for="HumanModel.FirstName" class="form-control" />
    <span asp-validation-for="HumanModel.FirstName" class="text-danger"></span>
 </div>

Since there are no controller classes in the Razor web project, let’s take a look at the corresponding C# classes for the Create and Edit pages, i.e. Create.cshtml.cs and Edit.cshtml.cs. In both of these classes, we’ll find the [BindProperty] attribute in use, right after the constructor and before the Get/Post methods.

 [BindProperty]
 public HumanModel HumanModel { get; set; }

This [BindProperty] attribute allows you to declaratively bind the HumanModel class and its properties for use by the HTML form in the corresponding Razor Page. This is an opt-in feature that allows to choose which properties to bind. If you wish, you could alternatively bind all public properties in the class by using the [BindProperties] attribute above the class, instead of above each individual member.

NOTE: By default, a Razor Page’s default methods for HTTP GET and HTTP POST are OnGet() and OnPost() respectively. If you wish to use custom page handlers in your HTML forms, you must create custom methods with the prefix OnPost followed by the name of the handler (and optionally followed by the word Async for async methods)

<!-- buttons with custom page handlers --> 
<input type="submit" asp-page-handler="Custom1" value="Submit 1" />
<input type="submit" asp-page-handler="Custom2" value="Submit 2" />
// action methods in .cs file associated with a Razor Page 
public async Task<IActionResult> OnPostCustom1Async() { } 
public async Task<IActionResult> OnPostCustom2sync() { }

The standard set of Get/Post methods are shown below, from Create.cshtml.cs:

public IActionResult OnGet()
{
   return Page();
}
public async Task<IActionResult> OnPostAsync()
{
   if (!ModelState.IsValid)
   {
      return Page();
   } 
   _context.HumanModel.Add(HumanModel);
   await _context.SaveChangesAsync(); 
   return RedirectToPage("./Index");
}

Note that the HumanModel is passed to the DB Context to add it to the database. If you were to remove the aforementioned [BindProperty] attribute, HumanModel would be null and the save operation would fail. The above approach only opts in to accepting HTTP POST requests. To enable use of BindProperty for HTTP GET requests as well, simply set the optional parameter SupportsGet to true, as shown below.

[BindProperty(SupportsGet = true)]

References

 

 

EF Core Relationships in ASP .NET Core

By Shahed C on February 4, 2019

This is the fifth of a new series of posts on ASP .NET Core for 2019. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2019, titled A-Z of ASP .NET Core!

ASPNETCoreLogo-300x267 A – Z of ASP .NET Core!

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 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 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 : InternetResource
{
    public List<LearningResourceItemList> LearningResourceItemLists
    {
        get; set;
    }
}

The abstract class InternetResource defines the common properties (e.g. Id, Name and Url) found in any Internet resource, and is also used by other classes ResourceRoot and RssFeed.

public abstract class InternetResource
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Url { get; set; }
}

The ResourceRoot class represents a root-level resource (e.g. a blog home or a podcast website) while the RssFeed class represents the RSS Feed for an online resource.

public class ResourceRoot: InternetResource
{
    public RssFeed RssFeed { get; set; }
    public List<LearningResource> LearningResources { get; set; }
}
public class RssFeed: InternetResource
{
    public int ResourceRootId { get; set; }
}

The ItemList 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 ItemList
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<LearningResourceItemList> LearningResourceItemLists
    {
        get; set;
    }
}

At this point, you may have noticed both the LearningResource and ItemList classes contain a List<T> property of LearningResourceItemList. 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 is a part of a list (which is a part of a ResourceCatalog), while each LearningResource also has a ResourceRoot.

NetLearner example

NetLearner example

One to One

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

  • Resource Root = Wake Up and Code! blog site
  • Rss Feed = RSS Feed for blog site

In the two classes, we see the following code:

public class ResourceRoot: InternetResource
{
    public RssFeed RssFeed { get; set; }
    public List<LearningResource> LearningResources { get; set; }
}
public class RssFeed: InternetResource
{
    public int ResourceRootId { get; set; }
}

Each Resource Root has a corresponding Rss Feed, so the ResourceRoot class has a property for RssFeed. That’s pretty simple. But in the RssFeed class, you don’t need a property pointing back to the ResourceRoot. In fact, all you need is a ResourceRootId property. EF Core will ensure that ResourceRoot.Id points to RssFeed.ResourceRootId in the database.

One to One Relationships

One to One Relationships

If you’re wondering how these two classes got their Id, Name and Url fields, you may recall that they are both derived from a common abstract parent class (InternetResource) that define all this fields for reuse. But wait a second… why doesn’t this parent class appear in the database? That’s because we don’t need it in the database and have intentionally ommitted it from the list of DBSet<> definitions in the DB Context for the application, found in ApplicationDbContext.cs:

public class ApplicationDbContext : IdentityDbContext
{
...
    public DbSet<ItemList> ItemList { get; set; }
    public DbSet<LearningResource> LearningResource { get; set; }
    public DbSet<ResourceCatalog> ResourceCatalog { get; set; }
    public DbSet<ResourceRoot> ResourceRoot { get; set; }
    public DbSet<RssFeed> RssFeed { get; set; }
...
}

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 RssFeed table also has a Foreign Key for the ResourceRootId field used for the constraint in the relationship.

RssFeed table

RssFeed table

ResourceRoot Table

ResourceRoot Table

NetLearner-Db-ResourceRoot-RssFeed

1-to-1 Relationship: ResourceRoot.Id points to RssFeed.ResourceRootId

One to Many (Example 1)

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

  • Resource Catalog = ASP .NET Core Blogs
  • Item List = ASP .NET Core A-Z Blog Series

In the two classes, we see the following code:

public class ResourceCatalog
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<ItemList> ItemLists { get; set; }
}
public class ItemList
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<LearningResourceItemList> LearningResourceItemLists
    {
        get; set;
    }
}

Each Resource Catalog has zero or more Item Lists, so the ResourceCatalog class has a List<T> property for ItemLists. This is even simpler than the previously described 1-to-1 relationship. In the ItemList class, you don’t need a property pointing back to the ResourceCatalog.

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 ItemList table also has a Foreign Key for the ResourceCatalogId field used for the constraint in the relationship.

NetLearner-Db-ResourceCatalog-ItemList-Constraint

1-to-Many Relationship: ResourceCatalog.Id points to ItemList.ResourceCatalogId

One to Many (Example 2)

Let’s also take a look at another One-to-Many relationship, for each ResourceRoot that has zero or more LearningResources. For example:

  • Resource Root = Wake Up and Code! blog site
  • Learning Resource = Specific blog post on site

In the two classes, we see the following code:

public class ResourceRoot: InternetResource
{
    public RssFeed RssFeed { get; set; }
    public List<LearningResource> LearningResources { get; set; }
}
public class LearningResource : InternetResource
{
    public List<LearningResourceItemList> LearningResourceItemLists
    {
        get; set;
    }
}

Each Resource Root has zero or more Learning Resources, so the ResourceRoot class has a List<T> property for LearningResources. This is just as simple as the aforementioned 1-to-many relationship. In the LearningResource class, you don’t need a property pointing back to the ResourceRoot.

Relationship between ResourceRoot and LearningResource

ResourceRoot and LearningResource

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 (inferred by EF Core, as you should know by now) while the LearningResource table also has a Foreign Key for the ResourceRootId field used for the constraint in the relationship.

1-to-Many Constraint

1-to-Many Constraint for ResourceRoot and LearningResource

Many to Many

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

  • Item List = ASP .NET Core A-Z Blog Series
  • Learning Resource = Specific blog post on site

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.

If you’re wondering when EF Core will support this type of relationship without a join table, check out the following GitHub issue discussions:

Specifically, take a look at this comment: “Current plan for 3.0 is to implement skip-level navigation properties as a stretch goal. If property bags (#9914) also make it into 3.0, enabling a seamless experience for many-to-many could become easier.

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

public class ItemList
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<LearningResourceItemList> LearningResourceItemLists
    {
        get; set;
    }
}
public class LearningResource : InternetResource
{
    public List<LearningResourceItemList> LearningResourceItemLists
    {
        get; set;
    }
}

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

public class LearningResourceItemList
{
    public int LearningResourceId { get; set; }
    public LearningResource LearningResource { get; set; }
    public int ItemListId { get; set; }
    public ItemList ItemList { 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
  • ItemListId: integer value, pointing back to ItemList.Id
  • ItemList:  optional “navigation” property, reference back to connected ItemList 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 LearningResourceItemList table has a Composite Key for the ItemListId and LearningResourceId fields used for the constraints in the relationship.

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

public class ApplicationDbContext : IdentityDbContext
{
    ...
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<LearningResourceItemList>()
            .HasKey(r => new { r.LearningResourceId, r.ItemListId });
        base.OnModelCreating(modelBuilder);
    }
}

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

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 to Azure App Service

By Shahed C on January 28, 2019

This is the fourth of a new series of posts on ASP .NET Core for 2019. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2019, titled A-Z of ASP .NET Core!

ASPNETCoreLogo-300x267 A – Z of ASP .NET Core!

In this Article:

Before you begin, make sure you sign in to Azure, or create a new trial account first to follow along.

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:

Tweet from Geoffrey Huntley: https://twitter.com/GeoffreyHuntley/status/994345821276536832

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.
VS2017-RightClick-Publish

Right-click Publish your project from Solution Explorer

Follow the onscreen instructions

Follow the onscreen instructions

Continue reading

Cookies and Consent in ASP .NET Core

By Shahed C on January 21, 2019

This is the third of a new series of posts on ASP .NET Core for 2019. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2019, titled A-Z of ASP .NET Core!

ASPNETCoreLogo-300x267 A – Z of ASP .NET Core!

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 one of the standard ASP .NET Core web app project (2.2) templates. Specifically, let’s take a look at how the template makes it very easy for you to store cookies and display a cookie policy.

NOTE: The way cookies are handled in the project templates may change with each new release of ASP .NET Core. 

Unless you’ve been living under a rock in the past year or so, you’ve no doubt noticed all the GDPR-related emails and website popups all over the place. 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).

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 Edge/Firefox, expand Cookies under the Storage tab.
  • In 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 Edge

Cookies in Edge

Cookies in Chrome

Cookies in Chrome 

Continue reading

Blazor Full-Stack Web Dev in ASP .NET Core

By Shahed C on January 14, 2019

This is the second of a new series of posts on ASP .NET Core for 2019. In this series, we’ll cover 26 topics over a span of 26 weeks from January through June 2019, titled A-Z of ASP .NET Core!

ASPNETCoreLogo-300x267 A – Z of ASP .NET Core!

In this Article:

B is for Blazor

In a previous post, I covered various types of Pages that you will see when developing ASP .NET Core web applications. In this article, we will take a look at a Blazor sample and see how it works. Blazor (Browser + Razor) is an experimental .NET web framework which allows you to write full-stack C# .NET web applications that run in a web browser, using WebAssembly.

NOTE: Server-slide Blazor (aka Razor Components) allows you to run your Blazor app on the server, while using SignalR for the connection to the client, but we will focus on client-only Blazor in this article.

To get started by yourself, follow the official instructions to set up your development environment and then build your first app. In the meantime, you may download the sample code from my GitHub repo.

Web Blazor projects on GitHub: https://github.com/shahedc/BlazorDemos

Specifically, take a look at the Blazor Dice project, which you can use to generate random numbers using dice graphics. The GIF below illustrates the web app in action!

blazor-dice

Entry Point and Configuration

Let’s start with Program.cs, the entry point of your 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 Blazor Web Assembly 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 UseBlazorStartup() 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 IWebAssemblyHostBuilder CreateHostBuilder(string[] args) =>
      BlazorWebAssemblyHost.CreateDefaultBuilder()
      .UseBlazorStartup<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 minimum of 1 line of  code that adds the client side “App”. (This is different for server-hosted apps.)

public class Startup
{
   public void ConfigureServices(IServiceCollection services)
   {
   } 

   public void Configure(IBlazorApplicationBuilder app)
   {
      app.AddComponent<App>("app");
   }
}

Note that the Configure() method takes in an app object of type IBlazorApplicationBuilder, unlike the usual IApplicationBuilder we see in regular ASP .NET Core web apps.  When it adds the App component, it specifies the client-side app with the name “app” in double quotes.

UPDATE: In the above statement, I’m referring to “app” as “the client-side app”. In the comments section, I explained to a reader (Jeff) how this refers to the HTML element in index.html, one of the 3 locations where you would have to change the name if you want to rename it. Another reader (Issac) pointed out that “app” should be described as “a DOM Element Selector Identifier for the element” in that HTML file, which Angular developers should also recognize. Issac is correct, as it refers to the <app> element in the index.html file.

NAME CHANGES: Issac also pointed out that “IBlazorApplicationBuilder has already become IComponentsApplicationBuilder”. This refers to recent name changes on Jan 18, 2019. I will periodically make changes to the articles and code samples in this series. In the meantime, please refer to the following GitHub commit:

NOTE: There is an App.cshtml file in the project root that specifies the AppAssembly as a temporary measure, but the app config in this file is expected to move to Program.cs at a future date. 

Continue reading