diff --git a/Directory.build.props b/Directory.build.props index c524074..8f5af2b 100644 --- a/Directory.build.props +++ b/Directory.build.props @@ -5,11 +5,11 @@ Dominic Jonas DoJo.EntityFrameworkCore.DbContextScope https://github.com/dojo90/DbContextScope - https://raw.githubusercontent.com/dojo90/DbContextScope/master/LICENSE + https://raw.githubusercontent.com/dojo90/DbContextScope/master/LICENSE git@github.com:dojo90/DbContextScope.git git sentinel nlog - DbContextScope for EF Core. Forked from https://github.com/tncalvert/DbContextScope. + DbContextScope for EF v2|v3 Core. Forked from https://github.com/tncalvert/DbContextScope. EntityFramework EFCore DbContext DbContextScope diff --git a/README.md b/README.md index e86e337..6cde05b 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,15 @@ ## Foreword -This is a fork from [tncalvert/DbContextScope](https://github.com/tncalvert/DbContextScope). The biggest innovation is the `multitarget support`. +This is a fork from [tncalvert/DbContextScope](https://github.com/tncalvert/DbContextScope). The target framework is `.NET Standart2.0` and minimum `Microsoft.EntityFrameworkCore` version is `v2.0.0`. -- `.NET Framework v4.6.1` -> **Microsoft.EntityFrameworkCore v2.x** -- `.NET CoreApp v2.0` -> **Microsoft.EntityFrameworkCore v2.x** -- `.NET CoreApp v3.0` -> **Microsoft.EntityFrameworkCore v3.x** +If you use this library in your project, please note to use same `nuget versions`! The `DbContextScope` uses `Microsoft.EntityFrameworkCore.Relational v2.0.0`! + +Example `*.csproj` to use `v3.1.1`: + +```xml + + +``` ## Nuget diff --git a/src/DbContextScope.DemoConsoleApp/DbContextScope.DemoConsoleApp.csproj b/src/DbContextScope.DemoConsoleApp/DbContextScope.DemoConsoleApp.csproj deleted file mode 100644 index cb582af..0000000 --- a/src/DbContextScope.DemoConsoleApp/DbContextScope.DemoConsoleApp.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - - Exe - net461;netcoreapp2.0;netcoreapp3.0 - DoJo.EntityFrameworkCore.DemoConsoleApp - - - NET461;NETFULL - - - NETCORE;NETSTANDARD;NETCOREAPP2_0 - - - NETCORE;NETSTANDARD;NETCOREAPP3_0 - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/DbContextScope.DemoConsoleApp/Program.cs b/src/DbContextScope.DemoConsoleApp/Program.cs deleted file mode 100644 index 4d41971..0000000 --- a/src/DbContextScope.DemoConsoleApp/Program.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System; -using System.Linq; -using DbContextScope.DemoConsoleApp.BusinessLogicServices; -using DbContextScope.DemoConsoleApp.CommandModel; -using DbContextScope.DemoConsoleApp.DatabaseContext; -using DbContextScope.DemoConsoleApp.Repositories; -using EntityFrameworkCore.DbContextScope; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Diagnostics; - -namespace DbContextScope.DemoConsoleApp -{ - class Program - { - static void Main(string[] args) - { - //-- Poor-man DI - build our dependencies by hand for this demo - var dbContextScopeFactory = new DbContextScopeFactory(new DbContextFactory()); - var ambientDbContextLocator = new AmbientDbContextLocator(); - var userRepository = new UserRepository(ambientDbContextLocator); - - var userCreationService = new UserCreationService(dbContextScopeFactory, userRepository); - var userQueryService = new UserQueryService(dbContextScopeFactory, userRepository); - var userEmailService = new UserEmailService(dbContextScopeFactory); - var userCreditScoreService = new UserCreditScoreService(dbContextScopeFactory); - - try - { - Console.WriteLine( - "This demo uses an EF Core In Memory database. It does not create any external databases."); - Console.WriteLine(""); - - //-- Demo of typical usage for read and writes - Console.WriteLine("Creating a user called Mary..."); - var marysSpec = new UserCreationSpec("Mary", "mary@example.com"); - userCreationService.CreateUser(marysSpec); - Console.WriteLine("Done.\n"); - - Console.WriteLine("Trying to retrieve our newly created user from the data store..."); - var mary = userQueryService.GetUser(marysSpec.Id); - Console.WriteLine("OK. Persisted user: {0}", mary); - - Console.WriteLine("Press enter to continue..."); - Console.ReadLine(); - - //-- Demo of nested DbContextScopes - Console.WriteLine("Creating 2 new users called John and Jeanne in an atomic transaction..."); - var johnSpec = new UserCreationSpec("John", "john@example.com"); - var jeanneSpec = new UserCreationSpec("Jeanne", "jeanne@example.com"); - userCreationService.CreateListOfUsers(johnSpec, jeanneSpec); - Console.WriteLine("Done.\n"); - - Console.WriteLine("Trying to retrieve our newly created users from the data store..."); - var createdUsers = userQueryService.GetUsers(johnSpec.Id, jeanneSpec.Id); - Console.WriteLine("OK. Found {0} persisted users.", createdUsers.Count()); - - Console.WriteLine("Press enter to continue..."); - Console.ReadLine(); - - //-- Demo of nested DbContextScopes in the face of an exception. - // If any of the provided users failed to get persisted, none should get persisted. - Console.WriteLine( - "Creating 2 new users called Julie and Marc in an atomic transaction. Will make the persistence of the second user fail intentionally in order to test the atomicity of the transaction..."); - var julieSpec = new UserCreationSpec("Julie", "julie@example.com"); - var marcSpec = new UserCreationSpec("Marc", "marc@example.com"); - try - { - userCreationService.CreateListOfUsersWithIntentionalFailure(julieSpec, marcSpec); - Console.WriteLine("Done.\n"); - } - catch (Exception e) - { - Console.WriteLine(e.Message); - Console.WriteLine(); - } - - Console.WriteLine("Trying to retrieve our newly created users from the data store..."); - var maybeCreatedUsers = userQueryService.GetUsers(julieSpec.Id, marcSpec.Id); - Console.WriteLine( - "Found {0} persisted users. If this number is 0, we're all good. If this number is not 0, we have a big problem.", - maybeCreatedUsers.Count()); - - Console.WriteLine("Press enter to continue..."); - Console.ReadLine(); - - //-- Demo of DbContextScope within an async flow - Console.WriteLine( - "Trying to retrieve two users John and Jeanne sequentially in an asynchronous manner..."); - // We're going to block on the async task here as we don't have a choice. No risk of deadlocking in any case as console apps - // don't have a synchronization context. - var usersFoundAsync = userQueryService.GetTwoUsersAsync(johnSpec.Id, jeanneSpec.Id).Result; - Console.WriteLine("OK. Found {0} persisted users.", usersFoundAsync.Count()); - - Console.WriteLine("Press enter to continue..."); - Console.ReadLine(); - - //-- Demo of explicit database transaction. - Console.WriteLine("Trying to retrieve user John within a READ UNCOMMITTED database transaction..."); - // You'll want to use SQL Profiler or Entity Framework Profiler to verify that the correct transaction isolation - // level is being used. - var userMaybeUncommitted = userQueryService.GetUserUncommitted(johnSpec.Id); - Console.WriteLine("OK. User found: {0}", userMaybeUncommitted); - - Console.WriteLine("Press enter to continue..."); - Console.ReadLine(); - - //-- Demo of disabling the DbContextScope nesting behaviour in order to force the persistence of changes made to entities - // This is a pretty advanced feature that you can safely ignore until you actually need it. - Console.WriteLine("Will simulate sending a Welcome email to John..."); - - using (var parentScope = dbContextScopeFactory.Create()) - { - var parentDbContext = parentScope.DbContexts.Get(); - - // Load John in the parent DbContext - var john = parentDbContext.Users.Find(johnSpec.Id); - Console.WriteLine("Before calling SendWelcomeEmail(), john.WelcomeEmailSent = " + - john.WelcomeEmailSent); - - // Now call our SendWelcomeEmail() business logic service method, which will - // update John in a non-nested child context - userEmailService.SendWelcomeEmail(johnSpec.Id); - - // Verify that we can see the modifications made to John by the SendWelcomeEmail() method - Console.WriteLine("After calling SendWelcomeEmail(), john.WelcomeEmailSent = " + - john.WelcomeEmailSent); - - // Note that even though we're not calling SaveChanges() in the parent scope here, the changes - // made to John by SendWelcomeEmail() will remain persisted in the database as SendWelcomeEmail() - // forced the creation of a new DbContextScope. - } - - Console.WriteLine("Press enter to continue..."); - Console.ReadLine(); - - //-- Demonstration of DbContextScope and parallel programming - Console.WriteLine( - "Calculating and storing the credit score of all users in the database in parallel..."); - userCreditScoreService.UpdateCreditScoreForAllUsers(); - Console.WriteLine("Done."); - } - catch (Exception e) - { - Console.WriteLine(e); - } - - Console.WriteLine(); - Console.WriteLine("The end."); - Console.WriteLine("Press enter to exit..."); - Console.ReadLine(); - } - } - - class DbContextFactory : IDbContextFactory - { - public TDbContext CreateDbContext() where TDbContext : DbContext - { - if (typeof(TDbContext) == typeof(UserManagementDbContext)) - { - var config = new DbContextOptionsBuilder() - .UseInMemoryDatabase("1337") - .ConfigureWarnings(warnings => { warnings.Ignore(InMemoryEventId.TransactionIgnoredWarning); }); - return new UserManagementDbContext(config.Options) as TDbContext; - } - - throw new NotImplementedException(typeof(TDbContext).Name); - } - } -} \ No newline at end of file diff --git a/src/DbContextScope.Test/BusinessLogicServices/UserCreationService.cs b/src/DbContextScope.EF2.Test/BusinessLogicServices/UserCreationService.cs similarity index 100% rename from src/DbContextScope.Test/BusinessLogicServices/UserCreationService.cs rename to src/DbContextScope.EF2.Test/BusinessLogicServices/UserCreationService.cs diff --git a/src/DbContextScope.Test/BusinessLogicServices/UserCreditScoreService.cs b/src/DbContextScope.EF2.Test/BusinessLogicServices/UserCreditScoreService.cs similarity index 100% rename from src/DbContextScope.Test/BusinessLogicServices/UserCreditScoreService.cs rename to src/DbContextScope.EF2.Test/BusinessLogicServices/UserCreditScoreService.cs diff --git a/src/DbContextScope.Test/BusinessLogicServices/UserEmailService.cs b/src/DbContextScope.EF2.Test/BusinessLogicServices/UserEmailService.cs similarity index 100% rename from src/DbContextScope.Test/BusinessLogicServices/UserEmailService.cs rename to src/DbContextScope.EF2.Test/BusinessLogicServices/UserEmailService.cs diff --git a/src/DbContextScope.Test/BusinessLogicServices/UserQueryService.cs b/src/DbContextScope.EF2.Test/BusinessLogicServices/UserQueryService.cs similarity index 100% rename from src/DbContextScope.Test/BusinessLogicServices/UserQueryService.cs rename to src/DbContextScope.EF2.Test/BusinessLogicServices/UserQueryService.cs diff --git a/src/DbContextScope.Test/CommandModel/UserCreationSpec.cs b/src/DbContextScope.EF2.Test/CommandModel/UserCreationSpec.cs similarity index 100% rename from src/DbContextScope.Test/CommandModel/UserCreationSpec.cs rename to src/DbContextScope.EF2.Test/CommandModel/UserCreationSpec.cs diff --git a/src/DbContextScope.Test/DatabaseContext/UserManagementDbContext.cs b/src/DbContextScope.EF2.Test/DatabaseContext/UserManagementDbContext.cs similarity index 100% rename from src/DbContextScope.Test/DatabaseContext/UserManagementDbContext.cs rename to src/DbContextScope.EF2.Test/DatabaseContext/UserManagementDbContext.cs diff --git a/src/DbContextScope.EF2.Test/DbContextScope.EF2.Test.csproj b/src/DbContextScope.EF2.Test/DbContextScope.EF2.Test.csproj new file mode 100644 index 0000000..b19401b --- /dev/null +++ b/src/DbContextScope.EF2.Test/DbContextScope.EF2.Test.csproj @@ -0,0 +1,23 @@ + + + + net461;netcoreapp2.0;netcoreapp3.0 + false + EntityFrameworkCore.DbContextScope.Test + DoJo.EntityFrameworkCore.DbContextScope.Test + + + + + + + + + + + + + + + + diff --git a/src/DbContextScope.Test/DbContextScopeTest.cs b/src/DbContextScope.EF2.Test/DbContextScopeTest.cs similarity index 100% rename from src/DbContextScope.Test/DbContextScopeTest.cs rename to src/DbContextScope.EF2.Test/DbContextScopeTest.cs diff --git a/src/DbContextScope.Test/DomainModel/User.cs b/src/DbContextScope.EF2.Test/DomainModel/User.cs similarity index 100% rename from src/DbContextScope.Test/DomainModel/User.cs rename to src/DbContextScope.EF2.Test/DomainModel/User.cs diff --git a/src/DbContextScope.Test/Repositories/IUserRepository.cs b/src/DbContextScope.EF2.Test/Repositories/IUserRepository.cs similarity index 100% rename from src/DbContextScope.Test/Repositories/IUserRepository.cs rename to src/DbContextScope.EF2.Test/Repositories/IUserRepository.cs diff --git a/src/DbContextScope.DemoConsoleApp/Repositories/UserRepository.cs b/src/DbContextScope.EF2.Test/Repositories/UserRepository.cs similarity index 87% rename from src/DbContextScope.DemoConsoleApp/Repositories/UserRepository.cs rename to src/DbContextScope.EF2.Test/Repositories/UserRepository.cs index 3bf9946..e93b165 100644 --- a/src/DbContextScope.DemoConsoleApp/Repositories/UserRepository.cs +++ b/src/DbContextScope.EF2.Test/Repositories/UserRepository.cs @@ -1,10 +1,9 @@ using System; using System.Threading.Tasks; -using DbContextScope.DemoConsoleApp.DatabaseContext; -using DbContextScope.DemoConsoleApp.DomainModel; -using EntityFrameworkCore.DbContextScope; +using EntityFrameworkCore.DbContextScope.Test.DatabaseContext; +using EntityFrameworkCore.DbContextScope.Test.DomainModel; -namespace DbContextScope.DemoConsoleApp.Repositories { +namespace EntityFrameworkCore.DbContextScope.Test.Repositories { /* * An example "repository" relying on an ambient DbContext instance. * @@ -45,12 +44,14 @@ public User Get(Guid userId) { public Task GetAsync(Guid userId) { -#if NETCOREAPP2_0 || NET461 return DbContext.Users.FindAsync(userId); -#endif -#if NETCOREAPP3_0 - return DbContext.Users.FindAsync(userId).AsTask(); -#endif + +//#if NETCOREAPP2_0 || NET461 +// return DbContext.Users.FindAsync(userId); +//#endif +//#if NETCOREAPP3_0 +// return DbContext.Users.FindAsync(userId).AsTask(); +//#endif } public void Add(User user) { diff --git a/src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserCreationService.cs b/src/DbContextScope.EF3.Test/BusinessLogicServices/UserCreationService.cs similarity index 94% rename from src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserCreationService.cs rename to src/DbContextScope.EF3.Test/BusinessLogicServices/UserCreationService.cs index 9c09c87..ece6d66 100644 --- a/src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserCreationService.cs +++ b/src/DbContextScope.EF3.Test/BusinessLogicServices/UserCreationService.cs @@ -1,10 +1,9 @@ using System; -using DbContextScope.DemoConsoleApp.CommandModel; -using DbContextScope.DemoConsoleApp.DomainModel; -using DbContextScope.DemoConsoleApp.Repositories; -using EntityFrameworkCore.DbContextScope; +using EntityFrameworkCore.DbContextScope.Test.CommandModel; +using EntityFrameworkCore.DbContextScope.Test.DomainModel; +using EntityFrameworkCore.DbContextScope.Test.Repositories; -namespace DbContextScope.DemoConsoleApp.BusinessLogicServices { +namespace EntityFrameworkCore.DbContextScope.Test.BusinessLogicServices { /* * Example business logic service implementing command functionalities (i.e. create / update actions). */ diff --git a/src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserCreditScoreService.cs b/src/DbContextScope.EF3.Test/BusinessLogicServices/UserCreditScoreService.cs similarity index 94% rename from src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserCreditScoreService.cs rename to src/DbContextScope.EF3.Test/BusinessLogicServices/UserCreditScoreService.cs index 0b1b06d..85be0fc 100644 --- a/src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserCreditScoreService.cs +++ b/src/DbContextScope.EF3.Test/BusinessLogicServices/UserCreditScoreService.cs @@ -2,10 +2,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using DbContextScope.DemoConsoleApp.DatabaseContext; -using EntityFrameworkCore.DbContextScope; +using EntityFrameworkCore.DbContextScope.Test.DatabaseContext; -namespace DbContextScope.DemoConsoleApp.BusinessLogicServices { +namespace EntityFrameworkCore.DbContextScope.Test.BusinessLogicServices { public class UserCreditScoreService { private readonly IDbContextScopeFactory _dbContextScopeFactory; diff --git a/src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserEmailService.cs b/src/DbContextScope.EF3.Test/BusinessLogicServices/UserEmailService.cs similarity index 93% rename from src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserEmailService.cs rename to src/DbContextScope.EF3.Test/BusinessLogicServices/UserEmailService.cs index e818489..44b90d3 100644 --- a/src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserEmailService.cs +++ b/src/DbContextScope.EF3.Test/BusinessLogicServices/UserEmailService.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; -using DbContextScope.DemoConsoleApp.DatabaseContext; -using DbContextScope.DemoConsoleApp.DomainModel; -using EntityFrameworkCore.DbContextScope; +using EntityFrameworkCore.DbContextScope.Test.DatabaseContext; +using EntityFrameworkCore.DbContextScope.Test.DomainModel; -namespace DbContextScope.DemoConsoleApp.BusinessLogicServices { +namespace EntityFrameworkCore.DbContextScope.Test.BusinessLogicServices { public class UserEmailService { private readonly IDbContextScopeFactory _dbContextScopeFactory; diff --git a/src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserQueryService.cs b/src/DbContextScope.EF3.Test/BusinessLogicServices/UserQueryService.cs similarity index 95% rename from src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserQueryService.cs rename to src/DbContextScope.EF3.Test/BusinessLogicServices/UserQueryService.cs index f29fa52..5857a80 100644 --- a/src/DbContextScope.DemoConsoleApp/BusinessLogicServices/UserQueryService.cs +++ b/src/DbContextScope.EF3.Test/BusinessLogicServices/UserQueryService.cs @@ -3,12 +3,11 @@ using System.Data; using System.Linq; using System.Threading.Tasks; -using DbContextScope.DemoConsoleApp.DatabaseContext; -using DbContextScope.DemoConsoleApp.DomainModel; -using DbContextScope.DemoConsoleApp.Repositories; -using EntityFrameworkCore.DbContextScope; +using EntityFrameworkCore.DbContextScope.Test.DatabaseContext; +using EntityFrameworkCore.DbContextScope.Test.DomainModel; +using EntityFrameworkCore.DbContextScope.Test.Repositories; -namespace DbContextScope.DemoConsoleApp.BusinessLogicServices { +namespace EntityFrameworkCore.DbContextScope.Test.BusinessLogicServices { /* * Example business logic service implementing query functionalities (i.e. read actions). */ diff --git a/src/DbContextScope.DemoConsoleApp/CommandModel/UserCreationSpec.cs b/src/DbContextScope.EF3.Test/CommandModel/UserCreationSpec.cs similarity index 91% rename from src/DbContextScope.DemoConsoleApp/CommandModel/UserCreationSpec.cs rename to src/DbContextScope.EF3.Test/CommandModel/UserCreationSpec.cs index 30bd483..05cfc47 100644 --- a/src/DbContextScope.DemoConsoleApp/CommandModel/UserCreationSpec.cs +++ b/src/DbContextScope.EF3.Test/CommandModel/UserCreationSpec.cs @@ -1,6 +1,6 @@ using System; -namespace DbContextScope.DemoConsoleApp.CommandModel { +namespace EntityFrameworkCore.DbContextScope.Test.CommandModel { /// /// Specifications of the CreateUser command. Defines the properties of a new user. /// diff --git a/src/DbContextScope.DemoConsoleApp/DatabaseContext/UserManagementDbContext.cs b/src/DbContextScope.EF3.Test/DatabaseContext/UserManagementDbContext.cs similarity index 83% rename from src/DbContextScope.DemoConsoleApp/DatabaseContext/UserManagementDbContext.cs rename to src/DbContextScope.EF3.Test/DatabaseContext/UserManagementDbContext.cs index 259786a..fe4112d 100644 --- a/src/DbContextScope.DemoConsoleApp/DatabaseContext/UserManagementDbContext.cs +++ b/src/DbContextScope.EF3.Test/DatabaseContext/UserManagementDbContext.cs @@ -1,7 +1,7 @@ -using DbContextScope.DemoConsoleApp.DomainModel; +using EntityFrameworkCore.DbContextScope.Test.DomainModel; using Microsoft.EntityFrameworkCore; -namespace DbContextScope.DemoConsoleApp.DatabaseContext { +namespace EntityFrameworkCore.DbContextScope.Test.DatabaseContext { public class UserManagementDbContext : DbContext { // Map our 'User' model by convention public DbSet Users { get; set; } diff --git a/src/DbContextScope.EF3.Test/DbContextScope.EF3.Test.csproj b/src/DbContextScope.EF3.Test/DbContextScope.EF3.Test.csproj new file mode 100644 index 0000000..24cf54c --- /dev/null +++ b/src/DbContextScope.EF3.Test/DbContextScope.EF3.Test.csproj @@ -0,0 +1,23 @@ + + + + net461;netcoreapp2.0;netcoreapp3.0 + false + EntityFrameworkCore.DbContextScope.Test + DoJo.EntityFrameworkCore.DbContextScope.Test + + + + + + + + + + + + + + + + diff --git a/src/DbContextScope.EF3.Test/DbContextScopeTest.cs b/src/DbContextScope.EF3.Test/DbContextScopeTest.cs new file mode 100644 index 0000000..2e70509 --- /dev/null +++ b/src/DbContextScope.EF3.Test/DbContextScopeTest.cs @@ -0,0 +1,165 @@ +using System; +using System.Linq; +using EntityFrameworkCore.DbContextScope.Test.BusinessLogicServices; +using EntityFrameworkCore.DbContextScope.Test.CommandModel; +using EntityFrameworkCore.DbContextScope.Test.DatabaseContext; +using EntityFrameworkCore.DbContextScope.Test.DomainModel; +using EntityFrameworkCore.DbContextScope.Test.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Xunit; +using Xunit.Abstractions; + +namespace EntityFrameworkCore.DbContextScope.Test +{ + public static class UserSpecExtensions + { + public static void Equal(this UserCreationSpec spec, User user) + { + Assert.NotNull(spec); + Assert.NotNull(user); + Assert.Equal(spec.Id, user.Id); + Assert.Equal(spec.Email, user.Email); + Assert.Equal(spec.Name, user.Name); + } + } + + public class DbContextScopeTest + { + private readonly ITestOutputHelper _Output; + + public DbContextScopeTest(ITestOutputHelper output) + { + _Output = output; + } + + private class DbContextFactory : IDbContextFactory + { + public TDbContext CreateDbContext() where TDbContext : DbContext + { + if (typeof(TDbContext) == typeof(UserManagementDbContext)) + { + var config = new DbContextOptionsBuilder() + .UseInMemoryDatabase("1337") + .ConfigureWarnings(warnings => { warnings.Ignore(InMemoryEventId.TransactionIgnoredWarning); }); + return new UserManagementDbContext(config.Options) as TDbContext; + } + + throw new NotImplementedException(typeof(TDbContext).Name); + } + } + + [Fact] + public void FullTest() + { + //-- Poor-man DI - build our dependencies by hand for this demo + var dbContextScopeFactory = new DbContextScopeFactory(new DbContextFactory()); + var ambientDbContextLocator = new AmbientDbContextLocator(); + var userRepository = new UserRepository(ambientDbContextLocator); + + var userCreationService = new UserCreationService(dbContextScopeFactory, userRepository); + var userQueryService = new UserQueryService(dbContextScopeFactory, userRepository); + var userEmailService = new UserEmailService(dbContextScopeFactory); + var userCreditScoreService = new UserCreditScoreService(dbContextScopeFactory); + + _Output.WriteLine( + "This demo uses an EF Core In Memory database. It does not create any external databases."); + _Output.WriteLine(""); + + //-- Demo of typical usage for read and writes + _Output.WriteLine("Creating a user called Mary..."); + var marysSpec = new UserCreationSpec("Mary", "mary@example.com"); + userCreationService.CreateUser(marysSpec); + _Output.WriteLine("Done.\n"); + + _Output.WriteLine("Trying to retrieve our newly created user from the data store..."); + var mary = userQueryService.GetUser(marysSpec.Id); + _Output.WriteLine("OK. Persisted user: {0}", mary); + marysSpec.Equal(mary); + + //-- Demo of nested DbContextScopes + _Output.WriteLine("Creating 2 new users called John and Jeanne in an atomic transaction..."); + var johnSpec = new UserCreationSpec("John", "john@example.com"); + var jeanneSpec = new UserCreationSpec("Jeanne", "jeanne@example.com"); + userCreationService.CreateListOfUsers(johnSpec, jeanneSpec); + _Output.WriteLine("Done.\n"); + + _Output.WriteLine("Trying to retrieve our newly created users from the data store..."); + var createdUsers = userQueryService.GetUsers(johnSpec.Id, jeanneSpec.Id).ToList(); + _Output.WriteLine("OK. Found {0} persisted users.", createdUsers.Count); + + Assert.Equal(2, createdUsers.Count); + johnSpec.Equal(createdUsers[0]); + jeanneSpec.Equal(createdUsers[1]); + + //-- Demo of nested DbContextScopes in the face of an exception. + // If any of the provided users failed to get persisted, none should get persisted. + _Output.WriteLine( + "Creating 2 new users called Julie and Marc in an atomic transaction. Will make the persistence of the second user fail intentionally in order to test the atomicity of the transaction..."); + var julieSpec = new UserCreationSpec("Julie", "julie@example.com"); + var marcSpec = new UserCreationSpec("Marc", "marc@example.com"); + + Assert.ThrowsAny(() => + { + userCreationService.CreateListOfUsersWithIntentionalFailure(julieSpec, marcSpec); + }); + + _Output.WriteLine("Trying to retrieve our newly created users from the data store..."); + var maybeCreatedUsers = userQueryService.GetUsers(julieSpec.Id, marcSpec.Id).ToList(); + _Output.WriteLine( + "Found {0} persisted users. If this number is 0, we're all good. If this number is not 0, we have a big problem.", + maybeCreatedUsers.Count); + Assert.Equal(0, maybeCreatedUsers.Count); + + //-- Demo of DbContextScope within an async flow + _Output.WriteLine("Trying to retrieve two users John and Jeanne sequentially in an asynchronous manner..."); + // We're going to block on the async task here as we don't have a choice. No risk of deadlocking in any case as console apps + // don't have a synchronization context. + var usersFoundAsync = userQueryService.GetTwoUsersAsync(johnSpec.Id, jeanneSpec.Id).Result; + _Output.WriteLine("OK. Found {0} persisted users.", usersFoundAsync.Count); + Assert.Equal(2, usersFoundAsync.Count); + johnSpec.Equal(usersFoundAsync[0]); + jeanneSpec.Equal(usersFoundAsync[1]); + + //-- Demo of explicit database transaction. + _Output.WriteLine("Trying to retrieve user John within a READ UNCOMMITTED database transaction..."); + // You'll want to use SQL Profiler or Entity Framework Profiler to verify that the correct transaction isolation + // level is being used. + var userMaybeUncommitted = userQueryService.GetUserUncommitted(johnSpec.Id); + _Output.WriteLine("OK. User found: {0}", userMaybeUncommitted); + johnSpec.Equal(userMaybeUncommitted); + + //-- Demo of disabling the DbContextScope nesting behaviour in order to force the persistence of changes made to entities + // This is a pretty advanced feature that you can safely ignore until you actually need it. + _Output.WriteLine("Will simulate sending a Welcome email to John..."); + + using (var parentScope = dbContextScopeFactory.Create()) + { + var parentDbContext = parentScope.DbContexts.Get(); + + // Load John in the parent DbContext + var john = parentDbContext.Users.Find(johnSpec.Id); + _Output.WriteLine("Before calling SendWelcomeEmail(), john.WelcomeEmailSent = " + + john.WelcomeEmailSent); + + // Now call our SendWelcomeEmail() business logic service method, which will + // update John in a non-nested child context + userEmailService.SendWelcomeEmail(johnSpec.Id); + + // Verify that we can see the modifications made to John by the SendWelcomeEmail() method + _Output.WriteLine("After calling SendWelcomeEmail(), john.WelcomeEmailSent = " + + john.WelcomeEmailSent); + + // Note that even though we're not calling SaveChanges() in the parent scope here, the changes + // made to John by SendWelcomeEmail() will remain persisted in the database as SendWelcomeEmail() + // forced the creation of a new DbContextScope. + } + + //-- Demonstration of DbContextScope and parallel programming + _Output.WriteLine( + "Calculating and storing the credit score of all users in the database in parallel..."); + userCreditScoreService.UpdateCreditScoreForAllUsers(); + _Output.WriteLine("Done."); + } + } +} \ No newline at end of file diff --git a/src/DbContextScope.DemoConsoleApp/DomainModel/User.cs b/src/DbContextScope.EF3.Test/DomainModel/User.cs similarity index 91% rename from src/DbContextScope.DemoConsoleApp/DomainModel/User.cs rename to src/DbContextScope.EF3.Test/DomainModel/User.cs index fdbbaec..ed9e808 100644 --- a/src/DbContextScope.DemoConsoleApp/DomainModel/User.cs +++ b/src/DbContextScope.EF3.Test/DomainModel/User.cs @@ -1,6 +1,6 @@ using System; -namespace DbContextScope.DemoConsoleApp.DomainModel { +namespace EntityFrameworkCore.DbContextScope.Test.DomainModel { // Anemic model to keep this demo application simple. public class User { public Guid Id { get; set; } diff --git a/src/DbContextScope.DemoConsoleApp/Repositories/IUserRepository.cs b/src/DbContextScope.EF3.Test/Repositories/IUserRepository.cs similarity index 61% rename from src/DbContextScope.DemoConsoleApp/Repositories/IUserRepository.cs rename to src/DbContextScope.EF3.Test/Repositories/IUserRepository.cs index b1d2129..6493fab 100644 --- a/src/DbContextScope.DemoConsoleApp/Repositories/IUserRepository.cs +++ b/src/DbContextScope.EF3.Test/Repositories/IUserRepository.cs @@ -1,8 +1,8 @@ using System; using System.Threading.Tasks; -using DbContextScope.DemoConsoleApp.DomainModel; +using EntityFrameworkCore.DbContextScope.Test.DomainModel; -namespace DbContextScope.DemoConsoleApp.Repositories { +namespace EntityFrameworkCore.DbContextScope.Test.Repositories { public interface IUserRepository { User Get(Guid userId); Task GetAsync(Guid userId); diff --git a/src/DbContextScope.Test/Repositories/UserRepository.cs b/src/DbContextScope.EF3.Test/Repositories/UserRepository.cs similarity index 93% rename from src/DbContextScope.Test/Repositories/UserRepository.cs rename to src/DbContextScope.EF3.Test/Repositories/UserRepository.cs index 692dacd..fccc3af 100644 --- a/src/DbContextScope.Test/Repositories/UserRepository.cs +++ b/src/DbContextScope.EF3.Test/Repositories/UserRepository.cs @@ -44,12 +44,14 @@ public User Get(Guid userId) { public Task GetAsync(Guid userId) { -#if NETCOREAPP2_0 || NET461 - return DbContext.Users.FindAsync(userId); -#endif -#if NETCOREAPP3_0 return DbContext.Users.FindAsync(userId).AsTask(); -#endif + +//#if NETCOREAPP2_0 || NET461 +// return DbContext.Users.FindAsync(userId); +//#endif +//#if NETCOREAPP3_0 +// return DbContext.Users.FindAsync(userId).AsTask(); +//#endif } public void Add(User user) { diff --git a/src/DbContextScope.Test/DbContextScope.Test.csproj b/src/DbContextScope.Test/DbContextScope.Test.csproj deleted file mode 100644 index 1bf0ac4..0000000 --- a/src/DbContextScope.Test/DbContextScope.Test.csproj +++ /dev/null @@ -1,44 +0,0 @@ - - - - net461;netcoreapp2.0;netcoreapp3.0 - false - EntityFrameworkCore.DbContextScope.Test - DoJo.EntityFrameworkCore.DbContextScope.Test - - - NET461;NETFULL - - - NETCORE;NETSTANDARD;NETCOREAPP2_0 - - - NETCORE;NETSTANDARD;NETCOREAPP3_0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/DbContextScope.sln b/src/DbContextScope.sln index 1564b97..98a52e6 100644 --- a/src/DbContextScope.sln +++ b/src/DbContextScope.sln @@ -13,7 +13,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbContextScope", "DbContextScope\DbContextScope.csproj", "{D84F0F17-48F7-4BF6-B679-07F57A23CEA6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DbContextScope.Test", "DbContextScope.Test\DbContextScope.Test.csproj", "{3E366ACD-5394-4D38-BBCF-7E524673A30A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbContextScope.EF2.Test", "DbContextScope.EF2.Test\DbContextScope.EF2.Test.csproj", "{A0EAD0E3-563E-4DBA-B372-6184F621D83A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DbContextScope.EF3.Test", "DbContextScope.EF3.Test\DbContextScope.EF3.Test.csproj", "{CDDBFD34-CE32-419F-B778-C54EFACA5B6C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -25,10 +27,14 @@ Global {D84F0F17-48F7-4BF6-B679-07F57A23CEA6}.Debug|Any CPU.Build.0 = Debug|Any CPU {D84F0F17-48F7-4BF6-B679-07F57A23CEA6}.Release|Any CPU.ActiveCfg = Release|Any CPU {D84F0F17-48F7-4BF6-B679-07F57A23CEA6}.Release|Any CPU.Build.0 = Release|Any CPU - {3E366ACD-5394-4D38-BBCF-7E524673A30A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E366ACD-5394-4D38-BBCF-7E524673A30A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E366ACD-5394-4D38-BBCF-7E524673A30A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E366ACD-5394-4D38-BBCF-7E524673A30A}.Release|Any CPU.Build.0 = Release|Any CPU + {A0EAD0E3-563E-4DBA-B372-6184F621D83A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A0EAD0E3-563E-4DBA-B372-6184F621D83A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A0EAD0E3-563E-4DBA-B372-6184F621D83A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A0EAD0E3-563E-4DBA-B372-6184F621D83A}.Release|Any CPU.Build.0 = Release|Any CPU + {CDDBFD34-CE32-419F-B778-C54EFACA5B6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CDDBFD34-CE32-419F-B778-C54EFACA5B6C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CDDBFD34-CE32-419F-B778-C54EFACA5B6C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CDDBFD34-CE32-419F-B778-C54EFACA5B6C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/DbContextScope/DbContextScope.csproj b/src/DbContextScope/DbContextScope.csproj index 9470df3..65d2238 100644 --- a/src/DbContextScope/DbContextScope.csproj +++ b/src/DbContextScope/DbContextScope.csproj @@ -1,31 +1,17 @@  - net461;netcoreapp2.0;netcoreapp3.0 + netstandard2.0 DoJo.EntityFrameworkCore.DbContextScope true DoJo.EntityFrameworkCore.DbContextScope - - NET461;NETFULL - - - NETCORE;NETSTANDARD;NETCOREAPP2_0 - - - NETCORE;NETSTANDARD;NETCOREAPP3_0 - - - - - - - all runtime; build; native; contentfiles; analyzers; buildtransitive + \ No newline at end of file diff --git a/src/DbContextScope/Extensions/DbContextExtensions.cs b/src/DbContextScope/Extensions/DbContextExtensions.cs index 521b4de..be771d1 100644 --- a/src/DbContextScope/Extensions/DbContextExtensions.cs +++ b/src/DbContextScope/Extensions/DbContextExtensions.cs @@ -1,5 +1,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking.Internal; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Internal; #if NETCOREAPP2_0 || NET461 using Microsoft.EntityFrameworkCore.Infrastructure; @@ -20,6 +22,10 @@ public static class DbContextExtensions /// public static IStateManager GetStateManager(this DbContext context) { + // seems to work for both frameworks + // v2.2.6 + // v3.1.1 + return context.GetDependencies().StateManager; #if NETCOREAPP2_0 || NET461 return context.ChangeTracker.GetInfrastructure(); #endif