namespace Netina.Repository.Extensions; public class ModelBuilderQueryFilter { public void AddQueryFilterToModelBuilder(ModelBuilder modelBuilder, Type type) { var method = GetType().GetMethod("RegisterQueryFilter").MakeGenericMethod(type); method.Invoke(this, [modelBuilder]); } public void RegisterQueryFilter(ModelBuilder modelBuilder) where TQFilter : ApiEntity { var tt = typeof(TQFilter); if (tt.BaseType == typeof(ApiEntity)) modelBuilder.Entity().HasQueryFilter(e => e.IsRemoved == false); } } public static class ModelBuilderExtensions { /// /// Singularizin table name like Posts to Post or People to Person /// /// public static void AddSingularizingTableNameConvention(this ModelBuilder modelBuilder) { var pluralizer = new Pluralizer(); foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { var tableName = entityType.GetTableName(); entityType.SetTableName(pluralizer.Singularize(tableName)); } } /// /// Set NEWSEQUENTIALID() sql function for all columns named "Id" /// /// /// Set to true if you want only "Identity" guid fields that named "Id" public static void AddSequentialGuidForIdConvention(this ModelBuilder modelBuilder) { foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { var property = entityType.GetProperties() .Where(p => p.Name.Contains("Id", StringComparison.OrdinalIgnoreCase)) .ToArray(); foreach (var mutableProperty in property) modelBuilder.AddDefaultValueSqlConvention(mutableProperty?.Name, typeof(Guid), "gen_random_uuid()"); } } /// /// Set DefaultValueSql for sepecific property name and type /// /// /// Name of property wants to set DefaultValueSql for /// Type of property wants to set DefaultValueSql for /// DefaultValueSql like "NEWSEQUENTIALID()" public static void AddDefaultValueSqlConvention(this ModelBuilder modelBuilder, string propertyName, Type propertyType, string defaultValueSql) { foreach (var entityType in modelBuilder.Model.GetEntityTypes()) { var property = entityType.GetProperties() .SingleOrDefault(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); if (property != null && property.ClrType == propertyType) property.SetDefaultValueSql(defaultValueSql); } } /// /// Set DeleteBehavior.Restrict by default for relations /// /// public static void AddRestrictDeleteBehaviorConvention(this ModelBuilder modelBuilder) { var cascadeFKs = modelBuilder.Model.GetEntityTypes() .SelectMany(t => t.GetForeignKeys()) .Where(fk => !fk.IsOwnership && fk.DeleteBehavior == DeleteBehavior.Cascade); foreach (var fk in cascadeFKs) { fk.DeleteBehavior = DeleteBehavior.Restrict; fk.IsRequired = false; } } /// /// Dynamicaly load all IEntityTypeConfiguration with Reflection /// /// /// Assemblies contains Entities public static void RegisterEntityTypeConfiguration(this ModelBuilder modelBuilder, params Assembly[] assemblies) { var applyGenericMethod = typeof(ModelBuilder) .GetMethods() .First(m => m.Name == nameof(ModelBuilder.ApplyConfiguration)); var types = assemblies.SelectMany(a => a.GetExportedTypes()) .Where(c => c.IsClass && !c.IsAbstract && c.IsPublic); foreach (var type in types) foreach (var iface in type.GetInterfaces()) if (iface.IsConstructedGenericType && iface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)) { var applyConcreteMethod = applyGenericMethod.MakeGenericMethod(iface.GenericTypeArguments[0]); applyConcreteMethod.Invoke(modelBuilder, [Activator.CreateInstance(type)]); } } /// /// Pluralizing table name like Post to Posts or Person to People /// /// public static void AddPluralizingTableNameConvention(this ModelBuilder modelBuilder) { var pluralizer = new Pluralizer(); foreach (var entityType in modelBuilder.Model.GetEntityTypes()) if (entityType.BaseType == null) { var tableName = entityType.GetTableName(); entityType.SetTableName(pluralizer.Pluralize(tableName)); } } /// /// Dynamicaly register all Entities that inherit from specific BaseType /// /// /// Base type that Entities inherit from this /// Assemblies contains Entities public static void RegisterAllEntities(this ModelBuilder modelBuilder, ILogger _logger, params Assembly[] assemblies) where BaseType : ApiEntity { var types = assemblies.SelectMany(a => a.GetExportedTypes()) .Where(c => c.IsClass && !c.IsAbstract && c.IsPublic && typeof(BaseType) .IsAssignableFrom(c)); var builderQueryFilter = new ModelBuilderQueryFilter(); foreach (var type in types) { modelBuilder.Entity(type); builderQueryFilter.AddQueryFilterToModelBuilder(modelBuilder, type); } } /// /// Dynamicaly register all Entities that inherit from specific BaseType /// /// /// Base type that Entities inherit from this /// Assemblies contains Entities public static void RegisterAllEntitiesV02(this ModelBuilder builder, ILogger _logger, params Assembly[] assemblies) where BaseType : ApiEntity { var types = assemblies.SelectMany(a => a.GetExportedTypes()) .Where(c => c.IsClass && !c.IsAbstract && c.IsPublic && typeof(BaseType) .IsAssignableFrom(c)); var builderQueryFilter = new ModelBuilderQueryFilter(); foreach (var type in types) { var stopwatch = new Stopwatch(); stopwatch.Start(); // On Model Creating var onModelCreatingMethod = type.GetMethods().FirstOrDefault(x => x.Name == "OnModelCreating"); if (onModelCreatingMethod != null) onModelCreatingMethod.Invoke(type, [builder]); else { // On Base Model Creating if (type.BaseType == null || type.BaseType != typeof(BaseType)) continue; var baseOnModelCreatingMethod = type.BaseType.GetMethods() .FirstOrDefault(x => x.Name == "OnModelCreating"); if (baseOnModelCreatingMethod == null) continue; baseOnModelCreatingMethod.Invoke(typeof(BaseType), [builder]); } builderQueryFilter.AddQueryFilterToModelBuilder(builder, type); stopwatch.Stop(); _logger.LogInformation($"MODEL BUILDER {type.Name} In : {stopwatch.ElapsedMilliseconds}ms"); } } }