The Tenantly Notification Service is a C# background service responsible for handling automated notifications, reminders, and background processing tasks for the Tenantly property rental management system. This service uses Entity Framework Core for data access while the database schema is managed by the Go API project.
- .NET 8: Modern C# runtime with performance improvements
- Entity Framework Core: ORM for database operations with PostgreSQL
- Serilog: Structured logging framework
- Background Services: Hosted services for continuous processing
- PostgreSQL: Database (schema managed by Go API)
- Notification Processing: SMS and email delivery to tenants
- Automated Reminders: Overdue payment notifications
- Background Tasks: Scheduled operations and data processing
- Audit Logging: Activity tracking and compliance
Important: Database migrations and schema management are handled by the Go API project. This service connects to the existing database and uses EF Core for data access only.
- Removed:
DapperandNpgsql(direct) - Added:
Microsoft.EntityFrameworkCore,Microsoft.EntityFrameworkCore.Design,Npgsql.EntityFrameworkCore.PostgreSQL
Created comprehensive EF Core entity models in Models/Entities/:
- User.cs - User entity with proper annotations
- Property.cs - Property entity for managing building properties
- Shop.cs - Shop entity with navigation properties and Property foreign key
- Tenant.cs - Tenant entity with navigation properties
- Lease.cs - Lease entity with foreign key relationships
- Payment.cs - Payment entity with foreign key relationships and enum status
- Attachment.cs - File attachment entity with polymorphic relationships
- NotificationQueue.cs - Updated with EF Core annotations and navigation properties
- DatabaseService.cs - Converted from Dapper to EF Core:
- Uses LINQ queries instead of raw SQL
- Proper async/await patterns
- Include() for related data loading
- Scoped service lifetime
- NotificationWorker.cs - Updated to use scoped services
- ReminderWorker.cs - Updated to use scoped services
- Both now use
IServiceProvider.CreateScope()for database access
- Type Safety: Compile-time checking of queries and IntelliSense support
- Maintainability: LINQ queries are more readable than raw SQL
- Performance: Query optimization by EF Core and connection pooling
- Development Productivity: Code-first approach and automatic model validation
public interface IAuditableEntity
{
DateTime CreatedAt { get; set; }
DateTime UpdatedAt { get; set; }
}All entities requiring automatic timestamp management implement this interface.
The AuditInterceptor class extends SaveChangesInterceptor and automatically:
- Sets
CreatedAtandUpdatedAtfor new entities (EntityState.Added) - Updates
UpdatedAtfor modified entities (EntityState.Modified) - Uses UTC timestamps for consistency
- Provides optional logging for debugging
All entities implement IAuditableEntity:
- User, Property, Shop, Tenant, Lease, Payment, Attachment, NotificationQueue
- Database Independence: Timestamps handled in application code
- Consistent UTC Handling: All timestamps use
DateTime.UtcNow - Centralized Logic: All audit timestamp logic in one interceptor
- Better Debugging: Optional logging support for tracking operations
- Type Safety: Interface-based approach ensures compile-time checking
- Performance: More efficient than database triggers
// Register the interceptor as a service
builder.Services.AddScoped<AuditInterceptor>();
builder.Services.AddDbContext<TenantlyDbContext>((serviceProvider, options) =>
options.UseNpgsql(connectionString)
.AddInterceptors(serviceProvider.GetRequiredService<AuditInterceptor>()));-
Entity Renaming
- Old:
Documententity withDocumentTypeenum - New:
Attachmententity withAttachmentTypeenum
- Old:
-
Database Schema Changes (handled by Go API migrations)
- Table:
documents→attachments - Column:
document_type→attachment_type - Constraint:
CK_Document_SingleEntity→CK_Attachment_SingleEntity - Indexes: All indexes updated to reflect new table and column names
- Table:
-
Entity Updates All entities with Document navigation properties updated:
Property.Documents→Property.AttachmentsShop.Documents→Shop.AttachmentsTenant.Documents→Tenant.AttachmentsLease.Documents→Lease.AttachmentsPayment.Documents→Payment.Attachments
-
DTO Changes
- Old:
DocumentDto,UploadDocumentRequest,UpdateDocumentRequest - New:
AttachmentDto,UploadAttachmentRequest,UpdateAttachmentRequest
- Old:
-
Enum Enhancements The new
AttachmentTypeenum includes additional generic types:// New generic attachment types Image = 60, PDF = 61, Spreadsheet = 62, TextDocument = 63, Other = 99
- More Generic Naming: "Attachment" is more applicable to various file types
- Enhanced Type System: Additional attachment types for better categorization
- Improved Clarity: Better alignment with common file management terminology
- Future Extensibility: Easier to extend with new attachment types
// Creating an Attachment
var attachment = new Attachment
{
FileName = "receipt.pdf",
AttachmentType = AttachmentType.PaymentReceipt,
PaymentId = paymentId,
UploadedBy = userId
};
context.Attachments.Add(attachment);
await context.SaveChangesAsync();
// Querying Attachments
var paymentAttachments = await context.Attachments
.Where(a => a.PaymentId == paymentId)
.ToListAsync();
// Navigation Properties
var property = await context.Properties
.Include(p => p.Attachments)
.FirstOrDefaultAsync(p => p.Id == propertyId);{
"ConnectionStrings": {
"DefaultConnection": "Host=localhost;Database=tenantly;Username=postgres;Password=password"
}
}{
"Notifications": {
"SmsProvider": {
"ApiUrl": "https://api.sms-provider.com",
"ApiKey": "your-api-key"
},
"EmailProvider": {
"SmtpHost": "smtp.gmail.com",
"SmtpPort": 587,
"Username": "your-email@gmail.com",
"Password": "your-password"
}
}
}{
"Serilog": {
"MinimumLevel": "Information",
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "logs/notification-service-.txt",
"rollingInterval": "Day"
}
}
]
}
}- .NET 8 SDK installed
- PostgreSQL database running
- Database schema created by Go API migrations
cd src/backend/notification-service
dotnet restore
dotnet rundotnet publish -c Release
dotnet TenantlyNotificationService.dllThe EF Core models are designed to work with the existing database schema:
- All table names match existing schema (
users,shops,tenants, etc.) - Column names preserved with
[Column]attributes - Foreign key relationships maintained and enhanced
- Indexes preserved through Fluent API configuration
Test the following components:
- Audit Interceptor: Verify timestamps are set correctly
- Database Service: Test LINQ queries and data operations
- Notification Workers: Test background processing logic
- Attachment Operations: Verify CRUD operations work correctly
- Database Connectivity: Ensure EF Core connects to PostgreSQL
- Entity Relationships: Test navigation properties function correctly
- Background Services: Verify workers process notifications correctly
- Cause: Entity doesn't implement
IAuditableEntity - Solution: Ensure entity implements the interface
- Cause: Interceptor not registered in DI container
- Solution: Verify interceptor registration in
Program.cs
- Cause: Incorrect connection string or database not available
- Solution: Check connection string and ensure PostgreSQL is running
- Cause: Attempting to run EF Core migrations
- Solution: Remember that migrations are handled by Go API, not this service
Enable debug logging for specific components:
{
"Logging": {
"LogLevel": {
"TenantlyNotificationService.Data.Interceptors.AuditInterceptor": "Debug",
"TenantlyNotificationService.Services.DatabaseService": "Debug"
}
}
}- File Storage: Consider implementing cloud storage integration
- Versioning: Add support for attachment versioning
- Metadata: Enhance metadata support for different file types
- Security: Implement role-based access control for attachments
- Thumbnails: Add thumbnail generation for image attachments
- Caching: Implement Redis caching for frequently accessed data
- Query Optimization: Add query performance monitoring
- Background Processing: Optimize worker service performance
- Connection Pooling: Fine-tune EF Core connection pooling
- Health Checks: Add comprehensive health check endpoints
- Metrics: Implement application metrics collection
- Distributed Tracing: Add OpenTelemetry support
- Alerting: Set up monitoring and alerting for critical operations