Building an ASP.NET Core Web App from Scratch

mattdyor

Matt Dyor

Posted on November 22, 2020

Building an ASP.NET Core Web App from Scratch

Get Started

  • https://github.com/OdeToCode/OdeToFood
  • Start with an asp.net core web application in visual studio
  • Select web application (not web application mvc) with defaults
  • Add a asp-page to _Layout pointing to Restaurats/list
  • Create a folder for Restaurants
  • Add a Razor Page (empty) called List in the Restaurants folder

Adding a property to List

(think of this as model in MVC)

  • inside the class, type prop TAB TAB string TAB TAB Message that will leave you with public string Message { get; set; }
  • In appsettings.config add a "Message": "hello world"
  • Back in List type ctor TAB TAB (this will add a constructor)
  • Add IConfiguration to () and hit CTRL + . then enter and call the variable config (looks like public ListModel(IConfiguration config)
  • hit CTRL + . on config and select "create and assign field config"
  • in the OnGet() method add Message = config["Message"];

Options

If you want to be able to refresh your browser while debugging in asp.net core, you need to add this nuget package:

If you want to use code generators, powershell in to project directory:

  • dotnet tool install --global dotnet-aspnet-codegenerator
  • dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design -v 3.1
  • dotnet aspnet-codegenerator razorpage -h
  • dotnet aspnet-codegenerator razorpage List2 Empty -udl -outDir .\Pages\Restaurants

Add Model

  • Right Click Solution and Add Project
  • Choose Class Library (.net core)
  • Delete the created class and add your own class, call it Restaurant, make it public
  • Add a int Id, string Name, and String Location property
  • Add an Enum and use CTRL + . to put it into its own class file
public enum CuisineType
        {
            None, 
            Mexican, 
            Chinese
        }
Enter fullscreen mode Exit fullscreen mode
  • Add a property of type CuisineType
  • CTRL+K then CTRL+D to format

Add Data Access

  • Add another library class
  • Delete the class and add an interface
  • Add an Interface with a GetAll() method
  • Implement a public class based on the interface
  • Add a readonly list of restaurants
  • You need to add a reference to the Restaurant class project to this project, and then add a using statement so that the restaurants is available in the data class
  • Create a constructor that populates the list of restaurants
  • Implement GetAll method - need to add a reference to System.Linq to get the orderby to work

Add Singleton of the data access to startup

  • Add this to the startup page services.AddSingleton<IRestaurantData, InMemoryRestaurantData>();

Add Data to List

  • Add the IRestaurantData interface to the data list constructor
  • CTRL + . on IRestaurantData and add "create and assign field config" - should look something like this public ListModel(IConfiguration config, IRestaurantData restaurantData)

Add Search

  • Add a form with method=get and no action (will point to itself).
  • Include a input type=search name=searchTerm
  • include an i tag with a class="fa-search"
  • To get font awesome to work, you need to get a fontawesome.io url and add it to your URL; if you want autocomplete you need to right click on the project name > add > client side library > provider=unpkg, search for font awesome. Code is like <i class="fas fa-search"></i>
  • Replace the GetAll on the restaurants interface with a GetRestaurantByName
  • In the implementation method, specify that the name is a string name=null, meaning that if not provided no filtering.
  • Update the linq query to include a where string.IsNullOrEmpty(name) || r.Name.StartsWith(name) that includes all if no filter (remember this filter is currently case sensitive)
  • Change the OnGet method of List code behind to include Restaurants = restaurantData.GetRestaurantsByName(searchTerm); here the searchTerm is pulled from the Request based on the name of the input field

Show Searched For Term

  • Instead of pulling the searchTerm from the request, specify a property with a capital S SearchTerm and a BindProperty that means it will Bind when the request is received
[BindProperty(SupportsGet =true)]
public string SearchTerm { get; set; }
Enter fullscreen mode Exit fullscreen mode
  • Replace name and value on input form to asp-for="SearchTerm" <input type="search" class="form-control" asp-for="SearchTerm" />

Make a Details Page

  • Right Click Restaurants folder, Add, Razor Page (empty)
  • Add public Restaurant Restaurant { get; set; } to the PageModel
  • On the cshtml, you can now use @Model.Restaurant.Name
  • You can make embed the restaurantId in the url with page "{restaurantId:int}"

Make an Edit Page

  • Largely the same as Details, but sometimes you need to use @Model and sometimes not
<form method="post">
    <input type="hidden" asp-for="@Model.Restaurant.Id" />
    <div class="form-group">
        <label asp-for="@Model.Restaurant.Name"></label>
        <input asp-for="@Model.Restaurant.Name" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="@Model.Restaurant.Location"></label>
        <input asp-for="@Model.Restaurant.Location" class="form-control" />
    </div>
    <div class="form-group">
        <label asp-for="@Model.Restaurant.Name"></label>
        <select class="form-control" asp-for="Restaurant.Cuisine" asp-items="Model.Cuisines" >
        </select>
    </div>
</form>
Enter fullscreen mode Exit fullscreen mode
  • In the PageModel, you need to pull in the IHTMLHelper
public class EditModel : PageModel
    {
        private readonly IRestaurantData restaurantData;
        private readonly IHtmlHelper htmlHelper;

        public  Restaurant Restaurant { get; set; }
        public IEnumerable<SelectListItem> Cuisines { get; set; }
        public EditModel(IRestaurantData restaurantData, IHtmlHelper htmlHelper)
        {
            this.restaurantData = restaurantData;
            this.htmlHelper = htmlHelper;
        }
        public IActionResult OnGet(int restaurantId)
        {
            Cuisines = htmlHelper.GetEnumSelectList<CuisineType>(); 
            Restaurant = restaurantData.GetById(restaurantId); 
            if (Restaurant == null)
            {
                RedirectToPage("./NotFound");
            }
            return Page(); 
        }
    }
Enter fullscreen mode Exit fullscreen mode

Connect to Local SQL

  • Add a connection string to localdb
"ConnectionStrings": {
    "OdeToFoodDb":  "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=OdeToFood;Integrated Security=True;"
  }
Enter fullscreen mode Exit fullscreen mode
  • Wire up the app settings via configure services using an options lambda expression:
            services.AddDbContextPool<OdeToFoodDbContext>(options => {
                options.UseSqlServer(Configuration.GetConnectionString("OdeToFoodDb")); 
            }); 

Enter fullscreen mode Exit fullscreen mode
  • Add a constructor to the DbContext that pulls in the options from the app settings:
public OdeToFoodDbContext(DbContextOptions<OdeToFoodDbContext> options) : base(options)
        {
        }
Enter fullscreen mode Exit fullscreen mode
  • Finally need to tell the .Data project to look at the base project for the options information dotnet ef dbcontext info -s ..\OdeToFood\OdeToFood.csproj

Add a Migration

  • in the CLI run dotnet ef migrations add initialcreate -s ..\OdeToFood\OdeToFood.csproj
  • then apply the migration dotnet ef database update -s ..\OdeToFood\OdeToFood.csproj

Implement Delete Functionality

  • Add a Delete function Restaurant Delete(int id);
  • CTRL+. on the InMemoryRestaurantData and select option to move to its own class file
  • in the new file, CTRL+. and select to implement interface (this will add the delete)
  • Add the delete code
       {
            var restaurant = restaurants.FirstOrDefault(r => r.Id == id); 
            if (restaurant != null)
            {
                restaurants.Remove(restaurant); 
            }
            return restaurant; 
        }
Enter fullscreen mode Exit fullscreen mode

Add the SQL

  • on the IRestaurantData file, type this:
    public class SqlRestaurantData : IRestaurantData
    {

    }
Enter fullscreen mode Exit fullscreen mode
  • CTRL+. and SqlRestaurantData and select option to move to its own file
  • in the new file, CTRL+. implement the interface
💖 💪 🙅 🚩
mattdyor
Matt Dyor

Posted on November 22, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related