Building Blocks DataAccess EntityFramework
$ dotnet add package Indiko.Blocks.DataAccess.EntityFrameworkEntity Framework Core implementation of the Indiko data access abstractions, providing full-featured ORM support for relational databases.
This package provides a complete Entity Framework Core implementation of the Indiko data access layer, supporting SQL Server, PostgreSQL, MySQL, SQLite, and other relational databases.
dotnet add package Indiko.Blocks.DataAccess.EntityFramework
using Indiko.Blocks.DataAccess.EntityFramework;
public class AppDbContext : BaseDbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Order> Orders { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Apply entity configurations
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
}
}
public class Startup : WebStartup
{
public override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
services.UseEntityFrameworkDataAccess<AppDbContext>(options =>
{
options.ConnectionString = Configuration.GetConnectionString("DefaultConnection");
options.DatabaseType = DatabaseType.SqlServer;
});
}
}public class User : BaseEntity<Guid>
{
public string Name { get; set; }
public string Email { get; set; }
public virtual ICollection<Order> Orders { get; set; }
}
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ToTable("Users");
builder.HasKey(u => u.Id);
builder.Property(u => u.Name)
.IsRequired()
.HasMaxLength(200);
builder.Property(u => u.Email)
.IsRequired()
.HasMaxLength(256);
builder.HasIndex(u => u.Email).IsUnique();
builder.HasMany(u => u.Orders)
.WithOne(o => o.User)
.HasForeignKey(o => o.UserId);
}
}public class UserService
{
private readonly IRepository<User, Guid> _userRepository;
private readonly IUnitOfWork _unitOfWork;
public UserService(IRepository<User, Guid> userRepository, IUnitOfWork unitOfWork)
{
_userRepository = userRepository;
_unitOfWork = unitOfWork;
}
public async Task<User> CreateAsync(string name, string email)
{
var user = new User { Name = name, Email = email };
await _userRepository.AddAsync(user);
await _unitOfWork.SaveChangesAsync();
return user;
}
public async Task<User> GetByIdAsync(Guid id)
{
return await _userRepository.ReadByIdAsync(id);
}
public async Task<IEnumerable<User>> GetActiveUsersAsync()
{
return await _userRepository.ReadManyByQueryAsync(u => u.Status == Status.Active);
}
}// Load user with orders
var user = await _userRepository.ReadByIdAsync(
userId,
asNotracking: false,
includes: u => u.Orders
);
// Load user with nested navigation properties
var user = await _userRepository.ReadByIdAsync(
userId,
asNotracking: true,
includes: new Expression<Func<User, object>>[]
{
u => u.Orders,
u => u.Orders.Select(o => o.Items)
}
);
// Using string-based includes
var users = await _userRepository.ReadManyByQueryWithIncludesAsync(
where: u => u.IsActive,
asNotracking: true,
includes: "Orders", "Orders.Items", "Profile"
);// Select specific properties
var userDtos = await _userRepository.ReadByQueryWithSelectorAsync(
where: u => u.IsActive,
selector: u => new UserDto
{
Id = u.Id,
Name = u.Name,
Email = u.Email,
OrderCount = u.Orders.Count
}
);
// Paged projection
var pagedUserNames = await _userRepository.ReadByQueryWithSelectorPagedAsync(
where: u => u.IsActive,
selector: u => u.Name,
page: 1,
pageSize: 50
);public async Task ProcessOrderAsync(Order order)
{
await _unitOfWork.BeginTransactionAsync();
try
{
var orderRepo = _manager.GetRepository<Order, Guid>();
var inventoryRepo = _manager.GetRepository<InventoryItem, Guid>();
// Add order
await orderRepo.AddAsync(order);
// Update inventory
foreach (var item in order.Items)
{
var inventoryItem = await inventoryRepo.ReadByIdAsync(item.ProductId);
inventoryItem.Quantity -= item.Quantity;
await inventoryRepo.UpdateAsync(inventoryItem);
}
await _unitOfWork.SaveChangesAsync();
await _unitOfWork.CommitTransactionAsync();
}
catch
{
await _unitOfWork.RollbackTransactionAsync();
throw;
}
}{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MyApp;Trusted_Connection=True;"
},
"DataAccess": {
"DatabaseType": "SqlServer",
"EnableSensitiveDataLogging": false,
"CommandTimeout": 30,
"MaxRetryCount": 3,
"EnableDetailedErrors": false
}
}services.UseEntityFrameworkDataAccess<AppDbContext>(options =>
{
options.ConnectionString = Configuration.GetConnectionString("DefaultConnection");
options.DatabaseType = DatabaseType.SqlServer;
options.EnableSensitiveDataLogging = env.IsDevelopment();
options.CommandTimeout = 60;
});dotnet ef migrations add InitialCreate --context AppDbContextdotnet ef database update --context AppDbContextdotnet ef migrations script --context AppDbContext --output migration.sqlpublic class AppDbContextFactory : BaseDbContextDesignTimeFactory<AppDbContext>
{
protected override AppDbContext CreateDbContext(DbContextOptions<AppDbContext> options)
{
return new AppDbContext(options);
}
}Entities inheriting from BaseEntity support soft delete:
// Soft delete (sets Status = Deleted)
await _userRepository.DeleteAsync(userId, useSoftDelete: true);
// Hard delete (removes from database)
await _userRepository.DeleteAsync(userId, useSoftDelete: false);
// Query filters automatically exclude soft-deleted entities
var activeUsers = await _userRepository.ReadAllAsync(); // Excludes deleted// For read-only scenarios
var users = await _userRepository.ReadManyByQueryAsync(
where: u => u.IsActive,
asNotracking: true // Faster, no change tracking overhead
);var query = _userRepository.AsQueryable()
.Where(u => u.IsActive)
.Include(u => u.Orders)
.ThenInclude(o => o.Items)
.OrderByDescending(u => u.CreatedAt)
.Take(10);
var users = await query.ToListAsync();// More efficient than individual operations
await _userRepository.AddRangeAsync(users);
await _unitOfWork.SaveChangesAsync();Indiko.Blocks.DataAccess.AbstractionsMicrosoft.EntityFrameworkCore (9.0+)Microsoft.EntityFrameworkCore.RelationalSee LICENSE file in the repository root.
Indiko.Blocks.DataAccess.Abstractions - Core data access abstractionsIndiko.Blocks.DataAccess.MongoDb - MongoDB implementationIndiko.Blocks.DataAccess.Marten - Event sourcing with MartenMicrosoft.EntityFrameworkCore.SqlServer - SQL Server providerNpgsql.EntityFrameworkCore.PostgreSQL - PostgreSQL provider