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
Top comments (0)