Last active
March 25, 2024 21:01
-
-
Save dbones/fbaa210afeda6c599e52d20a59ce8989 to your computer and use it in GitHub Desktop.
Laters EF
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.ComponentModel.DataAnnotations; | |
using System.Data; | |
using System.Data.Common; | |
using Laters; | |
using Laters.AspNet; | |
using Laters.ClientProcessing; | |
using Laters.Data.EntityFrameworkCore; | |
using Laters.Minimal.Application; | |
using Microsoft.AspNetCore.Mvc; | |
using Microsoft.EntityFrameworkCore; | |
var builder = WebApplication.CreateBuilder(args); | |
//connection to share | |
builder.Services.AddScoped<DbConnection>(_ => { | |
var connectionString = "Host=postgres;Port=5432;Database=laters;Username=application;Password=ABC123!!"; | |
return new Npgsql.NpgsqlConnection(connectionString); | |
}); | |
//applications db context | |
builder.Services.AddDbContext<TodoItemDbContext>((provider, options) => { | |
var connection = provider.GetRequiredService<DbConnection>(); | |
options.UseNpgsql(connection); | |
}); | |
builder.WebHost.ConfigureLaters((context, setup)=> | |
{ | |
//reference ourself for the demo | |
setup.Configuration.WorkerEndpoint = "http://localhost:5056"; | |
//setting up EF as the storage | |
setup.UseStorage<UseEntityFramework>(ef => | |
{ | |
//using the same connection as above | |
ef.ConnectionFactory = provider => provider.GetRequiredService<DbConnection>(); | |
ef.ApplyOptions =(_, connection, options) => | |
{ | |
//injecting the connection, and setting up the migration to be in this assembly | |
options.UseNpgsql(connection, b=> b.MigrationsAssembly("EpicTodoList.Service")); | |
}; | |
}); | |
}); | |
var app = builder.Build(); | |
app.UseLaters(); | |
app.UseMiddleware<TransactionMiddleware>(); | |
app.MapGet("/todo-items", (TodoItemDbContext context) => | |
{ | |
var items = context.TodoItems.ToList(); | |
return Task.FromResult(items); | |
}); | |
app.MapPost("/todo-items", ([FromBody]TodoItem item, TodoItemDbContext context) => | |
{ | |
item.Id ??= Guid.NewGuid().ToString("D"); | |
context.TodoItems.Add(item); | |
return Results.Created($"/todo-items/{item.Id}", item); | |
}); | |
app.MapPut("/todo-items/{id}", async ( | |
[FromBody]TodoItem item, | |
string id, | |
TodoItemDbContext context, | |
ISchedule schedule) => | |
{ | |
var existingItem = await context.TodoItems.FindAsync(id); | |
if (existingItem == null) | |
{ | |
return Results.NotFound(); | |
} | |
existingItem.Title = item.Title; | |
existingItem.Details = item.Details; | |
existingItem.Completed = item.Completed; | |
if (existingItem.Completed) | |
{ | |
var removeDateTime = DateTime.UtcNow.AddMinutes(1); | |
schedule.ForLater(new RemoveTodoItem { Id = existingItem.Id }, removeDateTime); | |
} | |
return Results.NoContent(); | |
}); | |
app.MapHandler<RemoveTodoItem>(async (JobContext<RemoveTodoItem> ctx, TodoItemDbContext context) => | |
{ | |
var item = await context.TodoItems.FindAsync(ctx.Payload!.Id); | |
if (item != null) | |
{ | |
context.TodoItems.Remove(item); | |
} | |
await Task.CompletedTask; | |
}); | |
app.Run(); | |
public class TransactionMiddleware | |
{ | |
private readonly RequestDelegate _next; | |
public TransactionMiddleware(RequestDelegate next) | |
{ | |
_next = next; | |
} | |
public async Task Invoke( | |
HttpContext context, | |
TodoItemDbContext todoDbContext, | |
LatersDbContext latersDbContext, | |
DbConnection connection) | |
{ | |
await _next(context); | |
if(connection.State != ConnectionState.Open) | |
{ | |
connection.Open(); | |
} | |
using var tx = connection.BeginTransaction(IsolationLevel.Serializable); | |
todoDbContext.Database.UseTransaction(tx); | |
latersDbContext.Database.UseTransaction(tx); | |
await todoDbContext.SaveChangesAsync(); | |
await latersDbContext.SaveChangesAsync(); | |
tx.Commit(); | |
} | |
} | |
public class RemoveTodoItem | |
{ | |
public string Id { get; set; } = string.Empty; | |
} | |
public class TodoItem | |
{ | |
public string Id { get; set; } = string.Empty; | |
public string Title { get; set; } = string.Empty; | |
public string Details { get; set; } = string.Empty; | |
public bool Completed { get; set; } = false; | |
} | |
public class TodoItemDbContext : DbContext | |
{ | |
public TodoItemDbContext(DbContextOptions<TodoItemDbContext> options) : base(options) | |
{ | |
} | |
public DbSet<TodoItem> TodoItems { get; set; } = null!; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Laters
dotnet add package Laters.Data.EntityFrameworkCore
dotnet ef migrations add InitialCreate --context TodoItemDbContext
dotnet ef database update --context TodoItemDbContext
dotnet ef migrations add InitialCreate --context LatersDbContext
dotnet ef database update --context LatersDbContext