Create a small blog in Blazor - Part 2 - Entity Framework integration

Published: Friday 29 May 2020

File Downloads

We are continuing with building a small blog in Blazor. In the first edition, we went ahead and created our Blazor application in Visual Studio. Next, we are going to integrate Entity Framework into our Blazor application.

Entities

We are going to begin by setting up some entities for Entity Framework.

The Base Class

We are going to set up a "Base" class, which our entities are going to inherit. This will include properties that are included on all our entities. The "Base" class inherits the "IBase" interface which mirrors the properties on "Base".

// IBase.cs
using System;
 
namespace RoundTheCode.Blazor.Data
{
	public partial interface IBase
	{
		int Id { get; set; }

		DateTimeOffset Created { get; set; }

		DateTimeOffset? LastUpdated { get; set; }

		DateTimeOffset? Deleted { get; set; }
	}
}
// Base.cs
using Microsoft.EntityFrameworkCore;
using System;
 
namespace RoundTheCode.Blazor.Data
{
	public abstract partial class Base : IBase
	{
		public virtual int Id { get; set; }

		public virtual DateTimeOffset Created { get; set; }

		public virtual DateTimeOffset? LastUpdated { get; set; }

		public virtual DateTimeOffset? Deleted { get; set; }

		public static void OnModelCreating<TEntity>(ModelBuilder builder)
			where TEntity : class, IBase
		{
			builder.Entity<TEntity>().HasKey(entity => entity.Id);
		}
	}
}

In addition, we've declared a static "OnModelCreating" generic method. What this does is that it passes in the entity as the generic method and tells Entity Framework to mark the "Id" property as the primary key.

The Post Class

The post class will store all our information about the post. It inherits the "Base" class, and gives us information regarding the title, content, whether it's enabled, and the published date.

// Post.cs
using System;
 
namespace RoundTheCode.Blazor.Data.PostSection
{
	public class Post : Base
	{
		public virtual string Title { get; set; }

		public virtual string Content { get; set; }

		public virtual bool Enabled { get; set; }

		public virtual DateTimeOffset? Published { get; set; }
	}
}

The Category Class

Next, we look at the Category class. Once again, it inherits the "Base" class and gives us the name and whether it's enabled.

// Category.cs
namespace RoundTheCode.Blazor.Data.CategorySection
{
	public partial class Category : Base
	{
		public virtual string Name { get; set; }

		public virtual bool Enabled { get; set; }
	}
}

The PostCategory Class

Lastly, we want a table that states which posts belong to which category. With our design, we have made it so a post can have multiple categories. Similar to the Category and Post classes, it inherits the "Base" class.

// PostCategory.cs
using Microsoft.EntityFrameworkCore;
using RoundTheCode.Blazor.Data.CategorySection;
 
namespace RoundTheCode.Blazor.Data.PostSection
{
	public class PostCategory : Base
	{
		public virtual int PostId { get; set; }

		public virtual int CategoryId { get; set; }

		public virtual Post Post { get; set; }

		public virtual Category Category { get; set; }

		public static void OnModelCreating(ModelBuilder builder)
		{
			builder.Entity<PostCategory>().HasOne(postCategory => postCategory.Category)
				.WithMany()
				.HasPrincipalKey(category => category.Id)
				.HasForeignKey(postCategory => postCategory.CategoryId);

			builder.Entity<PostCategory>().HasOne(postCategory => postCategory.Post)
				.WithMany()
				.HasPrincipalKey(post => post.Id)
				.HasForeignKey(postCategory => postCategory.PostId);
		}
	}
}

In addition, we have added a static OnModelCreating method. Inside that method, we tell Entity Framework to link PostCategory with Post, using the PostId. As well as that, we also tell Entity Framework to link PostCategory with Category, using the CategoryId.

The DbContext

Now that we have created our entities, we can set up our DbContext for Entity Framework. The "OnModelCreating" method is overridden to add our entities to our DbContext.

We have used some reflection to find all types that inherit "IBase" and invoked the static "OnModelCreating" method on each type, if it exists. In addition, we have also invoked the static "OnModelCreating" on the "Base" class for each type, using the type we are on as it's generic method. This ensures that we set the Id as the primary key for each type. This follows a similar approach to the one I took in my article "Using Reflection to Create a Dynamic OnModelCreating in Entity Framework".

C# coding challenges

C# coding challenges

Our online code editor allows you to compile the answer.

In addition, we also override the "OnConfiguring" method so we can get the connection string for our database from our appsettings.json file. You will need to ensure that the Nuget package "Microsoft.EntityFrameworkCore.SqlServer" has been added to the project.

// RoundTheCodeBlazorDbContext.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
 
namespace RoundTheCode.Blazor.Data
{
	public class RoundTheCodeBlazorDbContext : DbContext
	{
		protected IConfiguration _configuration;

		public RoundTheCodeBlazorDbContext()
		{
			_configuration = new ConfigurationBuilder().AddJsonFile(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + @"\appsettings.json").Build();
		}

		public RoundTheCodeBlazorDbContext([NotNull] IConfiguration configuration)
		{
			_configuration = configuration;
		}

		protected override void OnModelCreating(ModelBuilder builder)
		{
			// Loads all types from an assembly which have an interface of IBase and is a public class
			var types = Assembly.GetExecutingAssembly().GetTypes().Where(s => s.GetInterfaces().Any(_interface => _interface.Equals(typeof(IBase)) && s.IsClass && !s.IsAbstract && s.IsPublic));

			foreach (var type in types)
			{
				// On Model Creating
				var onModelCreatingMethod = type.GetMethods().FirstOrDefault(x => x.Name == "OnModelCreating" && x.IsStatic);

				if (onModelCreatingMethod != null)
				{
					onModelCreatingMethod.Invoke(type, new object[] { builder });
				}

				// On Base Model Creating
				if (type.BaseType == null || type.BaseType != typeof(Base))
				{
					continue;
				}

				var baseOnModelCreatingMethod = type.BaseType.GetMethods().FirstOrDefault(x => x.Name == "OnModelCreating" && x.IsStatic);

				if (baseOnModelCreatingMethod == null)
				{
					continue;
				}

				var baseOnModelCreatingGenericMethod = baseOnModelCreatingMethod.MakeGenericMethod(new Type[] { type });

				if (baseOnModelCreatingGenericMethod == null)
				{
					continue;
				}

				baseOnModelCreatingGenericMethod.Invoke(typeof(Base), new object[] { builder });
			}

		}

		protected override void OnConfiguring(DbContextOptionsBuilder builder)
		{
			// Sets the database connection from appsettings.json
			builder.UseSqlServer(_configuration["ConnectionStrings:RoundTheCodeBlazorDbContext"]);
		}

		public async override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
		{
			foreach (var entry in ChangeTracker.Entries())
			{
				if (entry.Entity is IBase)
				{
					if (entry.State == EntityState.Added)
					{
						entry.Property("Created").CurrentValue = DateTimeOffset.Now;
					}
					else if (entry.State == EntityState.Modified)
					{
						entry.Property("LastUpdated").CurrentValue = DateTimeOffset.Now;
					}
				}
			}

			return await base.SaveChangesAsync(cancellationToken);
		}
	}
}

Adding Entity Framework to our Application

Now that we have set up Entity Framework, we need to add it to our Blazor application. This is relatively simple to do. You just need to add the following line into the ConfigureServices method in the Startup class.

// Startup.cs
services.AddDbContext<RoundTheCodeBlazorDbContext>(options => { }, ServiceLifetime.Transient);

The reason why we use transient is documented in my article "Using Entity Framework in a Blazor Server Application".

Next, we need to create our SQL Server database. We open up SQL Server Management Studio and create a new database called "RoundTheCodeBlazor".

// appsettings.json
"ConnectionStrings": {
	"RoundTheCodeBlazorDbContext": "Server=localhost;Database=RoundTheCodeBlazor;Trusted_Connection=True;MultipleActiveResultSets=true;Integrated Security = true;"
}

Migration of Entities to Database

At present, we have an empty database with no tables. We need to fix that. For that, we use Entity Framework migrations. Before you do that, you need to ensure that the Nuget package "Microsoft.EntityFrameworkCore.Tools" is installed. This contains the migration script that we are going to use.

Then it's a simple case of going to Tools -> NuGet Package Manager -> Package Manager Console in Visual Studio and typing the following:

Lastly, we need to add our connection string into our appsettings.json file. This will look something like this.

Add-Migration Initial

This will set up our scripts to migrate the Category, Post and PostCategory entities to our database. At this point, it will not actually perform the migration. We need to run another script in the Package Manager Console to do this:

Update-Database

As a result of this, you should see your entities appearing in your database. Assuming no errors were reported.

See Entity Framework Running in the Application

Time to see a successful implementation of Entity Framework in Blazor. The video below shows the steps to get EF working in Blazor. In addition, it demonstrates a working example of calling a query from Entity Framework and displaying that data in the Blazor application.

Next Steps

Now that we've successfully integrated Entity Framework into our Blazor application, we can go ahead and build up our queries that we need for our blog. We will do that next.