diff --git a/AutoHistory.sln b/AutoHistory.sln index fd9d121..ec1eaaf 100644 --- a/AutoHistory.sln +++ b/AutoHistory.sln @@ -1,7 +1,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30907.101 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EEF95A1B-E224-4488-91ED-4CC4EC3CEEE3}" EndProject @@ -15,12 +15,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig NuGet.config = NuGet.config - README.md = README.md EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{DAEF4300-2306-4680-A609-86F2F9E4513A}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{1C12ACF7-CF58-460C-8EF3-2349D8E62AC1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore2.2.MVC.EF.Blogs", "samples\AspNetCore2.2.MVC.EF.Blogs\AspNetCore2.2.MVC.EF.Blogs.csproj", "{AB1074C0-4529-44F0-B837-07E34AB9B321}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore5.0.MVC.EF.Blogs", "samples\AspNetCore5.0.MVC.EF.Blogs\AspNetCore5.0.MVC.EF.Blogs.csproj", "{01180CD5-4F4E-4C51-9478-00090C03FE73}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -36,10 +35,10 @@ Global {2F6C239E-9F05-4654-936B-CFBB00412E4B}.Debug|Any CPU.Build.0 = Debug|Any CPU {2F6C239E-9F05-4654-936B-CFBB00412E4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {2F6C239E-9F05-4654-936B-CFBB00412E4B}.Release|Any CPU.Build.0 = Release|Any CPU - {AB1074C0-4529-44F0-B837-07E34AB9B321}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AB1074C0-4529-44F0-B837-07E34AB9B321}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AB1074C0-4529-44F0-B837-07E34AB9B321}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AB1074C0-4529-44F0-B837-07E34AB9B321}.Release|Any CPU.Build.0 = Release|Any CPU + {01180CD5-4F4E-4C51-9478-00090C03FE73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01180CD5-4F4E-4C51-9478-00090C03FE73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01180CD5-4F4E-4C51-9478-00090C03FE73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01180CD5-4F4E-4C51-9478-00090C03FE73}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -47,7 +46,7 @@ Global GlobalSection(NestedProjects) = preSolution {5927DE0F-FA62-4B71-8AAF-2B3DDC405E34} = {EEF95A1B-E224-4488-91ED-4CC4EC3CEEE3} {2F6C239E-9F05-4654-936B-CFBB00412E4B} = {504C6707-0D9C-4FA7-861D-B1A97AF39E32} - {AB1074C0-4529-44F0-B837-07E34AB9B321} = {DAEF4300-2306-4680-A609-86F2F9E4513A} + {01180CD5-4F4E-4C51-9478-00090C03FE73} = {1C12ACF7-CF58-460C-8EF3-2349D8E62AC1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {ED8B7FB1-308D-49C1-8124-E03D7D6E6D85} diff --git a/NuGet.config b/NuGet.config index 135a50b..4e11f1b 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,10 +1,10 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/README.md b/README.md deleted file mode 100644 index 88a2b7d..0000000 --- a/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# AutoHistory -A plugin for Microsoft.EntityFrameworkCore to support automatically recording data changes history. - -# How to use - -`AutoHistory` will recording all the data changing history in one `Table` named `AutoHistories`, this table will recording data -`UPDATE`, `DELETE` history. - -1. Install AutoHistory Package - -Run the following command in the `Package Manager Console` to install Microsoft.EntityFrameworkCore.AutoHistory - -`PM> Install-Package Microsoft.EntityFrameworkCore.AutoHistory` - -2. Enable AutoHistory - -```csharp -public class BloggingContext : DbContext -{ - public BloggingContext(DbContextOptions options) - : base(options) - { } - - public DbSet Blogs { get; set; } - public DbSet Posts { get; set; } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - // enable auto history functionality. - modelBuilder.EnableAutoHistory(); - } -} -``` - -3. Ensure AutoHistory in DbContext. This must be called before bloggingContext.SaveChanges() or bloggingContext.SaveChangesAsync(). - -```csharp -bloggingContext.EnsureAutoHistory() -``` - -# Use Custom AutoHistory Entity -You can use a custom auto history entity by extending the Microsoft.EntityFrameworkCore.AutoHistory class. - -```csharp -class CustomAutoHistory : AutoHistory -{ - public String CustomField { get; set; } -} -``` - -Then register it in the db context like follows: -```csharp -modelBuilder.EnableAutoHistory(o => { }); -``` - -Then provide a custom history entity creating factory when calling EnsureAutoHistory. The example shows using the -factory directly, but you should use a service here that fills out your history extended properties(The properties inherited from `AutoHistory` will be set by the framework automatically). -```csharp -db.EnsureAutoHistory(() => new CustomAutoHistory() - { - CustomField = "CustomValue" - }); -``` - -# Integrate AutoHistory into other Package - -[Microsoft.EntityFrameworkCore.UnitOfWork](https://github.com/lovedotnet/UnitOfWork) had integrated this package. - - - diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/AspNetCore2.2.MVC.EF.Blogs.csproj b/samples/AspNetCore.MVC.EF.Blogs/AspNetCore.MVC.EF.Blogs.csproj similarity index 60% rename from samples/AspNetCore2.2.MVC.EF.Blogs/AspNetCore2.2.MVC.EF.Blogs.csproj rename to samples/AspNetCore.MVC.EF.Blogs/AspNetCore.MVC.EF.Blogs.csproj index 46b3c03..ff7e2bc 100644 --- a/samples/AspNetCore2.2.MVC.EF.Blogs/AspNetCore2.2.MVC.EF.Blogs.csproj +++ b/samples/AspNetCore.MVC.EF.Blogs/AspNetCore.MVC.EF.Blogs.csproj @@ -1,12 +1,13 @@ - netcoreapp3.1 - InProcess + net6.0 + + - + diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Controllers/BlogsController.cs b/samples/AspNetCore.MVC.EF.Blogs/Controllers/BlogsController.cs similarity index 98% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Controllers/BlogsController.cs rename to samples/AspNetCore.MVC.EF.Blogs/Controllers/BlogsController.cs index dc5442c..37c82e7 100644 --- a/samples/AspNetCore2.2.MVC.EF.Blogs/Controllers/BlogsController.cs +++ b/samples/AspNetCore.MVC.EF.Blogs/Controllers/BlogsController.cs @@ -50,7 +50,7 @@ public IActionResult Create() // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] - public async Task Create([Bind("BlogId,Url")] Blog blog) + public async Task Create([Bind("BlogId,Url,PrivateURL")] Blog blog) { if (ModelState.IsValid) { @@ -83,7 +83,7 @@ public async Task Edit(int? id) // more details see http://go.microsoft.com/fwlink/?LinkId=317598. [HttpPost] [ValidateAntiForgeryToken] - public async Task Edit(int id, [Bind("BlogId,Url")] Blog blog) + public async Task Edit(int id, [Bind("BlogId,Url,PrivateURL")] Blog blog) { if (id != blog.BlogId) { diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Migrations/20181226223513_Initial.Designer.cs b/samples/AspNetCore.MVC.EF.Blogs/Migrations/20181226223513_Initial.Designer.cs similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Migrations/20181226223513_Initial.Designer.cs rename to samples/AspNetCore.MVC.EF.Blogs/Migrations/20181226223513_Initial.Designer.cs diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Migrations/20181226223513_Initial.cs b/samples/AspNetCore.MVC.EF.Blogs/Migrations/20181226223513_Initial.cs similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Migrations/20181226223513_Initial.cs rename to samples/AspNetCore.MVC.EF.Blogs/Migrations/20181226223513_Initial.cs diff --git a/samples/AspNetCore.MVC.EF.Blogs/Migrations/20210705113831_blog_privateURLProperty.Designer.cs b/samples/AspNetCore.MVC.EF.Blogs/Migrations/20210705113831_blog_privateURLProperty.Designer.cs new file mode 100644 index 0000000..9f11724 --- /dev/null +++ b/samples/AspNetCore.MVC.EF.Blogs/Migrations/20210705113831_blog_privateURLProperty.Designer.cs @@ -0,0 +1,114 @@ +// +using System; +using EFGetStarted.AspNetCore.NewDb.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace EFGetStarted.AspNetCore.NewDb.Migrations +{ + [DbContext(typeof(BloggingContext))] + [Migration("20210705113831_blog_privateURLProperty")] + partial class blog_privateURLProperty + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .UseIdentityColumns() + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("ProductVersion", "5.0.0"); + + modelBuilder.Entity("EFGetStarted.AspNetCore.NewDb.Models.Blog", b => + { + b.Property("BlogId") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .UseIdentityColumn(); + + b.Property("PrivateURL") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); + + b.HasKey("BlogId"); + + b.ToTable("Blogs"); + }); + + modelBuilder.Entity("EFGetStarted.AspNetCore.NewDb.Models.Post", b => + { + b.Property("PostId") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .UseIdentityColumn(); + + b.Property("BlogId") + .HasColumnType("int"); + + b.Property("Content") + .HasColumnType("nvarchar(max)"); + + b.Property("Title") + .HasColumnType("nvarchar(max)"); + + b.HasKey("PostId"); + + b.HasIndex("BlogId"); + + b.ToTable("Posts"); + }); + + modelBuilder.Entity("Microsoft.EntityFrameworkCore.AutoHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .UseIdentityColumn(); + + b.Property("Changed") + .HasColumnType("nvarchar(max)"); + + b.Property("Created") + .HasColumnType("datetime2"); + + b.Property("Kind") + .HasColumnType("int"); + + b.Property("RowId") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.Property("TableName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); + + b.HasKey("Id"); + + b.ToTable("AutoHistory"); + }); + + modelBuilder.Entity("EFGetStarted.AspNetCore.NewDb.Models.Post", b => + { + b.HasOne("EFGetStarted.AspNetCore.NewDb.Models.Blog", "Blog") + .WithMany("Posts") + .HasForeignKey("BlogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Blog"); + }); + + modelBuilder.Entity("EFGetStarted.AspNetCore.NewDb.Models.Blog", b => + { + b.Navigation("Posts"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/samples/AspNetCore.MVC.EF.Blogs/Migrations/20210705113831_blog_privateURLProperty.cs b/samples/AspNetCore.MVC.EF.Blogs/Migrations/20210705113831_blog_privateURLProperty.cs new file mode 100644 index 0000000..b020144 --- /dev/null +++ b/samples/AspNetCore.MVC.EF.Blogs/Migrations/20210705113831_blog_privateURLProperty.cs @@ -0,0 +1,23 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace EFGetStarted.AspNetCore.NewDb.Migrations +{ + public partial class blog_privateURLProperty : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "PrivateURL", + table: "Blogs", + type: "nvarchar(max)", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PrivateURL", + table: "Blogs"); + } + } +} diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Migrations/BloggingContextModelSnapshot.cs b/samples/AspNetCore.MVC.EF.Blogs/Migrations/BloggingContextModelSnapshot.cs similarity index 55% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Migrations/BloggingContextModelSnapshot.cs rename to samples/AspNetCore.MVC.EF.Blogs/Migrations/BloggingContextModelSnapshot.cs index f96433d..ad0d897 100644 --- a/samples/AspNetCore2.2.MVC.EF.Blogs/Migrations/BloggingContextModelSnapshot.cs +++ b/samples/AspNetCore.MVC.EF.Blogs/Migrations/BloggingContextModelSnapshot.cs @@ -15,17 +15,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.2.0-rtm-35687") + .UseIdentityColumns() .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasAnnotation("ProductVersion", "5.0.0"); modelBuilder.Entity("EFGetStarted.AspNetCore.NewDb.Models.Blog", b => { b.Property("BlogId") .ValueGeneratedOnAdd() - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasColumnType("int") + .UseIdentityColumn(); - b.Property("Url"); + b.Property("PrivateURL") + .HasColumnType("nvarchar(max)"); + + b.Property("Url") + .HasColumnType("nvarchar(max)"); b.HasKey("BlogId"); @@ -36,13 +41,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Property("PostId") .ValueGeneratedOnAdd() - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasColumnType("int") + .UseIdentityColumn(); - b.Property("BlogId"); + b.Property("BlogId") + .HasColumnType("int"); - b.Property("Content"); + b.Property("Content") + .HasColumnType("nvarchar(max)"); - b.Property("Title"); + b.Property("Title") + .HasColumnType("nvarchar(max)"); b.HasKey("PostId"); @@ -55,21 +64,27 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.Property("Id") .ValueGeneratedOnAdd() - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + .HasColumnType("int") + .UseIdentityColumn(); - b.Property("Changed"); + b.Property("Changed") + .HasColumnType("nvarchar(max)"); - b.Property("Created"); + b.Property("Created") + .HasColumnType("datetime2"); - b.Property("Kind"); + b.Property("Kind") + .HasColumnType("int"); b.Property("RowId") .IsRequired() - .HasMaxLength(50); + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); b.Property("TableName") .IsRequired() - .HasMaxLength(128); + .HasMaxLength(128) + .HasColumnType("nvarchar(128)"); b.HasKey("Id"); @@ -81,7 +96,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasOne("EFGetStarted.AspNetCore.NewDb.Models.Blog", "Blog") .WithMany("Posts") .HasForeignKey("BlogId") - .OnDelete(DeleteBehavior.Cascade); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Blog"); + }); + + modelBuilder.Entity("EFGetStarted.AspNetCore.NewDb.Models.Blog", b => + { + b.Navigation("Posts"); }); #pragma warning restore 612, 618 } diff --git a/samples/AspNetCore.MVC.EF.Blogs/Models/ContextFactory.cs b/samples/AspNetCore.MVC.EF.Blogs/Models/ContextFactory.cs new file mode 100644 index 0000000..ca94c43 --- /dev/null +++ b/samples/AspNetCore.MVC.EF.Blogs/Models/ContextFactory.cs @@ -0,0 +1,47 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +namespace EFGetStarted.AspNetCore.NewDb.Models +{ + /// + /// Class need for EF Migrations to know how the Context should be created. + /// This class is not intended for use on the applications. + /// + public class BloggingContextFactory : IDesignTimeDbContextFactory + { + /// + /// Creates a DbOptionsBuilder from a connectionString. + /// + /// ConnectionString to apply. + /// DbContextOptionsBuilder. + public static DbContextOptionsBuilder CreateDbOptionsBuilder(string connectionString) + { + var options = new DbContextOptionsBuilder(); + return SetDbOptions(options, connectionString); + } + + /// + /// Set options to a DbOptionsBuilder. + /// + /// Options. + /// ConnectionString to apply. + /// DbContextOptionsBuilder. + public static DbContextOptionsBuilder SetDbOptions(DbContextOptionsBuilder options, string connectionString) + { + return options.UseSqlServer(connectionString); + } + + + /// + /// Create a new DB Context, not intended to be used. + /// + /// + /// + public BloggingContext CreateDbContext(string[] args) + { + var connection = @"Server=.;Database=AutoHistoryTest;Trusted_Connection=True;ConnectRetryCount=0"; + var optionsBuilder = CreateDbOptionsBuilder(connection); + return new BloggingContext(optionsBuilder.Options); + } + } +} diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Models/ErrorViewModel.cs b/samples/AspNetCore.MVC.EF.Blogs/Models/ErrorViewModel.cs similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Models/ErrorViewModel.cs rename to samples/AspNetCore.MVC.EF.Blogs/Models/ErrorViewModel.cs diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Models/Model.cs b/samples/AspNetCore.MVC.EF.Blogs/Models/Model.cs similarity index 78% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Models/Model.cs rename to samples/AspNetCore.MVC.EF.Blogs/Models/Model.cs index 39b2bbe..88d7bc1 100644 --- a/samples/AspNetCore2.2.MVC.EF.Blogs/Models/Model.cs +++ b/samples/AspNetCore.MVC.EF.Blogs/Models/Model.cs @@ -5,16 +5,18 @@ namespace EFGetStarted.AspNetCore.NewDb.Models { public class BloggingContext : DbContext { - public BloggingContext(DbContextOptions options) + public BloggingContext(DbContextOptions options) : base(options) { } public DbSet Blogs { get; set; } public DbSet Posts { get; set; } + public DbSet AutoHistory { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) { // enable auto history functionality. - modelBuilder.EnableAutoHistory(changedMaxLength: null); + modelBuilder.EnableAutoHistory(); } } @@ -24,6 +26,9 @@ public class Blog public int BlogId { get; set; } public string Url { get; set; } + [ExcludeFromHistory] + public string PrivateURL { get; set; } + public ICollection Posts { get; set; } } diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Program.cs b/samples/AspNetCore.MVC.EF.Blogs/Program.cs similarity index 61% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Program.cs rename to samples/AspNetCore.MVC.EF.Blogs/Program.cs index 0d333cf..1d9e98a 100644 --- a/samples/AspNetCore2.2.MVC.EF.Blogs/Program.cs +++ b/samples/AspNetCore.MVC.EF.Blogs/Program.cs @@ -7,15 +7,14 @@ public class Program { public static void Main(string[] args) { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { - webBuilder.ConfigureKestrel(serverOptions => - { - // Set properties and call methods on options - }) - .UseStartup(); + webBuilder.UseStartup(); }); - } } } diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Properties/launchSettings.json b/samples/AspNetCore.MVC.EF.Blogs/Properties/launchSettings.json similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Properties/launchSettings.json rename to samples/AspNetCore.MVC.EF.Blogs/Properties/launchSettings.json diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Startup.cs b/samples/AspNetCore.MVC.EF.Blogs/Startup.cs similarity index 81% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Startup.cs rename to samples/AspNetCore.MVC.EF.Blogs/Startup.cs index 9139941..2775b29 100644 --- a/samples/AspNetCore2.2.MVC.EF.Blogs/Startup.cs +++ b/samples/AspNetCore.MVC.EF.Blogs/Startup.cs @@ -26,9 +26,9 @@ public void ConfigureServices(IServiceCollection services) options.MinimumSameSitePolicy = SameSiteMode.None; }); - services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0); + services.AddControllersWithViews(); - var connection = @"Server=(local);Database=AutoHistoryTest;Trusted_Connection=True;ConnectRetryCount=0"; + var connection = @"Server=.;Database=AutoHistoryTest;Trusted_Connection=True;ConnectRetryCount=0"; services.AddDbContext(options => { options.UseSqlServer(connection); @@ -53,10 +53,18 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseStaticFiles(); app.UseCookiePolicy(); + app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); + + // only runs once at system startup + using (var scope = app.ApplicationServices.CreateScope()) + { + var db = scope.ServiceProvider.GetRequiredService(); + db.Database.Migrate(); + } } } } diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Create.cshtml b/samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Create.cshtml similarity index 75% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Create.cshtml rename to samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Create.cshtml index 94e74b6..0066a0a 100644 --- a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Create.cshtml +++ b/samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Create.cshtml @@ -17,6 +17,11 @@ + + + + + diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Delete.cshtml b/samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Delete.cshtml similarity index 65% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Delete.cshtml rename to samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Delete.cshtml index 2e9a3cb..92badf7 100644 --- a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Delete.cshtml +++ b/samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Delete.cshtml @@ -11,14 +11,23 @@ Blog - + @Html.DisplayNameFor(model => model.Url) - + @Html.DisplayFor(model => model.Url) - + + + + @Html.DisplayNameFor(model => model.PrivateURL) + + + @Html.DisplayFor(model => model.PrivateURL) + + + | diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Details.cshtml b/samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Details.cshtml similarity index 59% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Details.cshtml rename to samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Details.cshtml index 2b20038..0520fe7 100644 --- a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Details.cshtml +++ b/samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Details.cshtml @@ -10,13 +10,21 @@ Blog - + @Html.DisplayNameFor(model => model.Url) - + @Html.DisplayFor(model => model.Url) + + + @Html.DisplayNameFor(model => model.PrivateURL) + + + @Html.DisplayFor(model => model.PrivateURL) + + Edit | Back to List diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Edit.cshtml b/samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Edit.cshtml similarity index 76% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Edit.cshtml rename to samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Edit.cshtml index 8e567e8..f91e117 100644 --- a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Edit.cshtml +++ b/samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Edit.cshtml @@ -18,6 +18,11 @@ + + + + + diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Index.cshtml b/samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Index.cshtml similarity index 80% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Index.cshtml rename to samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Index.cshtml index 42befd3..0cad7fd 100644 --- a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Blogs/Index.cshtml +++ b/samples/AspNetCore.MVC.EF.Blogs/Views/Blogs/Index.cshtml @@ -15,6 +15,9 @@ @Html.DisplayNameFor(model => model.Url) + + @Html.DisplayNameFor(model => model.PrivateURL) + @@ -24,6 +27,9 @@ @Html.DisplayFor(modelItem => item.Url) + + @Html.DisplayFor(modelItem => item.PrivateURL) + Edit | Details | diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Shared/Error.cshtml b/samples/AspNetCore.MVC.EF.Blogs/Views/Shared/Error.cshtml similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Views/Shared/Error.cshtml rename to samples/AspNetCore.MVC.EF.Blogs/Views/Shared/Error.cshtml diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Shared/_CookieConsentPartial.cshtml b/samples/AspNetCore.MVC.EF.Blogs/Views/Shared/_CookieConsentPartial.cshtml similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Views/Shared/_CookieConsentPartial.cshtml rename to samples/AspNetCore.MVC.EF.Blogs/Views/Shared/_CookieConsentPartial.cshtml diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Shared/_Layout.cshtml b/samples/AspNetCore.MVC.EF.Blogs/Views/Shared/_Layout.cshtml similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Views/Shared/_Layout.cshtml rename to samples/AspNetCore.MVC.EF.Blogs/Views/Shared/_Layout.cshtml diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/Shared/_ValidationScriptsPartial.cshtml b/samples/AspNetCore.MVC.EF.Blogs/Views/Shared/_ValidationScriptsPartial.cshtml similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Views/Shared/_ValidationScriptsPartial.cshtml rename to samples/AspNetCore.MVC.EF.Blogs/Views/Shared/_ValidationScriptsPartial.cshtml diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/_ViewImports.cshtml b/samples/AspNetCore.MVC.EF.Blogs/Views/_ViewImports.cshtml similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Views/_ViewImports.cshtml rename to samples/AspNetCore.MVC.EF.Blogs/Views/_ViewImports.cshtml diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/Views/_ViewStart.cshtml b/samples/AspNetCore.MVC.EF.Blogs/Views/_ViewStart.cshtml similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/Views/_ViewStart.cshtml rename to samples/AspNetCore.MVC.EF.Blogs/Views/_ViewStart.cshtml diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/appsettings.Development.json b/samples/AspNetCore.MVC.EF.Blogs/appsettings.Development.json similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/appsettings.Development.json rename to samples/AspNetCore.MVC.EF.Blogs/appsettings.Development.json diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/appsettings.json b/samples/AspNetCore.MVC.EF.Blogs/appsettings.json similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/appsettings.json rename to samples/AspNetCore.MVC.EF.Blogs/appsettings.json diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/css/site.css b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/css/site.css similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/css/site.css rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/css/site.css diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/favicon.ico b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/favicon.ico similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/favicon.ico rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/favicon.ico diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/js/site.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/js/site.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/js/site.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/js/site.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/LICENSE b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/LICENSE similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/LICENSE rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/LICENSE diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.css.map diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-grid.min.css.map diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.css.map diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap-reboot.min.css.map diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.css b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.css similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.css rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.css diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.css.map diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/css/bootstrap.min.css.map diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.js.map diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.bundle.min.js.map diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.js.map diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/bootstrap/dist/js/bootstrap.min.js.map diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/LICENSE.txt diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation/LICENSE.md b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation/LICENSE.md similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation/LICENSE.md rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation/LICENSE.md diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/additional-methods.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/additional-methods.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/additional-methods.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/additional-methods.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/additional-methods.min.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/additional-methods.min.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/additional-methods.min.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/additional-methods.min.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/jquery.validate.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/jquery.validate.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/jquery.validate.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/jquery.validate.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/jquery.validate.min.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/jquery.validate.min.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/jquery.validate.min.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery-validation/dist/jquery.validate.min.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery/LICENSE.txt b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery/LICENSE.txt similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery/LICENSE.txt rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery/LICENSE.txt diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.min.js b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.min.js similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.min.js rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.min.js diff --git a/samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.min.map b/samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.min.map similarity index 100% rename from samples/AspNetCore2.2.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.min.map rename to samples/AspNetCore.MVC.EF.Blogs/wwwroot/lib/jquery/dist/jquery.min.map diff --git a/src/Microsoft.EntityFrameworkCore.AutoHistory/Attributes/ExcludeFromHistoryAttribute.cs b/src/Microsoft.EntityFrameworkCore.AutoHistory/Attributes/ExcludeFromHistoryAttribute.cs new file mode 100644 index 0000000..cb47a16 --- /dev/null +++ b/src/Microsoft.EntityFrameworkCore.AutoHistory/Attributes/ExcludeFromHistoryAttribute.cs @@ -0,0 +1,6 @@ +using System; + +namespace Microsoft.EntityFrameworkCore +{ + public class ExcludeFromHistoryAttribute : Attribute { } +} diff --git a/src/Microsoft.EntityFrameworkCore.AutoHistory/AutoHistoryOptions.cs b/src/Microsoft.EntityFrameworkCore.AutoHistory/AutoHistoryOptions.cs index 6239dc2..1d05c45 100644 --- a/src/Microsoft.EntityFrameworkCore.AutoHistory/AutoHistoryOptions.cs +++ b/src/Microsoft.EntityFrameworkCore.AutoHistory/AutoHistoryOptions.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Generic; using System.Text; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; +using System.Text.Json; namespace Microsoft.EntityFrameworkCore.Internal { @@ -47,19 +46,8 @@ private AutoHistoryOptions() public int TableMaxLength { get; set; } = 128; /// - /// The JsonSerializerSettings for the changed column. + /// The json setting for the 'Changed' column /// - public JsonSerializerSettings JsonSerializerSettings { get; set; } = new JsonSerializerSettings() - { - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - ContractResolver = new CamelCasePropertyNamesContractResolver(), - Formatting = Formatting.None, - NullValueHandling = NullValueHandling.Ignore - }; - - /// - /// The json serializer to use when writing changes. Created internally. - /// - internal JsonSerializer JsonSerializer { get; set; } + public JsonSerializerOptions JsonSerializerOptions; } } diff --git a/src/Microsoft.EntityFrameworkCore.AutoHistory/Extensions/DbContextExtensions.cs b/src/Microsoft.EntityFrameworkCore.AutoHistory/Extensions/DbContextExtensions.cs index a4cde84..9f7b1ec 100644 --- a/src/Microsoft.EntityFrameworkCore.AutoHistory/Extensions/DbContextExtensions.cs +++ b/src/Microsoft.EntityFrameworkCore.AutoHistory/Extensions/DbContextExtensions.cs @@ -3,12 +3,10 @@ using System; using System.Collections.Generic; using System.Linq; - +using System.Text.Json; using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Internal; -using Newtonsoft.Json.Linq; - namespace Microsoft.EntityFrameworkCore { /// @@ -30,97 +28,45 @@ public static void EnsureAutoHistory(this DbContext context, Func< { // Must ToArray() here for excluding the AutoHistory model. // Currently, only support Modified and Deleted entity. - var entries = context.ChangeTracker.Entries().Where(e => e.State == EntityState.Modified || e.State == EntityState.Deleted).ToArray(); + var entries = context.ChangeTracker.Entries() + .Where(e => e.State == EntityState.Modified || e.State == EntityState.Deleted) + .ToArray(); foreach (var entry in entries) { - context.Add(entry.AutoHistory(createHistoryFactory)); + var autoHistory = entry.AutoHistory(createHistoryFactory); + if (autoHistory != null) + { + context.Add(autoHistory); + } } } internal static TAutoHistory AutoHistory(this EntityEntry entry, Func createHistoryFactory) where TAutoHistory : AutoHistory { - var history = createHistoryFactory(); - history.TableName = entry.Metadata.GetTableName(); + if (IsEntityExcluded(entry)) + { + return null; + } - // Get the mapped properties for the entity type. - // (include shadow properties, not include navigations & references) - var properties = entry.Properties; + var properties = GetPropertiesWithoutExcluded(entry); + if (!(properties.Any(p => p.IsModified) || entry.State == EntityState.Deleted)) + { + return null; + } - var formatting = AutoHistoryOptions.Instance.JsonSerializerSettings.Formatting; - var jsonSerializer = AutoHistoryOptions.Instance.JsonSerializer; - var json = new JObject(); + var history = createHistoryFactory(); + history.TableName = entry.Metadata.GetTableName(); switch (entry.State) { case EntityState.Added: - foreach (var prop in properties) - { - if (prop.Metadata.IsKey() || prop.Metadata.IsForeignKey()) - { - continue; - } - json[prop.Metadata.Name] = prop.CurrentValue != null - ? JToken.FromObject(prop.CurrentValue, jsonSerializer) - : JValue.CreateNull(); - } - - // REVIEW: what's the best way to set the RowId? - history.RowId = "0"; - history.Kind = EntityState.Added; - history.Changed = json.ToString(formatting); + WriteHistoryAddedState(history, properties); break; case EntityState.Modified: - var bef = new JObject(); - var aft = new JObject(); - - PropertyValues databaseValues = null; - foreach (var prop in properties) - { - if (prop.IsModified) - { - if (prop.OriginalValue != null) - { - if (!prop.OriginalValue.Equals(prop.CurrentValue)) - { - bef[prop.Metadata.Name] = JToken.FromObject(prop.OriginalValue, jsonSerializer); - } - else - { - databaseValues = databaseValues ?? entry.GetDatabaseValues(); - var originalValue = databaseValues.GetValue(prop.Metadata.Name); - bef[prop.Metadata.Name] = originalValue != null - ? JToken.FromObject(originalValue, jsonSerializer) - : JValue.CreateNull(); - } - } - else - { - bef[prop.Metadata.Name] = JValue.CreateNull(); - } - - aft[prop.Metadata.Name] = prop.CurrentValue != null - ? JToken.FromObject(prop.CurrentValue, jsonSerializer) - : JValue.CreateNull(); - } - } - - json["before"] = bef; - json["after"] = aft; - - history.RowId = entry.PrimaryKey(); - history.Kind = EntityState.Modified; - history.Changed = json.ToString(formatting); + WriteHistoryModifiedState(history, entry, properties); break; case EntityState.Deleted: - foreach (var prop in properties) - { - json[prop.Metadata.Name] = prop.OriginalValue != null - ? JToken.FromObject(prop.OriginalValue, jsonSerializer) - : JValue.CreateNull(); - } - history.RowId = entry.PrimaryKey(); - history.Kind = EntityState.Deleted; - history.Changed = json.ToString(formatting); + WriteHistoryDeletedState(history, entry, properties); break; case EntityState.Detached: case EntityState.Unchanged: @@ -131,6 +77,79 @@ internal static TAutoHistory AutoHistory(this EntityEntry entry, F return history; } + private static bool IsEntityExcluded(EntityEntry entry) => + entry.Metadata.ClrType.GetCustomAttributes(typeof(ExcludeFromHistoryAttribute), true).Any(); + + private static IEnumerable GetPropertiesWithoutExcluded(EntityEntry entry) + { + // Get the mapped properties for the entity type. + // (include shadow properties, not include navigations & references) + var excludedProperties = entry.Metadata.ClrType.GetProperties() + .Where(p => p.GetCustomAttributes(typeof(ExcludeFromHistoryAttribute), true).Count() > 0) + .Select(p => p.Name); + + var properties = entry.Properties.Where(f => !excludedProperties.Contains(f.Metadata.Name)); + return properties; + } + + /// + /// Ensures the history for added entries + /// + /// + /// + public static void EnsureAddedHistory( + this DbContext context, + EntityEntry[] addedEntries) + { + EnsureAddedHistory( + context, + () => new AutoHistory(), + addedEntries); + } + + public static void EnsureAddedHistory( + this DbContext context, + Func createHistoryFactory, + EntityEntry[] addedEntries) + where TAutoHistory : AutoHistory + { + foreach (var entry in addedEntries) + { + var autoHistory = entry.AutoHistory(createHistoryFactory); + if (autoHistory != null) + { + context.Add(autoHistory); + } + } + } + + internal static TAutoHistory AddedHistory( + this EntityEntry entry, + Func createHistoryFactory) + where TAutoHistory : AutoHistory + { + if (IsEntityExcluded(entry)) + { + return null; + } + + var properties = GetPropertiesWithoutExcluded(entry); + + dynamic json = new System.Dynamic.ExpandoObject(); + foreach (var prop in properties) + { + ((IDictionary)json)[prop.Metadata.Name] = prop.OriginalValue != null ? + prop.OriginalValue + : null; + } + var history = createHistoryFactory(); + history.TableName = entry.Metadata.GetTableName(); + history.RowId = entry.PrimaryKey(); + history.Kind = EntityState.Added; + history.Changed = JsonSerializer.Serialize(json, AutoHistoryOptions.Instance.JsonSerializerOptions); + return history; + } + private static string PrimaryKey(this EntityEntry entry) { var key = entry.Metadata.FindPrimaryKey(); @@ -147,5 +166,78 @@ private static string PrimaryKey(this EntityEntry entry) return string.Join(",", values); } + + private static void WriteHistoryAddedState(AutoHistory history, IEnumerable properties) + { + dynamic json = new System.Dynamic.ExpandoObject(); + + foreach (var prop in properties) + { + if (prop.Metadata.IsKey() || prop.Metadata.IsForeignKey()) + { + continue; + } + ((IDictionary)json)[prop.Metadata.Name] = prop.CurrentValue; + } + + // REVIEW: what's the best way to set the RowId? + history.RowId = "0"; + history.Kind = EntityState.Added; + history.Changed = JsonSerializer.Serialize(json); + } + + private static void WriteHistoryModifiedState(AutoHistory history, EntityEntry entry, IEnumerable properties) + { + dynamic json = new System.Dynamic.ExpandoObject(); + dynamic bef = new System.Dynamic.ExpandoObject(); + dynamic aft = new System.Dynamic.ExpandoObject(); + + PropertyValues databaseValues = null; + foreach (var prop in properties) + { + if (prop.IsModified) + { + if (prop.OriginalValue != null) + { + if (!prop.OriginalValue.Equals(prop.CurrentValue)) + { + ((IDictionary)bef)[prop.Metadata.Name] = prop.OriginalValue; + } + else + { + databaseValues ??= entry.GetDatabaseValues(); + var originalValue = databaseValues.GetValue(prop.Metadata.Name); + ((IDictionary)bef)[prop.Metadata.Name] = originalValue; + } + } + else + { + ((IDictionary)bef)[prop.Metadata.Name] = null; + } + + ((IDictionary)aft)[prop.Metadata.Name] = prop.CurrentValue; + } + } + + ((IDictionary)json)["before"] = bef; + ((IDictionary)json)["after"] = aft; + + history.RowId = entry.PrimaryKey(); + history.Kind = EntityState.Modified; + history.Changed = JsonSerializer.Serialize(json, AutoHistoryOptions.Instance.JsonSerializerOptions); + } + + private static void WriteHistoryDeletedState(AutoHistory history, EntityEntry entry, IEnumerable properties) + { + dynamic json = new System.Dynamic.ExpandoObject(); + + foreach (var prop in properties) + { + ((IDictionary)json)[prop.Metadata.Name] = prop.OriginalValue; + } + history.RowId = entry.PrimaryKey(); + history.Kind = EntityState.Deleted; + history.Changed = JsonSerializer.Serialize(json, AutoHistoryOptions.Instance.JsonSerializerOptions); + } } } diff --git a/src/Microsoft.EntityFrameworkCore.AutoHistory/Extensions/ModelBuilderExtensions.cs b/src/Microsoft.EntityFrameworkCore.AutoHistory/Extensions/ModelBuilderExtensions.cs index 9b8540d..4dd087e 100644 --- a/src/Microsoft.EntityFrameworkCore.AutoHistory/Extensions/ModelBuilderExtensions.cs +++ b/src/Microsoft.EntityFrameworkCore.AutoHistory/Extensions/ModelBuilderExtensions.cs @@ -1,9 +1,8 @@ // Copyright (c) Arch team. All rights reserved. using System; +using System.Text.Json; using Microsoft.EntityFrameworkCore.Internal; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; namespace Microsoft.EntityFrameworkCore { @@ -19,13 +18,15 @@ public static class ModelBuilderExtensions /// /// The to enable auto history feature. /// The maximum length of the 'Changed' column. null will use default setting 2048. + /// The json setting for the 'Changed' column /// The had enabled auto history feature. - public static ModelBuilder EnableAutoHistory(this ModelBuilder modelBuilder, int? changedMaxLength) + public static ModelBuilder EnableAutoHistory(this ModelBuilder modelBuilder, int? changedMaxLength = null, JsonSerializerOptions JsonSerializerOptions = null) { return ModelBuilderExtensions.EnableAutoHistory(modelBuilder, o => { o.ChangedMaxLength = changedMaxLength; o.LimitChangedLength = false; + o.JsonSerializerOptions = JsonSerializerOptions; }); } @@ -34,7 +35,6 @@ public static ModelBuilder EnableAutoHistory(this ModelBuilder mod { var options = AutoHistoryOptions.Instance; configure?.Invoke(options); - options.JsonSerializer = JsonSerializer.Create(options.JsonSerializerSettings); modelBuilder.Entity(b => { diff --git a/src/Microsoft.EntityFrameworkCore.AutoHistory/Internal/EntityContractResolver.cs b/src/Microsoft.EntityFrameworkCore.AutoHistory/Internal/EntityContractResolver.cs deleted file mode 100644 index e767f46..0000000 --- a/src/Microsoft.EntityFrameworkCore.AutoHistory/Internal/EntityContractResolver.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Arch team. All rights reserved. - -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; - -namespace Microsoft.EntityFrameworkCore.Internal { - internal class EntityContractResolver : DefaultContractResolver { - private readonly DbContext _dbContext; - - public EntityContractResolver(DbContext dbContext) { - _dbContext = dbContext; - } - - protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { - var list = base.CreateProperties(type, memberSerialization); - - var entry = _dbContext.ChangeTracker.Entries().FirstOrDefault(e => e.Entity.GetType() == type); - if (entry == null) { - return list; - } - - // Get the navigations - var navigations = entry.Metadata.GetNavigations().Select(n => n.Name); - - // Exclude the navigation properties - return list.Where(p => !navigations.Contains(p.PropertyName)).ToArray(); - } - } -} diff --git a/src/Microsoft.EntityFrameworkCore.AutoHistory/Microsoft.EntityFrameworkCore.AutoHistory.csproj b/src/Microsoft.EntityFrameworkCore.AutoHistory/Microsoft.EntityFrameworkCore.AutoHistory.csproj index c816fc6..6c41a53 100644 --- a/src/Microsoft.EntityFrameworkCore.AutoHistory/Microsoft.EntityFrameworkCore.AutoHistory.csproj +++ b/src/Microsoft.EntityFrameworkCore.AutoHistory/Microsoft.EntityFrameworkCore.AutoHistory.csproj @@ -1,11 +1,12 @@ A plugin for Microsoft.EntityFrameworkCore to support automatically recording data changes history. - 3.1.3 + 6.0.0 rigofunc;rigofunc@outlook.com - netstandard2.0 + net6.0 $(NoWarn);CS1591 true + README.md true Microsoft.EntityFrameworkCore.AutoHistory Microsoft.EntityFrameworkCore.AutoHistory @@ -16,7 +17,7 @@ https://github.com/arch/AutoHistory.git - - + + - \ No newline at end of file + diff --git a/src/Microsoft.EntityFrameworkCore.AutoHistory/README.md b/src/Microsoft.EntityFrameworkCore.AutoHistory/README.md new file mode 100644 index 0000000..73a71e5 --- /dev/null +++ b/src/Microsoft.EntityFrameworkCore.AutoHistory/README.md @@ -0,0 +1,135 @@ +# AutoHistory +A plugin for Microsoft.EntityFrameworkCore to support automatically recording data changes history. + +# How to use + +`AutoHistory` will recording all the data changing history in one `Table` named `AutoHistories`, this table will recording data +`UPDATE`, `DELETE` history. + +1. Install AutoHistory Package + +Run the following command in the `Package Manager Console` to install Microsoft.EntityFrameworkCore.AutoHistory + +`PM> Install-Package Microsoft.EntityFrameworkCore.AutoHistory` + +2. Enable AutoHistory + +```csharp +public class BloggingContext : DbContext +{ + public BloggingContext(DbContextOptions options) + : base(options) + { } + + public DbSet Blogs { get; set; } + public DbSet Posts { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + // enable auto history functionality. + modelBuilder.EnableAutoHistory(); + } +} +``` + +3. Ensure AutoHistory in DbContext. This must be called before `bloggingContext.SaveChanges()` or `bloggingContext.SaveChangesAsync()`. + +```csharp +bloggingContext.EnsureAutoHistory() +``` + +If you want to record data changes for all entities (except for Added - entities), just override `SaveChanges` and `SaveChangesAsync` methods and call `EnsureAutoHistory()` inside overridden version: +```csharp +public class BloggingContext : DbContext +{ + public BloggingContext(DbContextOptions options) + : base(options) + { } + + public override int SaveChanges() + { + this.EnsureAutoHistory(); + return base.SaveChanges(); + } + + public override Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) + { + this.EnsureAutoHistory(); + return base.SaveChangesAsync(cancellationToken); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + // enable auto history functionality. + modelBuilder.EnableAutoHistory(); + } +} +``` +4. If you also want to record Added - Entities, which is not possible per default, override `SaveChanges` and `SaveChangesAsync` methods this way: +```csharp +public class BloggingContext : DbContext +{ + public override int SaveChanges() + { + var addedEntities = this.ChangeTracker + .Entries() + .Where(e => e.State == EntityState.Added) + .ToArray(); // remember added entries, + // before EF Core is assigning valid Ids (it does on save changes, + // when ids equal zero) and setting their state to + // Unchanged (it does on every save changes) + this.EnsureAutoHistory(); + base.SaveChanges(); + + // after "SaveChanges" added enties now have gotten valid ids (if it was necessary) + // and the history for them can be ensured and be saved with another "SaveChanges" + this.EnsureAddedHistory(addedEntities); + base.SaveChanges(); + } +} +``` + + + +# Use Custom AutoHistory Entity +You can use a custom auto history entity by extending the Microsoft.EntityFrameworkCore.AutoHistory class. + +```csharp +class CustomAutoHistory : AutoHistory +{ + public String CustomField { get; set; } +} +``` + +Then register it in the db context like follows: +```csharp +modelBuilder.EnableAutoHistory(o => { }); +``` + +Then provide a custom history entity creating factory when calling EnsureAutoHistory. The example shows using the +factory directly, but you should use a service here that fills out your history extended properties(The properties inherited from `AutoHistory` will be set by the framework automatically). +```csharp +db.EnsureAutoHistory(() => new CustomAutoHistory() + { + CustomField = "CustomValue" + }); +``` + +# Excluding properties from AutoHistory +You can now excluded properties from being saved into the AutoHistory tables by adding a custom attribute[ExcludeFromHistoryAttribute] attribute to your model properties. + + +```csharp + public class Blog + { + [ExcludeFromHistory] + public string PrivateURL { get; set; } + } +``` + +# Integrate AutoHistory into other Package + +[Microsoft.EntityFrameworkCore.UnitOfWork](https://github.com/lovedotnet/UnitOfWork) had integrated this package. + + + diff --git a/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/AutoHistoryExcludePropertyTest.cs b/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/AutoHistoryExcludePropertyTest.cs new file mode 100644 index 0000000..c42a2db --- /dev/null +++ b/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/AutoHistoryExcludePropertyTest.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.Linq; +using Xunit; + +namespace Microsoft.EntityFrameworkCore.AutoHistory.Test +{ + public class AutoHistoryExcludePropertyTest + { + [Fact] + public void Entity_Update_AutoHistory_Exclude_Only_Modified_Property_Changed_Test() + { + using (var db = new BloggingContext()) + { + var blog = new Blog + { + Url = "http://blogs.msdn.com/adonet", + Posts = new List { + new Post { + Title = "xUnit", + Content = "Post from xUnit test." + } + }, + PrivateURL = "http://www.secret.com" + }; + + db.Attach(blog); + db.SaveChanges(); + + blog.PrivateURL = "http://new.secret.com"; + db.EnsureAutoHistory(); + + var count = db.ChangeTracker.Entries().Count(e => e.State == EntityState.Added); + + //No changes are made (excluded is the only property modified) + Assert.Equal(0, count); + } + } + + [Fact] + public void Entity_Update_AutoHistory_Exclude_Changed_Test() + { + using (var db = new BloggingContext()) + { + var blog = new Blog + { + Url = "http://blogs.msdn.com/adonet", + Posts = new List { + new Post { + Title = "xUnit", + Content = "Post from xUnit test." + } + }, + PrivateURL = "http://www.secret.com" + }; + + db.Attach(blog); + db.SaveChanges(); + + blog.PrivateURL = "http://new.secret.com"; + blog.Posts[0].NumViews = 10; + db.EnsureAutoHistory(); + + var count = db.ChangeTracker.Entries().Count(e => e.State == EntityState.Added); + + Assert.Equal(1, count); + } + } + + [Fact] + public void Excluded_Entity_Update_AutoHistory_OnlyModified_Changed_Test() + { + using (var db = new BloggingContext()) + { + var notTracked = new NotTracked { Title = "don't track me" }; + + db.Attach(notTracked); + db.SaveChanges(); + + notTracked.Title = "still not tracked"; + db.EnsureAutoHistory(); + + var count = db.ChangeTracker.Entries().Count(e => e.State == EntityState.Added); + + //No changes are made (entire entity is excluded) + Assert.Equal(0, count); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/AutoHistoryGenericTest.cs b/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/AutoHistoryGenericTest.cs index 8e90a3c..73a4c88 100644 --- a/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/AutoHistoryGenericTest.cs +++ b/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/AutoHistoryGenericTest.cs @@ -19,7 +19,8 @@ public void Entity_Add_AutoHistory_Test() Title = "xUnit", Content = "Post from xUnit test." } - } + }, + PrivateURL = "http://www.secret.com" }); db.EnsureAutoHistory(() => new CustomAutoHistory() { @@ -45,7 +46,8 @@ public void Entity_Update_AutoHistory_Test() Title = "xUnit", Content = "Post from xUnit test." } - } + }, + PrivateURL = "http://www.secret.com" }; db.Attach(blog); db.SaveChanges(); diff --git a/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/AutoHistoryTest.cs b/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/AutoHistoryTest.cs index 47e1880..33c5746 100644 --- a/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/AutoHistoryTest.cs +++ b/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/AutoHistoryTest.cs @@ -19,8 +19,9 @@ public void Entity_Add_AutoHistory_Test() Title = "xUnit", Content = "Post from xUnit test." } - } - }); + }, + PrivateURL = "http://www.secret.com" + }); ; db.EnsureAutoHistory(); var count = db.ChangeTracker.Entries().Count(e => e.State == EntityState.Added); @@ -42,7 +43,8 @@ public void Entity_Update_AutoHistory_Test() Title = "xUnit", Content = "Post from xUnit test." } - } + }, + PrivateURL = "http://www.secret.com" }; db.Attach(blog); db.SaveChanges(); @@ -55,5 +57,32 @@ public void Entity_Update_AutoHistory_Test() Assert.Equal(1, count); } } + + + [Fact] + public void Entity_Delete_AutoHistory_Test() + { + using var db = new BloggingContext(); + var blog = new Blog + { + Url = "http://blogs.msdn.com/adonet", + Posts = new List { + new Post { + Title = "xUnit", + Content = "Delete Post from xUnit test." + } + }, + PrivateURL = "http://www.secret.com" + }; + db.Attach(blog); + db.SaveChanges(); + + db.Remove(blog); + db.EnsureAutoHistory(); + var count = db.ChangeTracker.Entries().Count(e => e.State == EntityState.Deleted); + + // blog and post are deleted + Assert.Equal(2, count); + } } } diff --git a/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/BloggingContext.cs b/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/BloggingContext.cs index 203a606..3f32d37 100644 --- a/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/BloggingContext.cs +++ b/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/BloggingContext.cs @@ -8,6 +8,8 @@ public class BloggingContext : DbContext { public DbSet Blogs { get; set; } public DbSet Posts { get; set; } + public DbSet NotTracked { get; set; } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { @@ -46,6 +48,9 @@ public class Blog public int BlogId { get; set; } public string Url { get; set; } + [ExcludeFromHistory] + public string PrivateURL { get; set; } + public List Posts { get; set; } } @@ -58,4 +63,11 @@ public class Post public int BlogId { get; set; } public Blog Blog { get; set; } } + + [ExcludeFromHistory] + public class NotTracked + { + public int NotTrackedId { get; set; } + public string Title { get; set; } + } } diff --git a/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/Microsoft.EntityFrameworkCore.AutoHistory.Test.csproj b/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/Microsoft.EntityFrameworkCore.AutoHistory.Test.csproj index 41ec7d6..6e93f59 100644 --- a/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/Microsoft.EntityFrameworkCore.AutoHistory.Test.csproj +++ b/test/Microsoft.EntityFrameworkCore.AutoHistory.Test/Microsoft.EntityFrameworkCore.AutoHistory.Test.csproj @@ -1,6 +1,6 @@ - netcoreapp3.1 + net6.0 $(NoWarn);CS1591 true Microsoft.EntityFrameworkCore.AutoHistory.Test @@ -8,9 +8,12 @@ true - - - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -19,4 +22,4 @@ - \ No newline at end of file +