Resolved #93: MemoryDb implementation.

pull/96/head
Halil İbrahim Kalkan 8 years ago
parent 073f97ee0f
commit 42438d5308

@ -122,6 +122,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.AspNetCore.Mvc.Tes
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.TestApp", "test\Volo.Abp.TestApp\Volo.Abp.TestApp.csproj", "{DE160F1A-92FB-44BA-87E2-B8AD7A938AC7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.MemoryDb", "src\Volo.Abp.MemoryDb\Volo.Abp.MemoryDb.csproj", "{CF564447-8E0B-4A07-B0D2-396E00A8E437}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.MemoryDb.Tests", "test\Volo.Abp.MemoryDb.Tests\Volo.Abp.MemoryDb.Tests.csproj", "{D0279C94-E9A3-4A1B-968B-D3BBF3E06FD8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -300,6 +304,14 @@ Global
{DE160F1A-92FB-44BA-87E2-B8AD7A938AC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE160F1A-92FB-44BA-87E2-B8AD7A938AC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE160F1A-92FB-44BA-87E2-B8AD7A938AC7}.Release|Any CPU.Build.0 = Release|Any CPU
{CF564447-8E0B-4A07-B0D2-396E00A8E437}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CF564447-8E0B-4A07-B0D2-396E00A8E437}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CF564447-8E0B-4A07-B0D2-396E00A8E437}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CF564447-8E0B-4A07-B0D2-396E00A8E437}.Release|Any CPU.Build.0 = Release|Any CPU
{D0279C94-E9A3-4A1B-968B-D3BBF3E06FD8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D0279C94-E9A3-4A1B-968B-D3BBF3E06FD8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D0279C94-E9A3-4A1B-968B-D3BBF3E06FD8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D0279C94-E9A3-4A1B-968B-D3BBF3E06FD8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -357,6 +369,8 @@ Global
{8343BE23-6A7B-4C58-BF0D-95188B11B180} = {37087D1B-3693-4E96-983D-A69F210BDE53}
{27D76546-6091-4AEE-9079-1FE3991C81BC} = {37087D1B-3693-4E96-983D-A69F210BDE53}
{DE160F1A-92FB-44BA-87E2-B8AD7A938AC7} = {37087D1B-3693-4E96-983D-A69F210BDE53}
{CF564447-8E0B-4A07-B0D2-396E00A8E437} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8}
{D0279C94-E9A3-4A1B-968B-D3BBF3E06FD8} = {37087D1B-3693-4E96-983D-A69F210BDE53}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}

@ -0,0 +1,23 @@
using System;
using Volo.Abp.MemoryDb;
using Volo.Abp.MemoryDb.DependencyInjection;
namespace Microsoft.Extensions.DependencyInjection
{
public static class AbpMemoryDbServiceCollectionExtensions
{
public static IServiceCollection AddMemoryDbContext<TMemoryDbContext>(this IServiceCollection services, Action<IMemoryDbContextRegistrationOptionsBuilder> optionsBuilder = null)
where TMemoryDbContext : MemoryDbContext
{
var options = new MemoryDbContextRegistrationOptions();
optionsBuilder?.Invoke(options);
services.AddSingleton<TMemoryDbContext>();
new MemoryDbRepositoryRegistrar(options)
.AddRepositories(services, typeof(TMemoryDbContext));
return services;
}
}
}

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<AssemblyName>Volo.Abp.MemoryDb</AssemblyName>
<PackageId>Volo.Abp.MemoryDb</PackageId>
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Volo.Abp\Volo.Abp.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace Volo.Abp.Domain.Repositories.MemoryDb
{
public interface IMemoryDatabase
{
List<TEntity> Collection<TEntity>();
}
}

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.Domain.Repositories.MemoryDb
{
public interface IMemoryDbRepository<TEntity> : IMemoryDbRepository<TEntity, Guid>, IQueryableRepository<TEntity>
where TEntity : class, IEntity<Guid>
{
}
public interface IMemoryDbRepository<TEntity, TPrimaryKey> : IQueryableRepository<TEntity, TPrimaryKey>
where TEntity : class, IEntity<TPrimaryKey>
{
IMemoryDatabase Database { get; }
List<TEntity> Collection { get; }
}
}

@ -0,0 +1,23 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
namespace Volo.Abp.Domain.Repositories.MemoryDb
{
public class MemoryDatabase : IMemoryDatabase
{
private readonly ConcurrentDictionary<Type, object> _sets;
private readonly object _syncObj = new object();
public MemoryDatabase()
{
_sets = new ConcurrentDictionary<Type, object>();
}
public List<TEntity> Collection<TEntity>()
{
return _sets.GetOrAdd(typeof(TEntity), _ => new List<TEntity>()) as List<TEntity>;
}
}
}

@ -0,0 +1,19 @@
using System.Collections.Concurrent;
namespace Volo.Abp.Domain.Repositories.MemoryDb
{
public static class MemoryDatabaseManager
{
private static ConcurrentDictionary<string, IMemoryDatabase> _databases;
static MemoryDatabaseManager()
{
_databases = new ConcurrentDictionary<string, IMemoryDatabase>();
}
public static IMemoryDatabase Get(string databaseName)
{
return _databases.GetOrAdd(databaseName, _ => new MemoryDatabase());
}
}
}

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MemoryDb;
namespace Volo.Abp.Domain.Repositories.MemoryDb
{
public class MemoryDbRepository<TMemoryDbContext, TEntity> : MemoryDbRepository<TMemoryDbContext, TEntity, Guid>, IMemoryDbRepository<TEntity>
where TMemoryDbContext : MemoryDbContext
where TEntity : class, IEntity<Guid>
{
public MemoryDbRepository(IMemoryDatabaseProvider<TMemoryDbContext> databaseProvider)
: base(databaseProvider)
{
}
}
public class MemoryDbRepository<TMemoryDbContext, TEntity, TPrimaryKey> : QueryableRepositoryBase<TEntity, TPrimaryKey>, IMemoryDbRepository<TEntity, TPrimaryKey>
where TMemoryDbContext : MemoryDbContext
where TEntity : class, IEntity<TPrimaryKey>
{
public virtual List<TEntity> Collection => Database.Collection<TEntity>();
public virtual IMemoryDatabase Database => DatabaseProvider.GetDatabase();
protected IMemoryDatabaseProvider<TMemoryDbContext> DatabaseProvider { get; }
public MemoryDbRepository(IMemoryDatabaseProvider<TMemoryDbContext> databaseProvider)
{
DatabaseProvider = databaseProvider;
}
public override TEntity Insert(TEntity entity, bool autoSave = false)
{
Collection.Add(entity);
return entity;
}
public override TEntity Update(TEntity entity)
{
return entity;
}
public override void Delete(TEntity entity)
{
Collection.Remove(entity);
}
protected override IQueryable<TEntity> GetQueryable()
{
return Collection.AsQueryable();
}
}
}

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories.MemoryDb;
namespace Volo.Abp.Domain.Repositories
{
public static class MemoryDbCoreRepositoryExtensions
{
public static IMemoryDatabase GetDatabase<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
{
return repository.ToMemoryDbRepository().Database;
}
public static List<TEntity> GetCollection<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
{
return repository.ToMemoryDbRepository().Collection;
}
public static IMemoryDbRepository<TEntity, TPrimaryKey> ToMemoryDbRepository<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository)
where TEntity : class, IEntity<TPrimaryKey>
{
var memoryDbRepository = repository as IMemoryDbRepository<TEntity, TPrimaryKey>;
if (memoryDbRepository == null)
{
throw new ArgumentException("Given repository does not implement " + typeof(IMemoryDbRepository<TEntity, TPrimaryKey>).AssemblyQualifiedName, nameof(repository));
}
return memoryDbRepository;
}
}
}

@ -0,0 +1,16 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.Modularity;
using Volo.Abp.Uow.MemoryDb;
namespace Volo.Abp.MemoryDb
{
public class AbpMemoryDbModule : AbpModule
{
public override void ConfigureServices(IServiceCollection services)
{
services.TryAddTransient(typeof(IMemoryDatabaseProvider<>), typeof(UnitOfWorkMemoryDatabaseProvider<>));
services.AddAssemblyOf<AbpMemoryDbModule>();
}
}
}

@ -0,0 +1,9 @@
using Volo.Abp.Data;
namespace Volo.Abp.MemoryDb.DependencyInjection
{
public interface IMemoryDbContextRegistrationOptionsBuilder : ICommonDbContextRegistrationOptionsBuilder
{
}
}

@ -0,0 +1,9 @@
using Volo.Abp.Data;
namespace Volo.Abp.MemoryDb.DependencyInjection
{
public class MemoryDbContextRegistrationOptions : CommonDbContextRegistrationOptions, IMemoryDbContextRegistrationOptionsBuilder
{
}
}

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Domain.Repositories.MemoryDb;
namespace Volo.Abp.MemoryDb.DependencyInjection
{
public class MemoryDbRepositoryRegistrar : RepositoryRegistrarBase<MemoryDbContextRegistrationOptions>
{
public MemoryDbRepositoryRegistrar(MemoryDbContextRegistrationOptions options)
: base(options)
{
}
protected override IEnumerable<Type> GetEntityTypes(Type dbContextType)
{
var memoryDbContext = (MemoryDbContext)Activator.CreateInstance(dbContextType);
return memoryDbContext.GetEntityTypes();
}
protected override Type GetRepositoryTypeForDefaultPk(Type dbContextType, Type entityType)
{
return typeof(MemoryDbRepository<,>).MakeGenericType(dbContextType, entityType);
}
protected override Type GetRepositoryType(Type dbContextType, Type entityType, Type primaryKeyType)
{
return typeof(MemoryDbRepository<,,>).MakeGenericType(dbContextType, entityType, primaryKeyType);
}
}
}

@ -0,0 +1,12 @@
using Volo.Abp.Domain.Repositories.MemoryDb;
namespace Volo.Abp.MemoryDb
{
public interface IMemoryDatabaseProvider<TMemoryDbContext>
where TMemoryDbContext : MemoryDbContext
{
TMemoryDbContext DbContext { get; }
IMemoryDatabase GetDatabase();
}
}

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
namespace Volo.Abp.MemoryDb
{
public abstract class MemoryDbContext
{
private static readonly Type[] EmptyTypeList = new Type[0];
public virtual IReadOnlyList<Type> GetEntityTypes()
{
return EmptyTypeList;
}
}
}

@ -0,0 +1,14 @@
using Volo.Abp.Domain.Repositories.MemoryDb;
namespace Volo.Abp.Uow.MemoryDb
{
public class MemoryDbDatabaseApi: IDatabaseApi
{
public IMemoryDatabase Database { get; }
public MemoryDbDatabaseApi(IMemoryDatabase database)
{
Database = database;
}
}
}

@ -0,0 +1,45 @@
using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories.MemoryDb;
using Volo.Abp.MemoryDb;
namespace Volo.Abp.Uow.MemoryDb
{
public class UnitOfWorkMemoryDatabaseProvider<TMemoryDbContext> : IMemoryDatabaseProvider<TMemoryDbContext>
where TMemoryDbContext : MemoryDbContext
{
public TMemoryDbContext DbContext { get; }
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IConnectionStringResolver _connectionStringResolver;
public UnitOfWorkMemoryDatabaseProvider(
IUnitOfWorkManager unitOfWorkManager,
IConnectionStringResolver connectionStringResolver,
TMemoryDbContext dbContext)
{
_unitOfWorkManager = unitOfWorkManager;
_connectionStringResolver = connectionStringResolver;
DbContext = dbContext;
}
public IMemoryDatabase GetDatabase()
{
var unitOfWork = _unitOfWorkManager.Current;
if (unitOfWork == null)
{
throw new AbpException($"A {nameof(IMemoryDatabase)} instance can only be created inside a unit of work!");
}
var connectionString = _connectionStringResolver.Resolve<TMemoryDbContext>();
var dbContextKey = $"{typeof(TMemoryDbContext).FullName}_{connectionString}";
var databaseApi = unitOfWork.GetOrAddDatabaseApi(
dbContextKey,
() => new MemoryDbDatabaseApi(
MemoryDatabaseManager.Get(connectionString)
));
return ((MemoryDbDatabaseApi)databaseApi).Database;
}
}
}

@ -27,7 +27,7 @@ namespace Volo.Abp.Uow.MongoDB
var unitOfWork = _unitOfWorkManager.Current;
if (unitOfWork == null)
{
throw new AbpException("A IMongoDatabase instance can only be created inside a unit of work!");
throw new AbpException($"A {nameof(IMongoDatabase)} instance can only be created inside a unit of work!");
}
var connectionString = _connectionStringResolver.Resolve<TMongoDbContext>();

@ -1,6 +1,8 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.Uow;
using System.Threading.Tasks;
namespace Volo.Abp.TestBase
{
@ -55,6 +57,37 @@ namespace Volo.Abp.TestBase
return services.BuildServiceProviderFromFactory();
}
protected virtual void UseUnitOfWork(Action action)
{
using (IServiceScope scope = ServiceProvider.CreateScope())
{
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
using (var uow = uowManager.Begin())
{
action();
uow.Complete();
}
}
}
protected virtual async Task UseUnitOfWorkAsync(Func<Task> action)
{
using (IServiceScope scope = ServiceProvider.CreateScope())
{
var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
using (var uow = uowManager.Begin())
{
await action();
await uow.CompleteAsync();
}
}
}
public void Dispose()
{
Application.Shutdown();

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<AssemblyName>Volo.Abp.MemoryDb.Tests</AssemblyName>
<PackageId>Volo.Abp.MemoryDb.Tests</PackageId>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.MemoryDb\Volo.Abp.MemoryDb.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<ProjectReference Include="..\Volo.Abp.TestApp\Volo.Abp.TestApp.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.3.0" />
</ItemGroup>
</Project>

@ -0,0 +1,31 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
using Volo.Abp.TestApp.MemoryDb;
using Volo.Abp.Data;
using Volo.Abp.Autofac;
using Volo.Abp.TestApp;
namespace Volo.Abp.MemoryDb
{
[DependsOn(typeof(TestAppModule), typeof(AbpMemoryDbModule), typeof(AbpAutofacModule))]
public class AbpMemoryDbTestModule : AbpModule
{
public override void ConfigureServices(IServiceCollection services)
{
var connStr = Guid.NewGuid().ToString();
services.Configure<DbConnectionOptions>(options =>
{
options.ConnectionStrings.Default = connStr;
});
services.AddMemoryDbContext<TestAppMemoryDbContext>(options =>
{
options.WithDefaultRepositories();
});
services.AddAssemblyOf<AbpMemoryDbModule>();
}
}
}

@ -0,0 +1,12 @@
using Volo.Abp.TestBase;
namespace Volo.Abp.MemoryDb.Repositories
{
public abstract class MemoryDbTestBase : AbpIntegratedTest<AbpMemoryDbTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
}
}

@ -0,0 +1,43 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.TestApp.Domain;
using Xunit;
using System.Linq;
namespace Volo.Abp.MemoryDb.Repositories
{
public class MemoryDb_Basic_Repository_Tests : MemoryDbTestBase
{
private readonly IQueryableRepository<Person> _personRepository;
public MemoryDb_Basic_Repository_Tests()
{
_personRepository = ServiceProvider.GetRequiredService<IQueryableRepository<Person>>();
}
[Fact]
public void GetList()
{
var people = _personRepository.GetList();
people.Count.ShouldBeGreaterThan(0);
}
[Fact]
public void Insert()
{
//Arrange
var name = Guid.NewGuid().ToString();
//Act
_personRepository.Insert(new Person(name, 42));
//Assert
UseUnitOfWork(() =>
{
_personRepository.FirstOrDefault(p => p.Name == name).ShouldNotBeNull();
});
}
}
}

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using Volo.Abp.MemoryDb;
using Volo.Abp.TestApp.Domain;
namespace Volo.Abp.TestApp.MemoryDb
{
public class TestAppMemoryDbContext : MemoryDbContext
{
private static readonly Type[] EntityTypeList = new Type[]
{
typeof(Person)
};
public override IReadOnlyList<Type> GetEntityTypes()
{
return EntityTypeList;
}
}
}

@ -1,6 +1,8 @@
namespace App
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.TestApp.Domain
{
public class Person
public class Person : AggregateRoot
{
public string Name { get; set; }

@ -1,7 +1,7 @@
using System.Collections.Generic;
using Volo.Abp.Application.Services;
namespace App
namespace Volo.Abp.TestApp.Domain
{
public class PersonAppService : ApplicationService
{

@ -0,0 +1,24 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Modularity;
using Volo.Abp.TestApp.Domain;
namespace Volo.Abp.TestApp
{
public class TestAppModule : AbpModule
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddAssemblyOf<TestAppModule>();
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
using(IServiceScope scope = context.ServiceProvider.CreateScope())
{
var personRepository = scope.ServiceProvider.GetRequiredService<IRepository<Person>>();
personRepository.Insert(new Person("Douglas", 42));
}
}
}
}
Loading…
Cancel
Save