بلاخره تموم شد

This commit is contained in:
2025-09-01 21:02:10 +04:30
parent 81e43f5b92
commit 746b6159e4
55 changed files with 706 additions and 122 deletions
@@ -5,6 +5,7 @@ using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using VizhTalk.Learning.Application;
using VizhTalk.Learning.Domain.Entities;
namespace VizhTalk.Learning.Infrastructure.Configuration
{
@@ -12,5 +13,6 @@ namespace VizhTalk.Learning.Infrastructure.Configuration
{
public static Assembly Infrastructure = typeof(LearningStartup).Assembly;
public static Assembly Application = typeof(ILearningModule).Assembly;
public static Assembly Domain = typeof(Language).Assembly;
}
}
@@ -1,10 +1,14 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Data;
using VizhTalk.BuildingBlocks.Application.Configuration.Commnad;
using VizhTalk.BuildingBlocks.Application.Configuration.Query;
using VizhTalk.BuildingBlocks.Application.Contract;
using VizhTalk.BuildingBlocks.Domain;
using VizhTalk.BuildingBlocks.Domain.Contract;
using VizhTalk.BuildingBlocks.Infrastructure;
using VizhTalk.Learning.Application;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;
namespace VizhTalk.Learning.Infrastructure.Configuration;
@@ -21,19 +25,34 @@ public class LearningModule(IServiceProvider serviceProvider) : ILearningModule
public async Task<TResult> Execute<TResult>(ICommand<TResult> command, CancellationToken cancellationToken = default)
{
var handlerType = typeof(ICommandHandler<,>).MakeGenericType(command.GetType());
var commandType = command.GetType(); // مثلا CreateUserCommand
var resultType = commandType.GetInterface(typeof(ICommand<>).Name)!.GetGenericArguments()[0];
// حالا handlerType میشه:
var handlerType = typeof(ICommandHandler<,>).MakeGenericType(commandType, resultType);
var handler = serviceProvider.GetRequiredService(handlerType);
dynamic dynamicHandler = handler;
dynamic dynamicCommand = command;
return await dynamicHandler.Handle(dynamicCommand, cancellationToken);
}
public async Task<TResult> Execute<TResult>(IQuery<TResult> query, CancellationToken cancellationToken = default)
{
var handlerType = typeof(ICommandHandler<,>).MakeGenericType(query.GetType());
var queryType = query.GetType();
var resultType = queryType.GetInterface(typeof(IQuery<>).Name)!.GetGenericArguments()[0];
// حالا handlerType میشه:
var handlerType = typeof(IQueryHandler<,>).MakeGenericType(queryType, resultType);
var handler = serviceProvider.GetRequiredService(handlerType);
dynamic dynamicHandler = handler;
dynamic dynamicQuery = query;
return await dynamicHandler.Handle(dynamicQuery, cancellationToken);
}
@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VizhTalk.Learning.Domain.Entities;
namespace VizhTalk.Learning.Infrastructure.EF.Configurations
{
internal class LanguageConfiguration : IEntityTypeConfiguration<Language>
{
public void Configure(EntityTypeBuilder<Language> builder)
{
builder.ToTable(nameof(Language), Schema.Learning);
builder.HasKey(x => x.Id);
builder.Property(x => x.Id)
.ValueGeneratedOnAdd()
.HasConversion(x => x.Value, x => new Domain.TypeIds.LanguageId(x));
builder.Property(x => x.Title)
.HasConversion(s => s.Value, s => new Domain.ValueObjects.LanguageTitle(s))
.HasMaxLength(60);
}
}
}
@@ -1,15 +1,26 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using VizhTalk.BuildingBlocks.Domain;
using VizhTalk.BuildingBlocks.Infrastructure.EF.Helpers;
using VizhTalk.Learning.Infrastructure.Configuration;
namespace VizhTalk.Learning.Infrastructure.EF;
public class LearningDbContext : DbContext
{
public LearningDbContext(DbContextOptions<LearningDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.FindEntities()
modelBuilder.HasDefaultSchema(Schema.Learning);
modelBuilder.FindEntities(Assemblies.Domain)
.ApplyConfigurationsFromAssembly(Assemblies.Infrastructure, x => x.FullName.Contains("EF.Configurations"))
.PluralizeEntities();
}
}
}
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace VizhTalk.Learning.Infrastructure.EF
{
public class Schema
{
public const string Learning = "Learning";
}
}
@@ -1,14 +1,19 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using VizhTalk.BuildingBlocks.Application;
using VizhTalk.BuildingBlocks.Application.Configuration.Commnad;
using VizhTalk.BuildingBlocks.Application.Configuration.Query;
using VizhTalk.BuildingBlocks.Application.Data;
using VizhTalk.BuildingBlocks.Application.Events;
using VizhTalk.BuildingBlocks.Application.Outbox;
using VizhTalk.BuildingBlocks.Infrastructure;
using VizhTalk.BuildingBlocks.Infrastructure.DomainEventsDispatching;
using VizhTalk.Learning.Application;
using VizhTalk.Learning.Application.Mappers;
using VizhTalk.Learning.Infrastructure.Configuration;
using VizhTalk.Learning.Infrastructure.EF;
using VizhTalk.Learning.Infrastructure.Outbox;
namespace VizhTalk.Learning.Infrastructure;
@@ -16,16 +21,21 @@ public static class LearningStartup
{
public static void Initialize(this IServiceCollection services, string connectionString)
{
var domainNotificationsMap = new BiDictionary<string, Type>();
services.AddDbContext<LearningDbContext>(op =>
{
op.UseSqlServer(connectionString);
});
services.AddScoped<DbContext, LearningDbContext>();
services.AddScoped<ILearningModule, LearningModule>();
services.AddScoped<IExecutor, LearningModule>();
services.AddScoped<IUnitOfWorkBase, UnitOfWorkBase>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddScoped<IDomainEventsAccessor, DomainEventsAccessor>();
services.AddScoped<IDomainEventsDispatcher, DomainEventsDispatcher>();
services.AddScoped<IDomainNotificationsMapper, DomainNotificationsMapper>();
services.AddScoped(sp => domainNotificationsMap);
services.Scan(scan => scan.
FromAssemblies(Assemblies.Application, Assemblies.Infrastructure)
.AddClasses(classes => classes.AssignableTo(typeof(ICommandHandler<>)), publicOnly: false)
@@ -40,7 +50,11 @@ public static class LearningStartup
.AddClasses(classes => classes.AssignableTo(typeof(ICommandHandler<>)), publicOnly: false)
.AsImplementedInterfaces()
.WithScopedLifetime());
services.Decorate(typeof(ICommandHandler<,>), typeof(UnitOfWorkCommandHandlerDecorator<,>));
services.Decorate(typeof(ICommandHandler<>), typeof(UnitOfWorkCommandHandlerDecorator<>));
MapperConfig.Process();
services.AddScoped(sa =>
{
var notifications = sa.GetServices<IDomainEventNotification>();
@@ -52,9 +66,16 @@ public static class LearningStartup
});
services.AddScoped<IOutbox>(sa =>
{
return null; // ToDo
return new OutboxAccessor(); // ToDo
});
services.AddScoped<ISqlConnectionFactory>(x => new SqlConnectionFactory(connectionString));
}
public static void UseLearningModule(this IApplicationBuilder app)
{
using var scope = app.ApplicationServices.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<LearningDbContext>();
db.Database.Migrate();
}
}
@@ -0,0 +1,54 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using VizhTalk.Learning.Infrastructure.EF;
#nullable disable
namespace VizhTalk.Learning.Infrastructure.Migrations
{
[DbContext(typeof(LearningDbContext))]
[Migration("20250901155327_Initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("Learning")
.HasAnnotation("ProductVersion", "8.0.19")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("VizhTalk.Learning.Domain.Entities.Language", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.HasKey("Id");
b.ToTable("Languages", "Learning");
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,41 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace VizhTalk.Learning.Infrastructure.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.EnsureSchema(
name: "Learning");
migrationBuilder.CreateTable(
name: "Languages",
schema: "Learning",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(60)", maxLength: 60, nullable: false),
IsDeleted = table.Column<bool>(type: "bit", nullable: false),
IsActive = table.Column<bool>(type: "bit", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Languages", x => x.Id);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Languages",
schema: "Learning");
}
}
}
@@ -0,0 +1,51 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using VizhTalk.Learning.Infrastructure.EF;
#nullable disable
namespace VizhTalk.Learning.Infrastructure.Migrations
{
[DbContext(typeof(LearningDbContext))]
partial class LearningDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("Learning")
.HasAnnotation("ProductVersion", "8.0.19")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("VizhTalk.Learning.Domain.Entities.Language", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
b.Property<bool>("IsActive")
.HasColumnType("bit");
b.Property<bool>("IsDeleted")
.HasColumnType("bit");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(60)
.HasColumnType("nvarchar(60)");
b.HasKey("Id");
b.ToTable("Languages", "Learning");
});
#pragma warning restore 612, 618
}
}
}
@@ -15,12 +15,12 @@ namespace VizhTalk.Learning.Infrastructure.Outbox
}
public void Add(OutboxMessage message)
{
throw new NotImplementedException();
}
public Task Save()
{
throw new NotImplementedException();
return Task.CompletedTask;
}
}
}
@@ -0,0 +1,15 @@
using VizhTalk.BuildingBlocks.Infrastructure;
using VizhTalk.Learning.Domain.Entities;
using VizhTalk.Learning.Domain.Repositories;
using VizhTalk.Learning.Domain.TypeIds;
using VizhTalk.Learning.Infrastructure.EF;
namespace VizhTalk.Learning.Infrastructure.Repositories
{
internal class LanguageRepository : RepositoryBase<Language, LanguageId>, ILanguageRepository
{
public LanguageRepository(LearningDbContext dbContext) : base(dbContext)
{
}
}
}
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using VizhTalk.BuildingBlocks.Infrastructure;
using VizhTalk.BuildingBlocks.Infrastructure.DomainEventsDispatching;
using VizhTalk.Learning.Application;
using VizhTalk.Learning.Domain.Repositories;
using VizhTalk.Learning.Infrastructure.EF;
using VizhTalk.Learning.Infrastructure.Repositories;
namespace VizhTalk.Learning.Infrastructure
{
internal class UnitOfWork(LearningDbContext learningDbContext, IDomainEventsDispatcher domainEventsDispatcher)
: UnitOfWorkBase(learningDbContext, domainEventsDispatcher), IUnitOfWork
{
private ILanguageRepository _language;
public ILanguageRepository Language
{
get
{
if (_language == null)
_language = new LanguageRepository(learningDbContext);
return _language;
}
}
}
}
@@ -6,6 +6,18 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.3.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.19">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.19">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VizhTalk.BuildingBlocks.Infrastructure\VizhTalk.BuildingBlocks.Infrastructure.csproj" />
<ProjectReference Include="..\VizhTalk.Learning.Application\VizhTalk.Learning.Application.csproj" />