diff --git a/.gitignore b/.gitignore index f863a14..c77e706 100644 --- a/.gitignore +++ b/.gitignore @@ -163,3 +163,8 @@ SampleWebApp/Properties/PublishProfiles/samplemvcwebapp - Web Deploy.pubxml SampleWebApp/Properties/PublishProfiles/samplemvcwebapp.net - WebWiz.pubxml git/.gitattributes git/.gitignore + +# SQLite dev database +*.db +*.db-shm +*.db-wal diff --git a/BizLayer/App.config b/BizLayer/App.config deleted file mode 100644 index 13ecece..0000000 --- a/BizLayer/App.config +++ /dev/null @@ -1,17 +0,0 @@ - - - - -
- - - - - - - - - - - - \ No newline at end of file diff --git a/BizLayer/BizLayer.csproj b/BizLayer/BizLayer.csproj index aa58ad0..3ce449a 100644 --- a/BizLayer/BizLayer.csproj +++ b/BizLayer/BizLayer.csproj @@ -1,87 +1,14 @@ - - - + + - Debug - AnyCPU - {19592A8F-7C58-4221-8124-D38ACD8F5E31} - Library - Properties + net9.0 + disable + disable BizLayer - BizLayer - v4.5.1 - 512 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Autofac.3.5.0\lib\net40\Autofac.dll - - - ..\packages\AutoMapper.3.2.1\lib\net40\AutoMapper.dll - - - ..\packages\AutoMapper.3.2.1\lib\net40\AutoMapper.Net4.dll - - - False - ..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.dll - - - ..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.SqlServer.dll - - - False - ..\packages\GenericServices.1.0.0-beta4-003\lib\GenericServices.dll - - - - - - - - - - - - - - - - - - - + - - {264e1878-12de-4099-b8d7-cc53a73fea49} - DataLayer - + - - - - - \ No newline at end of file + + diff --git a/BizLayer/Properties/AssemblyInfo.cs b/BizLayer/Properties/AssemblyInfo.cs deleted file mode 100644 index f93aad5..0000000 --- a/BizLayer/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,62 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: AssemblyInfo.cs -// Date Created: 2014/07/11 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -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("BizLayer")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("BizLayer")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[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("3ea2faa1-4bb3-4d8e-8d35-f8038d375643")] - -// 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/BizLayer/Startup/BizLayerModule.cs b/BizLayer/Startup/BizLayerModule.cs deleted file mode 100644 index e924a90..0000000 --- a/BizLayer/Startup/BizLayerModule.cs +++ /dev/null @@ -1,46 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: BizLayerModule.cs -// Date Created: 2014/07/11 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -using Autofac; - -namespace BizLayer.Startup -{ - public class BizLayerModule : Module - { - - /// - /// This registers all items in service layer and below - /// - /// - protected override void Load(ContainerBuilder builder) - { - //--------------------------- - //Register service layer: autowire all - builder.RegisterAssemblyTypes(GetType().Assembly).AsImplementedInterfaces(); - } - - } -} diff --git a/BizLayer/packages.config b/BizLayer/packages.config deleted file mode 100644 index 4a74205..0000000 --- a/BizLayer/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/DataLayer/App.config b/DataLayer/App.config deleted file mode 100644 index 5b4002b..0000000 --- a/DataLayer/App.config +++ /dev/null @@ -1,25 +0,0 @@ - - - - -
- - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/DataLayer/DataClasses/EfConfiguration.cs b/DataLayer/DataClasses/EfConfiguration.cs deleted file mode 100644 index 8c3cfe7..0000000 --- a/DataLayer/DataClasses/EfConfiguration.cs +++ /dev/null @@ -1,48 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: EfConfiguration.cs -// Date Created: 2014/08/14 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -using System.Data.Entity; -using System.Data.Entity.SqlServer; - -namespace DataLayer.DataClasses -{ - public class EfConfiguration : DbConfiguration - { - /// - /// This flag should be set to true if we are working with an Azure database. - /// It should be set before EF uses the configuration, i.e. beofre the first access - /// - public static bool IsAzure { get; internal set; } - - public EfConfiguration() - { - if (IsAzure) - SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy()); - } - - } -} diff --git a/DataLayer/DataClasses/SampleWebAppDb.cs b/DataLayer/DataClasses/SampleWebAppDb.cs index 0a86cf3..0b59449 100644 --- a/DataLayer/DataClasses/SampleWebAppDb.cs +++ b/DataLayer/DataClasses/SampleWebAppDb.cs @@ -1,134 +1,84 @@ -#region licence -// The MIT License (MIT) -// -// Filename: SampleWebAppDb.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion using System.Collections.Generic; -using System.Data.Entity; -using System.Data.Entity.Infrastructure; -using System.Data.Entity.Validation; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Runtime.CompilerServices; -using System.Threading.Tasks; using DataLayer.DataClasses.Concrete; using DataLayer.DataClasses.Concrete.Helpers; using GenericServices; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; [assembly: InternalsVisibleTo("Tests")] namespace DataLayer.DataClasses { - public class SampleWebAppDb : DbContext, IGenericServicesDbContext { - internal const string NameOfConnectionString = "SampleWebAppDb"; - public DbSet Blogs { get; set; } public DbSet Posts { get; set; } public DbSet Tags { get; set; } - public SampleWebAppDb() : base("name=" + NameOfConnectionString) {} + public SampleWebAppDb(DbContextOptions options) : base(options) { } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); - internal SampleWebAppDb(string connectionString) : base(connectionString) { } + modelBuilder.Entity() + .HasOne(p => p.Blogger) + .WithMany(b => b.Posts) + .HasForeignKey(p => p.BlogId); + modelBuilder.Entity() + .HasMany(p => p.Tags) + .WithMany(t => t.Posts); + } /// - /// This has been overridden to handle: - /// a) Updating of modified items (see p194 in DbContext book) + /// Overridden to update the LastUpdated tracking information before saving (see TrackUpdate). /// - /// public override int SaveChanges() { HandleChangeTracking(); return base.SaveChanges(); } - /// - /// Same for async - /// - /// - public override Task SaveChangesAsync() + public override System.Threading.Tasks.Task SaveChangesAsync( + System.Threading.CancellationToken cancellationToken = default) { HandleChangeTracking(); - return base.SaveChangesAsync(); + return base.SaveChangesAsync(cancellationToken); } /// - /// This does validations that can only be done at the database level + /// Database level validation that cannot be expressed via DataAnnotations. + /// Enforces uniqueness of a Tag's Slug. /// - /// - /// - /// - protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, - IDictionary items) + public IEnumerable ExtraValidation(EntityEntry entry) { - - if (entityEntry.Entity is Tag && (entityEntry.State == EntityState.Added || entityEntry.State == EntityState.Modified)) + if (entry.Entity is Tag tagToCheck && + (entry.State == EntityState.Added || entry.State == EntityState.Modified)) { - var tagToCheck = ((Tag)entityEntry.Entity); - - //check for uniqueness of Tag's Slug property (note: because we may alter a Tag we need to exclude check against itself) if (Tags.Any(x => x.TagId != tagToCheck.TagId && x.Slug == tagToCheck.Slug)) - return new DbEntityValidationResult(entityEntry, - new List - { - new DbValidationError( "Slug", - string.Format( "The Slug on tag '{0}' must be unique and is already being used.", tagToCheck.Name)) - }); + yield return new ValidationResult( + string.Format("The Slug on tag '{0}' must be unique and is already being used.", tagToCheck.Name), + new[] { "Slug" }); } - - return base.ValidateEntity(entityEntry, items); } - //-------------------------------------------------- //private helpers - /// - /// This handles going through all the entities that have changed and seeing if they need any special handling. - /// private void HandleChangeTracking() { - //Debug.WriteLine("----------------------------------------------"); - //foreach (var entity in ChangeTracker.Entries() - //.Where( - // e => - // e.State == EntityState.Added || e.State == EntityState.Modified)) - //{ - // Debug.WriteLine("Entry {0}, state {1}", entity.Entity, entity.State); - //} - foreach (var entity in ChangeTracker.Entries() - .Where( - e => - e.State == EntityState.Added || e.State == EntityState.Modified)) + .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)) { var trackUpdateClass = entity.Entity as TrackUpdate; - if (trackUpdateClass == null) return; + if (trackUpdateClass == null) + continue; trackUpdateClass.UpdateTrackingInfo(); } } - } } diff --git a/DataLayer/DataLayer.csproj b/DataLayer/DataLayer.csproj index 01771cf..1556fba 100644 --- a/DataLayer/DataLayer.csproj +++ b/DataLayer/DataLayer.csproj @@ -1,135 +1,24 @@ - - - + + - Debug - AnyCPU - {264E1878-12DE-4099-B8D7-CC53A73FEA49} - Library - Properties + net9.0 + disable + disable DataLayer - DataLayer - v4.5.1 - 512 - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - bin\ReleaseAzure\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - bin\AzureRelease\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - bin\WebWizRelease\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - - False - ..\packages\Autofac.3.5.0\lib\net40\Autofac.dll - - - ..\packages\AutoMapper.4.2.1\lib\net45\AutoMapper.dll - True - - - ..\packages\DelegateDecompiler.0.18.0\lib\net40-Client\DelegateDecompiler.dll - True - - - ..\packages\DelegateDecompiler.EntityFramework.0.18.0\lib\net45\DelegateDecompiler.EntityFramework.dll - True - - - ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll - True - - - ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll - True - - - ..\packages\GenericLibsBase.1.0.1\lib\GenericLibsBase.dll - True - - - ..\packages\GenericServices.1.0.9\lib\GenericServices.dll - True - - - ..\packages\Mono.Reflection.1.0.0.0\lib\Mono.Reflection.dll - - - - - - - - - - - + - - - - - - - - - - - - - - + + + - + + + - + - - - \ No newline at end of file + + diff --git a/DataLayer/Properties/AssemblyInfo.cs b/DataLayer/Properties/AssemblyInfo.cs deleted file mode 100644 index 5cbbe5f..0000000 --- a/DataLayer/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,62 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: AssemblyInfo.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -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("DataLayer")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("DataLayer")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[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("f78cbd3c-264d-4fda-8d1a-3679faf55c2c")] - -// 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/DataLayer/Startup/DataLayerInitialise.cs b/DataLayer/Startup/DataLayerInitialise.cs index e7c2df5..53f5d9c 100644 --- a/DataLayer/Startup/DataLayerInitialise.cs +++ b/DataLayer/Startup/DataLayerInitialise.cs @@ -1,73 +1,47 @@ -#region licence -// The MIT License (MIT) -// -// Filename: DataLayerInitialise.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion using System; using System.Collections.Generic; -using System.Data.Entity; using System.Linq; using DataLayer.DataClasses; using DataLayer.Startup.Internal; using GenericLibsBase; using GenericServices; +using Microsoft.EntityFrameworkCore; namespace DataLayer.Startup { - public enum TestDataSelection { Small = 0, Medium = 1} + public enum TestDataSelection { Small = 0, Medium = 1 } public static class DataLayerInitialise { - private static IGenericLogger _logger; - private static readonly Dictionary XmlBlogsDataFileManifestPath = new Dictionary + private static readonly Dictionary XmlBlogsDataFileManifestPath = + new Dictionary { - {TestDataSelection.Small, "DataLayer.Startup.Internal.BlogsContentSimple.xml"}, - {TestDataSelection.Medium, "DataLayer.Startup.Internal.BlogsContextMedium.xml"} + { TestDataSelection.Small, "DataLayer.Startup.Internal.BlogsContentSimple.xml" }, + { TestDataSelection.Medium, "DataLayer.Startup.Internal.BlogsContextMedium.xml" } }; /// - /// This should be called at Startup + /// Called at startup. Ensures the database exists (creating it if the provider allows) + /// and seeds it with test data if it is empty. /// - /// true if running on azure (used for configuring retry policy and BuildSqlConnectionString UserId) - /// true if the database provider allows the app to drop/create a database - public static void InitialiseThis(bool isAzure, bool canCreateDatabase) + public static void InitialiseThis(SampleWebAppDb context, bool canCreateDatabase, + TestDataSelection selection = TestDataSelection.Medium) { - EfConfiguration.IsAzure = isAzure; _logger = GenericLibsBaseConfig.GetLogger("DataLayerInitialise"); - //Initialiser for the database. Only used when first access is made if (canCreateDatabase) - Database.SetInitializer(new CreateDatabaseIfNotExists()); - else - //This initializer will not try to change the database - Database.SetInitializer(new NullDatabaseInitializer()); + context.Database.EnsureCreated(); + + if (!context.Blogs.Any()) + ResetBlogs(context, selection); } public static void ResetBlogs(SampleWebAppDb context, TestDataSelection selection) { + _logger = _logger ?? GenericLibsBaseConfig.GetLogger("DataLayerInitialise"); + try { context.Posts.ToList().ForEach(x => context.Posts.Remove(x)); @@ -92,7 +66,5 @@ public static void ResetBlogs(SampleWebAppDb context, TestDataSelection selectio throw new FormatException("problem writing to database. See log."); } } - } - } diff --git a/DataLayer/Startup/DataLayerModule.cs b/DataLayer/Startup/DataLayerModule.cs deleted file mode 100644 index b2a6ecf..0000000 --- a/DataLayer/Startup/DataLayerModule.cs +++ /dev/null @@ -1,50 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: DataLayerModule.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -using System.Runtime.CompilerServices; -using Autofac; -using DataLayer.DataClasses; -using GenericServices; - -[assembly: InternalsVisibleTo("Tests")] - -namespace DataLayer.Startup -{ - public class DataLayerModule : Module - { - - protected override void Load(ContainerBuilder builder) - { - - //Autowire the classes with interfaces - builder.RegisterAssemblyTypes(GetType().Assembly).AsImplementedInterfaces(); - - //set Entity Framework context to instance per lifetime scope. - //This is important as we get one context per lifetime, so all db classes are tracked together. - builder.RegisterType().As().As().InstancePerLifetimeScope(); - } - } -} diff --git a/DataLayer/packages.config b/DataLayer/packages.config deleted file mode 100644 index 79bc6a6..0000000 --- a/DataLayer/packages.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/GenericServices/Core/CrudFunctions.cs b/GenericServices/Core/CrudFunctions.cs new file mode 100644 index 0000000..0972935 --- /dev/null +++ b/GenericServices/Core/CrudFunctions.cs @@ -0,0 +1,20 @@ +using System; + +namespace GenericServices.Core +{ + /// + /// Flags describing which CRUD operations a DTO supports. + /// Compatibility replacement for the original GenericServices CrudFunctions enum. + /// + [Flags] + public enum CrudFunctions + { + None = 0, + List = 1, + Detail = 2, + Create = 4, + Update = 8, + Delete = 16, + AllCrud = List | Detail | Create | Update | Delete + } +} diff --git a/GenericServices/Core/DtoMappingHelper.cs b/GenericServices/Core/DtoMappingHelper.cs new file mode 100644 index 0000000..40d02c7 --- /dev/null +++ b/GenericServices/Core/DtoMappingHelper.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace GenericServices.Core +{ + /// + /// Helper methods used by the EfGenericDto base classes to copy a DTO back onto its + /// data entity and to locate the primary key. + /// + internal static class DtoMappingHelper + { + /// + /// Copies the simple (and directly assignable navigation) properties from the DTO onto the + /// data entity. Properties marked with [DoNotCopyBackToDatabase], read only properties and + /// properties with no assignable target on the entity are skipped. + /// + public static void CopyDtoToData(object source, object destination) + { + var destType = destination.GetType(); + foreach (var srcProp in source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + if (!srcProp.CanRead) + continue; + if (srcProp.GetCustomAttribute() != null) + continue; + if (srcProp.GetIndexParameters().Length != 0) + continue; + + var destProp = destType.GetProperty(srcProp.Name, BindingFlags.Public | BindingFlags.Instance); + if (destProp == null || !destProp.CanWrite) + continue; + if (!destProp.PropertyType.IsAssignableFrom(srcProp.PropertyType)) + continue; + + destProp.SetValue(destination, srcProp.GetValue(source)); + } + } + + public static string GetKeyName(Type dtoType) + { + var keyProp = dtoType.GetProperties(BindingFlags.Public | BindingFlags.Instance) + .FirstOrDefault(p => p.GetCustomAttribute() != null); + if (keyProp == null) + throw new InvalidOperationException( + string.Format("The type {0} does not have a property marked with [Key].", dtoType.Name)); + return keyProp.Name; + } + + public static object GetKeyValue(object dto) + { + var keyName = GetKeyName(dto.GetType()); + return dto.GetType().GetProperty(keyName).GetValue(dto); + } + + public static Expression> BuildKeyEquals(string keyName, int id) + { + var param = Expression.Parameter(typeof(TData), "e"); + var body = Expression.Equal(Expression.Property(param, keyName), Expression.Constant(id)); + return Expression.Lambda>(body, param); + } + } +} diff --git a/GenericServices/Core/EfGenericDto.cs b/GenericServices/Core/EfGenericDto.cs new file mode 100644 index 0000000..3a5b2b7 --- /dev/null +++ b/GenericServices/Core/EfGenericDto.cs @@ -0,0 +1,116 @@ +using System.Linq; +using System.Threading.Tasks; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using GenericLibsBase.Core; + +namespace GenericServices.Core +{ + /// + /// Synchronous DTO base. TData is the database entity, TDto is the derived DTO type + /// (curiously recurring generic pattern, matching the original GenericServices design). + /// + public abstract class EfGenericDto : EfGenericDtoBase + where TData : class, new() + where TDto : EfGenericDto, new() + { + //-------------------------------------------------- + //overridable behaviour + + /// + /// Override to set up any secondary data (drop down lists etc.) needed by the view. + /// + protected virtual void SetupSecondaryData(IGenericServicesDbContext context, TDto dto) + { + } + + /// + /// Override to control how a new entity is created from the DTO. + /// + protected virtual ISuccessOrErrors CreateDataFromDto(IGenericServicesDbContext context, TDto source) + { + var data = new TData(); + DtoMappingHelper.CopyDtoToData(source, data); + return new SuccessOrErrors().SetSuccessWithResult(data, "Created the data from the dto."); + } + + /// + /// Override to control how an existing entity is updated from the DTO. + /// + protected virtual ISuccessOrErrors UpdateDataFromDto(IGenericServicesDbContext context, TDto source, TData destination) + { + DtoMappingHelper.CopyDtoToData(source, destination); + return SuccessOrErrors.Success("Updated the data from the dto."); + } + + //-------------------------------------------------- + //internal plumbing used by the service layer + + internal override IQueryable BuildListQuery(IGenericServicesDbContext context, IMapper mapper) + { + return context.Set().ProjectTo(mapper.ConfigurationProvider); + } + + internal override object GetDetailDto(IGenericServicesDbContext context, IMapper mapper, int id) + { + var keyName = DtoMappingHelper.GetKeyName(typeof(TDto)); + var dto = context.Set() + .Where(DtoMappingHelper.BuildKeyEquals(keyName, id)) + .ProjectTo(mapper.ConfigurationProvider) + .SingleOrDefault(); + + if (dto != null) + dto.SetupSecondaryData(context, dto); + return dto; + } + + internal override Task GetDetailDtoAsync(IGenericServicesDbContext context, IMapper mapper, int id) + { + return Task.FromResult(GetDetailDto(context, mapper, id)); + } + + internal override void RunSetupSecondaryData(IGenericServicesDbContext context, IMapper mapper) + { + SetupSecondaryData(context, (TDto)(object)this); + } + + internal override Task RunSetupSecondaryDataAsync(IGenericServicesDbContext context, IMapper mapper) + { + RunSetupSecondaryData(context, mapper); + return Task.CompletedTask; + } + + internal override ISuccessOrErrors CreateInDb(IGenericServicesDbContext context, IMapper mapper) + { + var status = CreateDataFromDto(context, (TDto)(object)this); + if (!status.IsValid) + return status; + context.Set().Add(status.Result); + return context.SaveChangesWithChecking(); + } + + internal override Task CreateInDbAsync(IGenericServicesDbContext context, IMapper mapper) + { + return Task.FromResult(CreateInDb(context, mapper)); + } + + internal override ISuccessOrErrors UpdateInDb(IGenericServicesDbContext context, IMapper mapper) + { + var keyValue = DtoMappingHelper.GetKeyValue(this); + var data = context.Set().Find(keyValue); + if (data == null) + return new SuccessOrErrors().AddSingleError( + "Could not find the {0} you requested. Did another user delete it?", typeof(TData).Name); + + var status = UpdateDataFromDto(context, (TDto)(object)this, data); + if (!status.IsValid) + return status; + return context.SaveChangesWithChecking(); + } + + internal override Task UpdateInDbAsync(IGenericServicesDbContext context, IMapper mapper) + { + return Task.FromResult(UpdateInDb(context, mapper)); + } + } +} diff --git a/GenericServices/Core/EfGenericDtoAsync.cs b/GenericServices/Core/EfGenericDtoAsync.cs new file mode 100644 index 0000000..1159079 --- /dev/null +++ b/GenericServices/Core/EfGenericDtoAsync.cs @@ -0,0 +1,113 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using GenericLibsBase.Core; +using Microsoft.EntityFrameworkCore; + +namespace GenericServices.Core +{ + /// + /// Asynchronous DTO base. Mirrors but exposes async hooks. + /// + public abstract class EfGenericDtoAsync : EfGenericDtoBase + where TData : class, new() + where TDto : EfGenericDtoAsync, new() + { + //-------------------------------------------------- + //overridable behaviour + + protected virtual Task SetupSecondaryDataAsync(IGenericServicesDbContext context, TDto dto) + { + return Task.CompletedTask; + } + + protected virtual Task> CreateDataFromDtoAsync(IGenericServicesDbContext context, TDto source) + { + var data = new TData(); + DtoMappingHelper.CopyDtoToData(source, data); + ISuccessOrErrors status = new SuccessOrErrors().SetSuccessWithResult(data, "Created the data from the dto."); + return Task.FromResult(status); + } + + protected virtual Task UpdateDataFromDtoAsync(IGenericServicesDbContext context, TDto source, TData destination) + { + DtoMappingHelper.CopyDtoToData(source, destination); + return Task.FromResult(SuccessOrErrors.Success("Updated the data from the dto.")); + } + + //-------------------------------------------------- + //internal plumbing used by the service layer + + internal override IQueryable BuildListQuery(IGenericServicesDbContext context, IMapper mapper) + { + return context.Set().ProjectTo(mapper.ConfigurationProvider); + } + + internal override async Task GetDetailDtoAsync(IGenericServicesDbContext context, IMapper mapper, int id) + { + var keyName = DtoMappingHelper.GetKeyName(typeof(TDto)); + var dto = await context.Set() + .Where(DtoMappingHelper.BuildKeyEquals(keyName, id)) + .ProjectTo(mapper.ConfigurationProvider) + .SingleOrDefaultAsync() + .ConfigureAwait(false); + + if (dto != null) + await dto.SetupSecondaryDataAsync(context, dto).ConfigureAwait(false); + return dto; + } + + internal override async Task RunSetupSecondaryDataAsync(IGenericServicesDbContext context, IMapper mapper) + { + await SetupSecondaryDataAsync(context, (TDto)(object)this).ConfigureAwait(false); + } + + internal override async Task CreateInDbAsync(IGenericServicesDbContext context, IMapper mapper) + { + var status = await CreateDataFromDtoAsync(context, (TDto)(object)this).ConfigureAwait(false); + if (!status.IsValid) + return status; + context.Set().Add(status.Result); + return await context.SaveChangesWithCheckingAsync().ConfigureAwait(false); + } + + internal override async Task UpdateInDbAsync(IGenericServicesDbContext context, IMapper mapper) + { + var keyValue = DtoMappingHelper.GetKeyValue(this); + var data = await context.Set().FindAsync(keyValue).ConfigureAwait(false); + if (data == null) + return new SuccessOrErrors().AddSingleError( + "Could not find the {0} you requested. Did another user delete it?", typeof(TData).Name); + + var status = await UpdateDataFromDtoAsync(context, (TDto)(object)this, data).ConfigureAwait(false); + if (!status.IsValid) + return status; + return await context.SaveChangesWithCheckingAsync().ConfigureAwait(false); + } + + //-------------------------------------------------- + //synchronous members are not supported on async DTOs (they are always used with async services) + + internal override object GetDetailDto(IGenericServicesDbContext context, IMapper mapper, int id) + { + throw new NotSupportedException("Async DTOs must be used with the async service methods."); + } + + internal override void RunSetupSecondaryData(IGenericServicesDbContext context, IMapper mapper) + { + throw new NotSupportedException("Async DTOs must be used with the async service methods."); + } + + internal override ISuccessOrErrors CreateInDb(IGenericServicesDbContext context, IMapper mapper) + { + throw new NotSupportedException("Async DTOs must be used with the async service methods."); + } + + internal override ISuccessOrErrors UpdateInDb(IGenericServicesDbContext context, IMapper mapper) + { + throw new NotSupportedException("Async DTOs must be used with the async service methods."); + } + } +} diff --git a/GenericServices/Core/EfGenericDtoBase.cs b/GenericServices/Core/EfGenericDtoBase.cs new file mode 100644 index 0000000..bc9551e --- /dev/null +++ b/GenericServices/Core/EfGenericDtoBase.cs @@ -0,0 +1,38 @@ +using System.Linq; +using System.Threading.Tasks; +using AutoMapper; +using GenericLibsBase.Core; + +namespace GenericServices.Core +{ + /// + /// Non generic base class for all DTOs handled by the service layer. The service layer talks to + /// the DTOs through these internal members so it does not need to know the entity (TData) type. + /// Compatibility replacement for the original GenericServices EfGenericDtoBase. + /// + public abstract class EfGenericDtoBase + { + /// + /// Defines which CRUD functions the DTO supports. + /// + protected abstract CrudFunctions SupportedFunctions { get; } + + internal abstract IQueryable BuildListQuery(IGenericServicesDbContext context, IMapper mapper); + + internal abstract object GetDetailDto(IGenericServicesDbContext context, IMapper mapper, int id); + + internal abstract Task GetDetailDtoAsync(IGenericServicesDbContext context, IMapper mapper, int id); + + internal abstract void RunSetupSecondaryData(IGenericServicesDbContext context, IMapper mapper); + + internal abstract Task RunSetupSecondaryDataAsync(IGenericServicesDbContext context, IMapper mapper); + + internal abstract ISuccessOrErrors CreateInDb(IGenericServicesDbContext context, IMapper mapper); + + internal abstract Task CreateInDbAsync(IGenericServicesDbContext context, IMapper mapper); + + internal abstract ISuccessOrErrors UpdateInDb(IGenericServicesDbContext context, IMapper mapper); + + internal abstract Task UpdateInDbAsync(IGenericServicesDbContext context, IMapper mapper); + } +} diff --git a/GenericServices/Core/SaveChangesExtensions.cs b/GenericServices/Core/SaveChangesExtensions.cs new file mode 100644 index 0000000..07aa0bf --- /dev/null +++ b/GenericServices/Core/SaveChangesExtensions.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading.Tasks; +using GenericLibsBase.Core; +using Microsoft.EntityFrameworkCore; + +namespace GenericServices +{ + /// + /// Provides EF Core save helpers that run DataAnnotations / IValidatableObject validation + /// (which EF Core does not do automatically) and return an . + /// + public static class SaveChangesExtensions + { + public static ISuccessOrErrors SaveChangesWithChecking(this IGenericServicesDbContext context) + { + var errors = CollectValidationErrors(context); + if (errors.Count > 0) + return BuildErrorStatus(errors); + + context.SaveChanges(); + return SuccessOrErrors.Success("Successfully saved the data."); + } + + public static async Task SaveChangesWithCheckingAsync(this IGenericServicesDbContext context) + { + var errors = CollectValidationErrors(context); + if (errors.Count > 0) + return BuildErrorStatus(errors); + + await context.SaveChangesAsync().ConfigureAwait(false); + return SuccessOrErrors.Success("Successfully saved the data."); + } + + private static List CollectValidationErrors(IGenericServicesDbContext context) + { + var errors = new List(); + var changed = context.ChangeTracker.Entries() + .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified) + .ToList(); + + foreach (var entry in changed) + { + var entity = entry.Entity; + var validationContext = new ValidationContext(entity, null, null); + var entityErrors = new List(); + Validator.TryValidateObject(entity, validationContext, entityErrors, true); + errors.AddRange(entityErrors); + errors.AddRange(context.ExtraValidation(entry)); + } + + return errors; + } + + private static ISuccessOrErrors BuildErrorStatus(IEnumerable errors) + { + var status = new SuccessOrErrors(); + status.AddValidationResults(errors); + return status; + } + } +} diff --git a/GenericServices/Core/SuccessOrErrors.cs b/GenericServices/Core/SuccessOrErrors.cs new file mode 100644 index 0000000..5c41db4 --- /dev/null +++ b/GenericServices/Core/SuccessOrErrors.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; + +namespace GenericLibsBase.Core +{ + /// + /// Non generic status result used throughout the service layer. + /// Compatibility replacement for the original GenericLibsBase ISuccessOrErrors. + /// + public interface ISuccessOrErrors + { + /// + /// True if the operation completed with no errors and a success message has been set. + /// + bool IsValid { get; } + + /// + /// The success message (only meaningful when IsValid is true). + /// + string SuccessMessage { get; } + + /// + /// The list of errors. Each error may carry the names of the members it relates to. + /// + IReadOnlyList Errors { get; } + + /// + /// Adds an error that is not associated with a specific property. + /// + ISuccessOrErrors AddSingleError(string errorformat, params object[] args); + + /// + /// Adds an error associated with the named property/parameter. + /// + ISuccessOrErrors AddNamedParameterError(string parameterName, string errorformat, params object[] args); + + /// + /// Sets the success message and marks the result as valid (assuming no errors). + /// + ISuccessOrErrors SetSuccessMessage(string successformat, params object[] args); + + /// + /// Returns the errors as a simple html (br separated) string. + /// + string ErrorsAsHtml(); + } + + /// + /// Generic version that also carries a result of type T. + /// + public interface ISuccessOrErrors : ISuccessOrErrors + { + T Result { get; } + + ISuccessOrErrors SetSuccessWithResult(T result, string successformat, params object[] args); + } + + /// + /// Default implementation of . + /// + public class SuccessOrErrors : ISuccessOrErrors + { + private readonly List _errors = new List(); + private bool _hasBeenSuccessfullySet; + private string _successMessage = string.Empty; + + public IReadOnlyList Errors => _errors; + + public bool IsValid => _hasBeenSuccessfullySet && _errors.Count == 0; + + public string SuccessMessage => IsValid ? _successMessage : string.Empty; + + public ISuccessOrErrors AddSingleError(string errorformat, params object[] args) + { + _errors.Add(new ValidationResult(string.Format(errorformat, args))); + return this; + } + + public ISuccessOrErrors AddNamedParameterError(string parameterName, string errorformat, params object[] args) + { + _errors.Add(new ValidationResult(string.Format(errorformat, args), new[] { parameterName })); + return this; + } + + public ISuccessOrErrors SetSuccessMessage(string successformat, params object[] args) + { + _successMessage = string.Format(successformat, args); + _hasBeenSuccessfullySet = true; + return this; + } + + public string ErrorsAsHtml() + { + var sb = new StringBuilder(); + foreach (var error in _errors) + { + if (sb.Length > 0) + sb.Append("
"); + sb.Append(error.ErrorMessage); + } + return sb.ToString(); + } + + internal void AddValidationResults(IEnumerable results) + { + _errors.AddRange(results); + } + + /// + /// Creates a new, valid status with the given success message. + /// + public static ISuccessOrErrors Success(string successformat, params object[] args) + { + return new SuccessOrErrors().SetSuccessMessage(successformat, args); + } + } + + /// + /// Default implementation of . + /// + public class SuccessOrErrors : ISuccessOrErrors + { + private readonly List _errors = new List(); + private bool _hasBeenSuccessfullySet; + private string _successMessage = string.Empty; + + public T Result { get; private set; } + + public IReadOnlyList Errors => _errors; + + public bool IsValid => _hasBeenSuccessfullySet && _errors.Count == 0; + + public string SuccessMessage => IsValid ? _successMessage : string.Empty; + + public ISuccessOrErrors AddSingleError(string errorformat, params object[] args) + { + _errors.Add(new ValidationResult(string.Format(errorformat, args))); + return this; + } + + public ISuccessOrErrors AddNamedParameterError(string parameterName, string errorformat, params object[] args) + { + _errors.Add(new ValidationResult(string.Format(errorformat, args), new[] { parameterName })); + return this; + } + + public ISuccessOrErrors SetSuccessMessage(string successformat, params object[] args) + { + _successMessage = string.Format(successformat, args); + _hasBeenSuccessfullySet = true; + return this; + } + + public ISuccessOrErrors SetSuccessWithResult(T result, string successformat, params object[] args) + { + Result = result; + _successMessage = string.Format(successformat, args); + _hasBeenSuccessfullySet = true; + return this; + } + + public string ErrorsAsHtml() + { + var sb = new StringBuilder(); + foreach (var error in _errors) + { + if (sb.Length > 0) + sb.Append("
"); + sb.Append(error.ErrorMessage); + } + return sb.ToString(); + } + + internal void AddValidationResults(IEnumerable results) + { + _errors.AddRange(results); + } + + /// + /// Copies the errors from a non result status into a new typed status (with default result). + /// + public static ISuccessOrErrors ConvertNonResultStatus(ISuccessOrErrors status) + { + var result = new SuccessOrErrors(); + result.AddValidationResults(status.Errors); + return result; + } + } +} diff --git a/GenericServices/DoNotCopyBackToDatabaseAttribute.cs b/GenericServices/DoNotCopyBackToDatabaseAttribute.cs new file mode 100644 index 0000000..48b6442 --- /dev/null +++ b/GenericServices/DoNotCopyBackToDatabaseAttribute.cs @@ -0,0 +1,14 @@ +using System; + +namespace GenericServices +{ + /// + /// Marks a DTO property that should NOT be copied back to the database entity + /// when creating or updating (e.g. values managed by the data layer such as LastUpdated). + /// Compatibility replacement for the original GenericServices attribute. + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] + public class DoNotCopyBackToDatabaseAttribute : Attribute + { + } +} diff --git a/GenericServices/GenericServices.csproj b/GenericServices/GenericServices.csproj new file mode 100644 index 0000000..b7cd8f5 --- /dev/null +++ b/GenericServices/GenericServices.csproj @@ -0,0 +1,15 @@ + + + + net9.0 + disable + disable + GenericServices + + + + + + + + diff --git a/GenericServices/IGenericServicesDbContext.cs b/GenericServices/IGenericServicesDbContext.cs new file mode 100644 index 0000000..b431f34 --- /dev/null +++ b/GenericServices/IGenericServicesDbContext.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.EntityFrameworkCore.Infrastructure; + +namespace GenericServices +{ + /// + /// Abstraction over the application's EF Core DbContext that the service layer depends on. + /// Compatibility replacement for the original GenericServices IGenericServicesDbContext. + /// + public interface IGenericServicesDbContext + { + DbSet Set() where TEntity : class; + + EntityEntry Entry(TEntity entity) where TEntity : class; + + ChangeTracker ChangeTracker { get; } + + DatabaseFacade Database { get; } + + int SaveChanges(); + + Task SaveChangesAsync(CancellationToken cancellationToken = default); + + /// + /// Hook for database level validation that cannot be expressed via DataAnnotations + /// (e.g. uniqueness checks). Called for every Added/Modified entity before saving. + /// Return an empty sequence if there is nothing extra to validate. + /// + IEnumerable ExtraValidation(EntityEntry entry); + } +} diff --git a/GenericServices/Logging/GenericLibsBaseConfig.cs b/GenericServices/Logging/GenericLibsBaseConfig.cs new file mode 100644 index 0000000..f583cd6 --- /dev/null +++ b/GenericServices/Logging/GenericLibsBaseConfig.cs @@ -0,0 +1,53 @@ +using System; + +namespace GenericLibsBase +{ + /// + /// Compatibility replacement for the original GenericLibsBaseConfig static logger factory. + /// By default it writes to the .NET trace/console output. The host can replace the factory + /// via . + /// + public static class GenericLibsBaseConfig + { + private static Func _loggerFactory = name => new ConsoleGenericLogger(name); + + public static Func SetLoggerMethod + { + set { _loggerFactory = value; } + } + + public static IGenericLogger GetLogger(string name) + { + return _loggerFactory(name); + } + } + + /// + /// Simple default logger that writes to the console. + /// + public class ConsoleGenericLogger : IGenericLogger + { + private readonly string _name; + + public ConsoleGenericLogger(string name) + { + _name = name; + } + + private void Write(string level, string message) + { + Console.WriteLine("{0:u} [{1}] {2}: {3}", DateTime.UtcNow, level, _name, message); + } + + public void Verbose(string message) => Write("VERBOSE", message); + public void Info(string message) => Write("INFO", message); + public void InfoFormat(string format, params object[] args) => Write("INFO", string.Format(format, args)); + public void Warn(string message) => Write("WARN", message); + public void WarnFormat(string format, params object[] args) => Write("WARN", string.Format(format, args)); + public void Error(string message) => Write("ERROR", message); + public void Error(string message, Exception ex) => Write("ERROR", message + " " + ex); + public void Critical(string message) => Write("CRITICAL", message); + public void Critical(string message, Exception ex) => Write("CRITICAL", message + " " + ex); + public void CriticalFormat(string format, params object[] args) => Write("CRITICAL", string.Format(format, args)); + } +} diff --git a/GenericServices/Logging/IGenericLogger.cs b/GenericServices/Logging/IGenericLogger.cs new file mode 100644 index 0000000..eb69fcd --- /dev/null +++ b/GenericServices/Logging/IGenericLogger.cs @@ -0,0 +1,21 @@ +using System; + +namespace GenericLibsBase +{ + /// + /// Minimal logging abstraction kept for compatibility with the original GenericLibsBase package. + /// + public interface IGenericLogger + { + void Verbose(string message); + void Info(string message); + void InfoFormat(string format, params object[] args); + void Warn(string message); + void WarnFormat(string format, params object[] args); + void Error(string message); + void Error(string message, Exception ex); + void Critical(string message); + void Critical(string message, Exception ex); + void CriticalFormat(string format, params object[] args); + } +} diff --git a/GenericServices/Services/ServiceInterfaces.cs b/GenericServices/Services/ServiceInterfaces.cs new file mode 100644 index 0000000..0b4e5cb --- /dev/null +++ b/GenericServices/Services/ServiceInterfaces.cs @@ -0,0 +1,76 @@ +using System.Linq; +using System.Threading.Tasks; +using GenericLibsBase.Core; +using GenericServices.Core; + +namespace GenericServices +{ + public interface IListService + { + IQueryable GetAll() where TDto : EfGenericDtoBase, new(); + } + + public interface IDetailService + { + ISuccessOrErrors GetDetail(int id) where T : class, new(); + } + + public interface IDetailServiceAsync + { + Task> GetDetailAsync(int id) where T : class, new(); + } + + public interface ICreateSetupService + { + TDto GetDto() where TDto : EfGenericDtoBase, new(); + } + + public interface ICreateSetupServiceAsync + { + Task GetDtoAsync() where TDto : EfGenericDtoBase, new(); + } + + public interface ICreateService + { + ISuccessOrErrors Create(T item) where T : class; + T ResetDto(T dto) where T : class; + } + + public interface ICreateServiceAsync + { + Task CreateAsync(T item) where T : class; + Task ResetDtoAsync(T dto) where T : class; + } + + public interface IUpdateSetupService + { + ISuccessOrErrors GetOriginal(int id) where T : class, new(); + } + + public interface IUpdateSetupServiceAsync + { + Task> GetOriginalAsync(int id) where T : class, new(); + } + + public interface IUpdateService + { + ISuccessOrErrors Update(T item) where T : class; + T ResetDto(T dto) where T : class; + } + + public interface IUpdateServiceAsync + { + Task UpdateAsync(T item) where T : class; + Task ResetDtoAsync(T dto) where T : class; + } + + public interface IDeleteService + { + ISuccessOrErrors Delete(int id) where T : class; + } + + public interface IDeleteServiceAsync + { + Task DeleteAsync(int id) where T : class; + } +} diff --git a/GenericServices/Services/Services.cs b/GenericServices/Services/Services.cs new file mode 100644 index 0000000..d3958d5 --- /dev/null +++ b/GenericServices/Services/Services.cs @@ -0,0 +1,356 @@ +using System.Linq; +using System.Threading.Tasks; +using AutoMapper; +using GenericLibsBase.Core; +using GenericServices.Core; +using Microsoft.EntityFrameworkCore; + +namespace GenericServices +{ + /// + /// Shared helpers for the concrete service implementations. + /// + internal static class ServiceHelpers + { + public static bool IsDto() + { + return typeof(EfGenericDtoBase).IsAssignableFrom(typeof(T)); + } + } + + public class ListService : IListService + { + private readonly IGenericServicesDbContext _context; + private readonly IMapper _mapper; + + public ListService(IGenericServicesDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public IQueryable GetAll() where TDto : EfGenericDtoBase, new() + { + var dto = new TDto(); + return (IQueryable)dto.BuildListQuery(_context, _mapper); + } + } + + public class DetailService : IDetailService + { + private readonly IGenericServicesDbContext _context; + private readonly IMapper _mapper; + + public DetailService(IGenericServicesDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public ISuccessOrErrors GetDetail(int id) where T : class, new() + { + if (ServiceHelpers.IsDto()) + { + var dto = (EfGenericDtoBase)(object)new T(); + var result = (T)dto.GetDetailDto(_context, _mapper, id); + return WrapResult(result); + } + + var entity = _context.Set().Find(id); + return WrapResult(entity); + } + + private static ISuccessOrErrors WrapResult(T result) where T : class + { + if (result == null) + return (ISuccessOrErrors)new SuccessOrErrors() + .AddSingleError("We could not find the item you requested. Did another user delete it?"); + return new SuccessOrErrors().SetSuccessWithResult(result, "Success"); + } + } + + public class DetailServiceAsync : IDetailServiceAsync + { + private readonly IGenericServicesDbContext _context; + private readonly IMapper _mapper; + + public DetailServiceAsync(IGenericServicesDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task> GetDetailAsync(int id) where T : class, new() + { + T result; + if (ServiceHelpers.IsDto()) + { + var dto = (EfGenericDtoBase)(object)new T(); + result = (T)await dto.GetDetailDtoAsync(_context, _mapper, id).ConfigureAwait(false); + } + else + { + result = await _context.Set().FindAsync(id).ConfigureAwait(false); + } + + if (result == null) + return (ISuccessOrErrors)new SuccessOrErrors() + .AddSingleError("We could not find the item you requested. Did another user delete it?"); + return new SuccessOrErrors().SetSuccessWithResult(result, "Success"); + } + } + + public class CreateSetupService : ICreateSetupService + { + private readonly IGenericServicesDbContext _context; + private readonly IMapper _mapper; + + public CreateSetupService(IGenericServicesDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public TDto GetDto() where TDto : EfGenericDtoBase, new() + { + var dto = new TDto(); + dto.RunSetupSecondaryData(_context, _mapper); + return dto; + } + } + + public class CreateSetupServiceAsync : ICreateSetupServiceAsync + { + private readonly IGenericServicesDbContext _context; + private readonly IMapper _mapper; + + public CreateSetupServiceAsync(IGenericServicesDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task GetDtoAsync() where TDto : EfGenericDtoBase, new() + { + var dto = new TDto(); + await dto.RunSetupSecondaryDataAsync(_context, _mapper).ConfigureAwait(false); + return dto; + } + } + + public class CreateService : ICreateService + { + private readonly IGenericServicesDbContext _context; + private readonly IMapper _mapper; + + public CreateService(IGenericServicesDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public ISuccessOrErrors Create(T item) where T : class + { + if (item is EfGenericDtoBase dto) + return dto.CreateInDb(_context, _mapper); + + _context.Set().Add(item); + return _context.SaveChangesWithChecking(); + } + + public T ResetDto(T dto) where T : class + { + if (dto is EfGenericDtoBase baseDto) + baseDto.RunSetupSecondaryData(_context, _mapper); + return dto; + } + } + + public class CreateServiceAsync : ICreateServiceAsync + { + private readonly IGenericServicesDbContext _context; + private readonly IMapper _mapper; + + public CreateServiceAsync(IGenericServicesDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task CreateAsync(T item) where T : class + { + if (item is EfGenericDtoBase dto) + return await dto.CreateInDbAsync(_context, _mapper).ConfigureAwait(false); + + _context.Set().Add(item); + return await _context.SaveChangesWithCheckingAsync().ConfigureAwait(false); + } + + public async Task ResetDtoAsync(T dto) where T : class + { + if (dto is EfGenericDtoBase baseDto) + await baseDto.RunSetupSecondaryDataAsync(_context, _mapper).ConfigureAwait(false); + return dto; + } + } + + public class UpdateSetupService : IUpdateSetupService + { + private readonly IGenericServicesDbContext _context; + private readonly IMapper _mapper; + + public UpdateSetupService(IGenericServicesDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public ISuccessOrErrors GetOriginal(int id) where T : class, new() + { + if (ServiceHelpers.IsDto()) + { + var dto = (EfGenericDtoBase)(object)new T(); + var result = (T)dto.GetDetailDto(_context, _mapper, id); + return Wrap(result); + } + + var entity = _context.Set().Find(id); + return Wrap(entity); + } + + private static ISuccessOrErrors Wrap(T result) where T : class + { + if (result == null) + return (ISuccessOrErrors)new SuccessOrErrors() + .AddSingleError("We could not find the item you requested. Did another user delete it?"); + return new SuccessOrErrors().SetSuccessWithResult(result, "Success"); + } + } + + public class UpdateSetupServiceAsync : IUpdateSetupServiceAsync + { + private readonly IGenericServicesDbContext _context; + private readonly IMapper _mapper; + + public UpdateSetupServiceAsync(IGenericServicesDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task> GetOriginalAsync(int id) where T : class, new() + { + T result; + if (ServiceHelpers.IsDto()) + { + var dto = (EfGenericDtoBase)(object)new T(); + result = (T)await dto.GetDetailDtoAsync(_context, _mapper, id).ConfigureAwait(false); + } + else + { + result = await _context.Set().FindAsync(id).ConfigureAwait(false); + } + + if (result == null) + return (ISuccessOrErrors)new SuccessOrErrors() + .AddSingleError("We could not find the item you requested. Did another user delete it?"); + return new SuccessOrErrors().SetSuccessWithResult(result, "Success"); + } + } + + public class UpdateService : IUpdateService + { + private readonly IGenericServicesDbContext _context; + private readonly IMapper _mapper; + + public UpdateService(IGenericServicesDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public ISuccessOrErrors Update(T item) where T : class + { + if (item is EfGenericDtoBase dto) + return dto.UpdateInDb(_context, _mapper); + + _context.Set().Update(item); + return _context.SaveChangesWithChecking(); + } + + public T ResetDto(T dto) where T : class + { + if (dto is EfGenericDtoBase baseDto) + baseDto.RunSetupSecondaryData(_context, _mapper); + return dto; + } + } + + public class UpdateServiceAsync : IUpdateServiceAsync + { + private readonly IGenericServicesDbContext _context; + private readonly IMapper _mapper; + + public UpdateServiceAsync(IGenericServicesDbContext context, IMapper mapper) + { + _context = context; + _mapper = mapper; + } + + public async Task UpdateAsync(T item) where T : class + { + if (item is EfGenericDtoBase dto) + return await dto.UpdateInDbAsync(_context, _mapper).ConfigureAwait(false); + + _context.Set().Update(item); + return await _context.SaveChangesWithCheckingAsync().ConfigureAwait(false); + } + + public async Task ResetDtoAsync(T dto) where T : class + { + if (dto is EfGenericDtoBase baseDto) + await baseDto.RunSetupSecondaryDataAsync(_context, _mapper).ConfigureAwait(false); + return dto; + } + } + + public class DeleteService : IDeleteService + { + private readonly IGenericServicesDbContext _context; + + public DeleteService(IGenericServicesDbContext context) + { + _context = context; + } + + public ISuccessOrErrors Delete(int id) where T : class + { + var entity = _context.Set().Find(id); + if (entity == null) + return new SuccessOrErrors().AddSingleError( + "Could not delete the {0}. Did another user delete it already?", typeof(T).Name); + _context.Set().Remove(entity); + return _context.SaveChangesWithChecking(); + } + } + + public class DeleteServiceAsync : IDeleteServiceAsync + { + private readonly IGenericServicesDbContext _context; + + public DeleteServiceAsync(IGenericServicesDbContext context) + { + _context = context; + } + + public async Task DeleteAsync(int id) where T : class + { + var entity = await _context.Set().FindAsync(id).ConfigureAwait(false); + if (entity == null) + return new SuccessOrErrors().AddSingleError( + "Could not delete the {0}. Did another user delete it already?", typeof(T).Name); + _context.Set().Remove(entity); + return await _context.SaveChangesWithCheckingAsync().ConfigureAwait(false); + } + } +} diff --git a/SampleWebApp.sln b/SampleWebApp.sln index b474189..13afc6b 100644 --- a/SampleWebApp.sln +++ b/SampleWebApp.sln @@ -1,61 +1,102 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleWebApp", "SampleWebApp\SampleWebApp.csproj", "{CFFEE5E0-3B99-46E0-9A82-2E74621C17C5}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenericServices", "GenericServices\GenericServices.csproj", "{76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataLayer", "DataLayer\DataLayer.csproj", "{264E1878-12DE-4099-B8D7-CC53A73FEA49}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataLayer", "DataLayer\DataLayer.csproj", "{20213A08-43B4-4272-95D9-CF381116AA58}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceLayer", "ServiceLayer\ServiceLayer.csproj", "{D2813927-0F38-43C3-B47C-AE8F00D50CAE}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceLayer", "ServiceLayer\ServiceLayer.csproj", "{72CCFD00-2A03-4C80-9FC7-57BE80502CFF}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{6D9E7904-B2AC-49E3-83A7-6B48876F46B9}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizLayer", "BizLayer\BizLayer.csproj", "{AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleWebApp", "SampleWebApp\SampleWebApp.csproj", "{14613D02-F6A5-4E6B-92E2-1E9A42C33F39}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{AF8764F7-FBEE-48AD-AF62-23010DA35D70}" - ProjectSection(SolutionItems) = preProject - Licence.txt = Licence.txt - README.md = README.md - EndProjectSection +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{6D9E7904-B2AC-49E3-83A7-6B48876F46B9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - AzureRelease|Any CPU = AzureRelease|Any CPU Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - WebWizRelease|Any CPU = WebWizRelease|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CFFEE5E0-3B99-46E0-9A82-2E74621C17C5}.AzureRelease|Any CPU.ActiveCfg = AzureRelease|Any CPU - {CFFEE5E0-3B99-46E0-9A82-2E74621C17C5}.AzureRelease|Any CPU.Build.0 = AzureRelease|Any CPU - {CFFEE5E0-3B99-46E0-9A82-2E74621C17C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CFFEE5E0-3B99-46E0-9A82-2E74621C17C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CFFEE5E0-3B99-46E0-9A82-2E74621C17C5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CFFEE5E0-3B99-46E0-9A82-2E74621C17C5}.Release|Any CPU.Build.0 = Release|Any CPU - {CFFEE5E0-3B99-46E0-9A82-2E74621C17C5}.WebWizRelease|Any CPU.ActiveCfg = WebWizRelease|Any CPU - {CFFEE5E0-3B99-46E0-9A82-2E74621C17C5}.WebWizRelease|Any CPU.Build.0 = WebWizRelease|Any CPU - {264E1878-12DE-4099-B8D7-CC53A73FEA49}.AzureRelease|Any CPU.ActiveCfg = AzureRelease|Any CPU - {264E1878-12DE-4099-B8D7-CC53A73FEA49}.AzureRelease|Any CPU.Build.0 = AzureRelease|Any CPU - {264E1878-12DE-4099-B8D7-CC53A73FEA49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {264E1878-12DE-4099-B8D7-CC53A73FEA49}.Debug|Any CPU.Build.0 = Debug|Any CPU - {264E1878-12DE-4099-B8D7-CC53A73FEA49}.Release|Any CPU.ActiveCfg = Release|Any CPU - {264E1878-12DE-4099-B8D7-CC53A73FEA49}.Release|Any CPU.Build.0 = Release|Any CPU - {264E1878-12DE-4099-B8D7-CC53A73FEA49}.WebWizRelease|Any CPU.ActiveCfg = WebWizRelease|Any CPU - {264E1878-12DE-4099-B8D7-CC53A73FEA49}.WebWizRelease|Any CPU.Build.0 = WebWizRelease|Any CPU - {D2813927-0F38-43C3-B47C-AE8F00D50CAE}.AzureRelease|Any CPU.ActiveCfg = AzureRelease|Any CPU - {D2813927-0F38-43C3-B47C-AE8F00D50CAE}.AzureRelease|Any CPU.Build.0 = AzureRelease|Any CPU - {D2813927-0F38-43C3-B47C-AE8F00D50CAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D2813927-0F38-43C3-B47C-AE8F00D50CAE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D2813927-0F38-43C3-B47C-AE8F00D50CAE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D2813927-0F38-43C3-B47C-AE8F00D50CAE}.Release|Any CPU.Build.0 = Release|Any CPU - {D2813927-0F38-43C3-B47C-AE8F00D50CAE}.WebWizRelease|Any CPU.ActiveCfg = WebWizRelease|Any CPU - {D2813927-0F38-43C3-B47C-AE8F00D50CAE}.WebWizRelease|Any CPU.Build.0 = WebWizRelease|Any CPU - {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.AzureRelease|Any CPU.ActiveCfg = AzureRelease|Any CPU - {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.AzureRelease|Any CPU.Build.0 = AzureRelease|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Debug|x64.ActiveCfg = Debug|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Debug|x64.Build.0 = Debug|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Debug|x86.ActiveCfg = Debug|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Debug|x86.Build.0 = Debug|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Release|Any CPU.Build.0 = Release|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Release|x64.ActiveCfg = Release|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Release|x64.Build.0 = Release|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Release|x86.ActiveCfg = Release|Any CPU + {76FEDF03-DC41-4C0A-8480-4EB3C1C2C80C}.Release|x86.Build.0 = Release|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Debug|x64.ActiveCfg = Debug|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Debug|x64.Build.0 = Debug|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Debug|x86.ActiveCfg = Debug|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Debug|x86.Build.0 = Debug|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Release|Any CPU.Build.0 = Release|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Release|x64.ActiveCfg = Release|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Release|x64.Build.0 = Release|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Release|x86.ActiveCfg = Release|Any CPU + {20213A08-43B4-4272-95D9-CF381116AA58}.Release|x86.Build.0 = Release|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Debug|x64.ActiveCfg = Debug|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Debug|x64.Build.0 = Debug|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Debug|x86.ActiveCfg = Debug|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Debug|x86.Build.0 = Debug|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Release|Any CPU.Build.0 = Release|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Release|x64.ActiveCfg = Release|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Release|x64.Build.0 = Release|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Release|x86.ActiveCfg = Release|Any CPU + {72CCFD00-2A03-4C80-9FC7-57BE80502CFF}.Release|x86.Build.0 = Release|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Debug|x64.ActiveCfg = Debug|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Debug|x64.Build.0 = Debug|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Debug|x86.ActiveCfg = Debug|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Debug|x86.Build.0 = Debug|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Release|Any CPU.Build.0 = Release|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Release|x64.ActiveCfg = Release|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Release|x64.Build.0 = Release|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Release|x86.ActiveCfg = Release|Any CPU + {AD5E9FE9-BC77-405D-B853-F0C6E6051A8F}.Release|x86.Build.0 = Release|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Debug|x64.ActiveCfg = Debug|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Debug|x64.Build.0 = Debug|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Debug|x86.ActiveCfg = Debug|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Debug|x86.Build.0 = Debug|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Release|Any CPU.Build.0 = Release|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Release|x64.ActiveCfg = Release|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Release|x64.Build.0 = Release|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Release|x86.ActiveCfg = Release|Any CPU + {14613D02-F6A5-4E6B-92E2-1E9A42C33F39}.Release|x86.Build.0 = Release|Any CPU {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Debug|x64.ActiveCfg = Debug|Any CPU + {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Debug|x64.Build.0 = Debug|Any CPU + {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Debug|x86.ActiveCfg = Debug|Any CPU + {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Debug|x86.Build.0 = Debug|Any CPU {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Release|Any CPU.Build.0 = Release|Any CPU - {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.WebWizRelease|Any CPU.ActiveCfg = WebWizRelease|Any CPU + {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Release|x64.ActiveCfg = Release|Any CPU + {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Release|x64.Build.0 = Release|Any CPU + {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Release|x86.ActiveCfg = Release|Any CPU + {6D9E7904-B2AC-49E3-83A7-6B48876F46B9}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/SampleWebApp/App_Start/BundleConfig.cs b/SampleWebApp/App_Start/BundleConfig.cs deleted file mode 100644 index ab2ec9f..0000000 --- a/SampleWebApp/App_Start/BundleConfig.cs +++ /dev/null @@ -1,97 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: BundleConfig.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -using System.Web.Optimization; - -namespace SampleWebApp -{ - public class BundleConfig - { - // For more information on bundling, visit http://go.microsoft.com/fwlink/?LinkId=301862 - public static void RegisterBundles(BundleCollection bundles) - { - - bundles.Add(new ScriptBundle("~/bundles/javascript").Include( - "~/Scripts/jquery-{version}.js", - "~/Scripts/bootstrap.js", - "~/Scripts/respond.js")); - - bundles.Add(new StyleBundle("~/Content/css").Include( - "~/Content/bootstrap.css", - //"~/Content/notify.css", - not used any more - "~/Content/site.css")); - - bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include( - "~/Scripts/jquery.validate*")); - - //------------------------------------------------------------------ - //the bundles below were once used but not any more. Left as I might need them for the code that has been moved out to another library - - //Combined with bootstrap.js into one javascript bundle - //bundles.Add(new ScriptBundle("~/bundles/jquery").Include( - // "~/Scripts/jquery-{version}.js")); - // - //bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include( - // "~/Scripts/bootstrap.js", - // "~/Scripts/respond.js")); - - //Not used anymore - //bundles.Add(new ScriptBundle("~/bundles/ActionRunner").Include( - // "~/Scripts/jquery-notify.js", - // "~/Scripts/jquery-ui-{version}.js", - // "~/Scripts/jquery.signalR-{version}.js", - // "~/Scripts/ActionRunnerComms.js", - // "~/Scripts/ActionRunnerUi.js")); - - //Not used - //// Use the development version of Modernizr to develop with and learn from. Then, when you're - //// ready for production, use the build tool at http://modernizr.com to pick only the tests you need. - //bundles.Add(new ScriptBundle("~/bundles/modernizr").Include( - // "~/Scripts/modernizr-*")); - - //Not used any more - ////Commented out the parts that are not used from the jQuery UI - //bundles.Add(new StyleBundle("~/Content/themes/base/css").Include( - // "~/Content/themes/base/jquery.ui.core.css", - // "~/Content/themes/base/jquery.ui.resizable.css", - // "~/Content/themes/base/jquery.ui.selectable.css", - // //"~/Content/themes/base/jquery.ui.accordion.css", - // //"~/Content/themes/base/jquery.ui.autocomplete.css", - // //"~/Content/themes/base/jquery.ui.button.css", - // "~/Content/themes/base/jquery.ui.dialog.css", - // //"~/Content/themes/base/jquery.ui.slider.css", - // //"~/Content/themes/base/jquery.ui.tabs.css", - // //"~/Content/themes/base/jquery.ui.datepicker.css", - // "~/Content/themes/base/jquery.ui.progressbar.css", - // "~/Content/themes/base/jquery.ui.theme.css")); - - // Set EnableOptimizations to false for debugging. For more information, - // visit http://go.microsoft.com/fwlink/?LinkId=301862 - //BundleTable.EnableOptimizations = true; - } - } -} diff --git a/SampleWebApp/App_Start/FilterConfig.cs b/SampleWebApp/App_Start/FilterConfig.cs deleted file mode 100644 index 3325902..0000000 --- a/SampleWebApp/App_Start/FilterConfig.cs +++ /dev/null @@ -1,39 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: FilterConfig.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -using System.Web; -using System.Web.Mvc; - -namespace SampleWebApp -{ - public class FilterConfig - { - public static void RegisterGlobalFilters(GlobalFilterCollection filters) - { - filters.Add(new HandleErrorAttribute()); - } - } -} diff --git a/SampleWebApp/App_Start/RouteConfig.cs b/SampleWebApp/App_Start/RouteConfig.cs deleted file mode 100644 index 5666db8..0000000 --- a/SampleWebApp/App_Start/RouteConfig.cs +++ /dev/null @@ -1,49 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: RouteConfig.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Mvc; -using System.Web.Routing; - -namespace SampleWebApp -{ - public class RouteConfig - { - public static void RegisterRoutes(RouteCollection routes) - { - routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); - - routes.MapRoute( - name: "Default", - url: "{controller}/{action}/{id}", - defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } - ); - } - } -} diff --git a/SampleWebApp/Controllers/BlogsController.cs b/SampleWebApp/Controllers/BlogsController.cs index 0027062..ac8ab76 100644 --- a/SampleWebApp/Controllers/BlogsController.cs +++ b/SampleWebApp/Controllers/BlogsController.cs @@ -1,4 +1,4 @@ -#region licence +#region licence // The MIT License (MIT) // // Filename: BlogsController.cs @@ -25,7 +25,7 @@ // SOFTWARE. #endregion using System.Linq; -using System.Web.Mvc; +using Microsoft.AspNetCore.Mvc; using DataLayer.DataClasses.Concrete; using GenericServices; using SampleWebApp.Infrastructure; @@ -41,19 +41,19 @@ namespace SampleWebApp.Controllers public class BlogsController : Controller { - public ActionResult Index(IListService service) + public ActionResult Index([FromServices] IListService service) { return View(service.GetAll().ToList()); } - public ActionResult Edit(int id, IUpdateSetupService service) + public ActionResult Edit(int id, [FromServices] IUpdateSetupService service) { return View(service.GetOriginal(id).Result); } [HttpPost] [ValidateAntiForgeryToken] - public ActionResult Edit(Blog blog, IUpdateService service) + public ActionResult Edit(Blog blog, [FromServices] IUpdateService service) { if (!ModelState.IsValid) //model errors so return immediately @@ -78,7 +78,7 @@ public ActionResult Create() [HttpPost] [ValidateAntiForgeryToken] - public ActionResult Create(Blog blog, ICreateService service) + public ActionResult Create(Blog blog, [FromServices] ICreateService service) { if (!ModelState.IsValid) //model errors so return immediately @@ -96,7 +96,7 @@ public ActionResult Create(Blog blog, ICreateService service) return View(blog); } - public ActionResult Delete(int id, IDeleteService service) + public ActionResult Delete(int id, [FromServices] IDeleteService service) { var response = service.Delete(id); diff --git a/SampleWebApp/Controllers/HomeController.cs b/SampleWebApp/Controllers/HomeController.cs index bfef31c..f46b006 100644 --- a/SampleWebApp/Controllers/HomeController.cs +++ b/SampleWebApp/Controllers/HomeController.cs @@ -1,4 +1,4 @@ -#region licence +#region licence // The MIT License (MIT) // // Filename: HomeController.cs @@ -24,7 +24,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #endregion -using System.Web.Mvc; +using Microsoft.AspNetCore.Mvc; using SampleWebApp.Models; namespace SampleWebApp.Controllers diff --git a/SampleWebApp/Controllers/PostsAsyncController.cs b/SampleWebApp/Controllers/PostsAsyncController.cs index 22e914f..552be77 100644 --- a/SampleWebApp/Controllers/PostsAsyncController.cs +++ b/SampleWebApp/Controllers/PostsAsyncController.cs @@ -1,4 +1,4 @@ -#region licence +#region licence // The MIT License (MIT) // // Filename: PostsAsyncController.cs @@ -25,10 +25,10 @@ // SOFTWARE. #endregion using System.Collections.Generic; -using System.Data.Entity; +using Microsoft.EntityFrameworkCore; using System.Linq; using System.Threading.Tasks; -using System.Web.Mvc; +using Microsoft.AspNetCore.Mvc; using DataLayer.DataClasses; using DataLayer.DataClasses.Concrete; using DataLayer.Startup; @@ -44,25 +44,25 @@ public class PostsAsyncController : Controller /// This is an example of a Controller using GenericServices database commands with a DTO. /// In this case we are using async commands /// - public async Task Index(IListService service) + public async Task Index([FromServices] IListService service) { return View(await service.GetAll().ToListAsync()); } - public async Task Details(int id, IDetailServiceAsync service) + public async Task Details(int id, [FromServices] IDetailServiceAsync service) { return View((await service.GetDetailAsync(id)).Result); } - public async Task Edit(int id, IUpdateSetupServiceAsync service) + public async Task Edit(int id, [FromServices] IUpdateSetupServiceAsync service) { return View((await service.GetOriginalAsync(id)).Result); } [HttpPost] [ValidateAntiForgeryToken] - public async Task Edit(DetailPostDtoAsync dto, IUpdateServiceAsync service) + public async Task Edit(DetailPostDtoAsync dto, [FromServices] IUpdateServiceAsync service) { if (!ModelState.IsValid) //model errors so return immediately @@ -80,7 +80,7 @@ public async Task Edit(DetailPostDtoAsync dto, IUpdateServiceAsync return View(dto); } - public async Task Create(ICreateSetupServiceAsync setupService) + public async Task Create([FromServices] ICreateSetupServiceAsync setupService) { var dto = await setupService.GetDtoAsync(); return View(dto); @@ -88,7 +88,7 @@ public async Task Create(ICreateSetupServiceAsync setupService) [HttpPost] [ValidateAntiForgeryToken] - public async Task Create(DetailPostDtoAsync dto, ICreateServiceAsync service) + public async Task Create(DetailPostDtoAsync dto, [FromServices] ICreateServiceAsync service) { if (!ModelState.IsValid) //model errors so return immediately @@ -106,7 +106,7 @@ public async Task Create(DetailPostDtoAsync dto, ICreateServiceAsy return View(dto); } - public async Task Delete(int id, IDeleteServiceAsync service) + public async Task Delete(int id, [FromServices] IDeleteServiceAsync service) { var response = await service.DeleteAsync(id); @@ -114,7 +114,7 @@ public async Task Delete(int id, IDeleteServiceAsync service) TempData["message"] = response.SuccessMessage; else //else errors, so send back an error message - TempData["errorMessage"] = new MvcHtmlString(response.ErrorsAsHtml()); + TempData["errorMessage"] = response.ErrorsAsHtml(); return RedirectToAction("Index"); } @@ -122,12 +122,12 @@ public async Task Delete(int id, IDeleteServiceAsync service) //----------------------------------------------------- //Code used in https://www.simple-talk.com/dotnet/.net-framework/the-.net-4.5-asyncawait-commands-in-promise-and-practice/ - public async Task NumPosts(SampleWebAppDb db) + public async Task NumPosts([FromServices] SampleWebAppDb db) { return View((object)await GetNumPostsAsync(db)); } - private static async Task GetNumPostsAsync(SampleWebAppDb db) + private static async Task GetNumPostsAsync([FromServices] SampleWebAppDb db) { var numPosts = await db.Posts.CountAsync(); return string.Format("The total number of Posts is {0}", numPosts); @@ -146,7 +146,7 @@ public async Task Delay() return View(500); } - public ActionResult Reset(SampleWebAppDb db) + public ActionResult Reset([FromServices] SampleWebAppDb db) { DataLayerInitialise.ResetBlogs(db, TestDataSelection.Medium); TempData["message"] = "Successfully reset the blogs data"; diff --git a/SampleWebApp/Controllers/PostsController.cs b/SampleWebApp/Controllers/PostsController.cs index d5cd076..c72f9eb 100644 --- a/SampleWebApp/Controllers/PostsController.cs +++ b/SampleWebApp/Controllers/PostsController.cs @@ -1,4 +1,4 @@ -#region licence +#region licence // The MIT License (MIT) // // Filename: PostsController.cs @@ -26,7 +26,7 @@ #endregion using System.Linq; using System.Threading; -using System.Web.Mvc; +using Microsoft.AspNetCore.Mvc; using DataLayer.DataClasses; using DataLayer.DataClasses.Concrete; using DataLayer.Startup; @@ -49,7 +49,7 @@ public class PostsController : Controller /// /// /// - public ActionResult Index(int? id, IListService service) + public ActionResult Index(int? id, [FromServices] IListService service) { var filtered = id != null && id != 0; var query = filtered ? service.GetAll().Where(x => x.BlogId == id) : service.GetAll(); @@ -59,19 +59,19 @@ public ActionResult Index(int? id, IListService service) return View(query.ToList()); } - public ActionResult Details(int id, IDetailService service) + public ActionResult Details(int id, [FromServices] IDetailService service) { return View(service.GetDetail(id).Result); } - public ActionResult Edit(int id, IUpdateSetupService service) + public ActionResult Edit(int id, [FromServices] IUpdateSetupService service) { return View(service.GetOriginal(id).Result); } [HttpPost] [ValidateAntiForgeryToken] - public ActionResult Edit(DetailPostDto dto, IUpdateService service) + public ActionResult Edit(DetailPostDto dto, [FromServices] IUpdateService service) { if (!ModelState.IsValid) //model errors so return immediately @@ -89,7 +89,7 @@ public ActionResult Edit(DetailPostDto dto, IUpdateService service) return View(dto); } - public ActionResult Create(ICreateSetupService setupService) + public ActionResult Create([FromServices] ICreateSetupService setupService) { var dto = setupService.GetDto(); return View(dto); @@ -97,7 +97,7 @@ public ActionResult Create(ICreateSetupService setupService) [HttpPost] [ValidateAntiForgeryToken] - public ActionResult Create(DetailPostDto dto, ICreateService service) + public ActionResult Create(DetailPostDto dto, [FromServices] ICreateService service) { if (!ModelState.IsValid) //model errors so return immediately @@ -115,7 +115,7 @@ public ActionResult Create(DetailPostDto dto, ICreateService service) return View(dto); } - public ActionResult Delete(int id, IDeleteService service) + public ActionResult Delete(int id, [FromServices] IDeleteService service) { var response = service.Delete(id); @@ -123,7 +123,7 @@ public ActionResult Delete(int id, IDeleteService service) TempData["message"] = response.SuccessMessage; else //else errors, so send back an error message - TempData["errorMessage"] = new MvcHtmlString(response.ErrorsAsHtml()); + TempData["errorMessage"] = response.ErrorsAsHtml(); return RedirectToAction("Index"); } @@ -131,13 +131,13 @@ public ActionResult Delete(int id, IDeleteService service) //----------------------------------------------------- //Code used in https://www.simple-talk.com/dotnet/.net-framework/the-.net-4.5-asyncawait-commands-in-promise-and-practice/ - public ActionResult NumPosts(SampleWebAppDb db) + public ActionResult NumPosts([FromServices] SampleWebAppDb db) { //The cast to object is to stop the View using the string as a view name return View((object)GetNumPosts(db)); } - public static string GetNumPosts(SampleWebAppDb db) + public static string GetNumPosts([FromServices] SampleWebAppDb db) { var numPosts = db.Posts.Count(); return string.Format("The total number of Posts is {0}", numPosts); @@ -156,7 +156,7 @@ public ActionResult Delay() return View(500); } - public ActionResult Reset(SampleWebAppDb db) + public ActionResult Reset([FromServices] SampleWebAppDb db) { DataLayerInitialise.ResetBlogs(db, TestDataSelection.Medium); TempData["message"] = "Successfully reset the blogs data"; diff --git a/SampleWebApp/Controllers/TagsAsyncController.cs b/SampleWebApp/Controllers/TagsAsyncController.cs index 6d9e9b9..6d99858 100644 --- a/SampleWebApp/Controllers/TagsAsyncController.cs +++ b/SampleWebApp/Controllers/TagsAsyncController.cs @@ -1,4 +1,4 @@ -#region licence +#region licence // The MIT License (MIT) // // Filename: TagsAsyncController.cs @@ -24,9 +24,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #endregion -using System.Data.Entity; +using Microsoft.EntityFrameworkCore; using System.Threading.Tasks; -using System.Web.Mvc; +using Microsoft.AspNetCore.Mvc; using DataLayer.DataClasses.Concrete; using GenericServices; using SampleWebApp.Infrastructure; @@ -41,25 +41,25 @@ namespace SampleWebApp.Controllers public class TagsAsyncController : Controller { // GET: TagsAsync - public async Task Index(IListService service) + public async Task Index([FromServices] IListService service) { return View(await service.GetAll().ToListAsync()); } - public async Task Details(int id, IDetailServiceAsync service) + public async Task Details(int id, [FromServices] IDetailServiceAsync service) { return View((await service.GetDetailAsync(id)).Result); } - public async Task Edit(int id, IUpdateSetupServiceAsync service) + public async Task Edit(int id, [FromServices] IUpdateSetupServiceAsync service) { return View((await service.GetOriginalAsync(id)).Result); } [HttpPost] [ValidateAntiForgeryToken] - public async Task Edit(Tag tag, IUpdateServiceAsync service) + public async Task Edit(Tag tag, [FromServices] IUpdateServiceAsync service) { if (!ModelState.IsValid) //model errors so return immediately @@ -84,7 +84,7 @@ public ActionResult Create() [HttpPost] [ValidateAntiForgeryToken] - public async Task Create(Tag tag, ICreateServiceAsync service) + public async Task Create(Tag tag, [FromServices] ICreateServiceAsync service) { if (!ModelState.IsValid) //model errors so return immediately @@ -102,7 +102,7 @@ public async Task Create(Tag tag, ICreateServiceAsync service) return View(tag); } - public async Task Delete(int id, IDeleteServiceAsync service) + public async Task Delete(int id, [FromServices] IDeleteServiceAsync service) { var response = await service.DeleteAsync(id); @@ -110,7 +110,7 @@ public async Task Delete(int id, IDeleteServiceAsync service) TempData["message"] = response.SuccessMessage; else //else errors, so send back an error message - TempData["errorMessage"] = new MvcHtmlString(response.ErrorsAsHtml()); + TempData["errorMessage"] = response.ErrorsAsHtml(); return RedirectToAction("Index"); } diff --git a/SampleWebApp/Controllers/TagsController.cs b/SampleWebApp/Controllers/TagsController.cs index a729d08..c50a094 100644 --- a/SampleWebApp/Controllers/TagsController.cs +++ b/SampleWebApp/Controllers/TagsController.cs @@ -1,4 +1,4 @@ -#region licence +#region licence // The MIT License (MIT) // // Filename: TagsController.cs @@ -25,7 +25,7 @@ // SOFTWARE. #endregion using System.Linq; -using System.Web.Mvc; +using Microsoft.AspNetCore.Mvc; using DataLayer.DataClasses.Concrete; using GenericServices; using SampleWebApp.Infrastructure; @@ -39,25 +39,25 @@ public class TagsController : Controller /// This is an example of a Controller using GenericServices database commands directly to the data class (other that List, which needs a DTO) /// In this case we are using normal, non-async commands /// - public ActionResult Index(IListService service) + public ActionResult Index([FromServices] IListService service) { return View(service.GetAll().ToList()); } - public ActionResult Details(int id, IDetailService service) + public ActionResult Details(int id, [FromServices] IDetailService service) { return View(service.GetDetail(id).Result); } - public ActionResult Edit(int id, IUpdateSetupService service) + public ActionResult Edit(int id, [FromServices] IUpdateSetupService service) { return View(service.GetOriginal(id).Result); } [HttpPost] [ValidateAntiForgeryToken] - public ActionResult Edit(Tag tag, IUpdateService service) + public ActionResult Edit(Tag tag, [FromServices] IUpdateService service) { if (!ModelState.IsValid) //model errors so return immediately @@ -82,7 +82,7 @@ public ActionResult Create() [HttpPost] [ValidateAntiForgeryToken] - public ActionResult Create(Tag tag, ICreateService service) + public ActionResult Create(Tag tag, [FromServices] ICreateService service) { if (!ModelState.IsValid) //model errors so return immediately @@ -100,7 +100,7 @@ public ActionResult Create(Tag tag, ICreateService service) return View(tag); } - public ActionResult Delete(int id, IDeleteService service) + public ActionResult Delete(int id, [FromServices] IDeleteService service) { var response = service.Delete(id); @@ -108,7 +108,7 @@ public ActionResult Delete(int id, IDeleteService service) TempData["message"] = response.SuccessMessage; else //else errors, so send back an error message - TempData["errorMessage"] = new MvcHtmlString(response.ErrorsAsHtml()); + TempData["errorMessage"] = response.ErrorsAsHtml(); return RedirectToAction("Index"); } diff --git a/SampleWebApp/Global.asax b/SampleWebApp/Global.asax deleted file mode 100644 index c209429..0000000 --- a/SampleWebApp/Global.asax +++ /dev/null @@ -1 +0,0 @@ -<%@ Application Codebehind="Global.asax.cs" Inherits="SampleWebApp.MvcApplication" Language="C#" %> diff --git a/SampleWebApp/Global.asax.cs b/SampleWebApp/Global.asax.cs deleted file mode 100644 index 610d5ce..0000000 --- a/SampleWebApp/Global.asax.cs +++ /dev/null @@ -1,51 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: Global.asax.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -using System.Web.Mvc; -using System.Web.Optimization; -using System.Web.Routing; -using SampleWebApp.Infrastructure; - -namespace SampleWebApp -{ - public class MvcApplication : System.Web.HttpApplication - { - protected void Application_Start() - { - AreaRegistration.RegisterAllAreas(); - FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); - RouteConfig.RegisterRoutes(RouteTable.Routes); - BundleConfig.RegisterBundles(BundleTable.Bundles); - - //This allows interfaces etc to be provided as parameters to action methods - ModelBinders.Binders.DefaultBinder = new DiModelBinder(); - - //Now call the method to initialise anything that is required before startup (which includes setting up DI) - WebUiInitialise.InitialiseThis(this); - - } - } -} diff --git a/SampleWebApp/Infrastructure/AutofacDi.cs b/SampleWebApp/Infrastructure/AutofacDi.cs deleted file mode 100644 index b2645e7..0000000 --- a/SampleWebApp/Infrastructure/AutofacDi.cs +++ /dev/null @@ -1,57 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: AutofacDi.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -using System.Runtime.CompilerServices; -using Autofac; -using ServiceLayer.Startup; - -[assembly: InternalsVisibleTo("Tests")] - -namespace SampleWebApp.Infrastructure -{ - internal static class AutofacDi - { - - private static IContainer _container; - - internal static IContainer SetupDependency() - { - - var builder = new ContainerBuilder(); - Load(builder); - - _container = builder.Build(); - - return _container; - } - - private static void Load(ContainerBuilder builder) - { - //register the service layer, which then registers all other dependencies in the rest of the system - builder.RegisterModule(new ServiceLayerModule()); - } - } -} \ No newline at end of file diff --git a/SampleWebApp/Infrastructure/DiModelBinder.cs b/SampleWebApp/Infrastructure/DiModelBinder.cs deleted file mode 100644 index e8115ba..0000000 --- a/SampleWebApp/Infrastructure/DiModelBinder.cs +++ /dev/null @@ -1,41 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: DiModelBinder.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -using System; -using System.Web.Mvc; - -namespace SampleWebApp.Infrastructure -{ - public class DiModelBinder : DefaultModelBinder - { - protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) - { - return modelType.IsInterface - ? DependencyResolver.Current.GetService(modelType) - : base.CreateModel(controllerContext, bindingContext, modelType); - } - } -} \ No newline at end of file diff --git a/SampleWebApp/Infrastructure/JsonNetResult.cs b/SampleWebApp/Infrastructure/JsonNetResult.cs deleted file mode 100644 index f429bba..0000000 --- a/SampleWebApp/Infrastructure/JsonNetResult.cs +++ /dev/null @@ -1,75 +0,0 @@ -#region License -// Copyright (c) 2007 James Newton-King -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. -#endregion - -using System; -using System.Text; -using System.Web; -using System.Web.Mvc; -using Newtonsoft.Json; - -//see blog http://james.newtonking.com/archive/2008/10/16/asp-net-mvc-and-json-net.aspx - -namespace SampleWebApp.Infrastructure -{ - public class JsonNetResult : ActionResult - { - public Encoding ContentEncoding { get; set; } - public string ContentType { get; set; } - public object Data { get; set; } - - public JsonSerializerSettings SerializerSettings { get; set; } - public Formatting Formatting { get; set; } - - public JsonNetResult() - { - SerializerSettings = new JsonSerializerSettings(); - } - - public override void ExecuteResult(ControllerContext context) - { - if (context == null) - throw new ArgumentNullException("context"); - - HttpResponseBase response = context.HttpContext.Response; - - response.ContentType = !string.IsNullOrEmpty(ContentType) - ? ContentType - : "application/json"; - - if (ContentEncoding != null) - response.ContentEncoding = ContentEncoding; - - if (Data != null) - { - JsonTextWriter writer = new JsonTextWriter(response.Output) { Formatting = Formatting }; - - JsonSerializer serializer = JsonSerializer.Create(SerializerSettings); - serializer.Serialize(writer, Data); - - writer.Flush(); - } - } - } -} \ No newline at end of file diff --git a/SampleWebApp/Infrastructure/Log4NetGenericLogger.cs b/SampleWebApp/Infrastructure/Log4NetGenericLogger.cs deleted file mode 100644 index 7b71457..0000000 --- a/SampleWebApp/Infrastructure/Log4NetGenericLogger.cs +++ /dev/null @@ -1,99 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: Log4NetGenericLogger.cs -// Date Created: 2014/07/03 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -using System; -using GenericLibsBase; - -namespace SampleWebApp.Infrastructure -{ - - public class Log4NetGenericLogger : IGenericLogger - { - - private readonly log4net.ILog _logger; - - public Log4NetGenericLogger(string name) - { - _logger = log4net.LogManager.GetLogger(name); - } - - public void Verbose(object message) - { - _logger.Debug(message); - } - - public void VerboseFormat(string format, params object[] args) - { - _logger.DebugFormat(format, args); - } - - public void Info(object message) - { - _logger.Info(message); - } - - public void InfoFormat(string format, params object[] args) - { - _logger.InfoFormat(format, args); - } - - public void Warn(object message) - { - _logger.Warn(message); - } - - public void WarnFormat(string format, params object[] args) - { - _logger.WarnFormat(format, args); - } - - public void Error(object message) - { - _logger.Error(message); - } - - public void ErrorFormat(string format, params object[] args) - { - _logger.ErrorFormat(format, args); - } - - public void Critical(object message) - { - _logger.Fatal(message); - } - - public void Critical(object message, Exception ex) - { - _logger.Fatal(message, ex); - } - - public void CriticalFormat(string format, params object[] args) - { - _logger.FatalFormat(format, args); - } - - } -} diff --git a/SampleWebApp/Infrastructure/TraceGenericLogger.cs b/SampleWebApp/Infrastructure/TraceGenericLogger.cs deleted file mode 100644 index 31584ad..0000000 --- a/SampleWebApp/Infrastructure/TraceGenericLogger.cs +++ /dev/null @@ -1,107 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: TraceGenericLogger.cs -// Date Created: 2014/07/07 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -using System; -using System.Diagnostics; -using GenericLibsBase; - -namespace SampleWebApp.Infrastructure -{ - - public class TraceGenericLogger : IGenericLogger - { - - private readonly string _loggerName; - - /// - /// Controls whether Verbose is written to Trace. - /// Defaults to excluding Verbose messages - /// - public bool IncludeVerbose { get; set; } - - public TraceGenericLogger(string name) - { - _loggerName = name; - } - - public void Verbose(object message) - { - if (IncludeVerbose) - Trace.TraceInformation("{0}: {1}", _loggerName, message); - } - - public void VerboseFormat(string format, params object[] args) - { - Verbose(string.Format(format, args)); - } - - public void Info(object message) - { - Trace.TraceInformation("{0}: {1}", _loggerName, message); - } - - public void InfoFormat(string format, params object[] args) - { - Info(string.Format(format, args)); - } - - public void Warn(object message) - { - Trace.TraceWarning("{0}: {1}", _loggerName, message); - } - - public void WarnFormat(string format, params object[] args) - { - Warn(string.Format(format, args)); - } - - public void Error(object message) - { - Trace.TraceError("{0}: {1}", _loggerName, message); - } - - public void ErrorFormat(string format, params object[] args) - { - Error(string.Format(format, args)); - } - - public void Critical(object message) - { - Trace.TraceError("{0}: {1}", _loggerName, message); - } - - public void Critical(object message, Exception ex) - { - Trace.TraceError("{0}, expection {1}: {2}\n{3}", _loggerName, ex.GetType().Name, message, ex.StackTrace); - } - - public void CriticalFormat(string format, params object[] args) - { - Critical(string.Format(format, args)); - } - - } -} diff --git a/SampleWebApp/Infrastructure/ValidationHelper.cs b/SampleWebApp/Infrastructure/ValidationHelper.cs index ab6bf7d..830b3d6 100644 --- a/SampleWebApp/Infrastructure/ValidationHelper.cs +++ b/SampleWebApp/Infrastructure/ValidationHelper.cs @@ -1,36 +1,10 @@ -#region licence -// The MIT License (MIT) -// -// Filename: ValidationHelper.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Web.Mvc; -using GenericLibsBase; -using GenericServices; +using GenericLibsBase.Core; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.ModelBinding; namespace SampleWebApp.Infrastructure { @@ -39,13 +13,10 @@ public static class ValidationHelper /// /// This transfers error messages from the DtoValidation methods to the MVC modelState error dictionary. /// It looks for errors that have member names corresponding to the properties in the displayDto. - /// This means that errors assciated with a field on display will show next to the name. + /// This means that errors assciated with a field on display will show next to the name. /// Other errors will be shown in the ValidationSummary /// - /// The interface that holds the errors - /// The MVC modelState to add errors to - /// This is the Dto that will be used to display the error messages - public static void CopyErrorsToModelState(this ISuccessOrErrors errorHolder, ModelStateDictionary modelState, T displayDto) + public static void CopyErrorsToModelState(this ISuccessOrErrors errorHolder, ModelStateDictionary modelState, T displayDto) { if (errorHolder.IsValid) return; @@ -65,25 +36,18 @@ public static void CopyErrorsToModelState(this ISuccessOrErrors errorHolder, /// /// This copies errors for general display where we are not returning to a page with the fields on them /// - /// - /// public static void CopyErrorsToModelState(this ISuccessOrErrors errorHolder, ModelStateDictionary modelState) { if (errorHolder.IsValid) return; foreach (var error in errorHolder.Errors) - modelState.AddModelError("", error.ErrorMessage); + modelState.AddModelError("", error.ErrorMessage); } - /// - /// This returns the ModelState errors as a json array containing objects with the PropertyName and the first error message. + /// This returns the ModelState errors as a json object containing the PropertyName and the error messages. /// Must only be called if there are model errors. /// - /// - /// It returns a JsonNetResult with one parameter called errors which contains key value pairs. - /// The key is the name of the property which had the error, or is empty string if global error. - /// The value is an array of error strings for that property key public static JsonResult ReturnModelErrorsAsJson(this ModelStateDictionary modelState) { if (modelState.IsValid) @@ -94,7 +58,6 @@ public static JsonResult ReturnModelErrorsAsJson(this ModelStateDictionary model foreach (var propertyError in modelState.Where(x => x.Value.Errors.Any())) { if (string.IsNullOrEmpty(propertyError.Key)) - //The modelState doesn't seem to combine empty named items so we do it for it emptyNameErrors.AddRange(propertyError.Value.Errors.Select(x => x.ErrorMessage)); else dict[propertyError.Key] = new { errors = propertyError.Value.Errors.Select(x => x.ErrorMessage) }; @@ -103,23 +66,13 @@ public static JsonResult ReturnModelErrorsAsJson(this ModelStateDictionary model if (emptyNameErrors.Any()) dict[string.Empty] = new { errors = emptyNameErrors }; - var result = new JsonResult { Data = new { errorsDict = dict } }; - - return result; + return new JsonResult(new { errorsDict = dict }); } /// - /// This returns and errorsDict with any errors in ISuccessOrErrors transferred - /// It looks for errors that have member names corresponding to the properties in the displayDto. - /// This means that errors assciated with a field on display will show next to the name. - /// Other errors will be shown in the ValidationSummary + /// This returns an errorsDict with any errors in ISuccessOrErrors transferred. /// Should only be called if there is an error /// - /// The interface that holds the errors - /// Dto that the error messages came from - /// It returns a JsonNetResult with one parameter called errors which contains key value pairs. - /// The key is the name of the property which had the error, or is empty string if global error. - /// The value is an array of error strings for that property key public static JsonResult ReturnErrorsAsJson(this ISuccessOrErrors errorHolder, T displayDto) { if (errorHolder.IsValid) @@ -130,7 +83,7 @@ public static JsonResult ReturnErrorsAsJson(this ISuccessOrErrors errorHolder return modelState.ReturnModelErrorsAsJson(); } - private static IList PropertyNamesInDto ( T objectToCheck) + private static IList PropertyNamesInDto(T objectToCheck) { return objectToCheck.GetType() @@ -138,6 +91,5 @@ private static IList PropertyNamesInDto ( T objectToCheck) .Select(x => x.Name) .ToList(); } - } -} \ No newline at end of file +} diff --git a/SampleWebApp/Infrastructure/WebUiInitialise.cs b/SampleWebApp/Infrastructure/WebUiInitialise.cs deleted file mode 100644 index 2bb8154..0000000 --- a/SampleWebApp/Infrastructure/WebUiInitialise.cs +++ /dev/null @@ -1,110 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: WebUiInitialise.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -using System; -using System.Web; -using System.Web.Mvc; -using Autofac.Integration.Mvc; -using GenericLibsBase; -using SampleWebApp.Properties; -using ServiceLayer.Startup; - -namespace SampleWebApp.Infrastructure -{ - public enum HostTypes { NotSet, LocalHost, WebWiz, Azure }; - - public static class WebUiInitialise - { - private const bool ResetIndentityDatabase = false; //set this to true to reset content of Identity database - - //Note: This is also used when running locally - private const string WebWizLog4NetRelPath = "~/Log4Net.xml"; - - public const string DatabaseConnectionStringName = "SampleWebAppDb"; - - /// - /// This provides the host we - /// - public static HostTypes HostType { get; private set; } - - /// - /// This should be called at Startup - /// - public static void InitialiseThis(HttpApplication application) - { - HostType = DecodeHostType(Settings.Default.HostTypeString); - //WebWiz does not allow drop/create database - var canDropCreateDatabase = HostType != HostTypes.WebWiz; - - SetupLogging(application, HostType); - - //This runs the ServiceLayer initialise, whoes job it is to initialise any of the lower layers - //NOTE: This MUST to come before the setup of the DI because it relies on the configInfo being set up - ServiceLayerInitialise.InitialiseThis(HostType == HostTypes.Azure, canDropCreateDatabase); - - //This sets up the Autofac container for all levels in the program - var container = AutofacDi.SetupDependency(); - - //// Set the dependency resolver for MVC. - var mvcResolver = new AutofacDependencyResolver(container); - DependencyResolver.SetResolver(mvcResolver); - } - - private static HostTypes DecodeHostType(string hostTypeString) - { - HostTypes hostType ; - Enum.TryParse(hostTypeString, true, out hostType); - return hostType; - } - - private static void SetupLogging(HttpApplication application, HostTypes hostType) - { - - switch (hostType) - { - case HostTypes.NotSet: - //we do not set up the logging - break; - case HostTypes.LocalHost: - //LocalHost uses WebWiz setup for log4Net - case HostTypes.WebWiz: - //We set up the log4net settings from a local file and then assign the logger to GenericServices.GenericLoggerFactory - var log4NetPath = application.Server.MapPath(WebWizLog4NetRelPath); - log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo(log4NetPath)); - GenericLibsBaseConfig.SetLoggerMethod = name => new Log4NetGenericLogger(name); - break; - case HostTypes.Azure: - //we use the TraceGenericLogger when in Azure - GenericLibsBaseConfig.SetLoggerMethod = name => new TraceGenericLogger(name); - break; - default: - throw new ArgumentOutOfRangeException("hostType"); - } - - GenericLibsBaseConfig.GetLogger("LoggerSetup").Info("We have just assegned a logger."); - } - } -} \ No newline at end of file diff --git a/SampleWebApp/Log4Net.xml b/SampleWebApp/Log4Net.xml deleted file mode 100644 index 6feb4c4..0000000 --- a/SampleWebApp/Log4Net.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/SampleWebApp/Models/InternalsInfo.cs b/SampleWebApp/Models/InternalsInfo.cs index f9df44e..749cf39 100644 --- a/SampleWebApp/Models/InternalsInfo.cs +++ b/SampleWebApp/Models/InternalsInfo.cs @@ -53,7 +53,14 @@ public InternalsInfo() WorkerThreads = workerThreads; AvailableThreads = availableThreads; - AvailableMbytes = (int)new PerformanceCounter("Memory", "Available MBytes", true).RawValue; + try + { + AvailableMbytes = (int)(GC.GetGCMemoryInfo().TotalAvailableMemoryBytes / (1024 * 1024)); + } + catch + { + AvailableMbytes = 0; + } HeapMemoryUsedKbytes = (int)(GC.GetTotalMemory(true)/1000); } diff --git a/SampleWebApp/Program.cs b/SampleWebApp/Program.cs new file mode 100644 index 0000000..c2c3537 --- /dev/null +++ b/SampleWebApp/Program.cs @@ -0,0 +1,49 @@ +using System; +using DataLayer.DataClasses; +using Microsoft.AspNetCore.Builder; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using ServiceLayer.Startup; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddControllersWithViews(); + +var provider = builder.Configuration.GetValue("DatabaseProvider") ?? "Sqlite"; +var connectionString = builder.Configuration.GetConnectionString("SampleWebAppDb"); + +builder.Services.AddDbContext(options => +{ + if (string.Equals(provider, "SqlServer", StringComparison.OrdinalIgnoreCase)) + options.UseSqlServer(connectionString); + else + options.UseSqlite(connectionString ?? "Data Source=SampleWebAppDb.db"); +}); + +builder.Services.AddServiceLayer(); + +var app = builder.Build(); + +if (!app.Environment.IsDevelopment()) +{ + app.UseExceptionHandler("/Home/Error"); +} + +app.UseStaticFiles(); +app.UseRouting(); +app.UseAuthorization(); + +app.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + +//Create and seed the database on startup +using (var scope = app.Services.CreateScope()) +{ + var context = scope.ServiceProvider.GetRequiredService(); + ServiceLayerInitialise.InitialiseThis(context, true); +} + +app.Run(); diff --git a/SampleWebApp/Project_Readme.html b/SampleWebApp/Project_Readme.html deleted file mode 100644 index cb9e793..0000000 --- a/SampleWebApp/Project_Readme.html +++ /dev/null @@ -1,151 +0,0 @@ - - - - - Your ASP.NET application - - - - - - -
-
-

This application consists of:

-
    -
  • Sample pages showing basic nav between Home, About, and Contact
  • -
  • Theming using Bootstrap
  • -
  • Authentication, if selected, shows how to register and sign in
  • -
  • ASP.NET features managed using NuGet
  • -
-
- - - - - -
-

Get help

- -
-
- - - \ No newline at end of file diff --git a/SampleWebApp/Properties/AssemblyInfo.cs b/SampleWebApp/Properties/AssemblyInfo.cs deleted file mode 100644 index 5605e92..0000000 --- a/SampleWebApp/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,61 +0,0 @@ -#region licence -// The MIT License (MIT) -// -// Filename: AssemblyInfo.cs -// Date Created: 2014/05/20 -// -// Copyright (c) 2014 Jon Smith (www.selectiveanalytics.com & www.thereformedprogrammer.net) -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion -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("SampleWebApp")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("SampleWebApp")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[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("fa016029-8cb0-4ef3-a0e0-c94ac71c1177")] - -// 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/SampleWebApp/Properties/Settings.Designer.cs b/SampleWebApp/Properties/Settings.Designer.cs deleted file mode 100644 index 3781ad7..0000000 --- a/SampleWebApp/Properties/Settings.Designer.cs +++ /dev/null @@ -1,44 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.18444 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace SampleWebApp.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("LocalHost")] - public string HostTypeString { - get { - return ((string)(this["HostTypeString"])); - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("jonsmith_")] - public string DatabaseLoginPrefix { - get { - return ((string)(this["DatabaseLoginPrefix"])); - } - } - } -} diff --git a/SampleWebApp/Properties/Settings.settings b/SampleWebApp/Properties/Settings.settings deleted file mode 100644 index cd30221..0000000 --- a/SampleWebApp/Properties/Settings.settings +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - LocalHost - - - jonsmith_ - - - \ No newline at end of file diff --git a/SampleWebApp/Properties/launchSettings.json b/SampleWebApp/Properties/launchSettings.json new file mode 100644 index 0000000..4fa2de2 --- /dev/null +++ b/SampleWebApp/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SampleWebApp/SampleWebApp.csproj b/SampleWebApp/SampleWebApp.csproj index eca4d77..961df07 100644 --- a/SampleWebApp/SampleWebApp.csproj +++ b/SampleWebApp/SampleWebApp.csproj @@ -1,485 +1,21 @@ - - - + + - Debug - AnyCPU - - - 2.0 - {CFFEE5E0-3B99-46E0-9A82-2E74621C17C5} - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties + net9.0 + disable + disable SampleWebApp - SampleWebApp - v4.5.1 - false - true - - - - - - - - true - full - false - bin\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\ - TRACE - prompt - 4 + - - False - ..\packages\Autofac.3.5.0\lib\net40\Autofac.dll - - - False - ..\packages\Autofac.Mvc5.3.3.1\lib\net45\Autofac.Integration.Mvc.dll - - - ..\packages\AutoMapper.4.2.1\lib\net45\AutoMapper.dll - True - - - ..\packages\DelegateDecompiler.0.18.0\lib\net40-Client\DelegateDecompiler.dll - True - - - ..\packages\DelegateDecompiler.EntityFramework.0.18.0\lib\net45\DelegateDecompiler.EntityFramework.dll - True - - - ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll - True - - - ..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll - True - - - ..\packages\GenericLibsBase.1.0.1\lib\GenericLibsBase.dll - True - - - ..\packages\GenericServices.1.0.9\lib\GenericServices.dll - True - - - ..\packages\log4net.2.0.3\lib\net40-full\log4net.dll - - - False - ..\packages\Microsoft.AspNet.Identity.Core.2.1.0\lib\net45\Microsoft.AspNet.Identity.Core.dll - - - False - ..\packages\Microsoft.AspNet.Identity.EntityFramework.2.1.0\lib\net45\Microsoft.AspNet.Identity.EntityFramework.dll - - - False - ..\packages\Microsoft.AspNet.Identity.Owin.2.1.0\lib\net45\Microsoft.AspNet.Identity.Owin.dll - - - ..\packages\Microsoft.AspNet.SignalR.Core.2.0.3\lib\net45\Microsoft.AspNet.SignalR.Core.dll - - - ..\packages\Microsoft.AspNet.SignalR.SystemWeb.2.0.3\lib\net45\Microsoft.AspNet.SignalR.SystemWeb.dll - True - - - - False - ..\packages\Microsoft.Owin.3.0.0\lib\net45\Microsoft.Owin.dll - - - False - ..\packages\Microsoft.Owin.Security.3.0.0\lib\net45\Microsoft.Owin.Security.dll - - - False - ..\packages\Microsoft.Owin.Security.Cookies.3.0.0\lib\net45\Microsoft.Owin.Security.Cookies.dll - - - False - ..\packages\Microsoft.Owin.Security.Facebook.3.0.0\lib\net45\Microsoft.Owin.Security.Facebook.dll - - - False - ..\packages\Microsoft.Owin.Security.Google.3.0.0\lib\net45\Microsoft.Owin.Security.Google.dll - - - False - ..\packages\Microsoft.Owin.Security.MicrosoftAccount.3.0.0\lib\net45\Microsoft.Owin.Security.MicrosoftAccount.dll - - - False - ..\packages\Microsoft.Owin.Security.OAuth.3.0.0\lib\net45\Microsoft.Owin.Security.OAuth.dll - - - ..\packages\Mono.Reflection.1.0.0.0\lib\Mono.Reflection.dll - - - False - ..\packages\Newtonsoft.Json.6.0.4\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - False - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.Helpers.dll - - - False - ..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll - - - False - ..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll - - - False - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll - - - False - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll - - - False - ..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Razor.dll - - - - - - - - - - - - True - ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll - - - - - - - ..\packages\Microsoft.AspNet.Web.Optimization.1.1.3\lib\net40\System.Web.Optimization.dll - - - True - ..\packages\WebGrease.1.5.2\lib\WebGrease.dll - - - True - ..\packages\Antlr.3.4.1.9004\lib\Antlr3.Runtime.dll - + + - - ..\packages\Owin.1.0\lib\net40\Owin.dll - - - ..\packages\Microsoft.Owin.Host.SystemWeb.2.1.0\lib\net45\Microsoft.Owin.Host.SystemWeb.dll - - - ..\packages\Microsoft.Owin.Security.Twitter.2.1.0\lib\net45\Microsoft.Owin.Security.Twitter.dll - + + + + - - - - - - - - - - - - Global.asax - - - - - - - - - - - True - True - Settings.settings - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - - - - - - - - - - - - - - - - - - - - Designer - - - Web.config - - - Web.config - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Web.config - - - Web.config - - - - - - - - - - - - - {264e1878-12de-4099-b8d7-cc53a73fea49} - DataLayer - - - {d2813927-0f38-43c3-b47c-ae8f00d50cae} - ServiceLayer - - - - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - bin\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - bin\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - bin\ - TRACE - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - - - - - - - - - - True - True - 50623 - / - http://localhost:50623/ - False - False - - - False - - - - - - \ No newline at end of file + + diff --git a/SampleWebApp/Scripts/ActionRunnerComms.js b/SampleWebApp/Scripts/ActionRunnerComms.js deleted file mode 100644 index 749916d..0000000 --- a/SampleWebApp/Scripts/ActionRunnerComms.js +++ /dev/null @@ -1,252 +0,0 @@ -/* - The ActionRunnerComms is a global Ctor which creates a new connection instance to the ActionHub via SignalR - The ActionRunnerComms object handles all the comms between the client (browser) and the long-running task - running on the service (MVC application). -*/ - -//The first parameter is the UI part of the ActionRunner -function ActionRunnerComms(actionUi) { - 'use strict'; - - //These are all the text messages output by this module. Placed in one place to allow localisation - var commsResources = { - nojQuery: 'jQuery was not found. Please ensure jQuery is referenced before this ActionRinner JavaScript file.', - noSignalR: 'SignalR was not found. Please ensure SignalR is referenced before this ActionRunner JavaScript file.', - confirmExitOnRunningSys: 'The system is waiting for the server to respond. Do you want to exit anyway? Press OK to exit.', - confirmExitOnNonCancellable: 'The action is not cancellable and is still running.\n' + - 'If you exit now the action will still continue,\n' + - 'but its output lost. Press OK if you really want to exit.', - }; - - //contructor checks - if (typeof ($) !== 'function') { - // no jQuery! - throw commsResources.nojQuery; - } - if (typeof ($.hubConnection) !== 'function') { - //no signalR - throw commsResources.noSignalR; - } - - //more private 'constants' - - //The text in the $ActionButton button is the control for the state machine. The text consists of one of the actionsStates - //Note: These states are shown via the button (if visible) so are set here to allow language changes. - var actionStates = { - transientSuffix: '...', //if the state ends with this it is transitary. Used to convey that to user and also allow forced abort - //items that must end with transientSuffix - connectingTransient: 'Connecting...', - startingTransient: 'Starting...', - cancellingTransient: 'Cancelling...', - runningNoCancel: 'Running...', //this is shown when the method does not support cancel - //now normal states - cancel: 'Cancel', //when method that supports cancelling is running then this allows user to cancel action - cancelled: 'Cancelled', - finishedOk: 'Finished Ok', - finishedErrors: 'Finished (errors)', - failed: 'Failed', - failedLink: 'Failed (link)', - failedConnecting: 'Failed (connecting)' - }; - - //These are the ProgressMessageTypes defined in the ProgressMessage class at the Server end - var messageTypes = { - verbose: 'Verbose', - info: 'Info', - warning: 'Warning', - error: 'Error', - critical: 'Critical', - cancelled: 'Cancelled', - finished: 'Finished', - failed: 'Failed' - }; - - var messageTypesThatAreErrors = ['Error', 'Critical', 'Failed']; - - //---------------------------- - //private variables - - var that = this; - - var actionChannel = null; - var actionGuid = null; - - var actionConfig = null; //this is set to an object containing various information about what the server action supports - var jsonResult = null; - - //---------------------------- - //private functions - - function endsWith(str, suffix) { - return str.indexOf(suffix, str.length - suffix.length) !== -1; - }; - - //This decodes actionConfigString into cleaner booleans and returns the object - //This places all actionConfig flags/decodes in one place - function decodeActionConfig(actionConfigString) { - actionConfig = {}; - actionConfig.exitOnSuccess = actionConfigString.indexOf('ExitOnSuccess') > -1; - actionConfig.noProgressSent = actionConfigString.indexOf('NoProgressSent') > -1; - actionConfig.noMessagesSent = actionConfigString.indexOf('NoMessagesSent') > -1; - actionConfig.cancelNotSupported = actionConfigString.indexOf('CancelNotSupported') > -1; - } - - //This deals with setting up the SignalR connections and events - function setupTaskChannel() { - - actionUi.setActionState(actionStates.connectingTransient); - - that.numErrorMessages = 0; - - //Setup connection and actionChannel with the functions to call - var connection = $.hubConnection(); - - //connection.logging = true; - actionChannel = connection.createHubProxy('ActionHub'); - setupTaskFunctions(); - - //Now make sure connection errors are handled - connection.error(function (error) { - actionUi.setActionState(actionStates.failedLink); - actionUi.reportSystemError('SignalR error: ' + error); - }); - //and start the connection and send the start message - connection.start() - .done(function () { - startAction(); - }) - .fail(function (error) { - actionUi.setActionState(actionStates.failedConnecting); - actionUi.reportSystemError('SignalR connection error: ' + error); - }); - } - - //------------------------------------------------------------------------ - //code to deal with the SignalR connections and events - - //This is called by setupTaskChannel to link to the SignalR events - function setupTaskFunctions() { - actionChannel.on('Progress', function (serverTaskId, percentDone, message) { - if (serverTaskId === actionGuid) { - incNumErrorsIfMessageTypeIsError(message.MessageTypeString); - actionUi.updateProgress(percentDone, that.numErrorMessages); - logMessage(message); - } - }); - actionChannel.on('Started', function (serverActionId, actionConfigFlags) { - if (serverActionId === actionGuid) { - decodeActionConfig(actionConfigFlags); - actionUi.startActionUi(actionConfig); - if (actionConfig.cancelNotSupported) { - actionUi.setActionState(actionStates.runningNoCancel); - } else { - actionUi.setActionState(actionStates.cancel); - } - } - }); - actionChannel.on('Stopped', function (serverTaskId, message, jsonFromServer) { - if (serverTaskId === actionGuid) { - incNumErrorsIfMessageTypeIsError(message.MessageTypeString); - logMessage(message); - jsonResult = jsonFromServer; - actionChannel.invoke('EndAction', actionGuid); //this cleans up the action at the server end - if (message.MessageTypeString === messageTypes.finished) { - actionUi.updateProgress(100); - if (that.numErrorMessages === 0) { - actionUi.setActionState(actionStates.finishedOk); - if (actionConfig.exitOnSuccess) { - exitComms(true); - } - } else { - actionUi.setActionState(actionStates.finishedErrors); - } - } else if (message.MessageTypeString === messageTypes.cancelled) { - actionUi.setActionState(actionStates.cancelled); - } else { - actionUi.setActionState(actionStates.failed); - } - } - }); - } - - function startAction() { - //we set the state first so that if the invoke fails the user can exit - actionUi.setActionState(actionStates.startingTransient); - actionChannel.invoke('StartAction', actionGuid); - } - - function cancelAction() { - //we set the state first so that if the invoke fails the user can exit - actionUi.setActionState(actionStates.cancellingTransient); - actionChannel.invoke('CancelAction', actionGuid); - } - - //------------------------------------------------------------ - //messages and stuff - - //This increments the numErrorMessages if the message was deemed to be an error - function incNumErrorsIfMessageTypeIsError (messageType) { - if ($.inArray(messageType, messageTypesThatAreErrors) > -1) - that.numErrorMessages++; - } - - function logMessage(actionMessage) { - if (actionMessage == null || !actionMessage.MessageTypeString || !actionMessage.MessageText) { - return; - } - actionUi.addMessageToProgressList(actionMessage.MessageTypeString, actionMessage.MessageText); - } - - function exitComms(successExit) { - actionUi.endActionUi(successExit, jsonResult); //close the panel - } - - //------------------------------------------------------------ - //public variables and methods - - this.numErrorMessages = 0; //number of error messages send from server - - //This will show the action window as a modal window and then starts the action. - //It then monitors the action channel for feeback to the user, and can send a cancel command - //if the user presses cancel. When the action finishes it changes the button to say 'Finished' - // - //It expects the jsonContent to contain TaskId (for communication) and TaskName, to show the user - //if it has a setup error it returns that error, else returns null - this.runAction = function (jsonContent) { - if (jsonContent.errorsDict) { - //there are validation errors so ask ui to display them - actionUi.displayValidationErrors(jsonContent.errorsDict); - } else if (jsonContent.ActionGuid) { - //Got back a sensible content so we run start the action - actionGuid = jsonContent.ActionGuid; - setupTaskChannel(); - } else { - actionUi.reportSystemError('bad call or bad json format data in response to ajax submit', true); - } - }; - - //This should be called when the user wants to execute the state that is currently shown - //It recieves the current state and steps on to the next state, e.g. Cancel moves to Cancelling... - //If the new state means that the action has finished the the finishAction method is called - this.respondToStateChangeRequest = function (currentState) { - if (currentState === actionStates.cancel) { - cancelAction(); - } else if (endsWith(currentState, actionStates.transientSuffix)) { - //The system is in the middle of an operation. Might be hung so give user chance to abandon, but only after confirmation. - var messageToShow = currentState == actionStates.runningNoCancel ? - commsResources.confirmExitOnNonCancellable : - commsResources.confirmExitOnRunningSys; - if (actionUi.confirmDialog(messageToShow)) { - //If user says OK then we exit - exitComms(false); - } - } else { - //we need to exit - exitComms(currentState === actionStates.finishedOk); - } - }; - -}; - - - diff --git a/SampleWebApp/Scripts/ActionRunnerUi.js b/SampleWebApp/Scripts/ActionRunnerUi.js deleted file mode 100644 index ab10bef..0000000 --- a/SampleWebApp/Scripts/ActionRunnerUi.js +++ /dev/null @@ -1,353 +0,0 @@ -/* - The ActionRunnerUi provides public methods for linking a form or button/link to starting an action (see return items at end) - Internally it provides a number of UI methods handed to the ActionRunnerComms when it is created (see actionUI object) - which the ActionRunnerComms uses to communicate between the user and the task running on the server. - - The ActionRunnerUi can be linked to multiple items on a page, but obviously only one can run at a time. - - This version of ActionRunnerUi uses JQuery UI Dialog and Progress bar widgets to provide the user interface. - If you wish to write your own then you need to replace these functions, and the createPanelBasic() function called at the start. - The code assuems you will put up a modal panel fo some kind. This seems sensible but is also needed to stop any button(s) linking - to ActionRunnerUi being pressed, which would cause problems dialogs being overwritten. -*/ - -var ActionRunnerUi = (function ($, window) { - 'use strict'; - - var uiResources = { - nojQuery: 'jQuery was not found. Please ensure jQuery is referenced before this ActionRunner JavaScript files.', - nojQueryUi: 'jQuery UI was not found. This module currently relies on JQuery UI for Dialog and Progress Bar.', - systemError: 'There was a system error. Please talk to your administrator.', - pleaseTryLater: 'An error occured while talking to the server. Please try again later.', - noActionPanel: 'You must have a
sction with the id of "action-panel" for ActionRunner to work', - noActionButton: 'You must have a