ASP.NET Core 2.2 - In Memory Entities


Ken Haggerty
Created 08/15/2019 - Updated 08/25/2019 15:58

This article will demonstrate the creation of a small in-memory database which I will use in upcoming articles about table functions. I will assume you have created a new ASP.NET Core 2.2 Razor Pages project. I won't use Identity or Individual User Accounts.

The research project I used for the articles about table functions is published to tables.kenhaggerty.com. I created a topic, ASP.NET Core 2.2 - Table Functions Project for discussions. Access to the source code may be purchased at Manage > Assets.

To describe table functions, I wanted a simple set of entities with a few different property types. I started my research with the article Using EF Core's InMemory Provider To Store A "Database" In Memory by Matthew Jones. I wanted more records with fields which could be filtered and sorted. I used lists of fruit, vegetable and spice names from Fruits and Vegetables: List, Names & Pictures to help create the entities. Using an in-memory database allowed quick data modification for testing purposes.

Let's start with the entity class. I will use an enum for FoodType. Create a new root folder named Entities.

Add a new class named Food to the Entities folder:
public enum FoodType
{
    All,
    Fruit,
    Vegtable,
    Other
}

public class Food
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public FoodType FoodType { get; set; }
    public bool ColdStore { get; set; }
    public DateTime Date { get; set; }
}

We need a DbContext for injection. Create a new root folder named Data.

Add a new class named FoodDbContext to the Data folder:
public class FoodDbContext : DbContext
{
    public FoodDbContext(DbContextOptions options)
        : base(options)
    {
    }

    public DbSet<Food> Foods { get; set; }
}

Employ the UseInMemoryDatabase option when adding the DbContext to services.

Edit Startup.cs > ConfigureServices, add the DbContext:
services.AddDbContext<FoodDbContext>(options => options.UseInMemoryDatabase(databaseName: "Foods"));

I like Matthew Jones' DataGenerator approach which creates the database at application start. Create a new root folder named Services.

Add a new class named DataGenerator to the Services folder:
public class DataGenerator
{
    public static void Initialize(IServiceProvider serviceProvider)
    {
        using (var context = new FoodDbContext(
            serviceProvider.GetRequiredService<DbContextOptions<FoodDbContext>>()))
        {
            // Look for any foods.
            if (context.Foods.Any())
            {
                return;   // Data was already seeded
            }

            int i = 0, id = 0, coldStoreFactor = 3;
            DateTime baseTime = DateTime.Now;

            var fruits = @"Apple,Watermelon,Orange,Pear,Cherry,Strawberry,Nectarine,
Grape,Mango,Blueberry,Pomegranate,Starfruit,Plum,Banana,Raspberry,Mandarin,Jackfruit,
Papaya,Kiwi,Pineapple,Lime,Lemon,Apricot,Grapefruit,Melon,Coconut,Avocado,Peach";
            var fruitList = fruits.Split(',').ToList();
            foreach (string fruit in fruitList)
            {
                i++;
                id++;
                context.Foods.Add(new Food()
                {
                    Id = id,
                    Name = fruit.Trim(),
                    FoodType = FoodType.Fruit,
                    ColdStore = i % coldStoreFactor == 0,
                    Date = baseTime.AddHours(i).AddMinutes(id)
                });
                context.SaveChanges();
            };

            i = 0;
            var vegtables = @"Corn,Mushroom,Broccoli,Cucumber,Red bell pepper,
Tomato,Rutabaga,Carrot,Brussels sprout,Pumpkin,Cabbage,Potato,Eggplant,
Sweet potato,Turnip,Zucchini,Green chilli,Onion,Lettuce,Radish,Pea,Asparagus,Celery,
Green pepper,French beans,Spinach,Beet,Red chili peppers,Bean";
            var vegtableList = vegtables.Split(',').ToList();
            foreach (string vegtable in vegtableList)
            {
                i++;
                id++;
                context.Foods.Add(new Food()
                {
                    Id = id,
                    Name = vegtable.Trim(),
                    FoodType = FoodType.Vegtable,
                    ColdStore = i % coldStoreFactor == 0,
                    Date = baseTime.AddHours(i).AddMinutes(id)
                });
                context.SaveChanges();
            };

            i = 0;
            var spices = @"Cilantro,Artichoke,Rosemary,Bay leaves,Mint leaves,Basil,Clove,
Olive,Shallot,Turmeric,Garlic,Ginger,Onion,Green onions,Lemongrass,Chives,Green chili";
            var spiceList = spices.Split(',').ToList();
            foreach (string spice in spiceList)
            {
                i++;
                id++;
                context.Foods.Add(new Food()
                {
                    Id = id,
                    Name = spice.Trim(),
                    FoodType = FoodType.Other,
                    ColdStore = i % coldStoreFactor == 0,
                    Date = baseTime.AddHours(i).AddMinutes(id)
                });
                context.SaveChanges();
            };
        }
    }
}

Call the DataGenerator > Initialize method after the build and before the run commands for CreateWebHostBuilder.

Edit Program.cs > Main:
//CreateWebHostBuilder(args).Build().Run();

//1. Get the IWebHost which will host this application.
var host = CreateWebHostBuilder(args).Build();

//2. Find the service layer within our scope.
using (var scope = host.Services.CreateScope())
{
    //3. Get the instance of FoodDbContext in our services layer
    var services = scope.ServiceProvider;
    var context = services.GetRequiredService<FoodDbContext>();

    //4. Call the DataGenerator to create sample data
    DataGenerator.Initialize(services);
}

//Continue to run the application
host.Run();

Resolve any namespace issues.

Access the Food entities from any page using dependency injection.

Edit Index.cs > IndexModel:
private readonly FoodDbContext _context;

public DataModel(FoodDbContext context)
{
    _context = context;
}

public IList<Food> Foods { get; set; } = new List<Food>();

public void OnGet()
{
    Foods = _context.Foods.ToList();
}
Edit Index.cshtml, add the table:
<div class="row">
    <div class="col-12">
        <table class="table">
            <thead>
                <tr>
                    <th>
                        @Html.DisplayNameFor(model => model.Foods[0].Id)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.Foods[0].Name)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.Foods[0].FoodType)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.Foods[0].ColdStore)
                    </th>
                    <th>
                        @Html.DisplayNameFor(model => model.Foods[0].Date)
                    </th>
                </tr>
            </thead>
            <tbody>
                @if (Model.Foods.Count > 0)
                {
                    foreach (var item in Model.Foods)
                    {
                        <tr>
                            <td>
                                @Html.DisplayFor(modelItem => item.Id)
                            </td>
                            <td>
                                @Html.DisplayFor(modelItem => item.Name)
                            </td>
                            <td>
                                @Html.DisplayFor(modelItem => item.FoodType)
                            </td>
                            <td>
                                <input type="checkbox" asp-for="@item.ColdStore">
                            </td>
                            <td>
                                @Html.DisplayFor(modelItem => item.Date)
                            </td>
                        </tr>
                    }
                }
                else
                {
                    <tr>
                        <td colspan="5" class="text-center">
                            No Food Found
                        </td>
                    </tr>
                }

            </tbody>
        </table>
    </div>
</div>

Build, run and test.

In Memory Data
Update 08/25/2019

Updated articles, asset and topic links.


Article Tags:

EF Core Model Table Functions

Comment Count = 0

Please log in to comment or follow.

Login Register
Follow to get web notifications when new comments are posted to this article.
Logged in users receive web notifications for new articles, topics and assets.
Web Notifications