From f01f8992a2e8f2e2302ef03ec9ea766bde5634f0 Mon Sep 17 00:00:00 2001 From: Rob Haden Date: Mon, 13 Aug 2018 19:03:55 -0400 Subject: [PATCH 01/17] Add solution and project files; add a global .gitignore file. --- .gitignore | 11 ++ .../Tntp.Database/Tntp.Database.sqlproj | 61 ++++++++ Tntp.Solution/Tntp.Solution.sln | 36 +++++ .../Properties/AssemblyInfo.cs | 36 +++++ .../Tntp.WebApplication.Tests.csproj | 68 ++++++++ .../App_Start/WebApiConfig.cs | 24 +++ Tntp.Solution/Tntp.WebApplication/Global.asax | 1 + .../Tntp.WebApplication/Global.asax.cs | 17 ++ .../Properties/AssemblyInfo.cs | 35 +++++ .../Tntp.WebApplication.csproj | 147 ++++++++++++++++++ .../Tntp.WebApplication/Web.Debug.config | 30 ++++ .../Tntp.WebApplication/Web.Release.config | 31 ++++ Tntp.Solution/Tntp.WebApplication/Web.config | 47 ++++++ .../Tntp.WebApplication/packages.config | 10 ++ 14 files changed, 554 insertions(+) create mode 100644 .gitignore create mode 100644 Tntp.Solution/Tntp.Database/Tntp.Database.sqlproj create mode 100644 Tntp.Solution/Tntp.Solution.sln create mode 100644 Tntp.Solution/Tntp.WebApplication.Tests/Properties/AssemblyInfo.cs create mode 100644 Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj create mode 100644 Tntp.Solution/Tntp.WebApplication/App_Start/WebApiConfig.cs create mode 100644 Tntp.Solution/Tntp.WebApplication/Global.asax create mode 100644 Tntp.Solution/Tntp.WebApplication/Global.asax.cs create mode 100644 Tntp.Solution/Tntp.WebApplication/Properties/AssemblyInfo.cs create mode 100644 Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj create mode 100644 Tntp.Solution/Tntp.WebApplication/Web.Debug.config create mode 100644 Tntp.Solution/Tntp.WebApplication/Web.Release.config create mode 100644 Tntp.Solution/Tntp.WebApplication/Web.config create mode 100644 Tntp.Solution/Tntp.WebApplication/packages.config diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2d8c822 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +# Tntp.Solution exclusions - .vs, packages, bin, and obj directories; .user files +Tntp.Solution/.vs/ +Tntp.Solution/packages/ +Tntp.Solution/Tntp.Database/bin/ +Tntp.Solution/Tntp.Database/obj/ +Tntp.Solution/Tntp.Database/Tntp.Database.sqlproj.user +Tntp.Solution/Tntp.WebApplication/bin/ +Tntp.Solution/Tntp.WebApplication/obj/ +Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj.user +Tntp.Solution/Tntp.WebApplication.Tests/bin/ +Tntp.Solution/Tntp.WebApplication.Tests/obj/ \ No newline at end of file diff --git a/Tntp.Solution/Tntp.Database/Tntp.Database.sqlproj b/Tntp.Solution/Tntp.Database/Tntp.Database.sqlproj new file mode 100644 index 0000000..c9bf847 --- /dev/null +++ b/Tntp.Solution/Tntp.Database/Tntp.Database.sqlproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + Tntp.Database + 2.0 + 4.1 + {ee3ce98d-6e0d-4b23-b524-763b5b65ba47} + Microsoft.Data.Tools.Schema.Sql.Sql130DatabaseSchemaProvider + Database + + + Tntp.Database + Tntp.Database + 1033, CI + BySchemaAndSchemaType + True + v4.6.1 + CS + Properties + False + True + True + + + bin\Release\ + $(MSBuildProjectName).sql + False + pdbonly + true + false + true + prompt + 4 + + + bin\Debug\ + $(MSBuildProjectName).sql + false + true + full + false + true + true + prompt + 4 + + + 11.0 + + True + 11.0 + + + + + + + \ No newline at end of file diff --git a/Tntp.Solution/Tntp.Solution.sln b/Tntp.Solution/Tntp.Solution.sln new file mode 100644 index 0000000..887ea11 --- /dev/null +++ b/Tntp.Solution/Tntp.Solution.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "Tntp.Database", "Tntp.Database\Tntp.Database.sqlproj", "{EE3CE98D-6E0D-4B23-B524-763B5B65BA47}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tntp.WebApplication", "Tntp.WebApplication\Tntp.WebApplication.csproj", "{A5487DA2-BDEB-4DF3-99BB-E983BCBB66CA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tntp.WebApplication.Tests", "Tntp.WebApplication.Tests\Tntp.WebApplication.Tests.csproj", "{42453A53-3307-432C-8ACA-04940576906B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EE3CE98D-6E0D-4B23-B524-763B5B65BA47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EE3CE98D-6E0D-4B23-B524-763B5B65BA47}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EE3CE98D-6E0D-4B23-B524-763B5B65BA47}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {EE3CE98D-6E0D-4B23-B524-763B5B65BA47}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EE3CE98D-6E0D-4B23-B524-763B5B65BA47}.Release|Any CPU.Build.0 = Release|Any CPU + {EE3CE98D-6E0D-4B23-B524-763B5B65BA47}.Release|Any CPU.Deploy.0 = Release|Any CPU + {A5487DA2-BDEB-4DF3-99BB-E983BCBB66CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A5487DA2-BDEB-4DF3-99BB-E983BCBB66CA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A5487DA2-BDEB-4DF3-99BB-E983BCBB66CA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A5487DA2-BDEB-4DF3-99BB-E983BCBB66CA}.Release|Any CPU.Build.0 = Release|Any CPU + {42453A53-3307-432C-8ACA-04940576906B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {42453A53-3307-432C-8ACA-04940576906B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {42453A53-3307-432C-8ACA-04940576906B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {42453A53-3307-432C-8ACA-04940576906B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Tntp.Solution/Tntp.WebApplication.Tests/Properties/AssemblyInfo.cs b/Tntp.Solution/Tntp.WebApplication.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b2e3700 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Tntp.WebApplication.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Tntp.WebApplication.Tests")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("42453a53-3307-432c-8aca-04940576906b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj b/Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj new file mode 100644 index 0000000..dd11b54 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj @@ -0,0 +1,68 @@ + + + + Debug + AnyCPU + {42453A53-3307-432C-8ACA-04940576906B} + Library + Properties + Tntp.WebApplication.Tests + Tntp.WebApplication.Tests + v4.6.1 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + False + UnitTest + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + {A5487DA2-BDEB-4DF3-99BB-E983BCBB66CA} + Tntp.WebApplication + + + + + + \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/App_Start/WebApiConfig.cs b/Tntp.Solution/Tntp.WebApplication/App_Start/WebApiConfig.cs new file mode 100644 index 0000000..dfb1c0b --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/App_Start/WebApiConfig.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web.Http; + +namespace Tntp.WebApplication +{ + public static class WebApiConfig + { + public static void Register(HttpConfiguration config) + { + // Web API configuration and services + + // Web API routes + config.MapHttpAttributeRoutes(); + + config.Routes.MapHttpRoute( + name: "DefaultApi", + routeTemplate: "api/{controller}/{id}", + defaults: new { id = RouteParameter.Optional } + ); + } + } +} diff --git a/Tntp.Solution/Tntp.WebApplication/Global.asax b/Tntp.Solution/Tntp.WebApplication/Global.asax new file mode 100644 index 0000000..a68fcf8 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Global.asax @@ -0,0 +1 @@ +<%@ Application Codebehind="Global.asax.cs" Inherits="Tntp.WebApplication.WebApiApplication" Language="C#" %> diff --git a/Tntp.Solution/Tntp.WebApplication/Global.asax.cs b/Tntp.Solution/Tntp.WebApplication/Global.asax.cs new file mode 100644 index 0000000..5a88c78 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Global.asax.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Web.Http; +using System.Web.Routing; + +namespace Tntp.WebApplication +{ + public class WebApiApplication : System.Web.HttpApplication + { + protected void Application_Start() + { + GlobalConfiguration.Configure(WebApiConfig.Register); + } + } +} diff --git a/Tntp.Solution/Tntp.WebApplication/Properties/AssemblyInfo.cs b/Tntp.Solution/Tntp.WebApplication/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ac84d3e --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Tntp.WebApplication")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Tntp.WebApplication")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a5487da2-bdeb-4df3-99bb-e983bcbb66ca")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj b/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj new file mode 100644 index 0000000..5b213b8 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj @@ -0,0 +1,147 @@ + + + + + + + Debug + AnyCPU + + + 2.0 + {A5487DA2-BDEB-4DF3-99BB-E983BCBB66CA} + {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Tntp.WebApplication + Tntp.WebApplication + v4.6.1 + true + + + + + + + + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll + True + + + + + + + + + + + + + + + + + + + + + + + ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll + + + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll + + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll + + + ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll + + + + + + + + + + Global.asax + + + + + + + Web.config + + + Web.config + + + + + + + + + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + + + + + + True + True + 61781 + / + http://localhost:61781/ + False + False + + + False + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/Web.Debug.config b/Tntp.Solution/Tntp.WebApplication/Web.Debug.config new file mode 100644 index 0000000..2e302f9 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Web.Debug.config @@ -0,0 +1,30 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/Web.Release.config b/Tntp.Solution/Tntp.WebApplication/Web.Release.config new file mode 100644 index 0000000..c358444 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Web.Release.config @@ -0,0 +1,31 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/Web.config b/Tntp.Solution/Tntp.WebApplication/Web.config new file mode 100644 index 0000000..89e6af2 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Web.config @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tntp.Solution/Tntp.WebApplication/packages.config b/Tntp.Solution/Tntp.WebApplication/packages.config new file mode 100644 index 0000000..fbae1ea --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/packages.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file From f8eaafa7c65a4a6aa1cd464c5ba5ae327cb14240 Mon Sep 17 00:00:00 2001 From: Rob Haden Date: Mon, 13 Aug 2018 19:14:46 -0400 Subject: [PATCH 02/17] Add a table for comments; update the .gitignore file. --- .gitignore | 4 +++- Tntp.Solution/Tntp.Database/Comments.sql | 11 +++++++++++ Tntp.Solution/Tntp.Database/Tntp.Database.sqlproj | 3 +++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 Tntp.Solution/Tntp.Database/Comments.sql diff --git a/.gitignore b/.gitignore index 2d8c822..a0ab064 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ -# Tntp.Solution exclusions - .vs, packages, bin, and obj directories; .user files +# Tntp.Solution exclusions - .vs, packages, bin, and obj directories; .dbmdl, .jfm, and .user files Tntp.Solution/.vs/ Tntp.Solution/packages/ Tntp.Solution/Tntp.Database/bin/ Tntp.Solution/Tntp.Database/obj/ +Tntp.Solution/Tntp.Database/Tntp.Database.dbmdl +Tntp.Solution/Tntp.Database/Tntp.Database.jfm Tntp.Solution/Tntp.Database/Tntp.Database.sqlproj.user Tntp.Solution/Tntp.WebApplication/bin/ Tntp.Solution/Tntp.WebApplication/obj/ diff --git a/Tntp.Solution/Tntp.Database/Comments.sql b/Tntp.Solution/Tntp.Database/Comments.sql new file mode 100644 index 0000000..1702b69 --- /dev/null +++ b/Tntp.Solution/Tntp.Database/Comments.sql @@ -0,0 +1,11 @@ +CREATE TABLE [dbo].[Comments] +( + [Id] INT NOT NULL PRIMARY KEY IDENTITY, + [Username] NVARCHAR(15) NOT NULL, + [Content] NVARCHAR(140) NOT NULL, + [CreationTimestamp] DATETIME2 NOT NULL DEFAULT sysdatetime() +) + +GO + +CREATE INDEX [IX_Comments_CreationTimestamp] ON [dbo].[Comments] ([CreationTimestamp] DESC) diff --git a/Tntp.Solution/Tntp.Database/Tntp.Database.sqlproj b/Tntp.Solution/Tntp.Database/Tntp.Database.sqlproj index c9bf847..253910e 100644 --- a/Tntp.Solution/Tntp.Database/Tntp.Database.sqlproj +++ b/Tntp.Solution/Tntp.Database/Tntp.Database.sqlproj @@ -58,4 +58,7 @@ + + + \ No newline at end of file From 2a49b7e28cc9d0f9f57a5f416319d4080ce15f78 Mon Sep 17 00:00:00 2001 From: Rob Haden Date: Mon, 13 Aug 2018 19:21:01 -0400 Subject: [PATCH 03/17] Update packages. --- .../Tntp.WebApplication.Tests.csproj | 3 ++ .../Tntp.WebApplication.Tests/app.config | 11 +++++ .../Tntp.WebApplication.csproj | 42 +++++++++-------- Tntp.Solution/Tntp.WebApplication/Web.config | 47 +++++++++---------- .../Tntp.WebApplication/packages.config | 14 +++--- 5 files changed, 66 insertions(+), 51 deletions(-) create mode 100644 Tntp.Solution/Tntp.WebApplication.Tests/app.config diff --git a/Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj b/Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj index dd11b54..8123bd8 100644 --- a/Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj +++ b/Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj @@ -56,6 +56,9 @@ Tntp.WebApplication + + + + + +
+ - @@ -36,11 +39,18 @@ - + - + + + + + + + + \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/packages.config b/Tntp.Solution/Tntp.WebApplication/packages.config index d43ed42..39e97fd 100644 --- a/Tntp.Solution/Tntp.WebApplication/packages.config +++ b/Tntp.Solution/Tntp.WebApplication/packages.config @@ -1,5 +1,6 @@  + From 67b823d731ca24e0b8972c959bc4255d6d9eb703 Mon Sep 17 00:00:00 2001 From: Rob Haden Date: Mon, 13 Aug 2018 19:44:18 -0400 Subject: [PATCH 05/17] Add entity and repository classes. --- .../Tntp.WebApplication/Models/Comment.cs | 15 +++++++++ .../Models/DatabaseContext.cs | 18 +++++++++++ .../Models/DatabaseRepository.cs | 31 +++++++++++++++++++ .../Tntp.WebApplication/Models/IRepository.cs | 13 ++++++++ .../Tntp.WebApplication.csproj | 5 ++- 5 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 Tntp.Solution/Tntp.WebApplication/Models/Comment.cs create mode 100644 Tntp.Solution/Tntp.WebApplication/Models/DatabaseContext.cs create mode 100644 Tntp.Solution/Tntp.WebApplication/Models/DatabaseRepository.cs create mode 100644 Tntp.Solution/Tntp.WebApplication/Models/IRepository.cs diff --git a/Tntp.Solution/Tntp.WebApplication/Models/Comment.cs b/Tntp.Solution/Tntp.WebApplication/Models/Comment.cs new file mode 100644 index 0000000..bdc5df9 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Models/Comment.cs @@ -0,0 +1,15 @@ +using System; + +namespace Tntp.WebApplication.Models +{ + /// + /// The Comment class is an entity class representing rows in the Comments database table. + /// + public class Comment + { + public int Id { get; set; } + public string Username { get; set; } + public string Content { get; set; } + public DateTime CreationTimestamp { get; set; } + } +} \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/Models/DatabaseContext.cs b/Tntp.Solution/Tntp.WebApplication/Models/DatabaseContext.cs new file mode 100644 index 0000000..16fc372 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Models/DatabaseContext.cs @@ -0,0 +1,18 @@ +using System.Data.Entity; + +namespace Tntp.WebApplication.Models +{ + /// + /// The DatabaseContext class is a subclass of DbContext that's aware of the Comments table. It's used by the DatabaseRepository class. + /// + public class DatabaseContext : DbContext + { + public DbSet Comments { get; set; } + + public DatabaseContext() : base("name=Tntp") + { + // Code First with Existing Database - Make sure to not automatically create a new database or update an existing one. + Database.SetInitializer(null); + } + } +} \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/Models/DatabaseRepository.cs b/Tntp.Solution/Tntp.WebApplication/Models/DatabaseRepository.cs new file mode 100644 index 0000000..1b9e342 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Models/DatabaseRepository.cs @@ -0,0 +1,31 @@ +using System.Data.Entity; + +namespace Tntp.WebApplication.Models +{ + /// + /// The DatabaseRepository class is an implementation of IRepository that connects to a database. + /// + public class DatabaseRepository : IRepository + { + private readonly DatabaseContext _databaseContext = new DatabaseContext(); + + private bool _isDisposed; + + public IDbSet Comments + { + get + { + return _databaseContext.Comments; + } + } + + public void Dispose() + { + if(!_isDisposed) + { + _databaseContext.Dispose(); + _isDisposed = true; + } + } + } +} \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/Models/IRepository.cs b/Tntp.Solution/Tntp.WebApplication/Models/IRepository.cs new file mode 100644 index 0000000..1eb4cf2 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Models/IRepository.cs @@ -0,0 +1,13 @@ +using System; +using System.Data.Entity; + +namespace Tntp.WebApplication.Models +{ + /// + /// The IRepository interface is a light abstraction for data access. + /// + public interface IRepository : IDisposable + { + IDbSet Comments { get; } + } +} \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj b/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj index 8b80e91..d8d8a8d 100644 --- a/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj +++ b/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj @@ -99,6 +99,10 @@ Global.asax + + + + @@ -113,7 +117,6 @@ - 10.0 From 29549dd888134930ae042fb86ece602a9b2907d0 Mon Sep 17 00:00:00 2001 From: Rob Haden Date: Mon, 13 Aug 2018 20:10:23 -0400 Subject: [PATCH 06/17] Add a controller class. --- .../Controllers/CommentsController.cs | 57 +++++++++++++++++++ .../Models/DatabaseRepository.cs | 13 +++-- .../Tntp.WebApplication/Models/IRepository.cs | 1 + .../Tntp.WebApplication.csproj | 2 +- 4 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 Tntp.Solution/Tntp.WebApplication/Controllers/CommentsController.cs diff --git a/Tntp.Solution/Tntp.WebApplication/Controllers/CommentsController.cs b/Tntp.Solution/Tntp.WebApplication/Controllers/CommentsController.cs new file mode 100644 index 0000000..998c630 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication/Controllers/CommentsController.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Web.Http; +using Tntp.WebApplication.Models; + +namespace Tntp.WebApplication.Controllers +{ + /// + /// The CommentsController class serves as a web-API controller for posting and viewing comments. + /// + [RoutePrefix("api/comments")] + public class CommentsController : ApiController + { + private const int UsernameMaxLength = 15; + private const int ContentMaxLength = 140; + + private readonly IRepository _repository; + + public CommentsController(IRepository repository) + { + _repository = repository; + } + + [HttpGet] + public IEnumerable GetComments() + { + return _repository.Comments.OrderByDescending(c => c.CreationTimestamp); + } + + [HttpPost] + public IHttpActionResult AddComment(Comment comment) + { + if(comment.Username?.Length == 0) + { + return BadRequest("A username is required."); + } + else if(comment.Username.Length > UsernameMaxLength) + { + return BadRequest(string.Format("A username must not exceed {0} characters.", UsernameMaxLength)); + } + else if(comment.Content?.Length == 0) + { + return BadRequest("A comment is required."); + } + else if(comment.Content.Length > ContentMaxLength) + { + return BadRequest(string.Format("A comment must not exceed {0} characters.", ContentMaxLength)); + } + + _repository.Comments.Add(comment); + _repository.SaveChanges(); + + return StatusCode(HttpStatusCode.OK); + } + } +} \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/Models/DatabaseRepository.cs b/Tntp.Solution/Tntp.WebApplication/Models/DatabaseRepository.cs index 1b9e342..725c905 100644 --- a/Tntp.Solution/Tntp.WebApplication/Models/DatabaseRepository.cs +++ b/Tntp.Solution/Tntp.WebApplication/Models/DatabaseRepository.cs @@ -1,4 +1,5 @@ -using System.Data.Entity; +using System; +using System.Data.Entity; namespace Tntp.WebApplication.Models { @@ -13,10 +14,12 @@ public class DatabaseRepository : IRepository public IDbSet Comments { - get - { - return _databaseContext.Comments; - } + get { return _databaseContext.Comments; } + } + + public void SaveChanges() + { + _databaseContext.SaveChanges(); } public void Dispose() diff --git a/Tntp.Solution/Tntp.WebApplication/Models/IRepository.cs b/Tntp.Solution/Tntp.WebApplication/Models/IRepository.cs index 1eb4cf2..2c6840a 100644 --- a/Tntp.Solution/Tntp.WebApplication/Models/IRepository.cs +++ b/Tntp.Solution/Tntp.WebApplication/Models/IRepository.cs @@ -9,5 +9,6 @@ namespace Tntp.WebApplication.Models public interface IRepository : IDisposable { IDbSet Comments { get; } + void SaveChanges(); } } \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj b/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj index d8d8a8d..83c7468 100644 --- a/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj +++ b/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj @@ -96,6 +96,7 @@ + Global.asax @@ -116,7 +117,6 @@ - 10.0 From 859dfc3976968f4361286646d27b9b75f6ac2879 Mon Sep 17 00:00:00 2001 From: Rob Haden Date: Mon, 13 Aug 2018 20:16:11 -0400 Subject: [PATCH 07/17] Install the Simple Injector packages. --- Tntp.Solution/Tntp.WebApplication.Tests/app.config | 8 ++++++++ .../Tntp.WebApplication/Tntp.WebApplication.csproj | 12 ++++++++++++ Tntp.Solution/Tntp.WebApplication/Web.config | 8 ++++++++ Tntp.Solution/Tntp.WebApplication/packages.config | 3 +++ 4 files changed, 31 insertions(+) diff --git a/Tntp.Solution/Tntp.WebApplication.Tests/app.config b/Tntp.Solution/Tntp.WebApplication.Tests/app.config index 2bbe771..eec1920 100644 --- a/Tntp.Solution/Tntp.WebApplication.Tests/app.config +++ b/Tntp.Solution/Tntp.WebApplication.Tests/app.config @@ -6,6 +6,14 @@ + + + + + + + + \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj b/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj index 83c7468..19c540f 100644 --- a/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj +++ b/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj @@ -60,6 +60,18 @@ ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll True + + ..\packages\SimpleInjector.4.3.0\lib\net45\SimpleInjector.dll + True + + + ..\packages\SimpleInjector.Extensions.ExecutionContextScoping.4.0.0\lib\net45\SimpleInjector.Extensions.ExecutionContextScoping.dll + True + + + ..\packages\SimpleInjector.Integration.WebApi.4.3.0\lib\net45\SimpleInjector.Integration.WebApi.dll + True + ..\packages\Microsoft.AspNet.WebApi.Client.5.2.6\lib\net45\System.Net.Http.Formatting.dll diff --git a/Tntp.Solution/Tntp.WebApplication/Web.config b/Tntp.Solution/Tntp.WebApplication/Web.config index 4b4f6f3..68f2fbd 100644 --- a/Tntp.Solution/Tntp.WebApplication/Web.config +++ b/Tntp.Solution/Tntp.WebApplication/Web.config @@ -31,6 +31,14 @@ + + + + + + + + diff --git a/Tntp.Solution/Tntp.WebApplication/packages.config b/Tntp.Solution/Tntp.WebApplication/packages.config index 39e97fd..3709fce 100644 --- a/Tntp.Solution/Tntp.WebApplication/packages.config +++ b/Tntp.Solution/Tntp.WebApplication/packages.config @@ -8,4 +8,7 @@ + + + \ No newline at end of file From 45ef4bcf2c0b3113afd5f4dc4734948f9b121f81 Mon Sep 17 00:00:00 2001 From: Rob Haden Date: Mon, 13 Aug 2018 20:20:51 -0400 Subject: [PATCH 08/17] Set up database connectivity. --- Tntp.Solution/Tntp.WebApplication/Web.config | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Tntp.Solution/Tntp.WebApplication/Web.config b/Tntp.Solution/Tntp.WebApplication/Web.config index 68f2fbd..32eeac0 100644 --- a/Tntp.Solution/Tntp.WebApplication/Web.config +++ b/Tntp.Solution/Tntp.WebApplication/Web.config @@ -61,4 +61,7 @@ + + + \ No newline at end of file From f20dc72c5b8a04285d6339696246b549045c8fd8 Mon Sep 17 00:00:00 2001 From: Rob Haden Date: Mon, 13 Aug 2018 20:26:23 -0400 Subject: [PATCH 09/17] Set up dependency injection and JSON as the only content/media type. --- .../App_Start/WebApiConfig.cs | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/Tntp.Solution/Tntp.WebApplication/App_Start/WebApiConfig.cs b/Tntp.Solution/Tntp.WebApplication/App_Start/WebApiConfig.cs index dfb1c0b..d630b63 100644 --- a/Tntp.Solution/Tntp.WebApplication/App_Start/WebApiConfig.cs +++ b/Tntp.Solution/Tntp.WebApplication/App_Start/WebApiConfig.cs @@ -1,24 +1,31 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using SimpleInjector; +using SimpleInjector.Integration.WebApi; +using SimpleInjector.Lifestyles; +using System.Net.Http.Formatting; using System.Web.Http; +using Tntp.WebApplication.Models; namespace Tntp.WebApplication { public static class WebApiConfig { - public static void Register(HttpConfiguration config) + public static void Register(HttpConfiguration configuration) { // Web API configuration and services + var container = new Container(); + container.Register(new AsyncScopedLifestyle()); + container.RegisterWebApiControllers(configuration); + container.Verify(); - // Web API routes - config.MapHttpAttributeRoutes(); + // We want JSON to be the only content/media type. + // In the future, we might not want to go through content negotiation at all - see https://www.strathweb.com/2013/06/supporting-only-json-in-asp-net-web-api-the-right-way/ + // for implementation details. + configuration.Formatters.Clear(); + configuration.Formatters.Add(new JsonMediaTypeFormatter()); - config.Routes.MapHttpRoute( - name: "DefaultApi", - routeTemplate: "api/{controller}/{id}", - defaults: new { id = RouteParameter.Optional } - ); + // Web API routes + configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container); + configuration.MapHttpAttributeRoutes(); } } -} +} \ No newline at end of file From af570e5a28ba8648ea4c62a9b10f5c8cd1451026 Mon Sep 17 00:00:00 2001 From: Rob Haden Date: Mon, 13 Aug 2018 21:13:17 -0400 Subject: [PATCH 10/17] Add unit testing and fix bugs. --- .../CommentsControllerTests.cs | 95 +++++++++++++++++++ .../Tntp.WebApplication.Tests/MockDbSet.cs | 76 +++++++++++++++ .../MockRepository.cs | 19 ++++ .../Tntp.WebApplication.Tests.csproj | 15 +++ .../Tntp.WebApplication.Tests/app.config | 10 ++ .../Tntp.WebApplication.Tests/packages.config | 4 + .../Controllers/CommentsController.cs | 4 +- 7 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 Tntp.Solution/Tntp.WebApplication.Tests/CommentsControllerTests.cs create mode 100644 Tntp.Solution/Tntp.WebApplication.Tests/MockDbSet.cs create mode 100644 Tntp.Solution/Tntp.WebApplication.Tests/MockRepository.cs create mode 100644 Tntp.Solution/Tntp.WebApplication.Tests/packages.config diff --git a/Tntp.Solution/Tntp.WebApplication.Tests/CommentsControllerTests.cs b/Tntp.Solution/Tntp.WebApplication.Tests/CommentsControllerTests.cs new file mode 100644 index 0000000..d772607 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication.Tests/CommentsControllerTests.cs @@ -0,0 +1,95 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Tntp.WebApplication.Controllers; +using System; +using System.Linq; +using Tntp.WebApplication.Models; +using System.Web.Http.Results; +using System.Net; + +namespace Tntp.WebApplication.Tests +{ + [TestClass] + public class CommentsControllerTests + { + private readonly IRepository _repository = new MockRepository(); + private readonly CommentsController _controller; + + public CommentsControllerTests() + { + _repository.Comments.Add(new Comment { Username = "Eddard", Content = "Stark", CreationTimestamp = DateTime.Now.AddDays(-1) }); + _repository.Comments.Add(new Comment { Username = "Jon", Content = "Snow", CreationTimestamp = DateTime.Now }); + _controller = new CommentsController(_repository); + } + + [TestMethod] + public void GetCommentsTest() + { + var results = _controller.GetComments(); + Assert.AreEqual("Jon", results.ElementAt(0).Username); + Assert.AreEqual("Snow", results.ElementAt(0).Content); + Assert.AreEqual("Eddard", results.ElementAt(1).Username); + Assert.AreEqual("Stark", results.ElementAt(1).Content); + } + + [TestMethod] + public void AddCommentTest_NullUsername() + { + var result = _controller.AddComment(new Comment { Content = "Seaworth" }) as BadRequestErrorMessageResult; + Assert.IsNotNull(result); + Assert.AreEqual("A username is required.", result.Message); + } + + [TestMethod] + public void AddCommentTest_EmptyUsername() + { + var result = _controller.AddComment(new Comment { Username = "", Content = "Seaworth" }) as BadRequestErrorMessageResult; + Assert.IsNotNull(result); + Assert.AreEqual("A username is required.", result.Message); + } + + [TestMethod] + public void AddCommentTest_TooLongUsername() + { + var result = _controller.AddComment(new Comment { Username = "Stannnnnnnnnnnis", Content = "Baratheon" }) as BadRequestErrorMessageResult; + Assert.IsNotNull(result); + Assert.AreEqual("A username must not exceed 15 characters.", result.Message); + } + + [TestMethod] + public void AddCommentTest_NullContent() + { + var result = _controller.AddComment(new Comment { Username = "Davos" }) as BadRequestErrorMessageResult; + Assert.IsNotNull(result); + Assert.AreEqual("A comment is required.", result.Message); + } + + [TestMethod] + public void AddCommentTest_EmptyContent() + { + var result = _controller.AddComment(new Comment { Username = "Davos", Content = "" }) as BadRequestErrorMessageResult; + Assert.IsNotNull(result); + Assert.AreEqual("A comment is required.", result.Message); + } + + [TestMethod] + public void AddCommentTest_TooLongContent() + { + var result = _controller.AddComment(new Comment + { + Username = "Hodor", + Content = "Hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor hodor" + }) as BadRequestErrorMessageResult; + + Assert.IsNotNull(result); + Assert.AreEqual("A comment must not exceed 140 characters.", result.Message); + } + + [TestMethod] + public void AddCommentTest_Successful() + { + var result = _controller.AddComment(new Comment { Username = "Davos", Content = "Seaworth" }) as StatusCodeResult; + Assert.IsNotNull(result); + Assert.AreEqual(HttpStatusCode.OK, result.StatusCode); + } + } +} \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication.Tests/MockDbSet.cs b/Tntp.Solution/Tntp.WebApplication.Tests/MockDbSet.cs new file mode 100644 index 0000000..10aa658 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication.Tests/MockDbSet.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Data.Entity; +using System.Linq; +using System.Linq.Expressions; + +namespace Tntp.WebApplication.Tests +{ + public class MockDbSet : IDbSet where T : class + { + private readonly List _entities = new List(); + + public Type ElementType + { + get { return typeof(T); } + } + + public Expression Expression + { + get { return _entities.AsQueryable().Expression; } + } + + public ObservableCollection Local + { + get { throw new NotImplementedException(); } + } + + public IQueryProvider Provider + { + get { return _entities.AsQueryable().Provider; } + } + + public T Add(T entity) + { + _entities.Add(entity); + return entity; + } + + public T Attach(T entity) + { + throw new NotImplementedException(); + } + + public T Create() + { + throw new NotImplementedException(); + } + + public TDerivedEntity Create() where TDerivedEntity : class, T + { + throw new NotImplementedException(); + } + + public T Find(params object[] keyValues) + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + return _entities.GetEnumerator(); + } + + public T Remove(T entity) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _entities.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication.Tests/MockRepository.cs b/Tntp.Solution/Tntp.WebApplication.Tests/MockRepository.cs new file mode 100644 index 0000000..6156bc0 --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication.Tests/MockRepository.cs @@ -0,0 +1,19 @@ +using System.Data.Entity; +using Tntp.WebApplication.Models; + +namespace Tntp.WebApplication.Tests +{ + public class MockRepository : IRepository + { + private readonly IDbSet _comments = new MockDbSet(); + + public IDbSet Comments + { + get { return _comments; } + } + + public void SaveChanges() { } + + public void Dispose() { } + } +} \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj b/Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj index 8123bd8..c1f228d 100644 --- a/Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj +++ b/Tntp.Solution/Tntp.WebApplication.Tests/Tntp.WebApplication.Tests.csproj @@ -32,6 +32,14 @@ 4 + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + True + + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + True + @@ -43,11 +51,17 @@ + + ..\packages\Microsoft.AspNet.WebApi.Core.5.2.6\lib\net45\System.Web.Http.dll + + + + @@ -58,6 +72,7 @@ + diff --git a/Tntp.Solution/Tntp.WebApplication.Tests/app.config b/Tntp.Solution/Tntp.WebApplication.Tests/app.config index eec1920..aec0c51 100644 --- a/Tntp.Solution/Tntp.WebApplication.Tests/app.config +++ b/Tntp.Solution/Tntp.WebApplication.Tests/app.config @@ -1,5 +1,9 @@  + + +
+ @@ -16,4 +20,10 @@ + + + + + + \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication.Tests/packages.config b/Tntp.Solution/Tntp.WebApplication.Tests/packages.config new file mode 100644 index 0000000..b3daf0d --- /dev/null +++ b/Tntp.Solution/Tntp.WebApplication.Tests/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/Controllers/CommentsController.cs b/Tntp.Solution/Tntp.WebApplication/Controllers/CommentsController.cs index 998c630..83b9949 100644 --- a/Tntp.Solution/Tntp.WebApplication/Controllers/CommentsController.cs +++ b/Tntp.Solution/Tntp.WebApplication/Controllers/CommentsController.cs @@ -31,7 +31,7 @@ public IEnumerable GetComments() [HttpPost] public IHttpActionResult AddComment(Comment comment) { - if(comment.Username?.Length == 0) + if((comment.Username?.Length ?? 0) == 0) { return BadRequest("A username is required."); } @@ -39,7 +39,7 @@ public IHttpActionResult AddComment(Comment comment) { return BadRequest(string.Format("A username must not exceed {0} characters.", UsernameMaxLength)); } - else if(comment.Content?.Length == 0) + else if((comment.Content?.Length ?? 0) == 0) { return BadRequest("A comment is required."); } From 13fff19147559f546a4a158d6b98c30e61468ed6 Mon Sep 17 00:00:00 2001 From: Rob Haden Date: Mon, 13 Aug 2018 22:28:12 -0400 Subject: [PATCH 11/17] Downgrade the version of the Microsoft.Net.Compilers package to work around a build issue. --- Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj | 4 ++-- Tntp.Solution/Tntp.WebApplication/packages.config | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj b/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj index 19c540f..bd1b10d 100644 --- a/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj +++ b/Tntp.Solution/Tntp.WebApplication/Tntp.WebApplication.csproj @@ -1,6 +1,6 @@  - + @@ -160,7 +160,7 @@ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - +
- + + + @@ -57,8 +59,8 @@ - - + + @@ -70,6 +72,6 @@ - + \ No newline at end of file diff --git a/Tntp.Solution/Tntp.WebApplication/packages.config b/Tntp.Solution/Tntp.WebApplication/packages.config index 3af90ae..178aed3 100644 --- a/Tntp.Solution/Tntp.WebApplication/packages.config +++ b/Tntp.Solution/Tntp.WebApplication/packages.config @@ -7,6 +7,7 @@ +