diff --git a/AbazovApp/AbazovApp.sln b/AbazovApp/AbazovApp.sln index 44da35c..3e003a3 100644 --- a/AbazovApp/AbazovApp.sln +++ b/AbazovApp/AbazovApp.sln @@ -15,6 +15,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AccountsContracts", "Accoun EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AccountsBusinessLogic", "AccountsBusinessLogic\AccountsBusinessLogic.csproj", "{D98A5D9F-A491-4A4B-A4A3-B1F388BAD18D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AccountsDataBaseImplement", "AccountsDataBaseImplement\AccountsDataBaseImplement.csproj", "{ACF95C0D-CEB3-41B9-8B7F-149BEEEE53CE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,10 @@ Global {D98A5D9F-A491-4A4B-A4A3-B1F388BAD18D}.Debug|Any CPU.Build.0 = Debug|Any CPU {D98A5D9F-A491-4A4B-A4A3-B1F388BAD18D}.Release|Any CPU.ActiveCfg = Release|Any CPU {D98A5D9F-A491-4A4B-A4A3-B1F388BAD18D}.Release|Any CPU.Build.0 = Release|Any CPU + {ACF95C0D-CEB3-41B9-8B7F-149BEEEE53CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACF95C0D-CEB3-41B9-8B7F-149BEEEE53CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACF95C0D-CEB3-41B9-8B7F-149BEEEE53CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACF95C0D-CEB3-41B9-8B7F-149BEEEE53CE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AbazovApp/AccountsApp/AccountsApp.csproj b/AbazovApp/AccountsApp/AccountsApp.csproj new file mode 100644 index 0000000..45a05e7 --- /dev/null +++ b/AbazovApp/AccountsApp/AccountsApp.csproj @@ -0,0 +1,17 @@ + + + + WinExe + net6.0-windows + enable + true + enable + + + + + + + + + \ No newline at end of file diff --git a/AbazovApp/AccountsApp/FormMain.Designer.cs b/AbazovApp/AccountsApp/FormMain.Designer.cs new file mode 100644 index 0000000..0318f8b --- /dev/null +++ b/AbazovApp/AccountsApp/FormMain.Designer.cs @@ -0,0 +1,46 @@ +namespace AccountsApp +{ + partial class FormMain + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // FormMain + // + this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Name = "FormMain"; + this.Text = "FormMain"; + this.ResumeLayout(false); + + } + + #endregion + } +} \ No newline at end of file diff --git a/AbazovApp/AccountsApp/FormMain.cs b/AbazovApp/AccountsApp/FormMain.cs new file mode 100644 index 0000000..fec03cc --- /dev/null +++ b/AbazovApp/AccountsApp/FormMain.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace AccountsApp +{ + public partial class FormMain : Form + { + public FormMain() + { + InitializeComponent(); + } + } +} diff --git a/AbazovApp/AccountsApp/FormMain.resx b/AbazovApp/AccountsApp/FormMain.resx new file mode 100644 index 0000000..f298a7b --- /dev/null +++ b/AbazovApp/AccountsApp/FormMain.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/AbazovApp/AccountsApp/Program.cs b/AbazovApp/AccountsApp/Program.cs new file mode 100644 index 0000000..313a10f --- /dev/null +++ b/AbazovApp/AccountsApp/Program.cs @@ -0,0 +1,17 @@ +namespace AccountsApp +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new FormMain()); + } + } +} \ No newline at end of file diff --git a/AbazovApp/AccountsContracts/BindingModels/AccountBindingModel.cs b/AbazovApp/AccountsContracts/BindingModels/AccountBindingModel.cs index df2c21d..07248b7 100644 --- a/AbazovApp/AccountsContracts/BindingModels/AccountBindingModel.cs +++ b/AbazovApp/AccountsContracts/BindingModels/AccountBindingModel.cs @@ -16,5 +16,11 @@ namespace AccountsContracts.BindingModels public string Email { get; set; } = string.Empty; public int Id { get; set; } + + public Dictionary AccountInterests + { + get; + set; + } = new(); } } diff --git a/AbazovApp/AccountsDataBaseImplement/AccountsDataBaseImplement.csproj b/AbazovApp/AccountsDataBaseImplement/AccountsDataBaseImplement.csproj new file mode 100644 index 0000000..5424601 --- /dev/null +++ b/AbazovApp/AccountsDataBaseImplement/AccountsDataBaseImplement.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/AbazovApp/AccountsDataBaseImplement/AccountsDatabase.cs b/AbazovApp/AccountsDataBaseImplement/AccountsDatabase.cs new file mode 100644 index 0000000..8b58018 --- /dev/null +++ b/AbazovApp/AccountsDataBaseImplement/AccountsDatabase.cs @@ -0,0 +1,20 @@ +using AccountsDataBaseImplement.Models; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AccountsDataBaseImplement +{ + public class AccountsDatabase : DbContext + { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + => optionsBuilder.UseNpgsql("Host=localhost;Database=AccountsDB;Username=postgres;Password=postgres"); + + public virtual DbSet Accounts { set; get; } + public virtual DbSet Interests { set; get; } + public virtual DbSet AccountInterests { set; get; } + } +} diff --git a/AbazovApp/AccountsDataBaseImplement/Implements/AccountStorage.cs b/AbazovApp/AccountsDataBaseImplement/Implements/AccountStorage.cs new file mode 100644 index 0000000..c0422ff --- /dev/null +++ b/AbazovApp/AccountsDataBaseImplement/Implements/AccountStorage.cs @@ -0,0 +1,94 @@ +using AccountsContracts.BindingModels; +using AccountsContracts.SearchModels; +using AccountsContracts.StorageContracts; +using AccountsContracts.ViewModels; +using AccountsDataBaseImplement.Models; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AccountsDataBaseImplement.Implements +{ + public class AccountStorage : IAccountStorage + { + public List GetFullList() + { + using var context = new AccountsDatabase(); + return context.Accounts + .Include(x => x.Interests) + .ThenInclude(x => x.Interest) + .Select(x => x.GetViewModel) + .ToList(); + } + public List GetFilteredList(AccountSearchModel model) + { + if (!model.Id.HasValue) + { + return new(); + } + + using var context = new AccountsDatabase(); + return context.Accounts + .Include(x => x.Interests) + .ThenInclude(x => x.Interest) + .Where(x => x.Id == model.Id) + .Select(x => x.GetViewModel) + .ToList(); + } + public AccountViewModel? GetElement(AccountSearchModel model) + { + if (!model.Id.HasValue) + { + return null; + } + + using var context = new AccountsDatabase(); + return context.Accounts + .Include(x => x.Interests) + .ThenInclude(x => x.Interest) + .FirstOrDefault(x => x.Id == model.Id) + ?.GetViewModel; + } + public AccountViewModel? Insert(AccountBindingModel model) + { + var newAccount = Account.Create(model); + if (newAccount == null) + { + return null; + } + using var context = new AccountsDatabase(); + context.Accounts.Add(newAccount); + context.SaveChanges(); + return newAccount.GetViewModel; + } + public AccountViewModel? Update(AccountBindingModel model) + { + using var context = new AccountsDatabase(); + var account = context.Accounts.FirstOrDefault(x => x.Id == model.Id); + if (account == null) + { + return null; + } + account.Update(model); + context.SaveChanges(); + return account.GetViewModel; + } + public AccountViewModel? Delete(AccountBindingModel model) + { + using var context = new AccountsDatabase(); + var element = context.Accounts + .Include(x => x.Interests) + .FirstOrDefault(rec => rec.Id == model.Id); + if (element != null) + { + context.Accounts.Remove(element); + context.SaveChanges(); + return element.GetViewModel; + } + return null; + } + } +} diff --git a/AbazovApp/AccountsDataBaseImplement/Implements/InterestStorage.cs b/AbazovApp/AccountsDataBaseImplement/Implements/InterestStorage.cs new file mode 100644 index 0000000..b22a76e --- /dev/null +++ b/AbazovApp/AccountsDataBaseImplement/Implements/InterestStorage.cs @@ -0,0 +1,83 @@ +using AccountsContracts.BindingModels; +using AccountsContracts.SearchModels; +using AccountsContracts.StorageContracts; +using AccountsContracts.ViewModels; +using AccountsDataBaseImplement.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AccountsDataBaseImplement.Implements +{ + public class InterestStorage : IInterestStorage + { + public List GetFullList() + { + using var context = new AccountsDatabase(); + return context.Interests + .Select(x => x.GetViewModel) + .ToList(); + } + public List GetFilteredList(InterestSearchModel model) + { + if (!model.Id.HasValue) + { + return new(); + } + using var context = new AccountsDatabase(); + return context.Interests + .Where(x => x.Id == model.Id) + .Select(x => x.GetViewModel) + .ToList(); + } + public InterestViewModel? GetElement(InterestSearchModel model) + { + if (!model.Id.HasValue) + { + return null; + } + using var context = new AccountsDatabase(); + return context.Interests + .FirstOrDefault(x => model.Id.HasValue && x.Id == model.Id) + ?.GetViewModel; + } + public InterestViewModel? Insert(InterestBindingModel model) + { + var newInterest = Interest.Create(model); + if (newInterest == null) + { + return null; + } + using var context = new AccountsDatabase(); + context.Interests.Add(newInterest); + context.SaveChanges(); + return newInterest.GetViewModel; + } + public InterestViewModel? Update(InterestBindingModel model) + { + using var context = new AccountsDatabase(); + var component = context.Interests.FirstOrDefault(x => x.Id == model.Id); + if (component == null) + { + return null; + } + component.Update(model); + context.SaveChanges(); + return component.GetViewModel; + } + public InterestViewModel? Delete(InterestBindingModel model) + { + using var context = new AccountsDatabase(); + var element = context.Interests.FirstOrDefault(rec => rec.Id == model.Id); + if (element != null) + { + context.Interests.Remove(element); + context.SaveChanges(); + return element.GetViewModel; + } + return null; + } + } +} diff --git a/AbazovApp/AccountsDataBaseImplement/Models/Account.cs b/AbazovApp/AccountsDataBaseImplement/Models/Account.cs new file mode 100644 index 0000000..6293782 --- /dev/null +++ b/AbazovApp/AccountsDataBaseImplement/Models/Account.cs @@ -0,0 +1,109 @@ +using AccountsContracts.BindingModels; +using AccountsContracts.ViewModels; +using AccountsDataModels.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AccountsDataBaseImplement.Models +{ + public class Account : IAccountModel + { + [Required] + public string Login { get; set; } = string.Empty; + [Required] + public string Password { get; set; } = string.Empty; + [Required] + public string Email { get; set; } = string.Empty; + + public int Id { get; private set; } + + private Dictionary? _accountInterests = null; + [NotMapped] + public Dictionary AccountInterests + { + get + { + if (_accountInterests == null) + { + _accountInterests = Interests + .ToDictionary(recPI => recPI.InterestId, recPI => recPI.Interest as IInterestModel); + } + return _accountInterests; + } + } + [ForeignKey("AccountId")] + public virtual List Interests { get; set; } = new(); + + public static Account? Create(AccountBindingModel? model) + { + if (model == null) + { + return null; + } + return new Account() + { + Id = model.Id, + Login = model.Login, + Password = model.Password, + Email = model.Email, + }; + } + public static Account? Create(AccountViewModel? model) + { + return new Account() + { + Id = model.Id, + Login = model.Login, + Password = model.Password, + Email = model.Email, + }; + } + public void Update(AccountBindingModel? model) + { + if (model == null) + { + return; + } + Login = model.Login; + Password = model.Password; + } + public AccountViewModel GetViewModel => new() + { + Id = Id, + Login = Login, + Password = Password, + Email = Email, + }; + public void UpdateInterests(AccountsDatabase context, AccountBindingModel model) + { + var accountInterests = context.AccountInterests.Where(rec => rec.AccountId == model.Id).ToList(); + if (accountInterests != null && accountInterests.Count > 0) + { + // удалили те, которых нет в модели + context.AccountInterests.RemoveRange(accountInterests.Where(rec => !model.AccountInterests.ContainsKey(rec.InterestId))); + context.SaveChanges(); + // обновили количество у существующих записей + foreach (var updateInterest in accountInterests) + { + model.AccountInterests.Remove(updateInterest.InterestId); + } + var account = context.Accounts.First(x => x.Id == Id); + foreach (var pc in model.AccountInterests) + { + context.AccountInterests.Add(new AccountInterest + { + Account = account, + Interest = context.Interests.First(x => x.Id == pc.Key), + }); + context.SaveChanges(); + } + _accountInterests = null; + } + } + } +} diff --git a/AbazovApp/AccountsDataBaseImplement/Models/AccountInterest.cs b/AbazovApp/AccountsDataBaseImplement/Models/AccountInterest.cs new file mode 100644 index 0000000..6caa59f --- /dev/null +++ b/AbazovApp/AccountsDataBaseImplement/Models/AccountInterest.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AccountsDataBaseImplement.Models +{ + public class AccountInterest + { + public int Id { get; set; } + [Required] + public int AccountId { get; set; } + [Required] + public int InterestId { get; set; } + public virtual Account Account { get; set; } = new(); + public virtual Interest Interest { get; set; } = new(); + } +} diff --git a/AbazovApp/AccountsDataBaseImplement/Models/Interest.cs b/AbazovApp/AccountsDataBaseImplement/Models/Interest.cs new file mode 100644 index 0000000..1548453 --- /dev/null +++ b/AbazovApp/AccountsDataBaseImplement/Models/Interest.cs @@ -0,0 +1,53 @@ +using AccountsContracts.BindingModels; +using AccountsContracts.ViewModels; +using AccountsDataModels.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AccountsDataBaseImplement.Models +{ + public class Interest : IInterestModel + { + public int Id { get; private set; } + [Required] + public string Name { get; private set; } = string.Empty; + + public static Interest? Create(InterestBindingModel? model) + { + if (model == null) + { + return null; + } + return new Interest() + { + Id = model.Id, + Name = model.Name, + }; + } + public static Interest? Create(InterestViewModel? model) + { + return new Interest() + { + Id = model.Id, + Name = model.Name, + }; + } + public void Update(InterestBindingModel? model) + { + if (model == null) + { + return; + } + Name = model.Name; + } + public InterestViewModel GetViewModel => new() + { + Id = Id, + Name = Name, + }; + } +}