Building an ASP.NET Core Web App from Scratch
Matt Dyor
Posted on November 22, 2020
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:
- https://stackoverflow.com/questions/54106058/why-does-page-not-update-after-refresh-when-cshtml-changes
- need to use the 3.x version if you are using .net core 3.x
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
}
- 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; }
- 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>
- 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();
}
}
Connect to Local SQL
- Add a connection string to localdb
"ConnectionStrings": {
"OdeToFoodDb": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=OdeToFood;Integrated Security=True;"
}
- Wire up the app settings via configure services using an options lambda expression:
services.AddDbContextPool<OdeToFoodDbContext>(options => {
options.UseSqlServer(Configuration.GetConnectionString("OdeToFoodDb"));
});
- Add a constructor to the DbContext that pulls in the options from the app settings:
public OdeToFoodDbContext(DbContextOptions<OdeToFoodDbContext> options) : base(options)
{
}
- 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;
}
Add the SQL
- on the IRestaurantData file, type this:
public class SqlRestaurantData : IRestaurantData
{
}
- CTRL+. and SqlRestaurantData and select option to move to its own file
- in the new file, CTRL+. implement the interface
💖 💪 🙅 🚩
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
githubcopilot AI Innovations at Microsoft Ignite 2024 What You Need to Know (Part 2)
November 29, 2024