From 6d2a1d32224ea73bdf1a50662aedeb75b15c309a Mon Sep 17 00:00:00 2001 From: Denis Prokhorchik Date: Sat, 22 Jan 2022 16:01:31 +0300 Subject: [PATCH] feat(issue-150): add data layer to e-sender api --- .../e-sender/O2NextGen.ESender.Api.sln | 6 ++ .../Extensions/DatabaseExtensions.cs | 20 ++++++ .../IoC/ServiceCollectionExtensions.cs | 15 ++++- .../e-sender/O2NextGen.ESender.Api/Program.cs | 2 + .../e-sender/O2NextGen.ESender.Api/Startup.cs | 1 + .../O2NextGen.ESender.Api/appsettings.json | 3 +- .../ESenderDbContext.cs | 43 ++++++++++++ .../Entities/MailRequestEntity.cs | 11 ++++ .../20220122133453_InitDatabase.Designer.cs | 47 +++++++++++++ .../Migrations/20220122133453_InitDatabase.cs | 38 +++++++++++ .../ESenderDbContextModelSnapshot.cs | 45 +++++++++++++ .../O2NextGen.ESender.Data.csproj | 14 ++++ .../Mappings/EmailRequestMappings.cs | 33 +++++----- .../O2NextGen.ESender.Impl.csproj | 1 + .../Services/EmailSenderService.cs | 66 +++++++++++++++++++ 15 files changed, 326 insertions(+), 19 deletions(-) create mode 100644 src/Services/e-sender/O2NextGen.ESender.Api/Extensions/DatabaseExtensions.cs create mode 100644 src/Services/e-sender/O2NextGen.ESender.Data/ESenderDbContext.cs create mode 100644 src/Services/e-sender/O2NextGen.ESender.Data/Entities/MailRequestEntity.cs create mode 100644 src/Services/e-sender/O2NextGen.ESender.Data/Migrations/20220122133453_InitDatabase.Designer.cs create mode 100644 src/Services/e-sender/O2NextGen.ESender.Data/Migrations/20220122133453_InitDatabase.cs create mode 100644 src/Services/e-sender/O2NextGen.ESender.Data/Migrations/ESenderDbContextModelSnapshot.cs create mode 100644 src/Services/e-sender/O2NextGen.ESender.Data/O2NextGen.ESender.Data.csproj create mode 100644 src/Services/e-sender/O2NextGen.ESender.Impl/Services/EmailSenderService.cs diff --git a/src/Services/e-sender/O2NextGen.ESender.Api.sln b/src/Services/e-sender/O2NextGen.ESender.Api.sln index 032a97e4..86a4e5ff 100644 --- a/src/Services/e-sender/O2NextGen.ESender.Api.sln +++ b/src/Services/e-sender/O2NextGen.ESender.Api.sln @@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "O2NextGen.ESender.Business" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "O2NextGen.ESender.Impl", "O2NextGen.ESender.Impl\O2NextGen.ESender.Impl.csproj", "{912084D1-1E1A-4170-A345-375621788E06}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "O2NextGen.ESender.Data", "O2NextGen.ESender.Data\O2NextGen.ESender.Data.csproj", "{307B36BA-BFB2-4294-93DB-6C2F3455C06A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -27,6 +29,10 @@ Global {912084D1-1E1A-4170-A345-375621788E06}.Debug|Any CPU.Build.0 = Debug|Any CPU {912084D1-1E1A-4170-A345-375621788E06}.Release|Any CPU.ActiveCfg = Release|Any CPU {912084D1-1E1A-4170-A345-375621788E06}.Release|Any CPU.Build.0 = Release|Any CPU + {307B36BA-BFB2-4294-93DB-6C2F3455C06A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {307B36BA-BFB2-4294-93DB-6C2F3455C06A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {307B36BA-BFB2-4294-93DB-6C2F3455C06A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {307B36BA-BFB2-4294-93DB-6C2F3455C06A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Services/e-sender/O2NextGen.ESender.Api/Extensions/DatabaseExtensions.cs b/src/Services/e-sender/O2NextGen.ESender.Api/Extensions/DatabaseExtensions.cs new file mode 100644 index 00000000..a368a7d1 --- /dev/null +++ b/src/Services/e-sender/O2NextGen.ESender.Api/Extensions/DatabaseExtensions.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using O2NextGen.ESender.Data; + +namespace O2NextGen.ESender.Api.Extensions +{ + internal static class DatabaseExtensions + { + internal static async Task EnsureDbUpdate(this IWebHost host) + { + using (var scope = host.Services.CreateScope()) + { + var context = scope.ServiceProvider.GetRequiredService(); + await context.Database.MigrateAsync(); + } + } + } +} \ No newline at end of file diff --git a/src/Services/e-sender/O2NextGen.ESender.Api/IoC/ServiceCollectionExtensions.cs b/src/Services/e-sender/O2NextGen.ESender.Api/IoC/ServiceCollectionExtensions.cs index 60d66284..4b39f4ec 100644 --- a/src/Services/e-sender/O2NextGen.ESender.Api/IoC/ServiceCollectionExtensions.cs +++ b/src/Services/e-sender/O2NextGen.ESender.Api/IoC/ServiceCollectionExtensions.cs @@ -2,6 +2,7 @@ using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; @@ -10,6 +11,7 @@ using O2NextGen.ESender.Api.Filters; using O2NextGen.ESender.Api.Helpers; using O2NextGen.ESender.Business.Services; +using O2NextGen.ESender.Data; using O2NextGen.ESender.Impl.Services; namespace O2NextGen.ESender.Api.IoC @@ -31,11 +33,20 @@ public static TConfig ConfigurePOCO(this IServiceCollection services, I services.AddSingleton(config); return config; } + + public static IServiceCollection AddConfigEf(this IServiceCollection services, IConfiguration configuration) + { + var connectionString = configuration["ConnectionString"]; + services.AddDbContext(x => + x.UseSqlServer(connectionString)); + return services; + } + public static IServiceCollection AddBusiness(this IServiceCollection services) { - services.AddSingleton(); + // services.AddSingleton(); // Include DataLayer - // services.AddScoped(); + services.AddScoped(); //more business services... services.AddSingleton(); diff --git a/src/Services/e-sender/O2NextGen.ESender.Api/Program.cs b/src/Services/e-sender/O2NextGen.ESender.Api/Program.cs index 73dec053..c2b958e6 100644 --- a/src/Services/e-sender/O2NextGen.ESender.Api/Program.cs +++ b/src/Services/e-sender/O2NextGen.ESender.Api/Program.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; +using O2NextGen.ESender.Api.Extensions; using Serilog; namespace O2NextGen.ESender.Api @@ -29,6 +30,7 @@ public static async Task Main(string[] args) var host = CreateWebHostBuilder(args).Build(); Log.Information($"############### {AppName} ##############"); Log.Information("################# Starting Application #################"); + await host.EnsureDbUpdate(); await host.RunAsync(); Log.Information($"============== {AppName} - state is started ====================="); return 0; diff --git a/src/Services/e-sender/O2NextGen.ESender.Api/Startup.cs b/src/Services/e-sender/O2NextGen.ESender.Api/Startup.cs index a497e640..54d65a6f 100644 --- a/src/Services/e-sender/O2NextGen.ESender.Api/Startup.cs +++ b/src/Services/e-sender/O2NextGen.ESender.Api/Startup.cs @@ -23,6 +23,7 @@ public void ConfigureServices(IServiceCollection services) { services.AddRequiredMvcComponents(); services.AddBusiness(); + services.AddConfigEf(AppConfiguration); services.ConfigurePOCO(AppConfiguration.GetSection("Sender")); } diff --git a/src/Services/e-sender/O2NextGen.ESender.Api/appsettings.json b/src/Services/e-sender/O2NextGen.ESender.Api/appsettings.json index 3c267c67..bd75a77a 100644 --- a/src/Services/e-sender/O2NextGen.ESender.Api/appsettings.json +++ b/src/Services/e-sender/O2NextGen.ESender.Api/appsettings.json @@ -29,6 +29,7 @@ "SmtpServerHost": "localhost", "SmtpServerPort": "25", "From": "support@pfr-centr.com" - } + }, + "ConnectionString": "Server=localhost;Initial Catalog=O2NextGen.ESenderDb;Persist Security Info=False;User ID=sa;Password=your@Password;Connection Timeout=30;" } diff --git a/src/Services/e-sender/O2NextGen.ESender.Data/ESenderDbContext.cs b/src/Services/e-sender/O2NextGen.ESender.Data/ESenderDbContext.cs new file mode 100644 index 00000000..7c8260a6 --- /dev/null +++ b/src/Services/e-sender/O2NextGen.ESender.Data/ESenderDbContext.cs @@ -0,0 +1,43 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using O2NextGen.ESender.Data.Entities; + +namespace O2NextGen.ESender.Data +{ + public class ESenderDbContext: DbContext + { + #region Fields + + public DbSet MailRequests { get; set; } + + #endregion + + #region Ctors + + public ESenderDbContext(DbContextOptions options) + : base(options) + { + } + + #endregion + + #region Configure + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(ConfigureMailRequestEntity); + } + + private void ConfigureMailRequestEntity(EntityTypeBuilder builder) + { + builder.ToTable("MailRequest"); + + builder.Property(ci => ci.Id) + .HasColumnType("bigint") + .ForSqlServerUseSequenceHiLo("mailrequest_hilo") + .IsRequired(); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Services/e-sender/O2NextGen.ESender.Data/Entities/MailRequestEntity.cs b/src/Services/e-sender/O2NextGen.ESender.Data/Entities/MailRequestEntity.cs new file mode 100644 index 00000000..cc0b474e --- /dev/null +++ b/src/Services/e-sender/O2NextGen.ESender.Data/Entities/MailRequestEntity.cs @@ -0,0 +1,11 @@ +namespace O2NextGen.ESender.Data.Entities +{ + public class MailRequestEntity + { + public long Id { get; set; } + public string From { get; set; } + public string To { get; set; } + public string Subject { get; set; } + public string Body { get; set; } + } +} \ No newline at end of file diff --git a/src/Services/e-sender/O2NextGen.ESender.Data/Migrations/20220122133453_InitDatabase.Designer.cs b/src/Services/e-sender/O2NextGen.ESender.Data/Migrations/20220122133453_InitDatabase.Designer.cs new file mode 100644 index 00000000..c6fa414c --- /dev/null +++ b/src/Services/e-sender/O2NextGen.ESender.Data/Migrations/20220122133453_InitDatabase.Designer.cs @@ -0,0 +1,47 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using O2NextGen.ESender.Data; + +namespace O2NextGen.ESender.Data.Migrations +{ + [DbContext(typeof(ESenderDbContext))] + [Migration("20220122133453_InitDatabase")] + partial class InitDatabase + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("Relational:Sequence:.mailrequest_hilo", "'mailrequest_hilo', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("O2NextGen.ESender.Data.Entities.MailRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:HiLoSequenceName", "mailrequest_hilo") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("Body"); + + b.Property("From"); + + b.Property("Subject"); + + b.Property("To"); + + b.HasKey("Id"); + + b.ToTable("MailRequest"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Services/e-sender/O2NextGen.ESender.Data/Migrations/20220122133453_InitDatabase.cs b/src/Services/e-sender/O2NextGen.ESender.Data/Migrations/20220122133453_InitDatabase.cs new file mode 100644 index 00000000..41e1d539 --- /dev/null +++ b/src/Services/e-sender/O2NextGen.ESender.Data/Migrations/20220122133453_InitDatabase.cs @@ -0,0 +1,38 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace O2NextGen.ESender.Data.Migrations +{ + public partial class InitDatabase : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateSequence( + name: "mailrequest_hilo", + incrementBy: 10); + + migrationBuilder.CreateTable( + name: "MailRequest", + columns: table => new + { + Id = table.Column(type: "bigint", nullable: false), + From = table.Column(nullable: true), + To = table.Column(nullable: true), + Subject = table.Column(nullable: true), + Body = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MailRequest", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "MailRequest"); + + migrationBuilder.DropSequence( + name: "mailrequest_hilo"); + } + } +} diff --git a/src/Services/e-sender/O2NextGen.ESender.Data/Migrations/ESenderDbContextModelSnapshot.cs b/src/Services/e-sender/O2NextGen.ESender.Data/Migrations/ESenderDbContextModelSnapshot.cs new file mode 100644 index 00000000..84f30c44 --- /dev/null +++ b/src/Services/e-sender/O2NextGen.ESender.Data/Migrations/ESenderDbContextModelSnapshot.cs @@ -0,0 +1,45 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using O2NextGen.ESender.Data; + +namespace O2NextGen.ESender.Data.Migrations +{ + [DbContext(typeof(ESenderDbContext))] + partial class ESenderDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.2.6-servicing-10079") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("Relational:Sequence:.mailrequest_hilo", "'mailrequest_hilo', '', '1', '10', '', '', 'Int64', 'False'") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("O2NextGen.ESender.Data.Entities.MailRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasAnnotation("SqlServer:HiLoSequenceName", "mailrequest_hilo") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.SequenceHiLo); + + b.Property("Body"); + + b.Property("From"); + + b.Property("Subject"); + + b.Property("To"); + + b.HasKey("Id"); + + b.ToTable("MailRequest"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Services/e-sender/O2NextGen.ESender.Data/O2NextGen.ESender.Data.csproj b/src/Services/e-sender/O2NextGen.ESender.Data/O2NextGen.ESender.Data.csproj new file mode 100644 index 00000000..e3b4623d --- /dev/null +++ b/src/Services/e-sender/O2NextGen.ESender.Data/O2NextGen.ESender.Data.csproj @@ -0,0 +1,14 @@ + + + + netcoreapp2.2 + + + + + + + + + + diff --git a/src/Services/e-sender/O2NextGen.ESender.Impl/Mappings/EmailRequestMappings.cs b/src/Services/e-sender/O2NextGen.ESender.Impl/Mappings/EmailRequestMappings.cs index 36ca2fca..d2f71d06 100644 --- a/src/Services/e-sender/O2NextGen.ESender.Impl/Mappings/EmailRequestMappings.cs +++ b/src/Services/e-sender/O2NextGen.ESender.Impl/Mappings/EmailRequestMappings.cs @@ -1,22 +1,23 @@ using System.Collections.Generic; using O2NextGen.ESender.Business.Models; +using O2NextGen.ESender.Data.Entities; namespace O2NextGen.ESender.Impl.Mappings { - // - // { - // public static EmailRequest ToService(this EmailRequest entity) - // { - // return entity != null ? new EmailRequest() {Id = entity.Id, Name = entity.Name} : null; - // } - // - // public static EmailRequestEntity ToEntity(this EmailRequest model) - // { - // return model != null ? new EmailRequestEntity() {Id = model.Id, Name = model.Name} : null; - // } - // - // public static IReadOnlyCollection - // ToService(this IReadOnlyCollection entities) => - // entities.MapCollection(ToService); - // } + internal static class EmailRequestMappings + { + public static EmailRequest ToService(this MailRequestEntity entity) + { + return entity != null ? new EmailRequest() {Id = entity.Id, From = entity.From, To = entity.To, Body = entity.Body,Subject = entity.Subject} : null; + } + + public static MailRequestEntity ToEntity(this EmailRequest model) + { + return model != null ? new MailRequestEntity() {Id = model.Id, From = model.From, To = model.To, Body = model.Body,Subject = model.Subject} : null; + } + + public static IReadOnlyCollection + ToService(this IReadOnlyCollection entities) => + entities.MapCollection(ToService); + } } \ No newline at end of file diff --git a/src/Services/e-sender/O2NextGen.ESender.Impl/O2NextGen.ESender.Impl.csproj b/src/Services/e-sender/O2NextGen.ESender.Impl/O2NextGen.ESender.Impl.csproj index f3333786..9fafbfec 100644 --- a/src/Services/e-sender/O2NextGen.ESender.Impl/O2NextGen.ESender.Impl.csproj +++ b/src/Services/e-sender/O2NextGen.ESender.Impl/O2NextGen.ESender.Impl.csproj @@ -6,6 +6,7 @@ + diff --git a/src/Services/e-sender/O2NextGen.ESender.Impl/Services/EmailSenderService.cs b/src/Services/e-sender/O2NextGen.ESender.Impl/Services/EmailSenderService.cs new file mode 100644 index 00000000..70cd2cdf --- /dev/null +++ b/src/Services/e-sender/O2NextGen.ESender.Impl/Services/EmailSenderService.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using O2NextGen.ESender.Business.Models; +using O2NextGen.ESender.Business.Services; +using O2NextGen.ESender.Data; +using O2NextGen.ESender.Impl.Mappings; + +namespace O2NextGen.ESender.Impl.Services +{ + public class EmailSenderService : IEmailSenderService + { + #region Fields + + private readonly ESenderDbContext _context; + + #endregion + + + #region Ctors + + public EmailSenderService(ESenderDbContext context) + { + _context = context; + } + + #endregion + + + public async Task> GetAllAsync(CancellationToken ct) + { + var mailRequestEntities = await _context.MailRequests.AsNoTracking().OrderBy(_=>_.Id).ToListAsync(); + return mailRequestEntities.ToService(); + } + + public async Task GetByIdAsync(long id, CancellationToken ct) + { + var entity = await _context.MailRequests.AsNoTracking().SingleAsync(x => x.Id == id, ct); + return entity.ToService(); + } + + public async Task UpdateAsync(EmailRequest certificate, CancellationToken ct) + { + var entityEntry = _context.MailRequests.Update(certificate.ToEntity()); + await _context.SaveChangesAsync(ct); + return entityEntry.Entity.ToService(); + } + + public async Task AddAsync(EmailRequest certificate, CancellationToken ct) + { + var entityEntry = await _context.MailRequests.AddAsync(certificate.ToEntity(), ct); + await _context.SaveChangesAsync(ct); + return entityEntry.Entity.ToService(); + } + + public async Task RemoveAsync(long id, CancellationToken ct) + { + var entityToRemove = await _context.MailRequests.SingleOrDefaultAsync(_ => _.Id == id, ct); + _context.MailRequests.Remove(entityToRemove); + await _context.SaveChangesAsync(ct); + } + + } +} \ No newline at end of file