diff --git a/Volo.Abp.sln b/Volo.Abp.sln index 4628b2ffa3..0ee3939ae5 100644 --- a/Volo.Abp.sln +++ b/Volo.Abp.sln @@ -86,6 +86,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Volo.Abp.Identity.Tests", " EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Volo.Abp.MongoDB", "src\Volo.Abp.MongoDB\Volo.Abp.MongoDB.xproj", "{B31FFAE3-5DAC-4E51-BD17-F7446B741A36}" EndProject +Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AbpDesk.MongoBlog", "src\AbpDesk\AbpDesk.MongoBlog\AbpDesk.MongoBlog.xproj", "{63244DC7-34BE-44E1-BF6F-F2672E59AF36}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -208,6 +210,10 @@ Global {B31FFAE3-5DAC-4E51-BD17-F7446B741A36}.Debug|Any CPU.Build.0 = Debug|Any CPU {B31FFAE3-5DAC-4E51-BD17-F7446B741A36}.Release|Any CPU.ActiveCfg = Release|Any CPU {B31FFAE3-5DAC-4E51-BD17-F7446B741A36}.Release|Any CPU.Build.0 = Release|Any CPU + {63244DC7-34BE-44E1-BF6F-F2672E59AF36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {63244DC7-34BE-44E1-BF6F-F2672E59AF36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {63244DC7-34BE-44E1-BF6F-F2672E59AF36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {63244DC7-34BE-44E1-BF6F-F2672E59AF36}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -250,5 +256,6 @@ Global {439DFC0F-1BA2-464F-900E-EA7E18C08975} = {1895A5C9-50D4-4568-9A3A-14657E615A5E} {4AB91077-82DC-4335-9274-BCE017BD9C8B} = {146F561E-C7B8-4166-9383-47E1BC1A2E62} {B31FFAE3-5DAC-4E51-BD17-F7446B741A36} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8} + {63244DC7-34BE-44E1-BF6F-F2672E59AF36} = {1187F469-0063-4065-9419-A1D956C80145} EndGlobalSection EndGlobal diff --git a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/AbpDeskConsoleDemoModule.cs b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/AbpDeskConsoleDemoModule.cs index 23f9c47460..82464ed9e9 100644 --- a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/AbpDeskConsoleDemoModule.cs +++ b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/AbpDeskConsoleDemoModule.cs @@ -1,4 +1,5 @@ using System.IO; +using AbpDesk.Blogging; using AbpDesk.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -13,7 +14,8 @@ namespace AbpDesk.ConsoleDemo [DependsOn( typeof(AbpDeskApplicationModule), typeof(AbpDeskEntityFrameworkCoreModule), - typeof(AbpIdentityEntityFrameworkCoreModule))] + typeof(AbpIdentityEntityFrameworkCoreModule), + typeof(AbpDeskMongoBlogModule))] public class AbpDeskConsoleDemoModule : AbpModule { public override void ConfigureServices(IServiceCollection services) diff --git a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/BlogPostLister.cs b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/BlogPostLister.cs new file mode 100644 index 0000000000..55b0f2c56f --- /dev/null +++ b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/BlogPostLister.cs @@ -0,0 +1,43 @@ +using System; +using AbpDesk.Blogging; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Uow; +using Volo.DependencyInjection; + +namespace AbpDesk.ConsoleDemo +{ + public class BlogPostLister : ITransientDependency + { + private readonly IQueryableRepository _blogPostRepository; //TODO: Should not be needed to string + private readonly IUnitOfWorkManager _unitOfWorkManager; + + public BlogPostLister(IQueryableRepository blogPostRepository, IUnitOfWorkManager unitOfWorkManager) + { + _blogPostRepository = blogPostRepository; + _unitOfWorkManager = unitOfWorkManager; + } + + public void List() + { + Console.WriteLine(); + Console.WriteLine("List of blog posts:"); + + using (var unitOfWork = _unitOfWorkManager.Begin()) + { + //_blogPostRepository.Insert(new BlogPost("Hello World 3!", "Hello World 3......")); + + foreach (var blogPost in _blogPostRepository) + { + Console.WriteLine("# " + blogPost); + + foreach (var comment in blogPost.Comments) + { + Console.WriteLine(" - " + comment); + } + } + + unitOfWork.Complete(); + } + } + } +} diff --git a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/Program.cs b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/Program.cs index 1b20d9c61b..10b47e6ad1 100644 --- a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/Program.cs +++ b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/Program.cs @@ -23,6 +23,13 @@ namespace AbpDesk.ConsoleDemo .GetRequiredService() .List(); + application + .ServiceProvider + .GetRequiredService() + .List(); + + Console.WriteLine(); + Console.WriteLine("Press ENTER to exit..."); Console.ReadLine(); application.Shutdown(); diff --git a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/TicketLister.cs b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/TicketLister.cs index 17c05d2cc4..c9ec569632 100644 --- a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/TicketLister.cs +++ b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/TicketLister.cs @@ -17,6 +17,9 @@ namespace AbpDesk.ConsoleDemo public void List() { + Console.WriteLine(); + Console.WriteLine("List of tickets:"); + var result = AsyncHelper.RunSync(() => _ticketAppService.GetAll(new GetAllTicketsInput())); foreach (var ticket in result.Items) diff --git a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/UserLister.cs b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/UserLister.cs index 4ac9e130c5..a6482af4d4 100644 --- a/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/UserLister.cs +++ b/src/AbpDesk/AbpDesk.ConsoleDemo/AbpDesk/ConsoleDemo/UserLister.cs @@ -26,6 +26,9 @@ namespace AbpDesk.ConsoleDemo public void List() { + Console.WriteLine(); + Console.WriteLine("List of users:"); + using (var uow = _unitOfWorkManager.Begin()) { foreach (var user in _userRepository.ToList()) diff --git a/src/AbpDesk/AbpDesk.ConsoleDemo/appsettings.json b/src/AbpDesk/AbpDesk.ConsoleDemo/appsettings.json index 0f39b1b157..aadbbcdae6 100644 --- a/src/AbpDesk/AbpDesk.ConsoleDemo/appsettings.json +++ b/src/AbpDesk/AbpDesk.ConsoleDemo/appsettings.json @@ -1,5 +1,6 @@ { "ConnectionStrings": { - "Default": "Server=localhost;Database=AbpDesk;Trusted_Connection=True;" + "Default": "Server=localhost;Database=AbpDesk;Trusted_Connection=True;", + "AbpDeskMongoBlog": "mongodb://127.0.0.1:27017|AbpDeskBlog" } -} +} \ No newline at end of file diff --git a/src/AbpDesk/AbpDesk.ConsoleDemo/project.json b/src/AbpDesk/AbpDesk.ConsoleDemo/project.json index f6285ebf40..be335e152b 100644 --- a/src/AbpDesk/AbpDesk.ConsoleDemo/project.json +++ b/src/AbpDesk/AbpDesk.ConsoleDemo/project.json @@ -22,7 +22,8 @@ "Microsoft.Extensions.Configuration.Json": "1.1.0", "Microsoft.Extensions.Options.ConfigurationExtensions": "1.1.0", "Volo.Abp.Identity": "1.0.0-*", - "Volo.Abp.Identity.EntityFrameworkCore": "1.0.0-*" + "Volo.Abp.Identity.EntityFrameworkCore": "1.0.0-*", + "AbpDesk.MongoBlog": "1.0.0-*" }, "tools": { diff --git a/src/AbpDesk/AbpDesk.Domain/AbpDesk/Tickets/Ticket.cs b/src/AbpDesk/AbpDesk.Domain/AbpDesk/Tickets/Ticket.cs index fae10ee781..338ebaa221 100644 --- a/src/AbpDesk/AbpDesk.Domain/AbpDesk/Tickets/Ticket.cs +++ b/src/AbpDesk/AbpDesk.Domain/AbpDesk/Tickets/Ticket.cs @@ -18,7 +18,7 @@ namespace AbpDesk.Tickets [MaxLength(MaxBodyLength)] public string Body { get; set; } - private Ticket() + protected Ticket() { } diff --git a/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk.MongoBlog.xproj b/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk.MongoBlog.xproj new file mode 100644 index 0000000000..9e02b68a2d --- /dev/null +++ b/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk.MongoBlog.xproj @@ -0,0 +1,20 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 63244dc7-34be-44e1-bf6f-f2672e59af36 + + + .\obj + .\bin\ + v4.6.1 + + + 2.0 + + + \ No newline at end of file diff --git a/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/AbpDeskMongoBlogModule.cs b/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/AbpDeskMongoBlogModule.cs new file mode 100644 index 0000000000..e75fa1c76d --- /dev/null +++ b/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/AbpDeskMongoBlogModule.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.DependencyInjection; +using Volo.Abp.Modularity; +using Volo.Abp.MongoDB; + +namespace AbpDesk.Blogging +{ + [DependsOn(typeof(AbpMongoDbModule))] + public class AbpDeskMongoBlogModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + services.AddMongoDbContext(options => + { + options.WithDefaultRepositories(); + }); + + services.AddAssemblyOf(); + } + } +} diff --git a/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/AbpDeskMongoDbContext.cs b/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/AbpDeskMongoDbContext.cs new file mode 100644 index 0000000000..aeb62dbcb0 --- /dev/null +++ b/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/AbpDeskMongoDbContext.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; + +namespace AbpDesk.Blogging +{ + [ConnectionStringName(ConnectionStringName)] + public class AbpDeskMongoDbContext : AbpMongoDbContext + { + public const string ConnectionStringName = "AbpDeskMongoBlog"; + + private static readonly Type[] EntityCollectionTypes = { + typeof(BlogPost) + }; + + public override IReadOnlyList GetEntityCollectionTypes() + { + return EntityCollectionTypes; + } + } +} \ No newline at end of file diff --git a/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/BlogPost.cs b/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/BlogPost.cs new file mode 100644 index 0000000000..dc13aa207b --- /dev/null +++ b/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/BlogPost.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using JetBrains.Annotations; +using Volo; +using Volo.Abp.Domain.Entities; +using Volo.ExtensionMethods; + +namespace AbpDesk.Blogging +{ + public class BlogPost : AggregateRoot + { + public virtual string Title { get; protected set; } + + public virtual string Body { get; protected set; } + + public virtual ICollection Comments { get; protected set; } + + protected BlogPost() + { + + } + + public BlogPost([NotNull] string title, [NotNull] string body) + { + Id = Guid.NewGuid().ToString("D"); + + Check.NotNull(title, nameof(title)); + Check.NotNull(body, nameof(body)); + + Title = title; + Body = body; + + Comments = new List(); + } + + public override string ToString() + { + return $"{base.ToString()}, Title = {Title}, Body = {Body.TruncateWithPostfix(32)}"; + } + } +} diff --git a/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/BlogPostComment.cs b/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/BlogPostComment.cs new file mode 100644 index 0000000000..33c72de16c --- /dev/null +++ b/src/AbpDesk/AbpDesk.MongoBlog/AbpDesk/Blogging/BlogPostComment.cs @@ -0,0 +1,41 @@ +using JetBrains.Annotations; +using Volo.Abp.Domain.Entities; + +namespace AbpDesk.Blogging +{ + public class BlogPostComment : Entity + { + [NotNull] + public virtual string Name { get; protected set; } + + [CanBeNull] + public virtual string Email { get; protected set; } + + public virtual byte? Star { get; protected set; } + + [NotNull] + public virtual string Message { get; protected set; } + + public BlogPostComment() + { + + } + + public BlogPostComment(string name, string message, string email = null, byte? star = null) + { + Name = name; + Email = email; + Message = message; + Star = star; + } + + public override string ToString() + { + return $"{base.ToString()}, " + + $"Name = {Name}, " + + $"Email = {Email}, " + + $"Message = {Message}, " + + $"Star = {(Star.HasValue ? Star.Value.ToString() : "none")}"; + } + } +} \ No newline at end of file diff --git a/src/AbpDesk/AbpDesk.MongoBlog/Properties/AssemblyInfo.cs b/src/AbpDesk/AbpDesk.MongoBlog/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..65aae8f6c1 --- /dev/null +++ b/src/AbpDesk/AbpDesk.MongoBlog/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AbpDesk.MongoBlog")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("63244dc7-34be-44e1-bf6f-f2672e59af36")] diff --git a/src/AbpDesk/AbpDesk.MongoBlog/project.json b/src/AbpDesk/AbpDesk.MongoBlog/project.json new file mode 100644 index 0000000000..3e6d5094da --- /dev/null +++ b/src/AbpDesk/AbpDesk.MongoBlog/project.json @@ -0,0 +1,15 @@ +{ + "version": "1.0.0-*", + + "dependencies": { + "NETStandard.Library": "1.6.1", + "Volo.Abp": "1.0.0-*", + "Volo.Abp.MongoDB": "1.0.0-*" + }, + + "frameworks": { + "netstandard1.6": { + "imports": "dnxcore50" + } + } +} diff --git a/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpDbContextRegistrationOptions.cs b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpDbContextRegistrationOptions.cs new file mode 100644 index 0000000000..03a8d70bb5 --- /dev/null +++ b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpDbContextRegistrationOptions.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Data; +using Volo.Abp.Domain.Repositories; + +namespace Microsoft.Extensions.DependencyInjection +{ + public class AbpDbContextRegistrationOptions : CommonDbContextRegistrationOptions, IAbpDbContextRegistrationOptionsBuilder + { + + } +} \ No newline at end of file diff --git a/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs index 0d3d78cdd9..41c81ff5df 100644 --- a/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs +++ b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AbpEfCoreServiceCollectionExtensions.cs @@ -1,17 +1,16 @@ using System; using System.Reflection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Volo.Abp.Data; using Volo.Abp.Domain.Entities; -using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore; -using Volo.Abp.Reflection; namespace Microsoft.Extensions.DependencyInjection { public static class AbpEfCoreServiceCollectionExtensions { - public static IServiceCollection AddAbpDbContext(this IServiceCollection services, Action optionsAction = null) //Created overload instead of default parameter + public static IServiceCollection AddAbpDbContext(this IServiceCollection services, Action optionsBuilder = null) //Created overload instead of default parameter where TDbContext : AbpDbContext { services //TODO: This code is copied from EntityFrameworkServiceCollectionExtensions, we should think on that later @@ -21,72 +20,48 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAddTransient(); services.TryAddSingleton(DbContextOptionsFactory.Create); - var options = new AddAbpDbContextOptions(); - optionsAction?.Invoke(options); + var options = new AbpDbContextRegistrationOptions(); + optionsBuilder?.Invoke(options); - services.AddRepositories(options.RepositoryOptions); + AddRepositories(services, options); return services; } - private static void AddRepositories(this IServiceCollection services, RepositoryRegistrationOptions options) + private static void AddRepositories(IServiceCollection services, AbpDbContextRegistrationOptions options) where TDbContext : AbpDbContext { - //TODO: Refactor!!! - - var dbContextType = typeof(TDbContext); - foreach (var customRepository in options.CustomRepositories) { - Register(services, customRepository.Key, customRepository.Value); + services.AddDefaultRepository(customRepository.Key, customRepository.Value); } if (options.RegisterDefaultRepositories) { - foreach (var entityType in DbContextHelper.GetEntityTypes(dbContextType)) - { - if (options.CustomRepositories.ContainsKey(entityType)) - { - continue; - } - - if (!options.IncludeAllEntitiesForDefaultRepositories && !ReflectionHelper.IsAssignableToGenericType(entityType, typeof(IAggregateRoot<>))) - { - continue; - } - - var primaryKeyType = EntityHelper.GetPrimaryKeyType(entityType); - - var repositoryImplementationType = typeof(IEntity).GetTypeInfo().IsAssignableFrom(entityType) - ? typeof(EfCoreRepository<,>).MakeGenericType(dbContextType, entityType) - : typeof(EfCoreRepository<,,>).MakeGenericType(dbContextType, entityType, primaryKeyType); - - Register(services, entityType, repositoryImplementationType); - } + RegisterDefaultRepositories(services, typeof(TDbContext), options); } } - private static void Register(IServiceCollection services, Type entityType, Type repositoryImplementationType) - where TDbContext : AbpDbContext + private static void RegisterDefaultRepositories(IServiceCollection services, Type dbContextType, AbpDbContextRegistrationOptions options) { - var primaryKeyType = EntityHelper.GetPrimaryKeyType(entityType); - - var repositoryInterfaceType = typeof(IRepository<,>).MakeGenericType(entityType, primaryKeyType); - var queryableRepositoryInterfaceType = typeof(IQueryableRepository<,>).MakeGenericType(entityType, primaryKeyType); - - services.TryAddTransient(repositoryInterfaceType, repositoryImplementationType); - services.TryAddTransient(queryableRepositoryInterfaceType, repositoryImplementationType); - - //Supports Default PK? - if (typeof(IEntity).GetTypeInfo().IsAssignableFrom(entityType) && - ReflectionHelper.IsAssignableToGenericType(repositoryImplementationType, typeof(IRepository<>))) + foreach (var entityType in DbContextHelper.GetEntityTypes(dbContextType)) { - var defaultPkRepositoryInterfaceType = typeof(IRepository<>).MakeGenericType(entityType); - var defaultPkQueryableRepositoryInterfaceType = typeof(IQueryableRepository<>).MakeGenericType(entityType); + if (!options.ShouldRegisterDefaultRepositoryFor(entityType)) + { + continue; + } - services.TryAddTransient(defaultPkRepositoryInterfaceType, repositoryImplementationType); - services.TryAddTransient(defaultPkQueryableRepositoryInterfaceType, repositoryImplementationType); + RegisterDefaultRepository(services, dbContextType, entityType, options); } } + + private static void RegisterDefaultRepository(IServiceCollection services, Type dbContextType, Type entityType, AbpDbContextRegistrationOptions options) + { + var repositoryImplementationType = typeof(IEntity).GetTypeInfo().IsAssignableFrom(entityType) + ? typeof(EfCoreRepository<,>).MakeGenericType(dbContextType, entityType) + : typeof(EfCoreRepository<,,>).MakeGenericType(dbContextType, entityType, EntityHelper.GetPrimaryKeyType(entityType)); + + services.AddDefaultRepository(entityType, repositoryImplementationType); + } } } diff --git a/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AddAbpDbContextOptions.cs b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AddAbpDbContextOptions.cs deleted file mode 100644 index f0ded835df..0000000000 --- a/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/AddAbpDbContextOptions.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using Volo.Abp; -using Volo.Abp.Domain.Entities; -using Volo.Abp.Domain.Repositories; -using Volo.Abp.Reflection; - -namespace Microsoft.Extensions.DependencyInjection -{ - public class AddAbpDbContextOptions - { - internal RepositoryRegistrationOptions RepositoryOptions { get; set; } - - public AddAbpDbContextOptions() - { - RepositoryOptions = new RepositoryRegistrationOptions(); - } - - /// - /// Registers default repositories for this DbContext. - /// - /// - /// Registers repositories only for aggregate root entities by default. - /// set to true to include all entities. - /// - public void WithDefaultRepositories(bool includeAllEntities = false) - { - RepositoryOptions.RegisterDefaultRepositories = true; - RepositoryOptions.IncludeAllEntitiesForDefaultRepositories = includeAllEntities; - } - - /// - /// Registers custom repository for a specific entity. - /// Custom repositories overrides default repositories. - /// - /// Entity type - /// Repository type - public void WithCustomRepository() - { - WithCustomRepository(typeof(TEntity), typeof(TRepository)); - } - - private void WithCustomRepository(Type entityType, Type repositoryType) - { - if (!ReflectionHelper.IsAssignableToGenericType(entityType, typeof(IEntity<>))) - { - throw new AbpException($"Given entityType is not an entity: {entityType.AssemblyQualifiedName}. It must implement {typeof(IEntity<>).AssemblyQualifiedName}."); - } - - if (!ReflectionHelper.IsAssignableToGenericType(repositoryType, typeof(IRepository<,>))) - { - throw new AbpException($"Given repositoryType is not a repository: {entityType.AssemblyQualifiedName}. It must implement {typeof(IRepository<,>).AssemblyQualifiedName}."); - } - - RepositoryOptions.CustomRepositories[entityType] = repositoryType; - } - } -} \ No newline at end of file diff --git a/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/IAbpDbContextRegistrationOptionsBuilder.cs b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/IAbpDbContextRegistrationOptionsBuilder.cs new file mode 100644 index 0000000000..efc990ca0b --- /dev/null +++ b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/IAbpDbContextRegistrationOptionsBuilder.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Data; +using Volo.Abp.Domain.Repositories; + +namespace Microsoft.Extensions.DependencyInjection +{ + public interface IAbpDbContextRegistrationOptionsBuilder : ICommonDbContextRegistrationOptionsBuilder + { + + } +} \ No newline at end of file diff --git a/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/RepositoryRegistrationOptions.cs b/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/RepositoryRegistrationOptions.cs deleted file mode 100644 index bc0d56cda3..0000000000 --- a/src/Volo.Abp.EntityFrameworkCore/Microsoft/Extensions/DependencyInjection/RepositoryRegistrationOptions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Microsoft.Extensions.DependencyInjection -{ - internal class RepositoryRegistrationOptions - { - public bool RegisterDefaultRepositories { get; set; } - - public bool IncludeAllEntitiesForDefaultRepositories { get; set; } - - public Dictionary CustomRepositories { get; set; } - - public RepositoryRegistrationOptions() - { - CustomRepositories = new Dictionary(); - } - } -} \ No newline at end of file diff --git a/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs b/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs index 83f4d56c78..748a217718 100644 --- a/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs +++ b/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/AbpIdentityEntityFrameworkCoreModule.cs @@ -11,7 +11,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore { services.AddAbpDbContext(options => { - options.WithDefaultRepositories(true); + options.WithDefaultRepositories(); options.WithCustomRepository(); options.WithCustomRepository(); }); diff --git a/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs b/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs new file mode 100644 index 0000000000..15be1808f0 --- /dev/null +++ b/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/AbpMongoDbServiceCollectionExtensions.cs @@ -0,0 +1,60 @@ +using System; +using System.Reflection; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories.MongoDB; +using Volo.Abp.MongoDB; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class AbpMongoDbServiceCollectionExtensions + { + public static IServiceCollection AddMongoDbContext(this IServiceCollection services, Action optionsBuilder = null) //Created overload instead of default parameter + where TMongoDbContext : AbpMongoDbContext + { + var options = new MongoDbContextRegistrationOptions(); + optionsBuilder?.Invoke(options); + + AddRepositories(services, options); + + return services; + } + + private static void AddRepositories(IServiceCollection services, MongoDbContextRegistrationOptions options) + where TMongoDbContext : AbpMongoDbContext + { + foreach (var customRepository in options.CustomRepositories) + { + services.AddDefaultRepository(customRepository.Key, customRepository.Value); + } + + if (options.RegisterDefaultRepositories) + { + RegisterDefaultRepositories(services, typeof(TMongoDbContext), options); + } + } + + private static void RegisterDefaultRepositories(IServiceCollection services, Type dbContextType, MongoDbContextRegistrationOptions options) + { + var mongoDbContext = (AbpMongoDbContext) Activator.CreateInstance(dbContextType); + + foreach (var entityType in mongoDbContext.GetEntityCollectionTypes()) + { + if (!options.ShouldRegisterDefaultRepositoryFor(entityType)) + { + continue; + } + + RegisterDefaultRepository(services, dbContextType, entityType, options); + } + } + + private static void RegisterDefaultRepository(IServiceCollection services, Type dbContextType, Type entityType, MongoDbContextRegistrationOptions options) + { + var repositoryImplementationType = typeof(IEntity).GetTypeInfo().IsAssignableFrom(entityType) + ? typeof(MongoDbRepository<,>).MakeGenericType(dbContextType, entityType) + : typeof(MongoDbRepository<,,>).MakeGenericType(dbContextType, entityType, EntityHelper.GetPrimaryKeyType(entityType)); + + services.AddDefaultRepository(entityType, repositoryImplementationType); + } + } +} diff --git a/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/IMongoDbContextRegistrationOptionsBuilder.cs b/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/IMongoDbContextRegistrationOptionsBuilder.cs new file mode 100644 index 0000000000..36774e9719 --- /dev/null +++ b/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/IMongoDbContextRegistrationOptionsBuilder.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Data; +using Volo.Abp.Domain.Repositories; + +namespace Microsoft.Extensions.DependencyInjection +{ + public interface IMongoDbContextRegistrationOptionsBuilder : ICommonDbContextRegistrationOptionsBuilder + { + + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/MongoDbContextRegistrationOptions.cs b/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/MongoDbContextRegistrationOptions.cs new file mode 100644 index 0000000000..2bc2f278b1 --- /dev/null +++ b/src/Volo.Abp.MongoDB/Microsoft/Extensions/DependencyInjection/MongoDbContextRegistrationOptions.cs @@ -0,0 +1,10 @@ +using Volo.Abp.Data; +using Volo.Abp.Domain.Repositories; + +namespace Microsoft.Extensions.DependencyInjection +{ + public class MongoDbContextRegistrationOptions : CommonDbContextRegistrationOptions, IMongoDbContextRegistrationOptionsBuilder + { + + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs index 4d0d058bcb..8b427ad263 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepository.cs @@ -12,10 +12,10 @@ namespace Volo.Abp.Domain.Repositories.MongoDB public interface IMongoDbRepository : IQueryableRepository where TEntity : class, IEntity { - } + IMongoDatabase Database { get; } - public interface IMongoDatabaseProvider - { - IMongoDatabase GetDatabase(); + IMongoCollection Collection { get; } + + string CollectionName { get; } } } diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index 5823085670..3f49b967bb 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -1,13 +1,15 @@ using System.Linq; using MongoDB.Driver; using Volo.Abp.Domain.Entities; +using Volo.Abp.MongoDB; namespace Volo.Abp.Domain.Repositories.MongoDB { - public class MongoDbRepository : MongoDbRepository, IMongoDbRepository + public class MongoDbRepository : MongoDbRepository, IMongoDbRepository + where TMongoDbContext : AbpMongoDbContext where TEntity : class, IEntity { - public MongoDbRepository(IMongoDatabaseProvider databaseProvider) + public MongoDbRepository(IMongoDatabaseProvider databaseProvider) : base(databaseProvider) { } @@ -15,20 +17,24 @@ namespace Volo.Abp.Domain.Repositories.MongoDB //TODO: MongoDb.Driver fully supports async, implement all of them! - public class MongoDbRepository : QueryableRepositoryBase, IMongoDbRepository + public class MongoDbRepository : QueryableRepositoryBase, IMongoDbRepository + where TMongoDbContext : AbpMongoDbContext where TEntity : class, IEntity { - //TODO: Define a MongoDbContext to relate to a connection string for modularity! + public virtual string CollectionName + { + get { return typeof(TEntity).Name; } //TODO: a better naming, or a way of easily overriding it? + } - public virtual IMongoCollection Collection => Database.GetCollection(""); + public virtual IMongoCollection Collection => Database.GetCollection(CollectionName); - public virtual IMongoDatabase Database => _databaseProvider.GetDatabase(); + public virtual IMongoDatabase Database => DatabaseProvider.GetDatabase(); - private readonly IMongoDatabaseProvider _databaseProvider; + protected IMongoDatabaseProvider DatabaseProvider { get; } - public MongoDbRepository(IMongoDatabaseProvider databaseProvider) + public MongoDbRepository(IMongoDatabaseProvider databaseProvider) { - _databaseProvider = databaseProvider; + DatabaseProvider = databaseProvider; } public override TEntity Insert(TEntity entity, bool autoSave = false) diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs new file mode 100644 index 0000000000..9cf85b703d --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbContext.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.MongoDB +{ + public abstract class AbpMongoDbContext + { + private static readonly Type[] EmptyTypeList = new Type[0]; + + public virtual IReadOnlyList GetEntityCollectionTypes() + { + return EmptyTypeList; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs new file mode 100644 index 0000000000..192e69733c --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/AbpMongoDbModule.cs @@ -0,0 +1,18 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Volo.Abp.Modularity; +using Volo.Abp.Uow.MongoDB; + +namespace Volo.Abp.MongoDB +{ + public class AbpMongoDbModule : AbpModule + { + public override void ConfigureServices(IServiceCollection services) + { + //TODO: ServiceCollection extensions to register mongodbcontext! + + services.TryAddTransient(typeof(IMongoDatabaseProvider<>), typeof(UnitOfWorkMongoDatabaseProvider<>)); + services.AddAssemblyOf(); + } + } +} diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoDatabaseProvider.cs b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoDatabaseProvider.cs new file mode 100644 index 0000000000..07cee9a52e --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/MongoDB/IMongoDatabaseProvider.cs @@ -0,0 +1,10 @@ +using MongoDB.Driver; + +namespace Volo.Abp.MongoDB +{ + public interface IMongoDatabaseProvider + where TMongoDbContext : AbpMongoDbContext + { + IMongoDatabase GetDatabase(); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDatabaseProvider.cs b/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDatabaseProvider.cs new file mode 100644 index 0000000000..be6a76926b --- /dev/null +++ b/src/Volo.Abp.MongoDB/Volo/Abp/Uow/MongoDB/UnitOfWorkMongoDatabaseProvider.cs @@ -0,0 +1,54 @@ +using MongoDB.Driver; +using Volo.Abp.Data; +using Volo.Abp.MongoDB; + +namespace Volo.Abp.Uow.MongoDB +{ + public class UnitOfWorkMongoDatabaseProvider : IMongoDatabaseProvider + where TMongoDbContext : AbpMongoDbContext + { + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly IConnectionStringResolver _connectionStringResolver; + + public UnitOfWorkMongoDatabaseProvider( + IUnitOfWorkManager unitOfWorkManager, + IConnectionStringResolver connectionStringResolver) + { + _unitOfWorkManager = unitOfWorkManager; + _connectionStringResolver = connectionStringResolver; + } + + public IMongoDatabase GetDatabase() + { + var unitOfWork = _unitOfWorkManager.Current; + if (unitOfWork == null) + { + throw new AbpException("A IMongoDatabase instance can only be created inside a unit of work!"); + } + + var connectionString = _connectionStringResolver.Resolve(); + var dbContextKey = $"{typeof(TMongoDbContext).FullName}_{connectionString}"; + + string databaseName; + if (connectionString.Contains("|")) + { + var splitted = connectionString.Split('|'); + connectionString = splitted[0]; + databaseName = splitted[1]; + } + else + { + databaseName = ConnectionStringNameAttribute.GetConnStringName(); + } + + //TODO: Create only single MongoDbClient per connection string in an application (extract MongoClientCache for example). + var databaseApi = unitOfWork.GetOrAddDatabaseApi( + dbContextKey, + () => new MongoDbDatabaseApi( + new MongoClient(connectionString).GetDatabase(databaseName) + )); + + return ((MongoDbDatabaseApi)databaseApi).Database; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/CommonDbContextRegistrationOptions.cs b/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/CommonDbContextRegistrationOptions.cs new file mode 100644 index 0000000000..73ddda0b4e --- /dev/null +++ b/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/CommonDbContextRegistrationOptions.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using Volo.Abp; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Reflection; + +namespace Microsoft.Extensions.DependencyInjection +{ + public class CommonDbContextRegistrationOptions : ICommonDbContextRegistrationOptionsBuilder + { + public bool RegisterDefaultRepositories { get; set; } + + public bool IncludeAllEntitiesForDefaultRepositories { get; set; } + + public Dictionary CustomRepositories { get; set; } + + public CommonDbContextRegistrationOptions() + { + CustomRepositories = new Dictionary(); + } + + public void WithDefaultRepositories(bool includeAllEntities = false) + { + RegisterDefaultRepositories = true; + IncludeAllEntitiesForDefaultRepositories = includeAllEntities; + } + + public void WithCustomRepository() + { + WithCustomRepository(typeof(TEntity), typeof(TRepository)); + } + + private void WithCustomRepository(Type entityType, Type repositoryType) + { + if (!ReflectionHelper.IsAssignableToGenericType(entityType, typeof(IEntity<>))) + { + throw new AbpException($"Given entityType is not an entity: {entityType.AssemblyQualifiedName}. It must implement {typeof(IEntity<>).AssemblyQualifiedName}."); + } + + if (!ReflectionHelper.IsAssignableToGenericType(repositoryType, typeof(IRepository<,>))) + { + throw new AbpException($"Given repositoryType is not a repository: {entityType.AssemblyQualifiedName}. It must implement {typeof(IRepository<,>).AssemblyQualifiedName}."); + } + + CustomRepositories[entityType] = repositoryType; + } + + public bool ShouldRegisterDefaultRepositoryFor(Type entityType) + { + if (!RegisterDefaultRepositories) + { + return false; + } + + if (CustomRepositories.ContainsKey(entityType)) + { + return false; + } + + if (!IncludeAllEntitiesForDefaultRepositories && !ReflectionHelper.IsAssignableToGenericType(entityType, typeof(IAggregateRoot<>))) + { + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ICommonDbContextRegistrationOptionsBuilder.cs b/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ICommonDbContextRegistrationOptionsBuilder.cs new file mode 100644 index 0000000000..d94f7194e9 --- /dev/null +++ b/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ICommonDbContextRegistrationOptionsBuilder.cs @@ -0,0 +1,22 @@ +namespace Microsoft.Extensions.DependencyInjection +{ + public interface ICommonDbContextRegistrationOptionsBuilder + { + /// + /// Registers default repositories for this DbContext. + /// + /// + /// Registers repositories only for aggregate root entities by default. + /// set to true to include all entities. + /// + void WithDefaultRepositories(bool includeAllEntities = false); + + /// + /// Registers custom repository for a specific entity. + /// Custom repositories overrides default repositories. + /// + /// Entity type + /// Repository type + void WithCustomRepository(); + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/AbpServiceCollectionExtensions.cs b/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ServiceCollectionAbpExtensions.cs similarity index 96% rename from src/Volo.Abp/Microsoft/Extensions/DependencyInjection/AbpServiceCollectionExtensions.cs rename to src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ServiceCollectionAbpExtensions.cs index d5428f6c5a..6d3638b8bc 100644 --- a/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/AbpServiceCollectionExtensions.cs +++ b/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ServiceCollectionAbpExtensions.cs @@ -5,7 +5,7 @@ using Volo.Abp.Modularity; namespace Microsoft.Extensions.DependencyInjection { - public static class AbpServiceCollectionExtensions + public static class ServiceCollectionAbpExtensions { public static AbpApplication AddApplication( [NotNull] this IServiceCollection services) diff --git a/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs b/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs new file mode 100644 index 0000000000..b605dd8810 --- /dev/null +++ b/src/Volo.Abp/Microsoft/Extensions/DependencyInjection/ServiceCollectionRepositoryExtensions.cs @@ -0,0 +1,48 @@ +using System; +using System.Reflection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Reflection; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceCollectionRepositoryExtensions + { + //TODO: validate repository and entity if they match! + + public static void AddDefaultRepository(this IServiceCollection services, Type entityType, Type repositoryImplementationType) + { + var primaryKeyType = EntityHelper.GetPrimaryKeyType(entityType); + + services.TryAddTransient( + typeof(IRepository<,>).MakeGenericType(entityType, primaryKeyType), + repositoryImplementationType + ); + + services.TryAddTransient( //TODO: May not support IQueryableRepository + typeof(IQueryableRepository<,>).MakeGenericType(entityType, primaryKeyType), + repositoryImplementationType + ); + + if (BothSupportsDefaultPrimaryKey(entityType, repositoryImplementationType)) + { + services.TryAddTransient( + typeof(IRepository<>).MakeGenericType(entityType), + repositoryImplementationType + ); + + services.TryAddTransient( //TODO: May not support IQueryableRepository + typeof(IQueryableRepository<>).MakeGenericType(entityType), + repositoryImplementationType + ); + } + } + + private static bool BothSupportsDefaultPrimaryKey(Type entityType, Type repositoryImplementationType) + { + return typeof(IEntity).GetTypeInfo().IsAssignableFrom(entityType) && + ReflectionHelper.IsAssignableToGenericType(repositoryImplementationType, typeof(IRepository<>)); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/AmbientUnitOfWork.cs b/src/Volo.Abp/Volo/Abp/Uow/AmbientUnitOfWork.cs new file mode 100644 index 0000000000..e5e4bfed20 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Uow/AmbientUnitOfWork.cs @@ -0,0 +1,23 @@ +using System.Threading; +using Volo.DependencyInjection; + +namespace Volo.Abp.Uow +{ + [ExposeServices(typeof(IAmbientUnitOfWork), typeof(IUnitOfWorkAccessor))] + public class AmbientUnitOfWork : IAmbientUnitOfWork, ISingletonDependency + { + public IUnitOfWork UnitOfWork => _currentUowInfo.Value; + + private readonly AsyncLocal _currentUowInfo; + + public AmbientUnitOfWork() + { + _currentUowInfo = new AsyncLocal(); + } + + public void SetUnitOfWork(IUnitOfWork unitOfWork) + { + _currentUowInfo.Value = unitOfWork; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/IAmbientUnitOfWork.cs b/src/Volo.Abp/Volo/Abp/Uow/IAmbientUnitOfWork.cs new file mode 100644 index 0000000000..894576ab71 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Uow/IAmbientUnitOfWork.cs @@ -0,0 +1,9 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.Uow +{ + public interface IAmbientUnitOfWork : IUnitOfWorkAccessor + { + void SetUnitOfWork([CanBeNull] IUnitOfWork unitOfWork); + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs b/src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs index 495ee631e8..533c09b972 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/IUnitOfWork.cs @@ -7,8 +7,6 @@ using Volo.DependencyInjection; namespace Volo.Abp.Uow { - //TODO: Sync versions of methods! - public interface IUnitOfWork : IDisposable, IServiceProviderAccessor, ITransientDependency { [CanBeNull] diff --git a/src/Volo.Abp/Volo/Abp/Uow/IUnitOfWorkAccessor.cs b/src/Volo.Abp/Volo/Abp/Uow/IUnitOfWorkAccessor.cs new file mode 100644 index 0000000000..c94fc74027 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Uow/IUnitOfWorkAccessor.cs @@ -0,0 +1,10 @@ +using JetBrains.Annotations; + +namespace Volo.Abp.Uow +{ + public interface IUnitOfWorkAccessor + { + [CanBeNull] + IUnitOfWork UnitOfWork { get; } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs index 3ff0f00ffc..259f1764ab 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWork.cs @@ -11,9 +11,13 @@ namespace Volo.Abp.Uow public IServiceProvider ServiceProvider { get; } private readonly Dictionary _databaseApis; + private readonly IAmbientUnitOfWork _ambientUnitOfWork; - public UnitOfWork(IServiceProvider serviceProvider) + private bool _isDisposed; + + public UnitOfWork(IServiceProvider serviceProvider, IAmbientUnitOfWork ambientUnitOfWork) { + _ambientUnitOfWork = ambientUnitOfWork; ServiceProvider = serviceProvider; _databaseApis = new Dictionary(); @@ -21,7 +25,15 @@ namespace Volo.Abp.Uow public void Dispose() { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + //TODO: Remove itself from IUnitOfWorkManager + _ambientUnitOfWork.SetUnitOfWork(null); } public async Task SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken)) diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkExtensions.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkExtensions.cs new file mode 100644 index 0000000000..5f0ceef6f1 --- /dev/null +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkExtensions.cs @@ -0,0 +1,18 @@ +using System.Threading; +using JetBrains.Annotations; +using Volo.Abp.Threading; + +namespace Volo.Abp.Uow +{ + public static class UnitOfWorkExtensions + { + //TODO: Implement all sync versions + + public static void Complete([NotNull] this IUnitOfWork unitOfWork, CancellationToken cancellationToken = default(CancellationToken)) + { + Check.NotNull(unitOfWork, nameof(unitOfWork)); + + AsyncHelper.RunSync(() => unitOfWork.CompleteAsync(cancellationToken)); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInfo.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInfo.cs index c34465bc63..2867d415ca 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInfo.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkInfo.cs @@ -2,16 +2,17 @@ using Microsoft.Extensions.DependencyInjection; namespace Volo.Abp.Uow { - internal class UnitOfWorkInfo - { - public IUnitOfWork UnitOfWork { get; set; } + //public class UnitOfWorkInfo + //{ + // public IUnitOfWork UnitOfWork { get; set; } - public IServiceScope ServiceScope { get; set; } + // //TODO: Remove if not used! + // public IServiceScope ServiceScope { get; set; } - public UnitOfWorkInfo(IUnitOfWork unitOfWork, IServiceScope serviceScope) - { - UnitOfWork = unitOfWork; - ServiceScope = serviceScope; - } - } + // public UnitOfWorkInfo(IUnitOfWork unitOfWork, IServiceScope serviceScope) + // { + // UnitOfWork = unitOfWork; + // ServiceScope = serviceScope; + // } + //} } \ No newline at end of file diff --git a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManager.cs b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManager.cs index 82d6755512..18894879e8 100644 --- a/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManager.cs +++ b/src/Volo.Abp/Volo/Abp/Uow/UnitOfWorkManager.cs @@ -1,5 +1,4 @@ using System; -using System.Threading; using Microsoft.Extensions.DependencyInjection; using Volo.DependencyInjection; @@ -8,33 +7,30 @@ namespace Volo.Abp.Uow public class UnitOfWorkManager : IUnitOfWorkManager, ISingletonDependency { //TODO: Skipped many feature of Abp 1.x + //TODO: Inner, real unit of works (RequiresNew option)! - public IUnitOfWork Current => _currentUowInfo.Value?.UnitOfWork; + public IUnitOfWork Current => _ambientUnitOfWork.UnitOfWork; //TODO: Remove Current! - private readonly AsyncLocal _currentUowInfo; private readonly IServiceProvider _serviceProvider; + private readonly IAmbientUnitOfWork _ambientUnitOfWork; - public UnitOfWorkManager(IServiceProvider serviceProvider) + public UnitOfWorkManager(IServiceProvider serviceProvider, IAmbientUnitOfWork ambientUnitOfWork) { _serviceProvider = serviceProvider; - _currentUowInfo = new AsyncLocal(); + _ambientUnitOfWork = ambientUnitOfWork; } public IUnitOfWork Begin() { - if (Current != null) + if (_ambientUnitOfWork.UnitOfWork != null) { - return new ChildUnitOfWork(Current); + return new ChildUnitOfWork(_ambientUnitOfWork.UnitOfWork); } var scope = _serviceProvider.CreateScope(); - try { - _currentUowInfo.Value = new UnitOfWorkInfo( - scope.ServiceProvider.GetRequiredService(), - scope - ); + _ambientUnitOfWork.SetUnitOfWork(scope.ServiceProvider.GetRequiredService()); } catch { @@ -42,7 +38,7 @@ namespace Volo.Abp.Uow throw; } - return Current; + return _ambientUnitOfWork.UnitOfWork; } } } \ No newline at end of file