Changeset d300631


Ignore:
Timestamp:
04/28/26 14:53:32 (2 months ago)
Author:
kikisrbinoska <srbinoskakristina07@…>
Branches:
main
Children:
b373fea
Parents:
dc383dd
Message:

Added transactions and pooling

Files:
1 added
6 edited

Legend:

Unmodified
Added
Removed
  • ChapterX.API/appsettings.json

    rdc383dd rd300631  
    1111    "Issuer": "ChapterX",
    1212    "Audience": "ChapterX"
     13  },
     14  "ConnectionPool": {
     15    "MinPoolSize": 1,
     16    "MaxPoolSize": 20,
     17    "ConnectionIdleLifetime": 300,
     18    "ConnectionPruningInterval": 10,
     19    "CommandTimeout": 30,
     20    "Timeout": 15
    1321  }
    1422}
  • ChapterX.Application/Abstractions/IApplicationDbContext.cs

    rdc383dd rd300631  
    11using Microsoft.EntityFrameworkCore;
     2using Microsoft.EntityFrameworkCore.Storage;
    23using UserEntity = ChapterX.Domain.Entities.User;
    34using StoryEntity = ChapterX.Domain.Entities.Story;
     
    4041        DbSet<NeedApprovalEntity> NeedApprovals { get; }
    4142        Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
     43        Task<IDbContextTransaction> BeginTransactionAsync(CancellationToken cancellationToken = default);
    4244    }
    4345}
  • ChapterX.Application/Auth/RegisterHandler.cs

    rdc383dd rd300631  
     1using ChapterX.Application.Abstractions;
    12using ChapterX.Domain.Repositories;
    23using MediatR;
     
    89        private readonly IUserRepository _userRepository;
    910        private readonly IWriterRepository _writerRepository;
     11        private readonly IApplicationDbContext _context;
    1012
    11         public RegisterHandler(IUserRepository userRepository, IWriterRepository writerRepository)
     13        public RegisterHandler(IUserRepository userRepository, IWriterRepository writerRepository, IApplicationDbContext context)
    1214        {
    1315            _userRepository = userRepository;
    1416            _writerRepository = writerRepository;
     17            _context = context;
    1518        }
    1619
     
    2124                throw new InvalidOperationException("Email already in use.");
    2225
    23             var user = new Domain.Entities.User
     26            await using var transaction = await _context.BeginTransactionAsync(cancellationToken);
     27            try
    2428            {
    25                 Username = request.Username,
    26                 Email = request.Email,
    27                 Name = request.Name,
    28                 Surname = request.Surname,
    29                 Password = BCrypt.Net.BCrypt.HashPassword(request.Password),
    30                 CreatedAt = DateTime.UtcNow,
    31                 UpdatedAt = DateTime.UtcNow
    32             };
     29                var user = new Domain.Entities.User
     30                {
     31                    Username = request.Username,
     32                    Email = request.Email,
     33                    Name = request.Name,
     34                    Surname = request.Surname,
     35                    Password = BCrypt.Net.BCrypt.HashPassword(request.Password),
     36                    CreatedAt = DateTime.UtcNow,
     37                    UpdatedAt = DateTime.UtcNow
     38                };
    3339
    34             await _userRepository.AddAsync(user, cancellationToken);
     40                await _userRepository.AddAsync(user, cancellationToken);
    3541
    36             var writer = new Domain.Entities.Writer { Id = user.Id };
    37             await _writerRepository.AddAsync(writer, cancellationToken);
     42                var writer = new Domain.Entities.Writer { Id = user.Id };
     43                await _writerRepository.AddAsync(writer, cancellationToken);
    3844
    39             return new RegisterResponse(user.Id, user.Username, user.Email);
     45                await transaction.CommitAsync(cancellationToken);
     46                return new RegisterResponse(user.Id, user.Username, user.Email);
     47            }
     48            catch
     49            {
     50                await transaction.RollbackAsync(cancellationToken);
     51                throw;
     52            }
    4053        }
    4154    }
  • ChapterX.Application/Story/Commands/AddHandler.cs

    rdc383dd rd300631  
     1using ChapterX.Application.Abstractions;
    12using ChapterX.Domain.Repositories;
    23using MediatR;
     
    1011        private readonly IGenreRepository _genreRepository;
    1112        private readonly IHasGenreRepository _hasGenreRepository;
     13        private readonly IApplicationDbContext _context;
    1214        private readonly ILogger<AddHandler> _logger;
    1315
    14         public AddHandler(IStoryRepository storyRepository, IGenreRepository genreRepository, IHasGenreRepository hasGenreRepository, ILogger<AddHandler> logger)
     16        public AddHandler(IStoryRepository storyRepository, IGenreRepository genreRepository, IHasGenreRepository hasGenreRepository, IApplicationDbContext context, ILogger<AddHandler> logger)
    1517        {
    1618            _storyRepository = storyRepository;
    1719            _genreRepository = genreRepository;
    1820            _hasGenreRepository = hasGenreRepository;
     21            _context = context;
    1922            _logger = logger;
    2023        }
     
    2225        public async Task<AddResponse> Handle(AddRequest request, CancellationToken cancellationToken)
    2326        {
    24             var story = new Domain.Entities.Story
     27            await using var transaction = await _context.BeginTransactionAsync(cancellationToken);
     28            try
    2529            {
    26                 MatureContent = request.MatureContent,
    27                 ShortDescription = request.ShortDescription,
    28                 Image = request.Image,
    29                 Content = request.Content,
    30                 UserId = request.UserId,
    31                 CreatedAt = DateTime.UtcNow,
    32                 UpdatedAt = DateTime.UtcNow
    33             };
     30                var story = new Domain.Entities.Story
     31                {
     32                    MatureContent = request.MatureContent,
     33                    ShortDescription = request.ShortDescription,
     34                    Image = request.Image,
     35                    Content = request.Content,
     36                    UserId = request.UserId,
     37                    CreatedAt = DateTime.UtcNow,
     38                    UpdatedAt = DateTime.UtcNow
     39                };
    3440
    35             await _storyRepository.AddAsync(story, cancellationToken);
     41                await _storyRepository.AddAsync(story, cancellationToken);
    3642
    37             foreach (var genreName in request.Genres ?? [])
     43                foreach (var genreName in request.Genres ?? [])
     44                {
     45                    var genre = await _genreRepository.GetByNameAsync(genreName, cancellationToken);
     46                    if (genre == null) continue;
     47                    await _hasGenreRepository.AddAsync(new Domain.Entities.HasGenre { StoryId = story.Id, GenreId = genre.Id }, cancellationToken);
     48                }
     49
     50                await transaction.CommitAsync(cancellationToken);
     51                return new AddResponse(story.Id);
     52            }
     53            catch
    3854            {
    39                 var genre = await _genreRepository.GetByNameAsync(genreName, cancellationToken);
    40                 if (genre == null) continue;
    41                 await _hasGenreRepository.AddAsync(new Domain.Entities.HasGenre { StoryId = story.Id, GenreId = genre.Id }, cancellationToken);
     55                await transaction.RollbackAsync(cancellationToken);
     56                throw;
    4257            }
    43 
    44             return new AddResponse(story.Id);
    4558        }
    4659    }
  • ChapterX.Infrastructure/Data/DataContext/ApplicationDbContext.cs

    rdc383dd rd300631  
    22using ChapterX.Domain.Entities;
    33using Microsoft.EntityFrameworkCore;
     4using Microsoft.EntityFrameworkCore.Storage;
    45
    56namespace ChapterX.Infrastructure.Data.DataContext
     
    3132        public DbSet<PermissionLevel> PermissionLevels { get; init; }
    3233
     34        public Task<IDbContextTransaction> BeginTransactionAsync(CancellationToken cancellationToken = default)
     35            => Database.BeginTransactionAsync(cancellationToken);
     36
    3337        protected override void OnModelCreating(ModelBuilder modelBuilder)
    3438        {
     
    4347                e.Property(x => x.Username).HasColumnName("username");
    4448                e.Property(x => x.Email).HasColumnName("email");
    45                 e.Property(x => x.Name).HasColumnName("name");
     49                e.Property(x => x.Name).HasColumnName("user_name");
    4650                e.Property(x => x.Surname).HasColumnName("surname");
    4751                e.Property(x => x.Password).HasColumnName("password");
    48                 e.Property(x => x.CreatedAt).HasColumnName("created_at");
    49                 e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
     52                e.Property(x => x.CreatedAt).HasColumnName("user_created_at");
     53                e.Property(x => x.UpdatedAt).HasColumnName("user_updated_at");
    5054            });
    5155
     
    8690                e.Property(x => x.ShortDescription).HasColumnName("short_description");
    8791                e.Property(x => x.Image).HasColumnName("image");
    88                 e.Property(x => x.Content).HasColumnName("content");
    89                 e.Property(x => x.UserId).HasColumnName("user_id");
    90                 e.Property(x => x.CreatedAt).HasColumnName("created_at");
    91                 e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
     92                e.Property(x => x.Content).HasColumnName("story_content");
     93                e.Property(x => x.UserId).HasColumnName("user_id");
     94                e.Property(x => x.CreatedAt).HasColumnName("story_created_at");
     95                e.Property(x => x.UpdatedAt).HasColumnName("story_updated_at");
    9296                e.HasOne(x => x.Writer).WithMany(w => w.Stories).HasForeignKey(x => x.UserId);
    9397            });
     
    113117                e.Property(x => x.Name).HasColumnName("chapter_name");
    114118                e.Property(x => x.Title).HasColumnName("title");
    115                 e.Property(x => x.Content).HasColumnName("content");
     119                e.Property(x => x.Content).HasColumnName("chapter_content");
    116120                e.Property(x => x.WordCount).HasColumnName("word_count");
    117121                e.Property(x => x.Rating).HasColumnName("rating");
     
    119123                e.Property(x => x.ViewCount).HasColumnName("view_count");
    120124                e.Property(x => x.StoryId).HasColumnName("story_id");
    121                 e.Property(x => x.CreatedAt).HasColumnName("created_at");
    122                 e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
     125                e.Property(x => x.CreatedAt).HasColumnName("chapter_created_at");
     126                e.Property(x => x.UpdatedAt).HasColumnName("chapter_updated_at");
    123127                e.HasOne(x => x.Story).WithMany(s => s.Chapters).HasForeignKey(x => x.StoryId);
    124128            });
     
    130134                e.HasKey(x => x.Id);
    131135                e.Property(x => x.Id).HasColumnName("genre_id");
    132                 e.Property(x => x.Name).HasColumnName("name");
     136                e.Property(x => x.Name).HasColumnName("genre_name");
    133137            });
    134138
     
    153157                e.Property(x => x.UserId).HasColumnName("user_id");
    154158                e.Property(x => x.StoryId).HasColumnName("story_id");
    155                 e.Property(x => x.CreatedAt).HasColumnName("created_at");
     159                e.Property(x => x.CreatedAt).HasColumnName("like_created_at");
    156160                e.HasOne(x => x.User).WithMany(u => u.Likes).HasForeignKey(x => x.UserId);
    157161                e.HasOne(x => x.Story).WithMany(s => s.Likes).HasForeignKey(x => x.StoryId);
     
    164168                e.HasKey(x => x.Id);
    165169                e.Property(x => x.Id).HasColumnName("comment_id");
    166                 e.Property(x => x.Content).HasColumnName("content");
    167                 e.Property(x => x.UserId).HasColumnName("user_id");
    168                 e.Property(x => x.StoryId).HasColumnName("story_id");
    169                 e.Property(x => x.CreatedAt).HasColumnName("created_at");
    170                 e.Property(x => x.UpdatedAt).HasColumnName("updated_at");
     170                e.Property(x => x.Content).HasColumnName("comment_content");
     171                e.Property(x => x.UserId).HasColumnName("user_id");
     172                e.Property(x => x.StoryId).HasColumnName("story_id");
     173                e.Property(x => x.CreatedAt).HasColumnName("comment_created_at");
     174                e.Property(x => x.UpdatedAt).HasColumnName("comment_updated_at");
    171175                e.HasOne(x => x.User).WithMany(u => u.Comments).HasForeignKey(x => x.UserId);
    172176                e.HasOne(x => x.Story).WithMany(s => s.Comments).HasForeignKey(x => x.StoryId);
     
    179183                e.HasKey(x => x.Id);
    180184                e.Property(x => x.Id).HasColumnName("list_id");
    181                 e.Property(x => x.Name).HasColumnName("name");
    182                 e.Property(x => x.Content).HasColumnName("content");
     185                e.Property(x => x.Name).HasColumnName("list_name");
     186                e.Property(x => x.Content).HasColumnName("list_content");
    183187                e.Property(x => x.IsPublic).HasColumnName("is_public");
    184188                e.Property(x => x.UserId).HasColumnName("user_id");
     
    207211                e.HasKey(x => x.Id);
    208212                e.Property(x => x.Id).HasColumnName("notification_id");
    209                 e.Property(x => x.Content).HasColumnName("content");
     213                e.Property(x => x.Content).HasColumnName("notification_content");
    210214                e.Property(x => x.IsRead).HasColumnName("is_read");
    211                 e.Property(x => x.CreatedAt).HasColumnName("created_at");
     215                e.Property(x => x.CreatedAt).HasColumnName("notification_created_at");
    212216                e.Property(x => x.RecipientUserId).HasColumnName("recipient_user_id");
    213217                e.Property(x => x.Type).HasColumnName("type");
     
    249253                e.Property(x => x.SuggestedText).HasColumnName("suggested_text");
    250254                e.Property(x => x.Accepted).HasColumnName("accepted");
    251                 e.Property(x => x.CreatedAt).HasColumnName("created_at");
     255                e.Property(x => x.CreatedAt).HasColumnName("suggestion_created_at");
    252256                e.Property(x => x.AppliedAt).HasColumnName("applied_at");
    253257                e.Property(x => x.StoryId).HasColumnName("story_id");
     
    288292                e.Property(x => x.UserId).HasColumnName("user_id");
    289293                e.Property(x => x.StoryId).HasColumnName("story_id");
    290                 e.Property(x => x.CreatedAt).HasColumnName("created_at");
     294                e.Property(x => x.CreatedAt).HasColumnName("collab_created_at");
    291295                e.HasOne(x => x.User).WithMany(u => u.Collaborations).HasForeignKey(x => x.UserId);
    292296                e.HasOne(x => x.Story).WithMany(s => s.Collaborations).HasForeignKey(x => x.StoryId);
  • ChapterX.Infrastructure/DependencyInjection.cs

    rdc383dd rd300631  
    1515        public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration configuration)
    1616        {
    17             services.AddDbContext<ApplicationDbContext>(options =>
    18                 options.UseNpgsql(configuration.GetConnectionString("Database")));
     17            var poolSection = configuration.GetSection("ConnectionPool");
     18            var minPoolSize = poolSection.GetValue<int>("MinPoolSize", 1);
     19            var maxPoolSize = poolSection.GetValue<int>("MaxPoolSize", 20);
     20            var idleLifetime = poolSection.GetValue<int>("ConnectionIdleLifetime", 300);
     21            var pruningInterval = poolSection.GetValue<int>("ConnectionPruningInterval", 10);
     22            var commandTimeout = poolSection.GetValue<int>("CommandTimeout", 30);
     23            var connectionTimeout = poolSection.GetValue<int>("Timeout", 15);
     24
     25            var baseConnectionString = configuration.GetConnectionString("Database")!;
     26            var connectionString =
     27                $"{baseConnectionString};Minimum Pool Size={minPoolSize};Maximum Pool Size={maxPoolSize};" +
     28                $"Connection Idle Lifetime={idleLifetime};Connection Pruning Interval={pruningInterval};" +
     29                $"Command Timeout={commandTimeout};Timeout={connectionTimeout}";
     30
     31            services.AddDbContextPool<ApplicationDbContext>(options =>
     32                options.UseNpgsql(connectionString));
    1933
    2034            services.AddScoped<IApplicationDbContext>(sp => sp.GetRequiredService<ApplicationDbContext>());
Note: See TracChangeset for help on using the changeset viewer.