Merge pull request #189 from aspnetzero/dbcontext-interfaces

Dbcontext interfaces
pull/191/head
Halil İbrahim Kalkan 8 years ago committed by GitHub
commit 9bf5e83f05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -12,6 +12,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Volo.Abp.MultiTenancy.EntityFrameworkCore\Volo.Abp.MultiTenancy.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\AbpDesk.Domain\AbpDesk.Domain.csproj" />
<ProjectReference Include="..\..\Volo.Abp.EntityFrameworkCore\Volo.Abp.EntityFrameworkCore.csproj" />
</ItemGroup>

@ -2,14 +2,20 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.MultiTenancy;
using Volo.Abp.MultiTenancy.EntityFrameworkCore;
namespace AbpDesk.EntityFrameworkCore
{
[ConnectionStringName(ConnectionStrings.DefaultConnectionStringName)] //Explicitly declares this module always uses the default connection string
public class AbpDeskDbContext : AbpDbContext<AbpDeskDbContext>
public class AbpDeskDbContext : AbpDbContext<AbpDeskDbContext>, IMultiTenancyDbContext
{
public DbSet<Ticket> Tickets { get; set; }
public DbSet<Tenant> Tenants { get; set; }
public DbSet<TenantConnectionString> TenantConnectionStrings { get; set; }
public AbpDeskDbContext(DbContextOptions<AbpDeskDbContext> options)
: base(options)
{
@ -20,6 +26,8 @@ namespace AbpDesk.EntityFrameworkCore
{
base.OnModelCreating(modelBuilder);
this.ConfigureAbpMultiTenancy(modelBuilder);
//Use different classes to map each entity type?
modelBuilder.Entity<Ticket>(b =>
{

@ -1,17 +1,23 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.Modularity;
using Volo.Abp.MultiTenancy.EntityFrameworkCore;
namespace AbpDesk.EntityFrameworkCore
{
[DependsOn(typeof(AbpDeskDomainModule), typeof(AbpEntityFrameworkCoreModule))]
[DependsOn(
typeof(AbpDeskDomainModule),
typeof(AbpEntityFrameworkCoreModule),
typeof(AbpMultiTenancyEntityFrameworkCoreModule)
)]
public class AbpDeskEntityFrameworkCoreModule : AbpModule
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddAbpDbContext<AbpDeskDbContext>(options =>
{
options.WithDefaultRepositories();
options.AddDefaultRepositories();
options.ReplaceDbContext<IMultiTenancyDbContext>();
});
services.AddAssemblyOf<AbpDeskEntityFrameworkCoreModule>();

@ -0,0 +1,92 @@
// <auto-generated />
using AbpDesk.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using System;
namespace AbpDesk.EntityFrameworkCore.Migrations
{
[DbContext(typeof(AbpDeskDbContext))]
[Migration("20180105184326_MultiTenancyModuleAdded")]
partial class MultiTenancyModuleAdded
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("AbpDesk.Tickets.Ticket", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Body")
.HasMaxLength(65536);
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(256);
b.HasKey("Id");
b.ToTable("DskTickets");
});
modelBuilder.Entity("Volo.Abp.MultiTenancy.Tenant", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64);
b.HasKey("Id");
b.HasIndex("Name");
b.ToTable("MtTenants");
});
modelBuilder.Entity("Volo.Abp.MultiTenancy.TenantConnectionString", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256);
b.Property<Guid>("TenantId");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(1024);
b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("MtTenantConnectionStrings");
});
modelBuilder.Entity("Volo.Abp.MultiTenancy.TenantConnectionString", b =>
{
b.HasOne("Volo.Abp.MultiTenancy.Tenant")
.WithMany("ConnectionStrings")
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

@ -0,0 +1,63 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace AbpDesk.EntityFrameworkCore.Migrations
{
public partial class MultiTenancyModuleAdded : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "MtTenants",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(64)", maxLength: 64, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_MtTenants", x => x.Id);
});
migrationBuilder.CreateTable(
name: "MtTenantConnectionStrings",
columns: table => new
{
Id = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Name = table.Column<string>(type: "nvarchar(256)", maxLength: 256, nullable: false),
TenantId = table.Column<Guid>(type: "uniqueidentifier", nullable: false),
Value = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_MtTenantConnectionStrings", x => x.Id);
table.ForeignKey(
name: "FK_MtTenantConnectionStrings_MtTenants_TenantId",
column: x => x.TenantId,
principalTable: "MtTenants",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_MtTenantConnectionStrings_TenantId",
table: "MtTenantConnectionStrings",
column: "TenantId");
migrationBuilder.CreateIndex(
name: "IX_MtTenants_Name",
table: "MtTenants",
column: "Name");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "MtTenantConnectionStrings");
migrationBuilder.DropTable(
name: "MtTenants");
}
}
}

@ -1,6 +1,12 @@
using Microsoft.EntityFrameworkCore;
// <auto-generated />
using AbpDesk.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using System;
namespace AbpDesk.EntityFrameworkCore.Migrations
{
@ -9,8 +15,9 @@ namespace AbpDesk.EntityFrameworkCore.Migrations
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "1.1.0-rtm-22752")
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452")
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("AbpDesk.Tickets.Ticket", b =>
@ -32,6 +39,53 @@ namespace AbpDesk.EntityFrameworkCore.Migrations
b.ToTable("DskTickets");
});
modelBuilder.Entity("Volo.Abp.MultiTenancy.Tenant", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64);
b.HasKey("Id");
b.HasIndex("Name");
b.ToTable("MtTenants");
});
modelBuilder.Entity("Volo.Abp.MultiTenancy.TenantConnectionString", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(256);
b.Property<Guid>("TenantId");
b.Property<string>("Value")
.IsRequired()
.HasMaxLength(1024);
b.HasKey("Id");
b.HasIndex("TenantId");
b.ToTable("MtTenantConnectionStrings");
});
modelBuilder.Entity("Volo.Abp.MultiTenancy.TenantConnectionString", b =>
{
b.HasOne("Volo.Abp.MultiTenancy.Tenant")
.WithMany("ConnectionStrings")
.HasForeignKey("TenantId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

@ -20,7 +20,7 @@ namespace AbpDesk.Blogging
{
services.AddMongoDbContext<AbpDeskMongoDbContext>(options =>
{
options.WithDefaultRepositories();
options.AddDefaultRepositories();
});
services.Configure<VirtualFileSystemOptions>(options =>

@ -12,8 +12,12 @@ namespace Volo.Abp.DependencyInjection
/// </summary>
public abstract class CommonDbContextRegistrationOptions : ICommonDbContextRegistrationOptionsBuilder
{
public bool SpecifiedDefaultRepositoryTypes => DefaultRepositoryImplementationType != null && DefaultRepositoryImplementationTypeWithDefaultPrimaryKey != null;
public Type OriginalDbContextType { get; }
public List<Type> ReplacedDbContextTypes { get; }
public Type DefaultRepositoryDbContextType { get; protected set; }
public Type DefaultRepositoryImplementationType { get; private set; }
public Type DefaultRepositoryImplementationTypeWithDefaultPrimaryKey { get; private set; }
@ -24,12 +28,34 @@ namespace Volo.Abp.DependencyInjection
public Dictionary<Type, Type> CustomRepositories { get; }
public CommonDbContextRegistrationOptions()
public bool SpecifiedDefaultRepositoryTypes => DefaultRepositoryImplementationType != null && DefaultRepositoryImplementationTypeWithDefaultPrimaryKey != null;
protected CommonDbContextRegistrationOptions(Type originalDbContextType)
{
OriginalDbContextType = originalDbContextType;
DefaultRepositoryDbContextType = originalDbContextType;
CustomRepositories = new Dictionary<Type, Type>();
ReplacedDbContextTypes = new List<Type>();
}
public ICommonDbContextRegistrationOptionsBuilder ReplaceDbContext<TOtherDbContext>()
{
return ReplaceDbContext(typeof(TOtherDbContext));
}
public ICommonDbContextRegistrationOptionsBuilder ReplaceDbContext(Type otherDbContextType)
{
if (!otherDbContextType.IsAssignableFrom(OriginalDbContextType))
{
throw new AbpException($"{OriginalDbContextType.AssemblyQualifiedName} should inherit/implement {otherDbContextType.AssemblyQualifiedName}!");
}
ReplacedDbContextTypes.Add(otherDbContextType);
return this;
}
public ICommonDbContextRegistrationOptionsBuilder WithDefaultRepositories(bool includeAllEntities = false)
public ICommonDbContextRegistrationOptionsBuilder AddDefaultRepositories(bool includeAllEntities = false)
{
RegisterDefaultRepositories = true;
IncludeAllEntitiesForDefaultRepositories = includeAllEntities;
@ -37,14 +63,31 @@ namespace Volo.Abp.DependencyInjection
return this;
}
public ICommonDbContextRegistrationOptionsBuilder WithCustomRepository<TEntity, TRepository>()
public ICommonDbContextRegistrationOptionsBuilder AddDefaultRepositories<TDefaultRepositoryDbContext>(bool includeAllEntities = false)
{
return AddDefaultRepositories(typeof(TDefaultRepositoryDbContext), includeAllEntities);
}
public ICommonDbContextRegistrationOptionsBuilder AddDefaultRepositories(Type defaultRepositoryDbContextType, bool includeAllEntities = false)
{
if (!defaultRepositoryDbContextType.IsAssignableFrom(OriginalDbContextType))
{
throw new AbpException($"{OriginalDbContextType.AssemblyQualifiedName} should inherit/implement {defaultRepositoryDbContextType.AssemblyQualifiedName}!");
}
DefaultRepositoryDbContextType = defaultRepositoryDbContextType;
return this;
}
public ICommonDbContextRegistrationOptionsBuilder AddCustomRepository<TEntity, TRepository>()
{
WithCustomRepository(typeof(TEntity), typeof(TRepository));
return this;
}
public ICommonDbContextRegistrationOptionsBuilder WithDefaultRepositoryClasses([NotNull] Type repositoryImplementationType, [NotNull] Type repositoryImplementationTypeWithDefaultPrimaryKey)
public ICommonDbContextRegistrationOptionsBuilder SetDefaultRepositoryClasses([NotNull] Type repositoryImplementationType, [NotNull] Type repositoryImplementationTypeWithDefaultPrimaryKey)
{
Check.NotNull(repositoryImplementationType, nameof(repositoryImplementationType));
Check.NotNull(repositoryImplementationTypeWithDefaultPrimaryKey, nameof(repositoryImplementationTypeWithDefaultPrimaryKey));

@ -11,7 +11,29 @@ namespace Volo.Abp.DependencyInjection
/// Registers repositories only for aggregate root entities by default.
/// set <see cref="includeAllEntities"/> to true to include all entities.
/// </param>
ICommonDbContextRegistrationOptionsBuilder WithDefaultRepositories(bool includeAllEntities = false);
ICommonDbContextRegistrationOptionsBuilder AddDefaultRepositories(bool includeAllEntities = false);
/// <summary>
/// Registers default repositories for this DbContext.
/// Default repositories will use given <see cref="TDefaultRepositoryDbContext"/>.
/// </summary>
/// <typeparam name="TDefaultRepositoryDbContext">DbContext type that will be used by default repositories</typeparam>
/// <param name="includeAllEntities">
/// Registers repositories only for aggregate root entities by default.
/// set <see cref="includeAllEntities"/> to true to include all entities.
/// </param>
ICommonDbContextRegistrationOptionsBuilder AddDefaultRepositories<TDefaultRepositoryDbContext>(bool includeAllEntities = false);
/// <summary>
/// Registers default repositories for this DbContext.
/// Default repositories will use given <see cref="defaultRepositoryDbContextType"/>.
/// </summary>
/// <param name="defaultRepositoryDbContextType">DbContext type that will be used by default repositories</param>
/// <param name="includeAllEntities">
/// Registers repositories only for aggregate root entities by default.
/// set <see cref="includeAllEntities"/> to true to include all entities.
/// </param>
ICommonDbContextRegistrationOptionsBuilder AddDefaultRepositories(Type defaultRepositoryDbContextType, bool includeAllEntities = false);
/// <summary>
/// Registers custom repository for a specific entity.
@ -19,7 +41,7 @@ namespace Volo.Abp.DependencyInjection
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <typeparam name="TRepository">Repository type</typeparam>
ICommonDbContextRegistrationOptionsBuilder WithCustomRepository<TEntity, TRepository>();
ICommonDbContextRegistrationOptionsBuilder AddCustomRepository<TEntity, TRepository>();
/// <summary>
/// Uses given class(es) for default repositories.
@ -27,6 +49,18 @@ namespace Volo.Abp.DependencyInjection
/// <param name="repositoryImplementationType">Repository implementation type</param>
/// <param name="repositoryImplementationTypeWithDefaultPrimaryKey">Repository implementation type for default primary key type (<see cref="string"/>)</param>
/// <returns></returns>
ICommonDbContextRegistrationOptionsBuilder WithDefaultRepositoryClasses(Type repositoryImplementationType, Type repositoryImplementationTypeWithDefaultPrimaryKey);
ICommonDbContextRegistrationOptionsBuilder SetDefaultRepositoryClasses(Type repositoryImplementationType, Type repositoryImplementationTypeWithDefaultPrimaryKey);
/// <summary>
/// Replaces given DbContext type with this DbContext type.
/// </summary>
/// <typeparam name="TOtherDbContext">The DbContext type to be replaced</typeparam>
ICommonDbContextRegistrationOptionsBuilder ReplaceDbContext<TOtherDbContext>();
/// <summary>
/// Replaces given DbContext type with this DbContext type.
/// </summary>
/// <param name="otherDbContextType">The DbContext type to be replaced</param>
ICommonDbContextRegistrationOptionsBuilder ReplaceDbContext(Type otherDbContextType);
}
}

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Reflection;
@ -18,7 +17,7 @@ namespace Volo.Abp.Domain.Repositories
Options = options;
}
public virtual void AddRepositories(IServiceCollection services, Type dbContextType)
public virtual void AddRepositories(IServiceCollection services)
{
foreach (var customRepository in Options.CustomRepositories)
{
@ -27,24 +26,24 @@ namespace Volo.Abp.Domain.Repositories
if (Options.RegisterDefaultRepositories)
{
RegisterDefaultRepositories(services, dbContextType);
RegisterDefaultRepositories(services);
}
}
protected virtual void RegisterDefaultRepositories(IServiceCollection services, Type dbContextType)
protected virtual void RegisterDefaultRepositories(IServiceCollection services)
{
foreach (var entityType in GetEntityTypes(dbContextType))
foreach (var entityType in GetEntityTypes(Options.DefaultRepositoryDbContextType))
{
if (!ShouldRegisterDefaultRepositoryFor(entityType))
{
continue;
}
RegisterDefaultRepository(services, dbContextType, entityType);
RegisterDefaultRepository(services, entityType);
}
}
protected void RegisterDefaultRepository(IServiceCollection services, Type dbContextType, Type entityType)
protected void RegisterDefaultRepository(IServiceCollection services, Type entityType)
{
var primaryKeyType = EntityHelper.GetPrimaryKeyType(entityType);
var isDefaultPrimaryKey = primaryKeyType == typeof(Guid);
@ -59,8 +58,8 @@ namespace Volo.Abp.Domain.Repositories
else
{
repositoryImplementationType = isDefaultPrimaryKey
? GetRepositoryTypeForDefaultPk(dbContextType, entityType)
: GetRepositoryType(dbContextType, entityType, primaryKeyType);
? GetRepositoryTypeForDefaultPk(Options.DefaultRepositoryDbContextType, entityType)
: GetRepositoryType(Options.DefaultRepositoryDbContextType, entityType, primaryKeyType);
}
services.AddDefaultRepository(entityType, repositoryImplementationType);

@ -7,19 +7,31 @@ namespace Microsoft.Extensions.DependencyInjection
{
public static class AbpEfCoreServiceCollectionExtensions
{
public static IServiceCollection AddAbpDbContext<TDbContext>(this IServiceCollection services, Action<IAbpDbContextRegistrationOptionsBuilder> optionsBuilder = null) //Created overload instead of default parameter
public static IServiceCollection AddAbpDbContext<TDbContext>(
this IServiceCollection services,
Action<IAbpDbContextRegistrationOptionsBuilder> optionsBuilder = null)
where TDbContext : AbpDbContext<TDbContext>
{
services.AddMemoryCache();
var options = new AbpDbContextRegistrationOptions(typeof(TDbContext));
optionsBuilder?.Invoke(options);
services.TryAddTransient<TDbContext>();
services.TryAddTransient(DbContextOptionsFactory.Create<TDbContext>);
var options = new AbpDbContextRegistrationOptions();
optionsBuilder?.Invoke(options);
if (options.DefaultRepositoryDbContextType != typeof(TDbContext))
{
services.TryAddTransient(options.DefaultRepositoryDbContextType, typeof(TDbContext));
}
foreach (var dbContextType in options.ReplacedDbContextTypes)
{
services.Replace(ServiceDescriptor.Transient(dbContextType, typeof(TDbContext)));
}
new EfCoreRepositoryRegistrar(options)
.AddRepositories(services, typeof(TDbContext));
.AddRepositories(services);
return services;
}

@ -12,7 +12,7 @@ using Volo.Abp.Threading;
namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
{
public class EfCoreRepository<TDbContext, TEntity> : EfCoreRepository<TDbContext, TEntity, Guid>, IEfCoreRepository<TEntity>
where TDbContext : AbpDbContext<TDbContext>
where TDbContext : IEfCoreDbContext
where TEntity : class, IEntity<Guid>
{
public EfCoreRepository(IDbContextProvider<TDbContext> dbContextProvider)
@ -25,12 +25,12 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
IEfCoreRepository<TEntity, TPrimaryKey>,
ISupportsExplicitLoading<TEntity, TPrimaryKey>
where TDbContext : AbpDbContext<TDbContext>
where TDbContext : IEfCoreDbContext
where TEntity : class, IEntity<TPrimaryKey>
{
public virtual DbSet<TEntity> DbSet => DbContext.Set<TEntity>();
DbContext IEfCoreRepository<TEntity, TPrimaryKey>.DbContext => DbContext;
DbContext IEfCoreRepository<TEntity, TPrimaryKey>.DbContext => DbContext.As<DbContext>();
protected virtual TDbContext DbContext => _dbContextProvider.GetDbContext();

@ -17,7 +17,7 @@ using Volo.Abp.Uow;
namespace Volo.Abp.EntityFrameworkCore
{
public abstract class AbpDbContext<TDbContext> : DbContext
public abstract class AbpDbContext<TDbContext> : DbContext, IEfCoreDbContext
where TDbContext : DbContext
{
public Guid? CurrentTenantId => CurrentTenant?.Id;

@ -1,10 +1,14 @@
using Volo.Abp.Data;
using System;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.EntityFrameworkCore.DependencyInjection
{
public class AbpDbContextRegistrationOptions : CommonDbContextRegistrationOptions, IAbpDbContextRegistrationOptionsBuilder
{
public AbpDbContextRegistrationOptions(Type originalDbContextType)
: base(originalDbContextType)
{
}
}
}

@ -1,10 +1,8 @@
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.EntityFrameworkCore.DependencyInjection
{
public interface IAbpDbContextRegistrationOptionsBuilder : ICommonDbContextRegistrationOptionsBuilder
{
}
}

@ -1,7 +1,7 @@
namespace Volo.Abp.EntityFrameworkCore
{
public interface IDbContextProvider<out TDbContext>
where TDbContext : AbpDbContext<TDbContext>
where TDbContext : IEfCoreDbContext
{
TDbContext GetDbContext();
}

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
namespace Volo.Abp.EntityFrameworkCore
{
public interface IEfCoreDbContext : IDisposable, IInfrastructure<IServiceProvider>, IDbContextDependencies, IDbSetCache, IDbContextPoolable
{
EntityEntry<TEntity> Attach<TEntity>([NotNull] TEntity entity) where TEntity : class;
EntityEntry Attach([NotNull] object entity);
int SaveChanges();
int SaveChanges(bool acceptAllChangesOnSuccess);
Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default);
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
DbSet<T> Set<T>()
where T: class;
DatabaseFacade Database { get; }
ChangeTracker ChangeTracker { get; }
EntityEntry Add([NotNull] object entity);
EntityEntry<TEntity> Add<TEntity>([NotNull] TEntity entity) where TEntity : class;
Task<EntityEntry> AddAsync([NotNull] object entity, CancellationToken cancellationToken = default);
Task<EntityEntry<TEntity>> AddAsync<TEntity>([NotNull] TEntity entity, CancellationToken cancellationToken = default) where TEntity : class;
void AddRange([NotNull] IEnumerable<object> entities);
void AddRange([NotNull] params object[] entities);
Task AddRangeAsync([NotNull] params object[] entities);
Task AddRangeAsync([NotNull] IEnumerable<object> entities, CancellationToken cancellationToken = default);
void AttachRange([NotNull] IEnumerable<object> entities);
void AttachRange([NotNull] params object[] entities);
EntityEntry<TEntity> Entry<TEntity>([NotNull] TEntity entity) where TEntity : class;
EntityEntry Entry([NotNull] object entity);
object Find([NotNull] Type entityType, [NotNull] params object[] keyValues);
TEntity Find<TEntity>([NotNull] params object[] keyValues) where TEntity : class;
Task<object> FindAsync([NotNull] Type entityType, [NotNull] object[] keyValues, CancellationToken cancellationToken);
Task<TEntity> FindAsync<TEntity>([NotNull] object[] keyValues, CancellationToken cancellationToken) where TEntity : class;
Task<TEntity> FindAsync<TEntity>([NotNull] params object[] keyValues) where TEntity : class;
Task<object> FindAsync([NotNull] Type entityType, [NotNull] params object[] keyValues);
EntityEntry<TEntity> Remove<TEntity>([NotNull] TEntity entity) where TEntity : class;
EntityEntry Remove([NotNull] object entity);
void RemoveRange([NotNull] IEnumerable<object> entities);
void RemoveRange([NotNull] params object[] entities);
EntityEntry<TEntity> Update<TEntity>([NotNull] TEntity entity) where TEntity : class;
EntityEntry Update([NotNull] object entity);
void UpdateRange([NotNull] params object[] entities);
void UpdateRange([NotNull] IEnumerable<object> entities);
}
}

@ -5,7 +5,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.Uow.EntityFrameworkCore
{
public class EfCoreDatabaseApi<TDbContext> : IDatabaseApi, ISupportsSavingChanges
where TDbContext : AbpDbContext<TDbContext>
where TDbContext : IEfCoreDbContext
{
public TDbContext DbContext { get; }

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@ -10,14 +11,14 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
public class EfCoreTransactionApi : ITransactionApi, ISupportsRollback
{
public IDbContextTransaction DbContextTransaction { get; }
public DbContext StarterDbContext { get; }
public List<DbContext> AttendedDbContexts { get; }
public IEfCoreDbContext StarterDbContext { get; }
public List<IEfCoreDbContext> AttendedDbContexts { get; }
public EfCoreTransactionApi(IDbContextTransaction dbContextTransaction, DbContext starterDbContext)
public EfCoreTransactionApi(IDbContextTransaction dbContextTransaction, IEfCoreDbContext starterDbContext)
{
DbContextTransaction = dbContextTransaction;
StarterDbContext = starterDbContext;
AttendedDbContexts = new List<DbContext>();
AttendedDbContexts = new List<IEfCoreDbContext>();
}
public void Commit()
@ -26,7 +27,7 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
foreach (var dbContext in AttendedDbContexts)
{
if (dbContext.HasRelationalTransactionManager())
if (dbContext.As<DbContext>().HasRelationalTransactionManager())
{
continue; //Relational databases use the shared transaction
}

@ -11,7 +11,7 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
//TODO: Implement logic in DefaultDbContextResolver.Resolve in old ABP.
public class UnitOfWorkDbContextProvider<TDbContext> : IDbContextProvider<TDbContext>
where TDbContext : AbpDbContext<TDbContext>
where TDbContext : IEfCoreDbContext
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IConnectionStringResolver _connectionStringResolver;
@ -100,7 +100,7 @@ namespace Volo.Abp.Uow.EntityFrameworkCore
var dbContext = unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
if (dbContext.HasRelationalTransactionManager())
if (dbContext.As<DbContext>().HasRelationalTransactionManager())
{
dbContext.Database.UseTransaction(activeTransaction.DbContextTransaction.GetDbTransaction());
}

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

@ -13,8 +13,8 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore
{
services.AddAbpDbContext<IdentityServerDbContext>(options =>
{
options.WithDefaultRepositories();
options.WithCustomRepository<Client, ClientRepository>();
options.AddDefaultRepositories();
options.AddCustomRepository<Client, ClientRepository>();
});
services.AddAssemblyOf<AbpIdentityServerEntityFrameworkCoreModule>();

@ -1,4 +1,5 @@
using System;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.MemoryDb;
using Volo.Abp.MemoryDb.DependencyInjection;
@ -9,13 +10,23 @@ namespace Microsoft.Extensions.DependencyInjection
public static IServiceCollection AddMemoryDbContext<TMemoryDbContext>(this IServiceCollection services, Action<IMemoryDbContextRegistrationOptionsBuilder> optionsBuilder = null)
where TMemoryDbContext : MemoryDbContext
{
var options = new MemoryDbContextRegistrationOptions();
var options = new MemoryDbContextRegistrationOptions(typeof(TMemoryDbContext));
optionsBuilder?.Invoke(options);
services.AddSingleton<TMemoryDbContext>();
if (options.DefaultRepositoryDbContextType != typeof(TMemoryDbContext))
{
services.TryAddSingleton(options.DefaultRepositoryDbContextType, sp => sp.GetRequiredService<TMemoryDbContext>());
}
foreach (var dbContextType in options.ReplacedDbContextTypes)
{
services.Replace(ServiceDescriptor.Singleton(dbContextType, sp => sp.GetRequiredService<TMemoryDbContext>()));
}
new MemoryDbRepositoryRegistrar(options)
.AddRepositories(services, typeof(TMemoryDbContext));
.AddRepositories(services);
return services;
}

@ -1,4 +1,3 @@
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.MemoryDb.DependencyInjection

@ -1,10 +1,13 @@
using Volo.Abp.Data;
using System;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.MemoryDb.DependencyInjection
{
public class MemoryDbContextRegistrationOptions : CommonDbContextRegistrationOptions, IMemoryDbContextRegistrationOptionsBuilder
{
public MemoryDbContextRegistrationOptions(Type originalDbContextType)
: base(originalDbContextType)
{
}
}
}

@ -1,4 +1,5 @@
using System;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Volo.Abp.MongoDB;
using Volo.Abp.MongoDB.DependencyInjection;
@ -9,13 +10,23 @@ namespace Microsoft.Extensions.DependencyInjection
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();
var options = new MongoDbContextRegistrationOptions(typeof(TMongoDbContext));
optionsBuilder?.Invoke(options);
services.AddSingleton<TMongoDbContext>();
services.TryAddSingleton<TMongoDbContext>();
if (options.DefaultRepositoryDbContextType != typeof(TMongoDbContext))
{
services.TryAddSingleton(options.DefaultRepositoryDbContextType, sp => sp.GetRequiredService<TMongoDbContext>());
}
foreach (var dbContextType in options.ReplacedDbContextTypes)
{
services.Replace(ServiceDescriptor.Singleton(dbContextType, sp => sp.GetRequiredService<TMongoDbContext>()));
}
new MongoDbRepositoryRegistrar(options)
.AddRepositories(services, typeof(TMongoDbContext));
.AddRepositories(services);
return services;
}

@ -1,10 +1,13 @@
using Volo.Abp.Data;
using System;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.MongoDB.DependencyInjection
{
public class MongoDbContextRegistrationOptions : CommonDbContextRegistrationOptions, IMongoDbContextRegistrationOptionsBuilder
{
public MongoDbContextRegistrationOptions(Type originalDbContextType)
: base(originalDbContextType)
{
}
}
}

@ -1,9 +1,9 @@
using System.Collections.Generic;
using Microsoft.Extensions.Options;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Data.MultiTenancy
namespace Volo.Abp.MultiTenancy
{
[Dependency(ReplaceServices = true)]
public class MultiTenantConnectionStringResolver : DefaultConnectionStringResolver

@ -10,6 +10,11 @@ namespace Volo.Abp.MultiTenancy.EntityFrameworkCore
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddAbpDbContext<MultiTenancyDbContext>(options =>
{
options.AddDefaultRepositories<IMultiTenancyDbContext>();
});
services.AddAssemblyOf<AbpMultiTenancyEntityFrameworkCoreModule>();
}
}

@ -0,0 +1,12 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.MultiTenancy.EntityFrameworkCore
{
public interface IMultiTenancyDbContext : IEfCoreDbContext
{
DbSet<Tenant> Tenants { get; set; }
DbSet<TenantConnectionString> TenantConnectionStrings { get; set; }
}
}

@ -3,7 +3,7 @@ using Volo.Abp.EntityFrameworkCore;
namespace Volo.Abp.MultiTenancy.EntityFrameworkCore
{
public class MultiTenancyDbContext : AbpDbContext<MultiTenancyDbContext>
public class MultiTenancyDbContext : AbpDbContext<MultiTenancyDbContext>, IMultiTenancyDbContext
{
public DbSet<Tenant> Tenants { get; set; }
@ -18,28 +18,7 @@ namespace Volo.Abp.MultiTenancy.EntityFrameworkCore
{
base.OnModelCreating(builder);
//TODO: Split configuration to dedicated classes (probably a resuable extension method)
builder.Entity<Tenant>(b =>
{
b.ToTable("MtTenants"); //TODO: Make all table and schema names changeable
b.Property(t => t.Name).IsRequired().HasMaxLength(TenantConsts.MaxNameLength);
b.HasMany(u => u.ConnectionStrings).WithOne().HasForeignKey(uc => uc.TenantId).IsRequired();
b.HasIndex(u => u.Name);
});
builder.Entity<TenantConnectionString>(b =>
{
b.ToTable("MtTenantConnectionStrings");
b.Property(cs => cs.Name).IsRequired().HasMaxLength(TenantConnectionStringConsts.MaxNameLength);
b.Property(cs => cs.Value).IsRequired().HasMaxLength(TenantConnectionStringConsts.MaxValueLength);
b.HasIndex(cs => cs.TenantId);
});
this.ConfigureAbpMultiTenancy(builder);
}
}
}

@ -0,0 +1,31 @@
using Microsoft.EntityFrameworkCore;
namespace Volo.Abp.MultiTenancy.EntityFrameworkCore
{
public static class MultiTenancyDbContextModelCreatingExtensions
{
public static void ConfigureAbpMultiTenancy(this IMultiTenancyDbContext dbContext, ModelBuilder builder)
{
builder.Entity<Tenant>(b =>
{
b.ToTable("MtTenants"); //TODO: Make all table and schema names changeable
b.Property(t => t.Name).IsRequired().HasMaxLength(TenantConsts.MaxNameLength);
b.HasMany(u => u.ConnectionStrings).WithOne().HasForeignKey(uc => uc.TenantId).IsRequired();
b.HasIndex(u => u.Name);
});
builder.Entity<TenantConnectionString>(b =>
{
b.ToTable("MtTenantConnectionStrings");
b.Property(cs => cs.Name).IsRequired().HasMaxLength(TenantConnectionStringConsts.MaxNameLength);
b.Property(cs => cs.Value).IsRequired().HasMaxLength(TenantConnectionStringConsts.MaxValueLength);
b.HasIndex(cs => cs.TenantId);
});
}
}
}

@ -17,12 +17,12 @@ namespace Volo.Abp.Domain.Repositories
var services = new ServiceCollection();
var options = new TestDbContextRegistrationOptions();
options.WithDefaultRepositories();
var options = new TestDbContextRegistrationOptions(typeof(MyFakeDbContext));
options.AddDefaultRepositories();
//Act
new MyTestRepositoryRegistrar(options).AddRepositories(services, typeof(MyFakeDbContext));
new MyTestRepositoryRegistrar(options).AddRepositories(services);
//Assert
@ -38,12 +38,12 @@ namespace Volo.Abp.Domain.Repositories
var services = new ServiceCollection();
var options = new TestDbContextRegistrationOptions();
options.WithDefaultRepositories(true);
var options = new TestDbContextRegistrationOptions(typeof(MyFakeDbContext));
options.AddDefaultRepositories(true);
//Act
new MyTestRepositoryRegistrar(options).AddRepositories(services, typeof(MyFakeDbContext));
new MyTestRepositoryRegistrar(options).AddRepositories(services);
//Assert
@ -59,14 +59,14 @@ namespace Volo.Abp.Domain.Repositories
var services = new ServiceCollection();
var options = new TestDbContextRegistrationOptions();
var options = new TestDbContextRegistrationOptions(typeof(MyFakeDbContext));
options
.WithDefaultRepositories(true)
.WithCustomRepository<MyTestAggregateRootWithDefaultPk, MyTestAggregateRootWithDefaultPkCustomRepository>();
.AddDefaultRepositories(true)
.AddCustomRepository<MyTestAggregateRootWithDefaultPk, MyTestAggregateRootWithDefaultPkCustomRepository>();
//Act
new MyTestRepositoryRegistrar(options).AddRepositories(services, typeof(MyFakeDbContext));
new MyTestRepositoryRegistrar(options).AddRepositories(services);
//Assert
@ -82,14 +82,14 @@ namespace Volo.Abp.Domain.Repositories
var services = new ServiceCollection();
var options = new TestDbContextRegistrationOptions();
var options = new TestDbContextRegistrationOptions(typeof(MyFakeDbContext));
options
.WithDefaultRepositories(true)
.WithDefaultRepositoryClasses(typeof(MyTestCustomBaseRepository<,>), typeof(MyTestCustomBaseRepository<>));
.AddDefaultRepositories(true)
.SetDefaultRepositoryClasses(typeof(MyTestCustomBaseRepository<,>), typeof(MyTestCustomBaseRepository<>));
//Act
new MyTestRepositoryRegistrar(options).AddRepositories(services, typeof(MyFakeDbContext));
new MyTestRepositoryRegistrar(options).AddRepositories(services);
//Assert
@ -196,7 +196,10 @@ namespace Volo.Abp.Domain.Repositories
public class TestDbContextRegistrationOptions : CommonDbContextRegistrationOptions
{
public TestDbContextRegistrationOptions(Type originalDbContextType)
: base(originalDbContextType)
{
}
}
}
}

@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext;
using Volo.Abp.Modularity;
namespace Volo.Abp.EntityFrameworkCore.TestApp.SecondContext
@ -8,6 +9,16 @@ namespace Volo.Abp.EntityFrameworkCore.TestApp.SecondContext
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddAbpDbContext<SecondDbContext>(options =>
{
options.AddDefaultRepositories();
});
services.AddAbpDbContext<ThirdDbContext.ThirdDbContext>(options =>
{
options.AddDefaultRepositories<IThirdDbContext>();
});
services.AddAssemblyOf<AbpEfCoreTestSecondContextModule>();
}

@ -0,0 +1,9 @@
using Microsoft.EntityFrameworkCore;
namespace Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext
{
public interface IThirdDbContext : IEfCoreDbContext
{
DbSet<ThirdDbContextDummyEntity> DummyEntities { get; set; }
}
}

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
namespace Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext
{
/* This dbcontext is just for testing to replace dbcontext from the application using AbpDbContextRegistrationOptions.ReplaceDbContext
*/
public class ThirdDbContext : AbpDbContext<ThirdDbContext>, IThirdDbContext
{
public DbSet<ThirdDbContextDummyEntity> DummyEntities { get; set; }
public ThirdDbContext(DbContextOptions<ThirdDbContext> options)
: base(options)
{
}
}
}

@ -0,0 +1,9 @@
using Volo.Abp.Domain.Entities;
namespace Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext
{
public class ThirdDbContextDummyEntity : AggregateRoot
{
public string Value { get; set; }
}
}

@ -3,6 +3,7 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Autofac;
using Volo.Abp.EntityFrameworkCore.TestApp.SecondContext;
using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext;
using Volo.Abp.Modularity;
using Volo.Abp.TestApp;
using Volo.Abp.TestApp.EntityFrameworkCore;
@ -21,12 +22,8 @@ namespace Volo.Abp.EntityFrameworkCore
services.AddAbpDbContext<TestAppDbContext>(options =>
{
options.WithDefaultRepositories();
});
services.AddAbpDbContext<SecondDbContext>(options =>
{
options.WithDefaultRepositories();
options.AddDefaultRepositories();
options.ReplaceDbContext<IThirdDbContext>();
});
var inMemorySqlite = new SqliteConnection("Data Source=:memory:");

@ -0,0 +1,28 @@
using Microsoft.Extensions.DependencyInjection;
using Shouldly;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext;
using Volo.Abp.TestApp.EntityFrameworkCore;
using Xunit;
namespace Volo.Abp.EntityFrameworkCore
{
public class DbContext_Replace_Tests : EntityFrameworkCoreTestBase
{
private readonly IRepository<ThirdDbContextDummyEntity> _dummyRepository;
public DbContext_Replace_Tests()
{
_dummyRepository = ServiceProvider.GetRequiredService<IRepository<ThirdDbContextDummyEntity>>();
}
[Fact]
public void Should_Replace_DbContext()
{
(ServiceProvider.GetRequiredService<IThirdDbContext>() is TestAppDbContext).ShouldBeTrue();
(_dummyRepository.GetDbContext() is IThirdDbContext).ShouldBeTrue();
(_dummyRepository.GetDbContext() is TestAppDbContext).ShouldBeTrue();
}
}
}

@ -1,13 +1,16 @@
using Microsoft.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.TestApp.ThirdDbContext;
using Volo.Abp.TestApp.Domain;
namespace Volo.Abp.TestApp.EntityFrameworkCore
{
public class TestAppDbContext : AbpDbContext<TestAppDbContext>
public class TestAppDbContext : AbpDbContext<TestAppDbContext>, IThirdDbContext
{
public DbSet<Person> People { get; set; }
public DbSet<ThirdDbContextDummyEntity> DummyEntities { get; set; }
public TestAppDbContext(DbContextOptions<TestAppDbContext> options)
: base(options)
{

@ -22,7 +22,7 @@ namespace Volo.Abp.MemoryDb
services.AddMemoryDbContext<TestAppMemoryDbContext>(options =>
{
options.WithDefaultRepositories();
options.AddDefaultRepositories();
});
services.AddAssemblyOf<AbpMemoryDbModule>();

@ -14,6 +14,7 @@ namespace Volo.Abp.TestApp
public override void ConfigureServices(IServiceCollection services)
{
ConfigureAutoMapper(services);
services.AddAssemblyOf<TestAppModule>();
}

Loading…
Cancel
Save