Getting Started with Redis, Elastic Beanstalk, and Razor Pages (.NET 6)
Tags: aws, redis, beginners, dotnet
Published: 2022-08-23 01:53:00

Hello! How are you today? I'm going to share my exploration of Redis, Elastic Beanstalk, and Razor Pages in ASP.NET 6. So, let's check it out.

## Required Tools

You will be required these tools. Please download and install it.

1. [.NET 6](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
2. [Docker](https://docs.docker.com/get-docker/)
3. [VS Code](https://code.visualstudio.com/) (Optional)

## Project Preparation

You will need to prepare the project of Razor Pages. You can follow the steps below.

* Generate the `.gitignore`. This will be useful when you are using a changes tracker like git.

```bash
dotnet new gitignore
```

![Generate .gitignore](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4y872b2pbjh44hfh7oj8.png)
 

* Generate the Razor Project

```bash
dotnet new razor -o RedisSample
```

![Generate Project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jsw719ctpbmf0mq31ty7.png)
 
* Generate the solution to link the root repository to the project directory.

```bash
dotnet new sln
```

![Generate sln](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8wresd2drs8oasf6ohyb.png)


* Add the project to the solution.

```bash
dotnet sln add RedisSample
```

![Add to sln](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvli0jt5j09msayewpdl.png)

* Install `Redis.OM` to the project.

```bash
dotnet add .\RedisSample\ package Redis.OM --version 0.2.1
```

![Add Redis.OM](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akkdv5xpgprjjgttprlb.png)

* Try to build, so we know the process is already done.

```bash
dotnet build
```

![Try build](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqb8kinfcti4jj6df652.png)

## Time to Code!

* If you want skip the step by step of coding steps, you may use this repository.

{% github bervproject/redis-sample %}

* Update the `Program.cs` like this.

```csharp
using Redis.OM;
using RedisSample.HostedService;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton(new RedisConnectionProvider(builder.Configuration["REDIS_CONNECTION_STRING"]));
builder.Services.AddHostedService<CreateIndexService>();

// Add services to the container.
builder.Services.AddRazorPages();

// the rest of the codes

// ...

app.Run();
```

> [Full file](https://github.com/bervProject/redis-sample/blob/main/RedisSample/Program.cs)

* Create the model file: `Models/Note.cs`. This model will be used to define the redis data model.

```csharp
using Redis.OM.Modeling;

namespace RedisSample.Models;

[Document(StorageType = StorageType.Json, Prefixes = new[] { "Note" })]
public class Note
{
    [RedisIdField]
    public Ulid Id { get; set; }
    [Indexed]
    public string Title { get; set; }

    [Indexed]
    public string Message { get; set; }
}
```

> [Full file](https://github.com/bervProject/redis-sample/blob/main/RedisSample/Models/Note.cs)

* Create file `HostedServices/CreateIndexService.cs`. This class will be used to create the index of the model.

```csharp
using Redis.OM;
using RedisSample.Models;

namespace RedisSample.HostedService;

public class CreateIndexService : IHostedService
{
  private readonly RedisConnectionProvider _redisConnectionProvider;
  public CreateIndexService(RedisConnectionProvider redisConnectionProvider)
  {
    _redisConnectionProvider = redisConnectionProvider;
  }

  public Task StartAsync(CancellationToken cancellationToken)
  {
    _redisConnectionProvider.Connection.CreateIndex(typeof(Note));
    return Task.CompletedTask;
  }

  public Task StopAsync(CancellationToken cancellationToken)
  {
    return Task.CompletedTask;
  }
}
```

> [Full file](https://github.com/bervProject/redis-sample/blob/main/RedisSample/HostedServices/CreateIndexService.cs)

* Now, let's go the Pages directory. We will update `Index.cshtml` and `Index.cshtml.cs` also create new file `AddNote.cshtml`, `AddNote.cshtml.cs`, `EditNote.cshtml`, `EditNote.cshtml.cs`. Here we go. You may check the files in [here](https://github.com/bervProject/redis-sample/tree/main/RedisSample/Pages).

* `Index.cshtml`

```cshtml

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div>
    <a class="btn btn-primary" asp-area="" asp-page="/AddNote" role="button">Add Note</a>
    <p>Total Notes: @Model.NoteList.Count</p>
    <div class="container">
        <div class="row gy-3">
            @foreach (var note in Model.NoteList)
            {
                <div class="col-6">
                    <div class="card">
                        <div class="card-body">
                            <p class="h1">@note.Title</p>
                            <p>@note.Message</p>
                            <div class="row">
                                <div class="col-2">
                                    <a class="btn btn-secondary" asp-area="" asp-page="/EditNote" asp-route-id="@note.Id"
                                    role="button">Edit</a>
                                </div>
                                <div class="col-2">
                                    <form method="post">
                                        <button class="btn btn-danger" type="submit" asp-page-handler="DeleteNote"
                                        asp-route-id="@note.Id">Delete</button>
                                    </form>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            }
        </div>
    </div>
</div>
```

* `Index.cshtml.cs`

```csharp
sing Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Redis.OM;
using Redis.OM.Searching;
using RedisSample.Models;

namespace RedisSample.Pages;

public class IndexModel : PageModel
{
  private readonly ILogger<IndexModel> _logger;
  private readonly IRedisCollection<Note> _notes;
  public List<Note> NoteList { get; set; } = new List<Note>();


  public IndexModel(ILogger<IndexModel> logger, RedisConnectionProvider redisConnection)
  {
    _logger = logger;
    _notes = redisConnection.RedisCollection<Note>();
  }

  public async Task OnGetAsync()
  {
    var notes = await _notes.ToListAsync();
    NoteList.AddRange(notes);
  }

  public async Task<IActionResult> OnPostDeleteNoteAsync(string id)
  {
    var note = await _notes.FindByIdAsync(id);

    _logger.LogDebug($"...... data: {note}");

    if (note == null)
    {
      return NotFound();
    }

    await _notes.DeleteAsync(note);
    await _notes.SaveAsync();

    return RedirectToPage();
  }
}
```

* `AddNote.cshtml`

```cshtml
@page
@model AddNoteModel
@{
    ViewData["Title"] = "Add Note";
}

<h1>Add Note</h1>

<div class="card">
    <div class="card-body">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Note!.Title" class="control-label">Title</label>
                <input asp-for="Note!.Title" class="form-control" placeholder="Enter the title of notes">
                <span asp-validation-for="Note!.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Note!.Message">Message</label>
                <textarea class="form-control" asp-for="Note!.Message" placeholder="Enter the message of notes here"> </textarea>
                <span asp-validation-for="Note!.Message" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </div>

</div>
```

* `AddNote.cshtml.cs`

```csharp
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Redis.OM;
using Redis.OM.Searching;
using RedisSample.Models;

namespace RedisSample.Pages;

public class AddNoteModel : PageModel
{
    private readonly ILogger<AddNoteModel> _logger;
    private readonly IRedisCollection<Note> _notes;

    [BindProperty]
    public Note? Note { get; set; }

    public AddNoteModel(ILogger<AddNoteModel> logger, RedisConnectionProvider redisConnection)
    {
        _logger = logger;
        _notes = redisConnection.RedisCollection<Note>();
    }

    public void OnGet()
    {

    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        if (Note != null)
        {
            await _notes.InsertAsync(Note);
        }
        await _notes.SaveAsync();
        return Redirect("/");
    }
}
```

* `EditNote.cshtml`

```cshtml
page "{id}"
@model EditNoteModel
@{
    ViewData["Title"] = "Edit Note";
}

<h1>Edit Note</h1>

<div class="card">
    <div class="card-body">
        <form method="post">
            <input type="hidden" asp-for="Note!.Id" />
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Note!.Title" class="control-label">Title</label>
                <input asp-for="Note!.Title" class="form-control" placeholder="Enter the title of notes">
                <span asp-validation-for="Note!.Title" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Note!.Message">Message</label>
                <textarea class="form-control" asp-for="Note!.Message" placeholder="Enter the message of notes here"> </textarea>
                <span asp-validation-for="Note!.Message" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Submit</button>
        </form>
    </div>

</div>
```

* `EditNote.cshtml.cs`

```csharp
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Redis.OM;
using Redis.OM.Searching;
using RedisSample.Models;

namespace RedisSample.Pages;

public class EditNoteModel : PageModel
{
  private readonly ILogger<AddNoteModel> _logger;
  private readonly IRedisCollection<Note> _notes;

  [BindProperty]
  public Note? Note { get; set; }

  public EditNoteModel(ILogger<AddNoteModel> logger, RedisConnectionProvider redisConnection)
  {
    _logger = logger;
    _notes = redisConnection.RedisCollection<Note>();
  }

  public async Task<IActionResult> OnGetAsync(string id)
  {
    Note = await _notes.FindByIdAsync(id);
    if (Note == null)
    {
      return NotFound();
    }
    return Page();
  }

  public async Task<IActionResult> OnPostAsync()
  {
    if (!ModelState.IsValid)
    {
      return Page();
    }

    if (Note != null)
    {
      await _notes.UpdateAsync(Note);
    }
    await _notes.SaveAsync();
    return Redirect("/");
  }
}
```

## Test our code locally

Finally done! Now, let's start to try our code. But, we will need to host the Redis Stack first. We will run the Redis Stack using docker. You can check this [link](https://redis.io/docs/stack/get-started/install/docker/) for more details. Basically you can run using this command.

```bash
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:6.2.2-v4
```

Ok, let's try our code. You can run the project using this command `dotnet run --project RedisSample` or `dotnet watch --project RedisSample`.

Now, let's explore your website! You can check this video for the demo.

{% embed https://youtu.be/QtEhxnWc9Vk %}


You might need to check the RedisInsight for more details about the stored data.

![Redis Insight](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2gggy5ze12jkv17zsgxi.png)


## Preparing Redis Stack

I think it will not be enough if we just implement it locally. So we will need to host it somewhere else. We will deploy it to Elastic Beanstalk, but I will host the Redis Stack using Redis Enterprise since they have options to host in AWS! For more details on how I set up the Redis Enterprise, you may check this video.

{% embed https://youtu.be/gnz28ChLyyM %}
 
## Preparing Elastic Beanstalk

Now, since the Redis Stack is ready. We will move to set up Elastic Beanstalk. Please check this video.

{% embed https://youtu.be/vUiAE4nJ_LA %}

## Testing Your App

Finally, we test our code that is hosted in Elastic Beanstalk. Here's my video.

{% embed https://youtu.be/IdBHent4DpM %}


> Why I didn't use ElastiCache? So, when I'm exploring the ElastiCache, seems the ElastiCache doesn't have the RediSearch module, so I can't create the index. Like this error logs.

![error logs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tt84moo5jerp2l3ijefh.png)
 

## Thank you

Thank you for reading and watching. If you have any comments and suggestions, please feel free to comment in the comment section and tell me about your suggestion.

![Yay GIF](https://media.giphy.com/media/MeIucAjPKoA120R7sN/giphy.gif)