Benevia Core

Build production-ready APIs with automatic database migrations and event-driven business logic in minutes, not days.

Benevia Core is a .NET 10 platform that eliminates boilerplate and lets you focus on your domain model and business rules. Define your entities once, and get a fully functional OData API with database schema management automatically.

Why Benevia Core?

Is this platform right for you? When to Use Benevia Core

  • Zero boilerplate API - Define entities, get REST endpoints automatically
  • Schema-first development - Add properties to models, database is created or updated automatically
  • Event-driven by design - Business logic lives where it belongs, not in controllers
  • Production-ready - Multi-tenant, authentication, validation, observability built-in
  • OData powered API - Filtering, sorting, paging, expansion out of the box
  • Client components from entity meta data - Property component types, labels, etc. all come from the entity meta data.

Key Features

1. Model-Driven Development

Your C# models are your API and database schema. Get this automatically when you write the following code.

  • ✅ Database table created
  • ✅ OData API endpoint: GET /odata/Author
  • ✅ Full CRUD operations with filtering: GET /odata/Author?$select=Id,Name,Email&$filter=contains(Name,'John')
  • ✅ Validation and authentication
  • ✅ Database migration automatically takes care of adding new properties or entities.
  • ✅ Meta data for your UI components.
[ApiEntity]
[NaturalKey(nameof(Author.Id))]
public partial class Author : EntityBase
{
    [Required]
    [MaxLength(15)]
    [Property<DataTypes.Text>("Id", Description = "Short Id for the author")]
    public partial string Id { get; set; }

    [Required]
    [Property<DataTypes.ProperNoun>("Name", Description = "The name of the author")]
    public partial string Name { get; set; }

    [Property<DataTypes.MultilineText>("Bio", Description = "The biography of the author")]
    public partial string Bio { get; set; }

    [Property<DataTypes.Email>("Email", Description = "The email address of the author")]
    public partial string Email { get; set; }

    [ReferenceProperty<Blob>("Profile picture", DeleteAction.Cascade, Description = "The profile picture of the author")]
    public virtual partial Blob? ProfilePicture { get; set; }

    [Virtual]
    [Property<DataTypes.Integer>("Article count", Description = "The number of articles that this author wrote")]
    public partial int ArticleCount { get; }

    [Method("Get published articles", MethodType.Read, Description = "Returns published articles for this author")]
    public partial IQueryable<Article> GetPublishedArticles();
}

You get:

  • OData endpoints: GET/POST/PUT/DELETE /odata/Authors
  • Database table with proper schema
  • File upload for profile picture: POST /odata/Authors with blob reference
  • Rich data types with validation (Text, MultilineText, etc.)
  • Relationship navigation automatically

Add a new entity? Create the class with [ApiEntity] - everything else is automatic.

2. Event-Driven Business Logic

No controllers needed. Business rules are defined as logic subscriptions, not scattered across controllers:

[Logic]
public class AuthorBL(Author.Logic author)
{
    [RegisterLogic]
    public void AuthorInfo()
    {
        author.Compute(a => a.Title)
            .From(a => $"{a.Name}({a.Id})")
            .DirtyBy(a => [a.Name, a.Id]);

        author.Compute(a => a.ArticleCount)
            .From(a => a.Articles.Count())
            .DirtyWithRelation(a => a.Articles);
        
        author.Method(a => a.GetPublishedArticles).Do((a, args) =>
        {
            return args.GetEntities<Article>()
                .Where(article => article.Author == a && article.Status == "Published")
                .OrderByDescending(article => article.PublishedDate);
        });

    }
    [RegisterLogic]
    public void AuthorId()
    {
        author.AutoCorrect(a => a.Id)
            .Transform(value => value.ToUpper());

        author.Validate(a => a.Id)
            .RejectIf(value => !string.IsNullOrWhiteSpace(value) && !Regex.IsMatch(value, @"^[A-Z0-9]+$"))
            .WithMessage(a => $"'{a._State.Id.ProposedValue}' is not a valid Id. It may only contain alphanumeric characters.");

        author.OnPreSave(a => a.Id)
            .IfEmpty()
            .From((a, arg) =>
            {
                string id = MakeIdFromName(a.Name);
                return AddSuffixIfDuplicate(id, arg.GetEntities<Author>());
            });
    }

	// ...
}

Business logic is:

  • Reactive: When a property is changed or any CRUD operation is occurs, your app can do business logic.
  • Modular, composable, and extensible: Additional features can add functionality to other feature's objects.
  • Descriptive fluent coding: Code is easy to read
  • Bullet proof code: Validation and reactive changes occur whether they are made in your own business logic or through the API. For example, in the above code, business logic cannot create an author with an invalid Id that has non-alphanumeric characters.

3. OData API created from the model

Full odata API for CRUD operations including filtering, expanding navigation properties, etc. See OData - the Best Way to REST

url GET https://localhost:7086/api/Article?$select=Title,Status,PublishedDate&$filter=Category/Name eq 'Health

{
    "@odata.context": "http://localhost:5205/api/$metadata#Article(Title,Status,PublishedDate)",
    "value": [
        {
            "Title": "10 tips for maintaining a healthy lifestyle. by Dr. Sarah Brown",
            "PublishedDate": "2026-01-21",
            "Status": "Archived"
        },
        {
            "Title": "Heart healthy foods by John Doe",
            "PublishedDate": "2024-08-23",
            "Status": "Draft"
        }
    ]
}

Documentation

Benevia Core documentation

License

A partnership agreement is required to use this platform. Please contact Benevia to obtain access.