diff --git a/csharp-intermediate-topics/DataTableOverview/DataTableOverview/DataTableOverview.csproj b/csharp-intermediate-topics/DataTableOverview/DataTableOverview/DataTableOverview.csproj index 48284b519e..1bf2c3bf8b 100644 --- a/csharp-intermediate-topics/DataTableOverview/DataTableOverview/DataTableOverview.csproj +++ b/csharp-intermediate-topics/DataTableOverview/DataTableOverview/DataTableOverview.csproj @@ -2,14 +2,11 @@ Exe - net7.0 + net10.0 enable enable - - - PreserveNewest diff --git a/csharp-intermediate-topics/DataTableOverview/DataTableOverview/Program.cs b/csharp-intermediate-topics/DataTableOverview/DataTableOverview/Program.cs index 87ae8054ad..2e21b00cbb 100644 --- a/csharp-intermediate-topics/DataTableOverview/DataTableOverview/Program.cs +++ b/csharp-intermediate-topics/DataTableOverview/DataTableOverview/Program.cs @@ -9,10 +9,12 @@ public static void Main(string[] args) var dt = CreateMainDataTable(); PopulateDataTable(dt); + AddPrimaryKey(dt); + PrintConstraints(dt); PrintData(dt); DataTable dtClone = dt.Clone(); - PrintColumns(dtClone); + PrintColumns(dtClone); DataTable dtCopy = dt.Copy(); PrintData(dtCopy); @@ -20,6 +22,8 @@ public static void Main(string[] args) AddNewRow(dt); FilterDataTable(dt); + PrintTable(dt); + SalesDataProcessor.ProcessSales(); } @@ -32,45 +36,41 @@ public static DataTable CreateMainDataTable() dt.Columns.Add("Date", typeof(DateTime)); dt.Columns.Add("Price", typeof(double)); - // add primary key - var pkCol = new DataColumn("Key", typeof(int)); - dt.Columns.Add(pkCol); - - var pkCols = new DataColumn[1]; - pkCols[0] = pkCol; - - dt.PrimaryKey = pkCols; - return dt; } public static void PopulateDataTable(DataTable dt) { - // add row // 1) declare and instantiate row - DataRow row; - row = dt.NewRow(); + DataRow row = dt.NewRow(); // 2) set column values row["Ticker"] = "MSFT"; row["Date"] = new DateTime(2023, 3, 3); - row["Price"] = 255.29; - row["Key"] = 0; + row["Price"] = 255.29; // by Ordinal/index row[0] = "MSFT"; row[1] = new DateTime(2023, 3, 3); row[2] = 255.29; - row[3] = 0; // 3) add populated row to table dt.Rows.Add(row); } + public static void AddPrimaryKey(DataTable dt) + { + var pkCol = new DataColumn("Key", typeof(int)); + dt.Columns.Add(pkCol); + + dt.Rows[0]["Key"] = 0; + + dt.PrimaryKey = [pkCol]; + } + public static void AddNewRow(DataTable dt) { - DataRow row; - row = dt.NewRow(); + DataRow row = dt.NewRow(); row["Ticker"] = "AMZN"; row["Date"] = new DateTime(2023, 3, 6); @@ -80,6 +80,11 @@ public static void AddNewRow(DataTable dt) dt.Rows.Add(row); } + public static void AddNewRowFromValues(DataTable dt) + { + dt.Rows.Add("AMZN", new DateTime(2023, 3, 6), 93.75, 1); + } + public static void PrintConstraints(DataTable dt) { foreach (Constraint constraint in dt.Constraints) @@ -124,5 +129,21 @@ public static void PrintData(DataTable dt) } } } + + public static void PrintTable(DataTable dt) + { + var header = dt.Columns.Cast().Select(col => col.ColumnName); + Console.WriteLine(string.Join(" | ", header)); + + foreach (DataRow row in dt.Rows) + Console.WriteLine(string.Join(" | ", row.ItemArray)); + } + + public static IEnumerable GetTickersBelow(DataTable dt, double price) + { + return dt.AsEnumerable() + .Where(row => row.Field("Price") < price) + .Select(row => row.Field("Ticker")); + } } -} \ No newline at end of file +} diff --git a/csharp-intermediate-topics/DataTableOverview/DataTableOverview/SalesDataProcessor.cs b/csharp-intermediate-topics/DataTableOverview/DataTableOverview/SalesDataProcessor.cs index 15c93de76d..627081d66f 100644 --- a/csharp-intermediate-topics/DataTableOverview/DataTableOverview/SalesDataProcessor.cs +++ b/csharp-intermediate-topics/DataTableOverview/DataTableOverview/SalesDataProcessor.cs @@ -16,21 +16,21 @@ public static void ProcessSales() public static DataTable CsvToDataTable(string filePath) { - DataTable dtRes = new DataTable(); + var dtRes = new DataTable(); using (var parser = new TextFieldParser(filePath)) { parser.TextFieldType = FieldType.Delimited; parser.TrimWhiteSpace = true; parser.SetDelimiters(","); - var columns = parser.ReadFields(); + var columns = parser.ReadFields() ?? []; foreach (var col in columns) dtRes.Columns.Add(col); while (!parser.EndOfData) { - var row = parser.ReadFields(); + var row = parser.ReadFields() ?? []; dtRes.LoadDataRow(row, true); } } diff --git a/csharp-intermediate-topics/DataTableOverview/DataTableOverview/vegetable-sales.csv b/csharp-intermediate-topics/DataTableOverview/DataTableOverview/vegetable-sales.csv index 3842b2ebbf..6f622e0a21 100644 --- a/csharp-intermediate-topics/DataTableOverview/DataTableOverview/vegetable-sales.csv +++ b/csharp-intermediate-topics/DataTableOverview/DataTableOverview/vegetable-sales.csv @@ -5,7 +5,7 @@ Pepper,2,2,4 Cucumber,4,1,4 Lettuce,6,0.5,3 Asparagus,2,2.5,5 -Carrot,1,3,1 +Carrot,1,3,3 Broccoli,2,0.75,1.5 Pepper,4,2,8 Cucumber,7,1,7 diff --git a/csharp-intermediate-topics/DataTableOverview/Tests/ProgramTests.cs b/csharp-intermediate-topics/DataTableOverview/Tests/ProgramTests.cs index 38216d363e..05499fef7c 100644 --- a/csharp-intermediate-topics/DataTableOverview/Tests/ProgramTests.cs +++ b/csharp-intermediate-topics/DataTableOverview/Tests/ProgramTests.cs @@ -5,13 +5,23 @@ namespace Tests { + [Collection("Console output")] public class ProgramTests { + private static DataTable CreateKeyedTable() + { + var dt = Program.CreateMainDataTable(); + Program.PopulateDataTable(dt); + Program.AddPrimaryKey(dt); + + return dt; + } + [Fact] public static void WhenCreateMainDataTable_ThenColumnsPopulateCorrectly() { var dt = Program.CreateMainDataTable(); - var expectedColumns = new string[] { "Ticker", "Date", "Price", "Key" }; + var expectedColumns = new string[] { "Ticker", "Date", "Price" }; var actualColumns = dt.Columns.Cast() .Select(x => x.ColumnName) @@ -21,13 +31,25 @@ public static void WhenCreateMainDataTable_ThenColumnsPopulateCorrectly() } [Fact] - public static void WhenCreateMaindataTable_ThenPrimaryKeyEqualsKey() + public static void WhenAddPrimaryKey_ThenPrimaryKeyEqualsKey() { - var dt = Program.CreateMainDataTable(); + var dt = CreateKeyedTable(); var pkActual = dt.PrimaryKey; Assert.Equal("Key", pkActual[0].Caption); + Assert.Equal(0, dt.Rows[0]["Key"]); + } + + [Fact] + public static void WhenPrimaryKeySetOnColumnWithEmptyValues_ThenDataExceptionThrown() + { + var dt = Program.CreateMainDataTable(); + Program.PopulateDataTable(dt); + var pkCol = new DataColumn("Key", typeof(int)); + dt.Columns.Add(pkCol); + + Assert.Throws(() => dt.PrimaryKey = [pkCol]); } [Fact] @@ -43,17 +65,36 @@ public static void WhenPopulateDataTable_ThenDataTableContainsMsftRecord() [Fact] public static void WhenAddNewRow_ThenDataTableContainsAmznRecord() { - var dt = Program.CreateMainDataTable(); + var dt = CreateKeyedTable(); Program.AddNewRow(dt); - Assert.Contains("AMZN", Convert.ToString(dt.Rows[0][0])); + Assert.Contains("AMZN", Convert.ToString(dt.Rows[1][0])); + } + + [Fact] + public static void WhenAddNewRowFromValues_ThenDataTableContainsAmznRecord() + { + var dt = CreateKeyedTable(); + + Program.AddNewRowFromValues(dt); + + Assert.Equal("AMZN", dt.Rows[1]["Ticker"]); + Assert.Equal(1, dt.Rows[1]["Key"]); + } + + [Fact] + public static void WhenRowAddedWithDuplicateKey_ThenConstraintExceptionThrown() + { + var dt = CreateKeyedTable(); + + Assert.Throws(() => dt.Rows.Add("AMZN", new DateTime(2023, 3, 6), 93.75, 0)); } [Fact] public static void WhenPrintConstraints_ThenReturnsConstraintOneUniqueConstraint() { - var dt = Program.CreateMainDataTable(); + var dt = CreateKeyedTable(); var stringWriter = new StringWriter(); Console.SetOut(stringWriter); @@ -65,10 +106,21 @@ public static void WhenPrintConstraints_ThenReturnsConstraintOneUniqueConstraint Assert.Contains("System.Data.UniqueConstraint", output); } + [Fact] + public static void WhenCloneCalled_ThenAllColumnsAndNoRowsCopied() + { + var dt = CreateKeyedTable(); + + var dtClone = dt.Clone(); + + Assert.Equal(4, dtClone.Columns.Count); + Assert.Empty(dtClone.Rows); + } + [Fact] public static void WhenFilterDataTable_ThenPrintsAmznDataRow() { - var dt = Program.CreateMainDataTable(); + var dt = CreateKeyedTable(); Program.AddNewRow(dt); var stringWriter = new StringWriter(); @@ -78,13 +130,13 @@ public static void WhenFilterDataTable_ThenPrintsAmznDataRow() var output = stringWriter.ToString(); Assert.Contains("AMZN", output); + Assert.DoesNotContain("MSFT", output); } [Fact] public static void WhenPrintData_ThenPrintsExpectedData() { - var dt = Program.CreateMainDataTable(); - Program.PopulateDataTable(dt); + var dt = CreateKeyedTable(); var stringWriter = new StringWriter(); Console.SetOut(stringWriter); @@ -95,7 +147,35 @@ public static void WhenPrintData_ThenPrintsExpectedData() Assert.Contains("MSFT", output); Assert.Contains(Convert.ToString(new DateTime(2023, 3, 3)), output); Assert.Contains("255.29", output); - Assert.Contains("0", output); + Assert.Contains("Key: 0", output); + } + + [Fact] + public static void WhenPrintTable_ThenPrintsHeaderAndOneLinePerRow() + { + var dt = CreateKeyedTable(); + Program.AddNewRow(dt); + + var stringWriter = new StringWriter(); + Console.SetOut(stringWriter); + + Program.PrintTable(dt); + var lines = stringWriter.ToString().Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); + + Assert.Equal("Ticker | Date | Price | Key", lines[0]); + Assert.Equal(3, lines.Length); + Assert.StartsWith("AMZN | ", lines[2]); + } + + [Fact] + public static void WhenGetTickersBelow_ThenReturnsOnlyCheaperTickers() + { + var dt = CreateKeyedTable(); + Program.AddNewRow(dt); + + var tickers = Program.GetTickersBelow(dt, 100); + + Assert.Equal(new[] { "AMZN" }, tickers); } } -} \ No newline at end of file +} diff --git a/csharp-intermediate-topics/DataTableOverview/Tests/SalesDataProcessorTests.cs b/csharp-intermediate-topics/DataTableOverview/Tests/SalesDataProcessorTests.cs index 0fd8f6d3d5..a9d1a8f9e7 100644 --- a/csharp-intermediate-topics/DataTableOverview/Tests/SalesDataProcessorTests.cs +++ b/csharp-intermediate-topics/DataTableOverview/Tests/SalesDataProcessorTests.cs @@ -1,10 +1,10 @@ using DataTableOverview; -using Microsoft.VisualStudio.TestPlatform.TestHost; using Xunit; using System; namespace Tests { + [Collection("Console output")] public class SalesDataProcessorTests { [Fact] @@ -32,6 +32,15 @@ public void WhenProcessSales_ThenPrintsSelectedRows() Assert.Contains("Carrot", output); } + + [Fact] + public void WhenNumericFilterRunsOnCsvColumn_ThenEvaluateExceptionThrown() + { + var filePath = AppDomain.CurrentDomain.BaseDirectory + "vegetable-sales.csv"; + var veggieSales = SalesDataProcessor.CsvToDataTable(filePath); + + Assert.Equal(typeof(string), veggieSales.Columns["Price"]!.DataType); + Assert.Throws(() => veggieSales.Select("Price > 1")); + } } } - diff --git a/csharp-intermediate-topics/DataTableOverview/Tests/Tests.csproj b/csharp-intermediate-topics/DataTableOverview/Tests/Tests.csproj index f805d3f2a2..3cd329e537 100644 --- a/csharp-intermediate-topics/DataTableOverview/Tests/Tests.csproj +++ b/csharp-intermediate-topics/DataTableOverview/Tests/Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net10.0 enable enable @@ -9,13 +9,13 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all