diff --git a/PhoneBook.EntityFramework/.gitattributes b/PhoneBook.EntityFramework/.gitattributes new file mode 100644 index 00000000..1ff0c423 --- /dev/null +++ b/PhoneBook.EntityFramework/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/PhoneBook.EntityFramework/.gitignore b/PhoneBook.EntityFramework/.gitignore new file mode 100644 index 00000000..9491a2fd --- /dev/null +++ b/PhoneBook.EntityFramework/.gitignore @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework.sln b/PhoneBook.EntityFramework/PhoneBook.EntityFramework.sln new file mode 100644 index 00000000..26dc30ac --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36811.4 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PhoneBook.EntityFramework", "PhoneBook.EntityFramework\PhoneBook.EntityFramework.csproj", "{C89DECA1-D049-4C08-9610-9048E119087F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C89DECA1-D049-4C08-9610-9048E119087F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C89DECA1-D049-4C08-9610-9048E119087F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C89DECA1-D049-4C08-9610-9048E119087F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C89DECA1-D049-4C08-9610-9048E119087F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6162A30D-5768-44F6-8F83-7524B99A49F2} + EndGlobalSection +EndGlobal diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Contact.cs b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Contact.cs new file mode 100644 index 00000000..403881a3 --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Contact.cs @@ -0,0 +1,14 @@ + + +namespace PhoneBook.EntityFramework +{ + internal class Contact + { + public int Id { get; set; } + public string Name { get; set; } + + public string Email { get; set; } + + public string Phone { get; set; } + } +} diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/ContactController.cs b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/ContactController.cs new file mode 100644 index 00000000..567cdcda --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/ContactController.cs @@ -0,0 +1,62 @@ + + + +using Spectre.Console; + +namespace PhoneBook.EntityFramework +{ + internal class ContactController + { + internal static void AddContact(string name, string email, string phone) + { + + using var db = new ContactsContext(); + db.Add(new Contact { Name = name, Email = email, Phone = phone }); + + db.SaveChanges(); + + } + + internal static void DeleteContact(Contact contact) + { + using var db = new ContactsContext(); + db.Remove(contact); + db.SaveChanges(); + + } + + internal static void UpdateContact(Contact contact) + { + using var db = new ContactsContext(); + db.Update(contact); + + db.SaveChanges(); + + + } + + internal static List ViewAllContacts() + { + using var db = new ContactsContext(); + + var contacts = db.Contacts.ToList(); + //access db, create variable that returns what the method is asking for + //in this case all the contacts in the db in a list + return contacts; + } + internal static Contact ViewContactById(int id) + { + using var db = new ContactsContext(); + var contact = db.Contacts.SingleOrDefault(x => x.Id == id); + //using LINQ to check if contact id matches the id passed into the method. + + //access db, create variable that returns what the method is asking for + //in this caase a single contact + return contact; + } + internal static void ViewContact() + { + throw new NotImplementedException(); + } + } +} diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/ContactService.cs b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/ContactService.cs new file mode 100644 index 00000000..1db86bd7 --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/ContactService.cs @@ -0,0 +1,77 @@ +using Spectre.Console; + +namespace PhoneBook.EntityFramework +{ + internal class ContactService + { + internal static void InsertContact() + { + var name = AnsiConsole.Ask("Contacts Name: "); + + string email; + do + { + email = AnsiConsole.Ask("Contacts Email (format: example@domain.com): "); + if (!Validation.IsValidEmail(email)) + AnsiConsole.MarkupLine("[red]Invalid email. Expected format: example@domain.com[/]"); + } while (!Validation.IsValidEmail(email)); + + string phone; + do + { + phone = AnsiConsole.Ask("Contacts Phone Number (format: +447911123456): "); + if (!Validation.IsValidPhone(phone)) + AnsiConsole.MarkupLine("[red]Invalid phone. Expected format: +447911123456[/]"); + } while (!Validation.IsValidPhone(phone)); + + ContactController.AddContact(name, email, phone); + } + + internal static void DeleteContact() + { + var contact = GetContactOptionInput(); + ContactController.DeleteContact(contact); + } + + internal static void UpdateContact() + { + var contact = GetContactOptionInput(); + + contact.Name = AnsiConsole.Ask("Contacts new Name"); + contact.Email = AnsiConsole.Ask("Contacts new Email"); + contact.Phone = AnsiConsole.Ask("Contacts new Phone"); + + + ContactController.UpdateContact(contact); + } + + internal static void ViewAllContacts() + { + var contacts = ContactController.ViewAllContacts(); + UserInterface.ShowContactTable(contacts); + } + + internal static void ViewContacts() + { + var contact = ContactService.GetContactOptionInput(); + UserInterface.ShowContact(contact); + } + + static private Contact GetContactOptionInput() + { + var contacts = ContactController.ViewAllContacts(); + var contactsArray = contacts.Select(x => x.Name).ToArray(); + var option = AnsiConsole.Prompt(new SelectionPrompt() + .Title("Choose Contact") + .AddChoices(contactsArray)); + + var id = contacts.Single(x => x.Name == option).Id; + var contact = ContactController.ViewContactById(id); + //displays selected name by the id it was given by entity framework + + return contact; + //return contact + + } + } +} diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/ContactsContext.cs b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/ContactsContext.cs new file mode 100644 index 00000000..c2ec8efd --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/ContactsContext.cs @@ -0,0 +1,13 @@ +using Microsoft.EntityFrameworkCore; +namespace PhoneBook.EntityFramework; + + +internal class ContactsContext : DbContext +{ + public DbSet Contacts { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => + + optionsBuilder.UseSqlite($"Data source = contacts.db"); + +} diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Migrations/20260630205537_initial-migration.Designer.cs b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Migrations/20260630205537_initial-migration.Designer.cs new file mode 100644 index 00000000..f4584c9c --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Migrations/20260630205537_initial-migration.Designer.cs @@ -0,0 +1,47 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PhoneBook.EntityFramework; + +#nullable disable + +namespace PhoneBook.EntityFramework.Migrations +{ + [DbContext(typeof(ContactsContext))] + [Migration("20260630205537_initial-migration")] + partial class initialmigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("PhoneBook.EntityFramework.Contact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("contacts"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Migrations/20260630205537_initial-migration.cs b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Migrations/20260630205537_initial-migration.cs new file mode 100644 index 00000000..c530fc7c --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Migrations/20260630205537_initial-migration.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace PhoneBook.EntityFramework.Migrations +{ + /// + public partial class initialmigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "contacts", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: false), + Email = table.Column(type: "TEXT", nullable: false), + Phone = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_contacts", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "contacts"); + } + } +} diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Migrations/ContactsContextModelSnapshot.cs b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Migrations/ContactsContextModelSnapshot.cs new file mode 100644 index 00000000..6381ddb8 --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Migrations/ContactsContextModelSnapshot.cs @@ -0,0 +1,47 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PhoneBook.EntityFramework; + +#nullable disable + +namespace PhoneBook.EntityFramework.Migrations +{ + [DbContext(typeof(ContactsContext))] + partial class ContactsContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("PhoneBook.EntityFramework.Contact", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("contacts"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/PhoneBook.EntityFramework.csproj b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/PhoneBook.EntityFramework.csproj new file mode 100644 index 00000000..6e85393f --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/PhoneBook.EntityFramework.csproj @@ -0,0 +1,20 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Program.cs b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Program.cs new file mode 100644 index 00000000..8e5054c0 --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Program.cs @@ -0,0 +1,57 @@ +using PhoneBook.EntityFramework; +using Spectre.Console; + +var isRunnning = true; + +while (isRunnning) +{ + var option = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("What would you like to do?") + .AddChoices( + MenuOptions.AddContact, + MenuOptions.DeleteContact, + MenuOptions.UpdateContact, + MenuOptions.ViewAllContacts, + MenuOptions.ViewContact, + MenuOptions.Quit + )); + + + switch (option) + { + case MenuOptions.AddContact: + ContactService.InsertContact(); + break; + + case MenuOptions.DeleteContact: + ContactService.DeleteContact(); + break; + + case MenuOptions.UpdateContact: + ContactService.UpdateContact(); + break; + + case MenuOptions.ViewContact: + ContactService.ViewContacts(); + break; + + case MenuOptions.ViewAllContacts: + ContactService.ViewAllContacts(); + break; + + case MenuOptions.Quit: + Environment.Exit(0); + break; + } +} +enum MenuOptions +{ + AddContact, + DeleteContact, + UpdateContact, + ViewContact, + ViewAllContacts, + Quit + +} \ No newline at end of file diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Properties/launchSettings.json b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Properties/launchSettings.json new file mode 100644 index 00000000..745e4f5f --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "PhoneBook.EntityFramework": { + "commandName": "Project", + "workingDirectory": "C:\\Users\\j_men\\source\\repos\\PhoneBook.EntityFramework\\PhoneBook.EntityFramework\\" + } + } +} \ No newline at end of file diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/UserInterface.cs b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/UserInterface.cs new file mode 100644 index 00000000..e66c2f65 --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/UserInterface.cs @@ -0,0 +1,45 @@ +using Spectre.Console; + +namespace PhoneBook.EntityFramework +{ + static internal class UserInterface + { + internal static void ShowContact(Contact contact) + { + var panel = new Panel($@"Id: {contact.Id} +Name: {contact.Name}"); + panel.Header = new PanelHeader("Contact Info"); + panel.Padding = new Padding(2, 2, 2, 2); + + AnsiConsole.Write(panel); + + Console.WriteLine("Press any key to continue."); + Console.ReadLine(); + Console.Clear(); + + } + + static internal void ShowContactTable(List contacts) + { + var table = new Table(); + + table.AddColumn("Id"); + table.AddColumn("Name"); + table.AddColumn("Email"); + table.AddColumn("Number"); + + + foreach (var item in contacts) + { + table.AddRow(item.Id.ToString(), item.Name,item.Email, item.Phone.ToString()); + + } + + Console.WriteLine("Press any key to continue."); + Console.ReadLine(); + Console.Clear(); + + AnsiConsole.Write(table); + } + } +} diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Validation.cs b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Validation.cs new file mode 100644 index 00000000..290a06af --- /dev/null +++ b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/Validation.cs @@ -0,0 +1,18 @@ + +using System.Text.RegularExpressions; + +namespace PhoneBook.EntityFramework +{ + internal class Validation + { + internal static bool IsValidEmail(string email) + { + return Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$"); + } + + internal static bool IsValidPhone(string phone) + { + return Regex.IsMatch(phone, @"^\+?[\d\s\-]{7,15}$"); + } + } +} \ No newline at end of file diff --git a/PhoneBook.EntityFramework/PhoneBook.EntityFramework/contacts.db b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/contacts.db new file mode 100644 index 00000000..4dbfe04d Binary files /dev/null and b/PhoneBook.EntityFramework/PhoneBook.EntityFramework/contacts.db differ diff --git a/README.txt b/README.txt new file mode 100644 index 00000000..b69a485c --- /dev/null +++ b/README.txt @@ -0,0 +1,117 @@ +Phone book - entity framework + + +The flow: +Program.cs +Shows menu loop (switch on MenuOptions enum) + +AddContact → ContactController.AddContact() +Asks for input, opens db, saves new Contact + +ViewAllContacts → ContactController.ViewAllContacts() +Opens db, returns List + +ViewContact/Update/Delete + +ContactService.GetContactOptionInput() +Calls ViewAllContacts() → gets all contacts +Extracts names into array with Select() +Displays names as Spectre menu +User picks name → Single() finds matching contact → extracts Id +Calls ViewContactById(id) → returns Contact object + +ViewContact → displays the contact +UpdateContact → modifies and SaveChanges() +DeleteContact → removes and SaveChanges() + + +DbContext is EF Core's engine. It handles opening connections, tracking changes, generating SQL, executing queries.For our Context class to have these functions it needs to inherit DBContext. + +ContactsContext is my version of that engine, it controls: + +Where the database is (OnConfiguring to connection string) +What tables exist (DbSet Contacts to contacts table) + +Packages Used: +Spectre.Console +Microsoft.EntityFrameworkCore +Microsoft.EntityFrameworkCore.Console + +The GetContactOptionInput + +I first started by creating a menu using a switch for each CRUD operation and used an enumerator to generate types for each option, AddContact, DeleteContact, UpdateContact, ViewAllContacts, ViewContact. If a user chooses one of the menu options they will be taken to the ContactController class method that applies to the option they chose. + +The ContactsContext class is responsible for holding the database string, this class inherits the properties of the DbContext This means ContactsContext gains all the functionality provided by DbContext, such as Managing the database connection and Querying the database. To use the Dbcontexct class I had to install the EntityFrameworkCore package which is responsible for database management, once this was installed ContactsContext could inherit from DbContext. I then created a model class named Contact.cs which is responsible for holding the fields of the records we will collect from the user. + +Below the inheritance I created a method that uses Entity Framework to use a SQLite database stored in a local file named contacts.db, which will be used as the application's data source once the database is created or initialized. I then used the package manager console to add a migration, whilst the onConfiguring method is responsible for setting up the connection, migration is responsible for carrying out those instructions. + +ContactContext.cs +This class is responsible for creating / configuring the database and defining which tables Entity Framework Core manages through DbSet properties. + +Contact.cs +This class is the model, the model class is responsible for the data inside the database table in this case being contact. +Each contact will need to a users ID, Name, Number and Phone. +All fields in this method are string with the exception of the ID field which is an integer. + +UserInterface.cs +This is responsible for listing out all the records in our database. +Spectre.Console is used to list in data in a presentable table instead of generic tables. + +ContactService.cs +The ContactService class is responsible for handling contact selection. + +Before carrying out CRUD operations delete, update or view, the user needs to select a contact from the database. This class handles that selection logic so it doesn't have to be repeated across every method. + +GetContactOptionInput first calls ViewAllContacts to retrieve all contacts from the database. + +It then puts just the names into an array using LINQ's Select and displays them as an interactive menu using Spectre.Console's SelectionPrompt. + +Once the user picks a name, Single searches the contacts list for the matching contact object and extracts its database Id. That Id is then passed to ViewContactById which fetches the full contact record and returns it to the caller. + +DeleteContact uses GetContactOptionInput to let the user select a contact, then passes that contact Object to ContactController.DeleteContact to remove it from the database. + +We do this instead of asking a user to enter their id is because a user shouldn't need to know database Ids. + +Contact Controller.cs +This class is responsible for where the database interactions will live. +This class holds the methods that the users choice from the switch sends them to. + +The addcontact method is designed to ask the user for input for the fields we need to populate which are name, email and phone number. + +Inside the AddContact method, the user is prompted for a name, email and phone number. Once the input is collected, we open a connection to the database by creating an instance of ContactsContext. We then call db.Add(), passing in a new Contact object with the users input assigned to each property using an object initialiser. Nothing is written to the database until db.SaveChanges() is called, which is what actually executes the INSERT. We populate the add function with the properties. + +ListViewAllContacts() +This method is designed to view the contacts in our database. + +Inside the ViewAllContacts method we open the connection to the database by creating an instance of ContactContext. + +Then we create a variable that accesses the contacts from the database and adds them to a list we then return the variable, the point of creating this is to have a variable that returns data to the list, Calling .ToList() on it executes the SELECT query and loads all records into memory as a List. + + +DeleteContact accepts a Contact object as a parameter. +The contact is passed in from GetContactOptionInput which returns the full contact the user selected, it does this by following the flow in the method which displays all contacts in the database, then asks a user to pick one, from the names in the database that name is used to look up the contact object it was given by EF and returned back to us. We open the database, call db.Remove() passing the contact object in from the GetContactOptionInput, and then SaveChanges() executes the DELETE against the database. + +Created DeleteContact in the ContactService class this method is responsible for collecting the names in our database using the GetContactOptionInput method, it collects the names in the database using LINQ and list them in an array which users will choose from in order to delete a contact based on the contact name. The chosen name is used to find the matching contact in the list and retrieve the Id EF already assigned it, that Id is then used to fetch the full contact object which is returned and passed into DeleteContact. + +Created DeleteContact method in contact controller, this takes the contact object passed into it from the contact service class. + +Updated the DeleteContact case in Program.cs to call ContactService.DeleteContact() instead of ContactController.DeleteContact() directly. The controller is only responsible for database operations, + +Updated the DeleteContact case in Program.cs to call ContactService.DeleteContact instead of ContactController.DeleteContact directly. ContactService.DeleteContact is responsible for displaying the contacts to the user and returning the selected contact object. Once we have that object we pass it into ContactController.DeleteContact which carries out the deletion against the database. + + + + + + + + + + + + + + + + +