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

Comment Count = 0

Please log in to comment.

Login Register
Logged in users receive web notifications.
Web Notifications