using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading.Tasks;
public class ServiceOptions
public string ConnectionString
public static void Main()
var options = new ServiceOptions{ConnectionString = "Let's pretend that we have one"};
using var context = new CommandDbContext(options);
public interface IUnitOfWork
IRepository<Customer> CustomerRepository
public class Customer : BaseEntity<Guid>
public string CustomerName
public class CommandDbContext : DbContext
private readonly ServiceOptions _serviceOptions;
public virtual DbSet<Customer> Customers
public CommandDbContext(ServiceOptions serviceOptions)
_serviceOptions = serviceOptions;
public CommandDbContext(DbContextOptions<CommandDbContext> options, ServiceOptions serviceOptions): base(options)
_serviceOptions = serviceOptions;
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
if (!optionsBuilder.IsConfigured)
optionsBuilder.UseSqlServer(_serviceOptions.ConnectionString);
protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
public interface IRepository<TEntity, TId> : IQueryable<TEntity> where TEntity : class, IEntity<TId> where TId : IEquatable<TId>
Task<TEntity> GetByIdAsync(TId id);
Task<List<TEntity>> FindAllAsync();
Task<List<TEntity>> FindAllAsync(Expression<Func<TEntity, bool>> predicate);
List<TEntity> FindAll(Expression<Func<TEntity, bool>> predicate);
void Update(TEntity entity);
TEntity Add(TEntity entity);
void AddRange(IEnumerable<TEntity> entities);
TEntity Remove(TEntity entity);
void RemoveRange(Expression<Func<TEntity, bool>> predicate);
void RemoveRange(IEnumerable<TEntity> entities);
Task RemoveRangeAsync(Expression<Func<TEntity, bool>> predicate);
Task RemoveRangeAsync(IEnumerable<TEntity> entities);
Task<int> SaveChangeAsync();
ITransaction BeginTransaction();
void RejectChange(TEntity entity, bool reload = false);
void RejectChanges(bool reload = false);
Task RejectChangeAsync(TEntity entity, bool reload = false);
Task RejectChangesAsync(bool reload = false);
public interface IRepository<TEntity> : IRepository<TEntity, Guid> where TEntity : class, IEntity<Guid>
public interface IEntity<TId> : IEntity
public interface ITransaction : IDisposable
public abstract class BaseEntity<TId> : IEntity<TId>
public override bool Equals(object obj)
return obj is BaseEntity<TId> baseEntity && baseEntity.GetType() == GetType() && Equals(baseEntity.Id, Id);
public override int GetHashCode()
return new MultiKey(GetType(), Id).GetHashCode();
public abstract class BaseEntity : BaseEntity<Guid>
private readonly List<object> _parts;
public MultiKey(params object[] parts)
_parts = new List<object>(parts);
public override int GetHashCode()
_parts.Where(p => null != p).ToList().ForEach(p =>
hash = hash * 31 + p.GetHashCode();
public override bool Equals(object obj)
if (null != obj && obj is MultiKey key)
if (key._parts.Count != _parts.Count)
for (var i = 0; i < _parts.Count; i++)
if (!Equals(_parts[i], key._parts[i]))