Ticomix.EFCore 2025.12.11.1
Ticomix.EFCore
Purpose Statement
A comprehensive .NET Entity Framework Core extension library that provides enhanced database functionality including automatic audit fields, advanced logging, SQL utilities, and database context management. Built to work with both .NET 6.0 and .NET 8.0 with Entity Framework Core for enterprise applications requiring robust data access patterns and comprehensive logging capabilities.
Core Systems
1. Logging System
Comprehensive database-backed logging with support for error tracking, batch operations, and integration with Microsoft.Extensions.Logging.
2. Audit Fields System
Automatically manages audit fields (AddDate, AddUser, ChangeDate, ChangeUser) for all entities implementing the IAudit or IAuditUtc interfaces.
3. SQL Utilities
Advanced SQL execution utilities for raw queries, stored procedures, and data mapping with async support.
4. Database Schema Management
String length attributes for easy configuration of database string lengths.
Easy overrides of default column types for specific C# data types.
5. Startup and One-Time Scripts
Automated running of sql scripts either every time the application starts or only once.
Core Classes and Interfaces
Database Context
- IDbContext: Core database context interface with user tracking and enhanced functionality
- IErrorLogDbContext: Interface for error logging database context
- DbContextSetup: Automated model configuration for entity properties
- GenericDbSet: Enhanced DbSet with generic operations and key management
Audit System
- AuditHelper: Automatically populates audit fields on entity changes
Logging System
- Logger<TKey>: Default ILogger implementation - Database-backed logger with batch support and error tracking
- ILogger: Interface for logging operations
- LogEntryErrorLevel: Enumeration for log levels (Information, Warning, Error, Debug, etc.)
SQL Utilities
- SqlExtensionMethods: Extension methods for raw SQL execution and data mapping
- SqlSchemaHelper: Database schema inspection and management utilities
- SqlStream: Stream-based SQL data processing
- KeyHelper: Primary key management and generation utilities
Data Context Interfaces - Types referenced by other Core Modules
- IAccountingDbContext: Accounting system database context
- IAttachmentsDbContext: File attachment management context
- ICompanyDbContext: Company data management context
- IEmailNoteDbContext: Email and note management context
- IPrintingDbContext: Printing system context
- IReportingDbContext: Reporting system context
- IWebJobDbContext: Background job management context
- IWorkflowDbContext: Workflow management context
- ICodeMaintenanceDbContext: Code maintenance context
Security and Validation
- Reflection: Reflection utilities for dynamic property access respecting ACL
- ExtensionMethods: Security-related extension methods for setting entity state by ACL
Installation Instructions
Prerequisites
Ensure your application targets:
- .NET 6.0 or .NET 8.0
- Entity Framework Core 6.0+ or 8.0+
- SQL Server 2016+
Configuration
1. Error Logging Setup
Model and DbContext Setup
ErrorLog.cs
public class ErrorLog : IAudit, IErrorLog<int>
{
// Columns omitted for brevity...
}
ApplicationDbContext.cs
public class ApplicationDbContext : DbContext, IDbContext
{
// Rest of class omitted for brevity...
// Error logging support - Required DbSet
public DbSet<ErrorLog> ErrorLog { get; set; }
// ...
}
ApplicationDbContextErrorLog.cs
public partial class ApplicationDbContext : IErrorLogDbContext<int>
{
// Implement IErrorLogDbContext interface for Logger compatibility
GenericDbSet<IErrorLog<int>> IErrorLogDbContext<int>.ErrorLog =>
new GenericDbSet<IErrorLog<int>>(this, this.ErrorLog);
}
Startup.cs - Dependency Injection Setup
// ConfigureServices method
services.AddScoped<IErrorLogDbContext<int>>(provider =>
provider.GetRequiredService<ApplicationDbContext>());
services.AddScoped<ILogger, Ticomix.Common.Helpers.Logger<int>>();
Usage Examples
public class CustomerService
{
private readonly ILogger logger;
private readonly ApplicationDbContext db;
public CustomerService(ILogger logger, ApplicationDbContext db)
{
this.logger = logger;
this.db = db;
}
public async Task<Customer> CreateCustomerAsync(Customer customer)
{
try
{
db.Set<Customer>().Add(customer);
await db.SaveChangesAsync();
logger.LogMessage(LogEntryErrorLevel.Information,
$"Customer created: {customer.Name}");
return customer;
}
catch (Exception ex)
{
logger.LogError(ex);
throw;
}
}
// Batch logging for related operations
public async Task ProcessCustomerBatch(List<Customer> customers)
{
logger.Batch = Guid.NewGuid();
foreach (var customer in customers)
{
try
{
await CreateCustomerAsync(customer);
logger.LogMessageBatch(LogEntryErrorLevel.Information,
$"Processed customer: {customer.Name}");
}
catch (Exception ex)
{
logger.LogErrorBatch(ex);
}
}
}
}
2. Audit Fields Implementation
Entity with Audit Fields
public class Customer : IAudit
{
// Columns omitted for brevity...
// Audit fields
public DateTime? AddDate { get; set; }
public string? AddUser { get; set; }
public DateTime? ChangeDate { get; set; }
public string? ChangeUser { get; set; }
}
Audit Configuration
When used in the SaveChanges or SaveChangesAsync methods, the AuditHelper automatically:
- Sets
AddDateandAddUseron entity creation - Updates
ChangeDateandChangeUseron entity modification - Prevents modification of
AddDateandAddUserafter creation - Supports both
DateTimeandDateTimeOffsettypes
ApplicationDbContext.cs - Audit Helper usage
public class ApplicationDbContext : DbContext, IDbContext
{
// Rest of class omitted for brevity...
public bool SaveChangesAuditEnabled { get; set; } = true;
public override int SaveChanges()
{
try
{
if (SaveChangesAuditEnabled)
{
new AuditHelper().SaveChangesAudit(this, UserName);
}
return base.SaveChanges();
}
catch (DbUpdateException ex)
{
if (ex.InnerException != null)
{
string message = ex.Message + "\r\n" + ex.InnerException.Message;
if (ex.InnerException.InnerException != null)
{
message += "\r\n" + ex.InnerException.InnerException.Message;
}
foreach (var entry in this.ChangeTracker.Entries().Where(a => a.State == EntityState.Added || a.State == EntityState.Modified))
{
string values = "\r\n";
foreach (var prop in entry.CurrentValues.Properties.Select(a => a.Name))
{
values += "\r\n" + prop + ": " + entry.CurrentValues[prop];
}
message += values;
}
throw new DbUpdateException(message, ex);
}
else
{
throw;
}
}
}
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
{
try
{
if (SaveChangesAuditEnabled)
{
new AuditHelper().SaveChangesAudit(this, UserName);
}
return base.SaveChangesAsync(cancellationToken);
}
catch (DbUpdateException ex)
{
if (ex.InnerException != null)
{
string message = ex.Message + "\r\n" + ex.InnerException.Message;
if (ex.InnerException.InnerException != null)
{
message += "\r\n" + ex.InnerException.InnerException.Message;
}
foreach (var entry in this.ChangeTracker.Entries().Where(a => a.State == EntityState.Added || a.State == EntityState.Modified))
{
string values = "\r\n";
foreach (var prop in entry.CurrentValues.Properties.Select(a => a.Name))
{
values += "\r\n" + prop + ": " + entry.CurrentValues[prop];
}
message += values;
}
throw new DbUpdateException(message, ex);
}
else
{
throw;
}
}
}
}
3. SQL Utilities Usage
Raw SQL Queries
public class ReportService
{
private readonly IDbContext db;
public ReportService(IDbContext db)
{
this.db = db;
}
// Execute scalar query
public async Task<int> GetCustomerCountAsync()
{
return await db.ExecuteScalarAsync<int>(
"SELECT COUNT(*) FROM Customers WHERE Active = 1");
}
// Execute mapped query
public async Task<List<CustomerSummary>> GetCustomerSummariesAsync()
{
var results = new List<CustomerSummary>();
await foreach (var item in db.SQLexecAsync<CustomerSummary>(
"SELECT Id, Name, Email, LastOrderDate FROM vw_CustomerSummary"))
{
results.Add(item);
}
return results;
}
// Parameterized queries
public List<Customer> GetCustomersByState(string state)
{
return db.SQLexec<Customer>(
"SELECT * FROM Customers WHERE State = @State",
new Dictionary<string, object?> { { "@State", state } });
}
// Custom mapping
public async Task<List<CustomResult>> GetCustomDataAsync()
{
var results = new List<CustomResult>();
await foreach (var item in db.RawSqlQueryAsync(
"SELECT Col1, Col2, Col3 FROM CustomView",
reader => new CustomResult
{
Value1 = reader.GetString("Col1"),
Value2 = reader.GetInt32("Col2"),
Value3 = reader.GetStringNullable(2) // Nullable string helper
}))
{
results.Add(item);
}
return results;
}
}
4. Database Schema Configuration
String Length Attributes
public class Product
{
public int Id { get; set; }
[StringLengthSmall] // Uses StringLengths.Small
public string Code { get; set; }
[StringLengthMedium] // Uses StringLengths.Medium
public string Name { get; set; }
[StringLengthMax] // Uses maximum length (nvarchar(max))
public string Description { get; set; }
// Default length applied automatically
public string Category { get; set; }
}
5. One-Time Scripts and Startup Scripts
The package supports exexution of one-time scripts and startup scripts:
ApplicationDbContext.cs - One-Time Script History
public class ApplicationDbContext : DbContext, IOneTimeScriptHistoryDbContext
{
public DbSet<OneTimeScriptHistory> OneTimeScriptHistory { get; set; }
// Implementation automatically tracks script execution
}
Startup.cs - add the following lines after migrations are applied
if (!env.IsDevelopment())
{
SqlSchemaHelper.RunStartupScripts(context);
}
SqlSchemaHelper.RunOneTimeScripts(context);
var logger = serviceScope.ServiceProvider.GetService<ILogger>();
logger.Source = "Startup";
logger.LogMessage(LogEntryErrorLevel.Information, "Database updated");
Advanced Features
Custom Schema Setup
public class CustomDbContextSetup : DbContextSetup
{
public CustomDbContextSetup()
{
// Set default string length (default is StringLengths.Large)
DefaultStringLength = 500;
}
// Override property filters for custom behavior
public override bool StringPropertyFilter(ModelBuilderProperty p)
{
// Custom logic for string property configuration
return base.StringPropertyFilter(p) && !p.MutableProperty.Name.EndsWith("Code");
}
// Custom property building
public override void BuildStringProperty(ModelBuilderProperty property)
{
if (property.MutableProperty.Name.EndsWith("Code"))
{
property.PropertyBuilder.HasMaxLength(20).IsUnicode(false);
}
else
{
base.BuildStringProperty(property);
}
}
}
ApplicationDbContext.cs - OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
var dbContextSetup = new DbContextSetup();
dbContextSetup.OnModelCreating(modelBuilder);
}
Schema Inspection
public class SchemaService
{
private readonly IDbContext db;
public SchemaService(IDbContext db)
{
this.db = db;
}
public async Task<List<string>> GetTableColumnsAsync(string tableName)
{
var columns = await db.SQLexecAsync<SysColumn>(
"SELECT name FROM sys.columns WHERE object_id = OBJECT_ID(@TableName)",
new Dictionary<string, object?> { { "@TableName", tableName } });
return await columns.Select(c => c.name).ToListAsync();
}
}
Exception Handling
try
{
await db.SaveChangesAsync();
}
catch (Exception ex)
{
var parser = new EfCoreExceptionParser();
var friendlyMessage = parser.GetUserFriendlyMessage(ex);
logger.LogError(ex);
return BadRequest(friendlyMessage);
}
Showing the top 20 packages that depend on Ticomix.EFCore.
| Packages | Downloads |
|---|---|
|
Ticomix.AspNetCore.Mvc
Package Description
|
221 |
|
Ticomix.AspNetCore.Web
Package Description
|
220 |
|
Ticomix.AspNetCore.Web
Package Description
|
173 |
|
Ticomix.AspNetCore.Mvc
Package Description
|
171 |
|
Ticomix.Attachments.Common
Package Description
|
165 |
|
Ticomix.EmailNote
Package Description
|
165 |
|
Ticomix.AspNetCore.Security.AspNetGroups
Package Description
|
164 |
|
Ticomix.Accounting
Package Description
|
164 |
|
Ticomix.EmailNote.Core
Package Description
|
164 |
|
Ticomix.CodeMaintenance
Package Description
|
163 |
|
Ticomix.Company
Package Description
|
163 |
|
Ticomix.Excel
Package Description
|
163 |
|
Ticomix.Workflow
Package Description
|
163 |
|
Ticomix.TelerikReporting
A .NET library that provides Telerik Reporting integration for web applications, enabling report design, viewing, and management capabilities with support for both Angular and MVC frameworks. Built on Telerik Reporting 18.1.24.709 with enhanced features for enterprise reporting solutions including report subscriptions, versioning, and distributed architecture support.
|
162 |
|
Ticomix.WebJobs
A .NET library that provides comprehensive background job scheduling and execution capabilities for web applications, including support for both scheduled jobs and on-demand background tasks. Built with Quartz.NET for robust job scheduling with distributed locking, progress tracking, and SignalR integration for real-time updates. Ideal for enterprise applications requiring reliable background processing including job management, task execution, and progress monitoring.
|
162 |
|
Ticomix.AspNetCore.Mvc
Package Description
|
120 |
|
Ticomix.AspNetCore.Web
Package Description
|
120 |
|
Ticomix.Attachments.Common
Package Description
|
119 |
|
Ticomix.AspNetCore.Mvc
Package Description
|
118 |
|
Ticomix.EmailNote
Package Description
|
118 |
.NET 8.0
- Ticomix.Common (>= 2025.12.11.1)
- Microsoft.EntityFrameworkCore (>= 8.0.0)
- Microsoft.EntityFrameworkCore.SqlServer (>= 8.0.0)
- Microsoft.Extensions.Configuration.Binder (>= 8.0.0)
| Version | Downloads | Last updated |
|---|---|---|
| 2025.12.11.1 | 2 | 12/11/2025 |
| 2025.12.10.2 | 2 | 12/10/2025 |
| 2025.12.10.1 | 2 | 12/10/2025 |
| 2025.12.9.3 | 6 | 12/9/2025 |
| 2025.12.9.2 | 6 | 12/9/2025 |
| 2025.12.9.1 | 0 | 12/9/2025 |
| 2025.12.8.2 | 1 | 12/8/2025 |
| 2025.12.7.1 | 12 | 12/7/2025 |
| 2025.12.5.2 | 6 | 12/5/2025 |
| 2025.12.5.1 | 1 | 12/5/2025 |
| 2025.12.3.2 | 16 | 12/3/2025 |
| 2025.12.3.1 | 2 | 12/3/2025 |
| 2025.11.25.1 | 1 | 11/25/2025 |
| 2025.11.24.1 | 12 | 11/24/2025 |
| 2025.11.22.1 | 8 | 11/22/2025 |
| 2025.11.21.2 | 3 | 11/22/2025 |
| 2025.11.21.1 | 2 | 11/21/2025 |
| 2025.11.20.4 | 3 | 11/20/2025 |
| 2025.11.20.3 | 2 | 11/20/2025 |
| 2025.11.20.2 | 2 | 11/20/2025 |
| 2025.11.20.1 | 2 | 11/20/2025 |
| 2025.11.18.2 | 4 | 11/18/2025 |
| 2025.11.18.1 | 4 | 11/18/2025 |
| 2025.11.4.1 | 31 | 11/4/2025 |
| 2025.10.31.1 | 13 | 10/31/2025 |
| 2025.10.29.1 | 24 | 10/29/2025 |
| 2025.10.23.2 | 59 | 10/23/2025 |
| 2025.10.23.1 | 63 | 10/23/2025 |
| 2025.10.22.1 | 21 | 10/22/2025 |
| 2025.10.15.1 | 72 | 10/15/2025 |
| 2025.10.9.1 | 14 | 10/10/2025 |
| 2025.10.3.2 | 20 | 10/3/2025 |
| 2025.10.3.1 | 3 | 10/3/2025 |
| 2025.10.1.4 | 24 | 10/1/2025 |
| 2025.10.1.3 | 3 | 10/1/2025 |
| 2025.10.1.2 | 3 | 10/1/2025 |
| 2025.9.24.1 | 33 | 9/24/2025 |
| 2025.9.23.1 | 12 | 9/23/2025 |
| 2025.9.22.2 | 6 | 9/22/2025 |
| 2025.9.17.1 | 15 | 9/17/2025 |
| 2025.9.10.1 | 49 | 9/10/2025 |
| 2025.9.2.3 | 75 | 9/2/2025 |
| 2025.8.19.4 | 5 | 8/19/2025 |
| 2025.8.19.3 | 41 | 8/19/2025 |
| 2025.8.19.2 | 5 | 8/19/2025 |
| 2025.7.25.1 | 221 | 7/25/2025 |
| 2025.7.21.3 | 111 | 7/21/2025 |
| 2025.7.19.2 | 21 | 7/19/2025 |
| 2025.7.16.1 | 42 | 7/16/2025 |
| 2025.7.15.1 | 20 | 7/15/2025 |
| 2025.7.11.2 | 29 | 7/11/2025 |
| 2025.7.2.2 | 8 | 7/2/2025 |
| 2025.6.25.3 | 98 | 6/25/2025 |
| 2025.6.24.1 | 10 | 6/24/2025 |
| 2025.6.23.1 | 11 | 6/23/2025 |
| 2025.6.20.2 | 96 | 6/20/2025 |
| 2025.6.18.1 | 28 | 6/18/2025 |
| 2025.6.16.5 | 25 | 6/16/2025 |
| 2025.6.16.4 | 7 | 6/16/2025 |
| 2025.6.11.2 | 53 | 6/11/2025 |
| 2025.6.5.2 | 56 | 6/5/2025 |
| 2025.6.4.1 | 82 | 6/4/2025 |
| 2025.6.2.1 | 32 | 6/2/2025 |
| 2025.5.22.1 | 115 | 5/22/2025 |
| 2025.5.14.1 | 171 | 5/14/2025 |
| 2025.5.8.2 | 20 | 5/8/2025 |
| 2025.5.2.1 | 74 | 5/2/2025 |
| 2025.5.1.1 | 58 | 5/1/2025 |
| 2025.4.18.12 | 57 | 4/18/2025 |
| 2025.4.15.1 | 97 | 4/15/2025 |
| 2025.4.10.1 | 35 | 4/10/2025 |
| 2025.4.8.4 | 16 | 4/8/2025 |
| 2025.4.4.10 | 9 | 4/4/2025 |
| 2025.4.4.7 | 30 | 4/4/2025 |
| 2025.3.28.1 | 38 | 3/28/2025 |
| 2025.3.25.3 | 75 | 3/25/2025 |
| 2025.3.25.2 | 26 | 3/25/2025 |
| 2025.3.20.1 | 25 | 3/20/2025 |
| 2025.3.19.5 | 19 | 3/19/2025 |
| 2025.3.19.3 | 14 | 3/19/2025 |
| 2025.3.18.5 | 18 | 3/18/2025 |
| 2025.3.18.4 | 14 | 3/18/2025 |
| 2025.3.18.3 | 11 | 3/18/2025 |
| 2025.3.18.2 | 10 | 3/18/2025 |
| 2025.3.12.1 | 39 | 3/12/2025 |
| 2025.3.7.1 | 26 | 3/7/2025 |
| 2025.3.4.1 | 64 | 3/4/2025 |
| 2025.2.17.2 | 40 | 2/17/2025 |
| 2025.2.17.1 | 11 | 2/17/2025 |
| 2025.2.14.2 | 62 | 2/14/2025 |
| 2025.2.7.1 | 26 | 2/7/2025 |
| 2025.2.6.6 | 11 | 2/6/2025 |
| 2025.2.6.1 | 120 | 2/6/2025 |
| 2025.2.5.2 | 19 | 2/6/2025 |
| 2025.2.5.1 | 15 | 2/5/2025 |
| 2025.1.30.1 | 18 | 1/30/2025 |
| 2025.1.29.2 | 14 | 1/29/2025 |
| 2025.1.29.1 | 15 | 1/29/2025 |
| 2025.1.28.2 | 20 | 1/28/2025 |
| 2025.1.28.1 | 47 | 1/28/2025 |
| 2025.1.27.4 | 14 | 1/27/2025 |
| 2025.1.27.3 | 13 | 1/27/2025 |
| 2025.1.27.2 | 18 | 1/27/2025 |
| 2025.1.27.1 | 9 | 1/27/2025 |
| 2025.1.6.1 | 214 | 1/6/2025 |
| 2025.1.2.2 | 86 | 1/2/2025 |
| 2024.12.31.2 | 46 | 12/31/2024 |
| 2024.12.31.1 | 11 | 12/31/2024 |
| 2024.12.30.1 | 15 | 12/30/2024 |
| 2024.12.20.2 | 25 | 12/20/2024 |
| 2024.12.17.18 | 22 | 12/17/2024 |
| 2024.12.17.2 | 16 | 12/17/2024 |
| 2024.12.11.3 | 28 | 12/11/2024 |
| 2024.12.10.1 | 18 | 12/10/2024 |
| 2024.12.5.3 | 22 | 12/5/2024 |
| 2024.12.5.2 | 15 | 12/5/2024 |
| 2024.12.4.10 | 13 | 12/4/2024 |
| 2024.12.4.9 | 12 | 12/4/2024 |
| 2024.11.15.4 | 48 | 11/16/2024 |
| 2024.11.15.1 | 14 | 11/15/2024 |
| 2024.11.6.3 | 35 | 11/6/2024 |
| 2024.11.6.1 | 15 | 11/6/2024 |
| 2024.11.5.6 | 16 | 11/5/2024 |
| 2024.11.5.4 | 12 | 11/5/2024 |
| 2024.10.28.3 | 28 | 10/28/2024 |
| 2024.10.28.2 | 14 | 10/28/2024 |
| 2024.10.28.1 | 13 | 10/28/2024 |
| 2024.10.24.1 | 17 | 10/24/2024 |
| 2024.10.23.1 | 15 | 10/23/2024 |
| 2024.10.17.2 | 21 | 10/17/2024 |
| 2024.10.8.1 | 171 | 10/8/2024 |
| 2024.9.13.1 | 146 | 9/13/2024 |