Skip to content

Instantly share code, notes, and snippets.

@stevehansen
Created November 18, 2025 10:11
Show Gist options
  • Select an option

  • Save stevehansen/bb3ee35febec1a74f428ee36adf4ff8e to your computer and use it in GitHub Desktop.

Select an option

Save stevehansen/bb3ee35febec1a74f428ee36adf4ff8e to your computer and use it in GitHub Desktop.
#!/usr/bin/env dotnet
#:sdk Microsoft.NET.Sdk.Web
#:property PublishAot=false
#:package [email protected].*
using System.ComponentModel.DataAnnotations;
using Vidyano.Service;
using Vidyano.Service.Repository;
var builder = WebApplication.CreateBuilder(args);
// Configure Vidyano Minimal API - everything in code, no disk files needed!
builder.AddVidyanoMinimal<ProductContext>(vidyano => vidyano
.WithUrls("https://products.dev.localhost:44355")
.WithDefaultAdmin()
.WithSchemaRights() // Grant CRUD rights to Administrators on Product schema
.WithMenuItem("Products") // Add Products query to Home menu
.WithMenuItem("ProductCategories") // Add Product Categories query to Home menu
.WithModel(builder =>
{
var query = builder.GetQuery(nameof(ProductActions.ProductCategory_Products))!;
query.IsHidden = false;
builder.GetPersistentObject("Product." + nameof(ProductCategory))!.Queries.Add(query);
})
);
var app = builder.Build();
app.UseVidyano(app.Environment, app.Configuration);
app.Run();
// Context class - Vidyano will auto-discover entities from properties
public class ProductContext : NullTargetContext
{
private static readonly List<Product> products =
[
new Product { Id = "1", Name = "Widget", Color = "Blue", Category = "1" },
new Product { Id = "2", Name = "Gadget", Color = "Red", Category = "2" },
new Product { Id = "3", Name = "Gizmo", Color = "Green", Category = "1" },
];
private static readonly List<ProductCategory> categories =
[
new ProductCategory { Id = "1", Name = "Tools" },
new ProductCategory { Id = "2", Name = "Electronics" },
];
public ProductContext()
{
Register(products);
Register(categories);
}
public IQueryable<Product> Products => Query<Product>();
public IQueryable<ProductCategory> ProductCategories => Query<ProductCategory>();
public override void AddObject(PersistentObject obj, object entity)
{
if (entity is Product product)
product.Id ??= (products.Count + 1).ToString();
else if (entity is ProductCategory category)
category.Id ??= (categories.Count + 1).ToString();
base.AddObject(obj, entity);
}
public static void Initialize()
{
Console.WriteLine("ProductContext initialized.");
}
}
public class ProductWeb : CustomApiController
{
public override void GetWebsiteContent(WebsiteArgs args)
{
base.GetWebsiteContent(args);
args.Contents = args.Contents.Replace("Product", "Product Demo");
}
}
// Product entity class
public class Product
{
public string Id { get; set; } = null!;
public string Name { get; set; } = string.Empty;
[MaxLength(20)]
public string Color { get; set; } = string.Empty;
[Reference(typeof(ProductCategory))]
public string? Category { get; set; }
[DataType(DataTypes.MultiLineString)]
public string? Remarks { get; set; }
}
// Product actions - Vidyano will auto-wire this
public class ProductActions(ProductContext context) : PersistentObjectActions<ProductContext, Product>(context)
{
public override void OnNew(PersistentObject obj, PersistentObject? parent, Query? query, Dictionary<string, string>? parameters)
{
base.OnNew(obj, parent, query, parameters);
obj.SetAttributeValue("Name", "New Product");
obj.SetAttributeValue("Color", "Blue");
}
public IEnumerable<Product> ProductCategory_Products(CustomQueryArgs args)
{
args.EnsureParent(nameof(ProductCategory));
var categoryId = args.Parent.ObjectId;
return Context.Products.Where(p => p.Category == categoryId);
}
}
public class ProductCategory
{
public string Id { get; set; } = null!;
public string Name { get; set; } = string.Empty;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment