- Creating the basic form
- Integrating with AWS
- Integrating with S3
- Adding background tasks
- Sending emails and creating download links
This first article will go over creating the basics that we need for the application – setting up the models, the database, and the basic frontend. If you haven’t read the introduction about what we are going to build, it’s a good idea to go over that first.
Models and local database
Let’s think about what data we need to represent in our application. We clearly need the email address of the receiver, so we know where to send the email. Having the address of the sender would also be nice, to let the receiver know where the files come from. We also, obviously, need the URI from where to download the file. It sounds like we can have all of these in one model.
Let’s also think back to the flow of the application; there are two processes going on: the sender uploading the files, and the receiver downloading them. Instead of uploading the file, waiting for it to succeed, and then saving all the data in the database, we can take a different approach:
- Save the data that we have (receiver and sender email addresses)
- Upload the file to the cloud and retrieve the its key
- Save the file key to the database
We can also take two approaches on how to structure the models:
- have a single class/table, which we create when we first receive the data and then we update after uploading the file, or
- split them in separate models, and link them together through a foreign key.
It’s hard to say that one is better than the other, and in such a simple case, you can do either of them. Personally, though, I like having “complete” rows in the database (no NULL values) when possible, so we will go with the second approach in this series.
Creating the models
So, as decided, we will create 2 models: FileSendData – containing the sender/receiver email addresses – and FileStorageData – containing the URI of the file. The latter will have a foreign key to the former, which will also be used as the primary key. Since these models will also be used later in the frontend, adding some validation attributes is a good idea:
Setting up the database
If you don’t already have an SQL server and a database, you should get Microsoft SQL Server Management Studio and create a local database. You can see it in Visual Studio by clicking View -> SQL Server Object Explorer and using the navigation system there:
If you right click on the database and click “Properties”, you will one called “Connection string”. In your project, open the appsettings.json file and add your connection string there:
Once you have that set up, you can go ahead and install Entity Framework, which will handle the communication between the application and the database. Right click on the project and click on “Manage NuGet Packages…”. In the “Browse” tab, search for “EntityFrameworkCore” and install the following 3 packages:
- Microsoft.EntityFrameworkCore — the base package that will let you perform operations on your database;
- Microsoft.EntityFrameworkCore.SqlServer — extensions that are needed to setup Entity Framework with your SQL server;
- Microsoft.EntityFrameworkCore.Tools —tools that are needed to update the database;
Next, create a folder called “Data” (if it doesn’t already exist), and add a class called “TransferzorDbContext” to it. This class will contain repositories for each of our tables. You can read more about it in the Microsoft Docs.
The class contains a constructor with a DbContextOptions parameter ,which we will inject from the Startup class, as well as two DbSet properties, which represent the two tables that will be created in our database.
Open the Startup.cs file and add the first 3 lines to the ConfigureServices method, in order to configure the DB context to use your local database:
Creating the migration
Now that the context and database are connected, we need to create the first migration. A migration records all changes to the models (adding models, adding fields to a model, removing fields, etc.) that happened since the last migration was made (in this case, it will add everything, since it’s the first migration). This makes it easy to drop a database and recreate it or create identical databases in different environments based on the same code, and it will be useful when we create the remote database. To create the first migration, run the following command in the Package Manager Console (that you can find in View -> Other Windows): Add-Migration FileData.
Once the commands is ran, you should see a new class with the following content:
This contains all the SQL statements that need to be ran to create the tables for both models, as well as linking them through the foreign key. To apply the migration to the database (meaning, to run the SQL statements), run the following command: Update-Database – which will apply all migrations since the last applied one.
Creating the form
The last step is to create the form that the sender will use to upload the file. The form should include two text inputs – for the sender and receiver email addresses -, one file input, and the “Send” button. Since the file input is not part of ASP.NET (yet?), we need to install a package first. Go to “Manage NuGet Packages…” again and install the “DataJuggler.Blazor.FileUpload” package:
Next, in the “Shared” folder, create another folder called “Components”, and a Razor Component file inside called “SendFileForm”:
We can divide the file in 3 parts. In the first one, we will add “using” directives for the namespaces that we will need, as well as an “inject” one for the TransferzorDbContext:
Next, we will write the code component of the file. This means creating a data field that will be bound to the form, as well as writing the methods to be called when the form is submitted and files are uploaded. It’s important to initialize the field – Blazor will throw an exception otherwise. After the data has been saved, we set the field to a new empty object so the form is reset, and we announce Blazor that the state of the component has changed. We are creating two different methods as the file uploading has to be handled separately, due to the way the library works.
Finally, we will create the form. Blazor provides the EditForm component which will make linking the form components to an object’s properties much easier. Use the Model property to link the form to a field from the code, and the OnValidSubmit one to specify what method to call once the form is submitted. Each field from the form has a @bind-value property which specifies what property from the object to map it to. The exception is the file input, which will make use of the OnChange property to specify which method to call; this will only save the files in the same object, which will then be handled by the HandleValidSubmit method as well:
The form is also wrapped inside a div, so we can style it better by adding the following to the site.css file from wwwroot->css:
Finally, let’s modify the Index.razor file to display our form:
We don’t have any other page in our application, so the sidebar is useless for us. You can remove it from the MainLayout.razor file:
And delete the NavMenu.razor file from the Shared folder afterwards.