Server Setup
This guide covers setting up the ASP.NET Core backend for Neatoo applications. The server handles remote factory operations, persistence, and serves as the authoritative tier for validation and authorization.
NuGet Packages
Install these packages in your ASP.NET Core server project:
| Package | Purpose |
|---|---|
| Neatoo | Core framework: EntityBase, ValidateBase, Base, Rules |
| Neatoo.RemoteFactory | 3-tier factory infrastructure and remote request handling |
dotnet add package Neatoo
dotnet add package Neatoo.RemoteFactory
Your server project should also reference your shared domain model library (containing your Neatoo entities) and any data access libraries (e.g., EF Core DbContext).
Service Registration
Register Neatoo services in your Program.cs using AddNeatooServices():
builder.Services.AddNeatooServices(NeatooFactory.Server, typeof(IPerson).Assembly);
The parameters:
NeatooFactory.Server- Configures factories to execute all operations locally on the server. This is required for handling remote requests from clients.- Assembly parameter(s) - Pass all assemblies containing your Neatoo entities. The framework scans these to register generated factories and serializers.
The Neatoo Endpoint
Neatoo uses a single POST endpoint to handle all remote factory operations. Add this endpoint to your Program.cs:
app.MapPost("/api/neatoo", (HttpContext httpContext, RemoteRequestDto request) =>
{
var handleRemoteDelegateRequest = httpContext.RequestServices
.GetRequiredService<HandleRemoteDelegateRequest>();
return handleRemoteDelegateRequest(request);
});
This single endpoint:
- Receives serialized entities and operation metadata from the client
- Deserializes using Neatoo’s custom serializers
- Routes to the appropriate factory method (Create, Fetch, Insert, Update, Delete)
- Returns the result serialized back to the client
No matter how many entities and factories you have, everything flows through this one endpoint.
EF Core Integration
Register your EF Core DbContext as you normally would:
// Register your DbContext
builder.Services.AddDbContext<PersonDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// Register the DbContext interface used by your entities
builder.Services.AddScoped<IPersonDbContext>(sp => sp.GetRequiredService<PersonDbContext>());
Your factory methods inject the DbContext via the [Service] attribute:
[Remote]
[Fetch]
public async Task<bool> Fetch(
[Service] IPersonDbContext dbContext,
[Service] IPersonPhoneListFactory phoneListFactory)
{
var personEntity = await dbContext.Persons.FindAsync(Id);
if (personEntity == null) return false;
MapFrom(personEntity);
return true;
}
CORS Configuration
For Blazor WebAssembly clients hosted on a different origin, configure CORS:
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins("https://localhost:5002") // Your Blazor client URL
.AllowAnyMethod()
.AllowAnyHeader();
});
});
Apply CORS in the middleware pipeline:
app.UseCors();
For development, you might use a more permissive policy:
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
Complete Program.cs Example
Here is a complete Program.cs for an ASP.NET Core server:
using Microsoft.EntityFrameworkCore;
using Neatoo;
using Neatoo.RemoteFactory;
using MyApp.DomainModel;
using MyApp.Ef;
var builder = WebApplication.CreateBuilder(args);
// Configure CORS for Blazor WASM client
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(policy =>
{
policy.WithOrigins(builder.Configuration["ClientUrl"] ?? "https://localhost:5002")
.AllowAnyMethod()
.AllowAnyHeader();
});
});
// Register Neatoo services with Server factory mode
// Include all assemblies containing your Neatoo entities
builder.Services.AddNeatooServices(
NeatooFactory.Server,
typeof(IPerson).Assembly,
typeof(IOrder).Assembly // Add more assemblies as needed
);
// Register EF Core DbContext
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
// Register DbContext interfaces for injection into factory methods
builder.Services.AddScoped<IPersonDbContext>(sp => sp.GetRequiredService<AppDbContext>());
// Register additional application services
builder.Services.AddScoped<IEmailService, EmailService>();
var app = builder.Build();
// Apply CORS middleware
app.UseCors();
// Add authentication/authorization if needed
app.UseAuthentication();
app.UseAuthorization();
// The single Neatoo endpoint handles all remote factory operations
app.MapPost("/api/neatoo", (HttpContext httpContext, RemoteRequestDto request) =>
{
var handleRemoteDelegateRequest = httpContext.RequestServices
.GetRequiredService<HandleRemoteDelegateRequest>();
return handleRemoteDelegateRequest(request);
});
app.Run();
Authentication and Authorization
Integrating ASP.NET Core Identity
For authenticated endpoints, add authentication middleware before the Neatoo endpoint:
app.UseAuthentication();
app.UseAuthorization();
Passing User Context to Neatoo
To make user information available to your authorization classes, register a user service and populate it from the request:
// Define a user interface for your domain
public interface IUser
{
string? UserId { get; set; }
string? Role { get; set; }
}
public class User : IUser
{
public string? UserId { get; set; }
public string? Role { get; set; }
}
// Register the user service
builder.Services.AddScoped<IUser, User>();
// Populate user from request context with middleware
app.Use(async (context, next) =>
{
var user = context.RequestServices.GetRequiredService<IUser>();
// Populate from claims, headers, or other source
if (context.User.Identity?.IsAuthenticated == true)
{
user.UserId = context.User.FindFirst("sub")?.Value;
user.Role = context.User.FindFirst("role")?.Value;
}
await next();
});
Your authorization classes can then inject IUser:
public class PersonAuth : IAuthorization
{
private readonly IUser _user;
public PersonAuth(IUser user)
{
_user = user;
}
[Authorize(AuthorizeOperation.Create)]
public Authorized CanCreate()
{
return _user.Role == "Admin"
? Authorized.Yes
: Authorized.No("Only administrators can create persons");
}
}
Convention-Based Registration
Neatoo provides RegisterMatchingName() for convention-based service registration. This automatically registers services where the implementation name matches the interface name (minus the “I” prefix):
builder.Services.RegisterMatchingName(
typeof(IPerson).Assembly,
ServiceLifetime.Scoped
);
This would register:
IPersonRule->PersonRuleIEmailService->EmailService- etc.
Middleware Pipeline Order
The order of middleware matters. Here is a recommended order:
var app = builder.Build();
// 1. Exception handling (development vs production)
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/error");
}
// 2. HTTPS redirection
app.UseHttpsRedirection();
// 3. CORS (must be before endpoints)
app.UseCors();
// 4. Authentication and Authorization
app.UseAuthentication();
app.UseAuthorization();
// 5. Custom middleware (user context, etc.)
app.Use(async (context, next) =>
{
// Populate user context
await next();
});
// 6. Endpoints
app.MapPost("/api/neatoo", (HttpContext httpContext, RemoteRequestDto request) =>
{
var handleRemoteDelegateRequest = httpContext.RequestServices
.GetRequiredService<HandleRemoteDelegateRequest>();
return handleRemoteDelegateRequest(request);
});
app.Run();
Troubleshooting
“No factory found for type X”
Ensure:
- The assembly containing the entity is passed to
AddNeatooServices() - The server uses
NeatooFactory.Server(notRemote) - The entity class is
partialand inherits fromEntityBase<T>
Serialization errors
If deserialization fails:
- Verify client and server reference the same domain model assembly version
- Check that all entity properties are serializable
- Ensure custom types have appropriate serializers registered
DbContext not found
If factory methods fail to resolve DbContext:
- Verify DbContext is registered with
AddDbContext<T>() - Register any interface aliases:
AddScoped<IMyDbContext>(sp => sp.GetRequiredService<MyDbContext>())
CORS errors
If the browser blocks requests:
- Verify CORS is configured in services:
builder.Services.AddCors(...) - Apply CORS middleware:
app.UseCors() - Ensure the client origin is in the allowed origins list
- Check that CORS middleware is before the endpoint mapping
Next Steps
- Client Setup - Configure the Blazor client
- Factory Overview - Learn about factory operations
- Authorization Overview - Implement access control