mongodb integration #52

pull/81/head
Halil İbrahim Kalkan 9 years ago
parent 86a352c605
commit c39deb10e6

@ -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

@ -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)

@ -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<BlogPost, string> _blogPostRepository; //TODO: Should not be needed to string
private readonly IUnitOfWorkManager _unitOfWorkManager;
public BlogPostLister(IQueryableRepository<BlogPost, string> 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();
}
}
}
}

@ -23,6 +23,13 @@ namespace AbpDesk.ConsoleDemo
.GetRequiredService<UserLister>()
.List();
application
.ServiceProvider
.GetRequiredService<BlogPostLister>()
.List();
Console.WriteLine();
Console.WriteLine("Press ENTER to exit...");
Console.ReadLine();
application.Shutdown();

@ -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)

@ -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())

@ -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"
}
}
}

@ -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": {

@ -18,7 +18,7 @@ namespace AbpDesk.Tickets
[MaxLength(MaxBodyLength)]
public string Body { get; set; }
private Ticket()
protected Ticket()
{
}

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>63244dc7-34be-44e1-bf6f-f2672e59af36</ProjectGuid>
<RootNamespace>
</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

@ -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<AbpDeskMongoDbContext>(options =>
{
options.WithDefaultRepositories();
});
services.AddAssemblyOf<AbpDeskMongoBlogModule>();
}
}
}

@ -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<Type> GetEntityCollectionTypes()
{
return EntityCollectionTypes;
}
}
}

@ -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<BlogPostComment> 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<BlogPostComment>();
}
public override string ToString()
{
return $"{base.ToString()}, Title = {Title}, Body = {Body.TruncateWithPostfix(32)}";
}
}
}

@ -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")}";
}
}
}

@ -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")]

@ -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"
}
}
}

@ -0,0 +1,10 @@
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;
namespace Microsoft.Extensions.DependencyInjection
{
public class AbpDbContextRegistrationOptions : CommonDbContextRegistrationOptions, IAbpDbContextRegistrationOptionsBuilder
{
}
}

@ -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<TDbContext>(this IServiceCollection services, Action<AddAbpDbContextOptions> optionsAction = null) //Created overload instead of default parameter
public static IServiceCollection AddAbpDbContext<TDbContext>(this IServiceCollection services, Action<IAbpDbContextRegistrationOptionsBuilder> optionsBuilder = null) //Created overload instead of default parameter
where TDbContext : AbpDbContext<TDbContext>
{
services //TODO: This code is copied from EntityFrameworkServiceCollectionExtensions, we should think on that later
@ -21,72 +20,48 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAddTransient<TDbContext>();
services.TryAddSingleton(DbContextOptionsFactory.Create<TDbContext>);
var options = new AddAbpDbContextOptions();
optionsAction?.Invoke(options);
var options = new AbpDbContextRegistrationOptions();
optionsBuilder?.Invoke(options);
services.AddRepositories<TDbContext>(options.RepositoryOptions);
AddRepositories<TDbContext>(services, options);
return services;
}
private static void AddRepositories<TDbContext>(this IServiceCollection services, RepositoryRegistrationOptions options)
private static void AddRepositories<TDbContext>(IServiceCollection services, AbpDbContextRegistrationOptions options)
where TDbContext : AbpDbContext<TDbContext>
{
//TODO: Refactor!!!
var dbContextType = typeof(TDbContext);
foreach (var customRepository in options.CustomRepositories)
{
Register<TDbContext>(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<TDbContext>(services, entityType, repositoryImplementationType);
}
RegisterDefaultRepositories(services, typeof(TDbContext), options);
}
}
private static void Register<TDbContext>(IServiceCollection services, Type entityType, Type repositoryImplementationType)
where TDbContext : AbpDbContext<TDbContext>
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);
}
}
}

@ -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();
}
/// <summary>
/// Registers default repositories for this DbContext.
/// </summary>
/// <param name="includeAllEntities">
/// Registers repositories only for aggregate root entities by default.
/// set <see cref="includeAllEntities"/> to true to include all entities.
/// </param>
public void WithDefaultRepositories(bool includeAllEntities = false)
{
RepositoryOptions.RegisterDefaultRepositories = true;
RepositoryOptions.IncludeAllEntitiesForDefaultRepositories = includeAllEntities;
}
/// <summary>
/// Registers custom repository for a specific entity.
/// Custom repositories overrides default repositories.
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <typeparam name="TRepository">Repository type</typeparam>
public void WithCustomRepository<TEntity, TRepository>()
{
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;
}
}
}

@ -0,0 +1,10 @@
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;
namespace Microsoft.Extensions.DependencyInjection
{
public interface IAbpDbContextRegistrationOptionsBuilder : ICommonDbContextRegistrationOptionsBuilder
{
}
}

@ -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<Type, Type> CustomRepositories { get; set; }
public RepositoryRegistrationOptions()
{
CustomRepositories = new Dictionary<Type, Type>();
}
}
}

@ -11,7 +11,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore
{
services.AddAbpDbContext<IdentityDbContext>(options =>
{
options.WithDefaultRepositories(true);
options.WithDefaultRepositories();
options.WithCustomRepository<IdentityUser, EfCoreIdentityUserRepository>();
options.WithCustomRepository<IdentityRole, EfCoreIdentityRoleRepository>();
});

@ -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<TMongoDbContext>(this IServiceCollection services, Action<IMongoDbContextRegistrationOptionsBuilder> optionsBuilder = null) //Created overload instead of default parameter
where TMongoDbContext : AbpMongoDbContext
{
var options = new MongoDbContextRegistrationOptions();
optionsBuilder?.Invoke(options);
AddRepositories<TMongoDbContext>(services, options);
return services;
}
private static void AddRepositories<TMongoDbContext>(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);
}
}
}

@ -0,0 +1,10 @@
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;
namespace Microsoft.Extensions.DependencyInjection
{
public interface IMongoDbContextRegistrationOptionsBuilder : ICommonDbContextRegistrationOptionsBuilder
{
}
}

@ -0,0 +1,10 @@
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories;
namespace Microsoft.Extensions.DependencyInjection
{
public class MongoDbContextRegistrationOptions : CommonDbContextRegistrationOptions, IMongoDbContextRegistrationOptionsBuilder
{
}
}

@ -12,10 +12,10 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
public interface IMongoDbRepository<TEntity, TPrimaryKey> : IQueryableRepository<TEntity, TPrimaryKey>
where TEntity : class, IEntity<TPrimaryKey>
{
}
IMongoDatabase Database { get; }
public interface IMongoDatabaseProvider
{
IMongoDatabase GetDatabase();
IMongoCollection<TEntity> Collection { get; }
string CollectionName { get; }
}
}

@ -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<TEntity> : MongoDbRepository<TEntity, string>, IMongoDbRepository<TEntity>
public class MongoDbRepository<TMongoDbContext, TEntity> : MongoDbRepository<TMongoDbContext, TEntity, string>, IMongoDbRepository<TEntity>
where TMongoDbContext : AbpMongoDbContext
where TEntity : class, IEntity<string>
{
public MongoDbRepository(IMongoDatabaseProvider databaseProvider)
public MongoDbRepository(IMongoDatabaseProvider<TMongoDbContext> 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<TEntity, TPrimaryKey> : QueryableRepositoryBase<TEntity, TPrimaryKey>, IMongoDbRepository<TEntity, TPrimaryKey>
public class MongoDbRepository<TMongoDbContext, TEntity, TPrimaryKey> : QueryableRepositoryBase<TEntity, TPrimaryKey>, IMongoDbRepository<TEntity, TPrimaryKey>
where TMongoDbContext : AbpMongoDbContext
where TEntity : class, IEntity<TPrimaryKey>
{
//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<TEntity> Collection => Database.GetCollection<TEntity>("");
public virtual IMongoCollection<TEntity> Collection => Database.GetCollection<TEntity>(CollectionName);
public virtual IMongoDatabase Database => _databaseProvider.GetDatabase();
public virtual IMongoDatabase Database => DatabaseProvider.GetDatabase();
private readonly IMongoDatabaseProvider _databaseProvider;
protected IMongoDatabaseProvider<TMongoDbContext> DatabaseProvider { get; }
public MongoDbRepository(IMongoDatabaseProvider databaseProvider)
public MongoDbRepository(IMongoDatabaseProvider<TMongoDbContext> databaseProvider)
{
_databaseProvider = databaseProvider;
DatabaseProvider = databaseProvider;
}
public override TEntity Insert(TEntity entity, bool autoSave = false)

@ -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<Type> GetEntityCollectionTypes()
{
return EmptyTypeList;
}
}
}

@ -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<AbpMongoDbModule>();
}
}
}

@ -0,0 +1,10 @@
using MongoDB.Driver;
namespace Volo.Abp.MongoDB
{
public interface IMongoDatabaseProvider<TMongoDbContext>
where TMongoDbContext : AbpMongoDbContext
{
IMongoDatabase GetDatabase();
}
}

@ -0,0 +1,54 @@
using MongoDB.Driver;
using Volo.Abp.Data;
using Volo.Abp.MongoDB;
namespace Volo.Abp.Uow.MongoDB
{
public class UnitOfWorkMongoDatabaseProvider<TMongoDbContext> : IMongoDatabaseProvider<TMongoDbContext>
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<TMongoDbContext>();
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<TMongoDbContext>();
}
//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;
}
}
}

@ -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<Type, Type> CustomRepositories { get; set; }
public CommonDbContextRegistrationOptions()
{
CustomRepositories = new Dictionary<Type, Type>();
}
public void WithDefaultRepositories(bool includeAllEntities = false)
{
RegisterDefaultRepositories = true;
IncludeAllEntitiesForDefaultRepositories = includeAllEntities;
}
public void WithCustomRepository<TEntity, TRepository>()
{
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;
}
}
}

@ -0,0 +1,22 @@
namespace Microsoft.Extensions.DependencyInjection
{
public interface ICommonDbContextRegistrationOptionsBuilder
{
/// <summary>
/// Registers default repositories for this DbContext.
/// </summary>
/// <param name="includeAllEntities">
/// Registers repositories only for aggregate root entities by default.
/// set <see cref="includeAllEntities"/> to true to include all entities.
/// </param>
void WithDefaultRepositories(bool includeAllEntities = false);
/// <summary>
/// Registers custom repository for a specific entity.
/// Custom repositories overrides default repositories.
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <typeparam name="TRepository">Repository type</typeparam>
void WithCustomRepository<TEntity, TRepository>();
}
}

@ -5,7 +5,7 @@ using Volo.Abp.Modularity;
namespace Microsoft.Extensions.DependencyInjection
{
public static class AbpServiceCollectionExtensions
public static class ServiceCollectionAbpExtensions
{
public static AbpApplication AddApplication<TStartupModule>(
[NotNull] this IServiceCollection services)

@ -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<>));
}
}
}

@ -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<IUnitOfWork> _currentUowInfo;
public AmbientUnitOfWork()
{
_currentUowInfo = new AsyncLocal<IUnitOfWork>();
}
public void SetUnitOfWork(IUnitOfWork unitOfWork)
{
_currentUowInfo.Value = unitOfWork;
}
}
}

@ -0,0 +1,9 @@
using JetBrains.Annotations;
namespace Volo.Abp.Uow
{
public interface IAmbientUnitOfWork : IUnitOfWorkAccessor
{
void SetUnitOfWork([CanBeNull] IUnitOfWork unitOfWork);
}
}

@ -7,8 +7,6 @@ using Volo.DependencyInjection;
namespace Volo.Abp.Uow
{
//TODO: Sync versions of methods!
public interface IUnitOfWork : IDisposable, IServiceProviderAccessor, ITransientDependency
{
[CanBeNull]

@ -0,0 +1,10 @@
using JetBrains.Annotations;
namespace Volo.Abp.Uow
{
public interface IUnitOfWorkAccessor
{
[CanBeNull]
IUnitOfWork UnitOfWork { get; }
}
}

@ -11,9 +11,13 @@ namespace Volo.Abp.Uow
public IServiceProvider ServiceProvider { get; }
private readonly Dictionary<string, IDatabaseApi> _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<string, IDatabaseApi>();
@ -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))

@ -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));
}
}
}

@ -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;
// }
//}
}

@ -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<UnitOfWorkInfo> _currentUowInfo;
private readonly IServiceProvider _serviceProvider;
private readonly IAmbientUnitOfWork _ambientUnitOfWork;
public UnitOfWorkManager(IServiceProvider serviceProvider)
public UnitOfWorkManager(IServiceProvider serviceProvider, IAmbientUnitOfWork ambientUnitOfWork)
{
_serviceProvider = serviceProvider;
_currentUowInfo = new AsyncLocal<UnitOfWorkInfo>();
_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<IUnitOfWork>(),
scope
);
_ambientUnitOfWork.SetUnitOfWork(scope.ServiceProvider.GetRequiredService<IUnitOfWork>());
}
catch
{
@ -42,7 +38,7 @@ namespace Volo.Abp.Uow
throw;
}
return Current;
return _ambientUnitOfWork.UnitOfWork;
}
}
}
Loading…
Cancel
Save