An overview of how Bookings data was stored and retrieved in the app using a Sqlite Database.

As in the previous post, a server local Sqlite databse was used in the app to store user authentication data. This functionality came prepackaged in the BlazorWithIdenity project using Entity Framework Core. The approach here was to use the same databse file to store app data. I initally tried to use the existing database authentication tables for user data but kept running into a not found found issue wrt to the virtual property, IsAuthenticated. This rears it head when you try to use the ApplicationDbContext. Whilst there is probably a way around this, I found the quick and dirty approach was to just to create a table in the database using the UserInfo class in the Shared project. I modified the AuthoriseController class such that when a new user registers it also mirrors that user name, email addres and phone number in a UserInfo table (reminder that the original project did not create a UserInfo table). For this a BookingsDBContext based upon the DbContext class was used to connect to the same database.

namespace AthsEssGymBook.Server.Data
{
    public class BookingsDBContext : DbContext
    {
        public DbSet<Athlete> Athletes { get; set; }
        public DbSet<BookingInfo> BookingInfo { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlite("Data Source=data.db");
        }
    }
}

The BookingsDBContext class

This DBContext, called BookingInfoDBContext* connects to the Athlete and BookingInfo table in the same database. The Bookinfo class is defined in the Shared project. The original BookingsDBContext had reference to the UserInfo table. Further down the track I further distanced the authentication UserInfo from the user information in BookinFo. I removed the UserInfo table and reverted the UserInfo class to its original state and created an Athletes table based upom Athlete class in the Shared project. Athlete class entries did not include a reference to the corresponding UserInfo authentication table entry as the name which is unique can be used to search UserInfo if required. The Athlete class was extended to add user properties required for the app’s bookings functionality. The BookingInfo has a ForeignKey, AthleteId of the user making the booking. To facilitate all, of this the Athletes and BookInfo tables were manually added to the Server Migrations.

    migrationBuilder.CreateTable(
        name: "BookingInfo",
        columns: table => new
        {
            Id = table.Column<int>(nullable: false)
                .Annotation("Sqlite:Autoincrement", true),
            AthleteId = table.Column<int>(nullable: false),
            Slot = table.Column<int>(nullable: false),
            _Date = table.Column<string>(nullable: false),
            _Time = table.Column<int>(nullable: false),
            _Duration = table.Column<int>(nullable: false)
        },
        constraints: table =>
        {
            table.PrimaryKey("PK_BookingInfo", x => x.Id);
            table.ForeignKey(
                name: "FK_BookingInfo_Athlete_Id",
                column: x => x.AthleteId,
                principalTable: "Athletes",
                principalColumn: "Id",
                onDelete: ReferentialAction.Cascade);
        });

The BookInfo EF table creation

Controllers

An AthetesControlller and a BookingInfosController were added to the Server in the Controllers folder. I did spend some time getting suitable Http contexts to work from the Client app. Evenmtually I used the template as from davidfowl/Todos project on GitHub (thx David Fowls), the file ToDoWithControllers.

athletescontrollertemplate
The AthletesContrller template

This provides for GET, PUT, POST and DELETE Http paths for each controller. I did find a problem when wanting, for example, multiple parameterised GET paths where the query was by Id versus via AthleteName. The fix for this specific case was to make the parameterised Http GET parameter a string. If in parsing it is a valid integer a query based upon Id was used. Otherwise, it is assumed that the parameter is a name and so the query is by AtheleteName. More general GET queries, such as filtering dates and times were implemented by the query filtering being performed after a more general query was returned to the client. This may be rather ineffient, but as a first pass. it works!
So that all calls to a controller use the same DBConntext, Controller constructor instantiate the connection context and so all paths automatically create this:

    private BookingsDBContext _context;

    public BookingInfosController() 
    {
        DbContextOptions<ApplicationDbContext> optionsBuilder = new DbContextOptions<ApplicationDbContext> ();
        _context = new BookingsDBContext();
    }  

Http Calls from the Client

Whilst these could have been implemented in the razor pages, (actually during development they were), they were ultimately quarantined to Services in the Client (in the Services folder). In The original HelloWorld app, that is where the WeatherForecastClient resides.

bookingsclient
The BookingsClient Template

As can be seen there are a range of different Http GET calls to the Server at this level in the Client app. Most start with:

    bookings = await client.GetFromJsonAsync<BookingInfo[]>(ServiceEndpoint);

They then use Linq to filter the returned list as required.

Http Paths

Where the call to the server doesn’t require parameters, such as The Http GET to get all recorsd in a table, the ServiceEnpoint is use unqualified. The clientg app prepends the URL of the server to this when making a call.

private const string ServiceEndpoint = "/api/BookingInfos"

This works regardless of whether the server is running locally, as with development, or remotely as for example, with an Azure Web App Deployment.
Where a parameter is required, the syntax differents whether it is a basic data type (int, string bool etc) parameter or a class instance. For basic types, it is appended to the ServiceEndpoint. For more complex types, it is specified as an additional parameter. For these types, the class needs to be able to seamlessly Json serialised and reflated. This may require directives in the class property specifications. Also it is important to similarly tag any properties (eg. constaructed properties) taht need to not be part of the serialisation.

    public async Task DeleteBooking(int id)
    {
        await client.DeleteAsync($"{ServiceEndpoint}/{id}");
    }

Http DELETE with an Integer parameter

    public async Task AddBooking(BookingInfo booking)
    {
        await client.PostAsJsonAsync(ServiceEndpoint, booking);
    }

Http POST with a BookingInfo instance parameter


**Next: ** The App Projects Later Deployment.


 TopicSubtopic
  Next: > Blazor Gym Booking App
<  Prev:   Blazor Gym Booking App
This Category:Blazor
  Next: > Blazor Gym Booking App WebAssembly App - The Projects 
<  Prev:   Blazor Gym Booking App