有两种方式对数据库进行配置:
1、重写 DbContext.OnConfiguring(DbContextOptionsBuilder)
方法
class MyDBContext : DBContext
{
...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"connectionString");
}
}
2、通过 DbContextOptionsBuilder
类创建 DbContextOptions
实例,并通过构造函数 DbContext(DbContextOptions)
传递给 DbContext
DbContext
必须具有的实例DbContextOptions
才能执行任何工作
- 数据库提供程序,若要使用,通常选择通过调用的方法,如
UseSqlServer
或UseSqlite
。 这些扩展方法需要相应的提供程序包,如Microsoft.EntityFrameworkCore.SqlServer
或Microsoft.EntityFrameworkCore.Sqlite
。 中定义的方法Microsoft.EntityFrameworkCore
命名空间。 - 任何必要的连接字符串或标识符的数据库实例中,通常作为参数传递到上述提供程序选择方法
- 任何提供程序级别的可选行为选择器,通常还链接到提供程序选择方法调用中
- 任何常规 EF Core 行为选择器,通常链接之后或之前提供程序选择器方法
下面的示例将配置DbContextOptions
若要使用 SQL Server 提供程序,在连接包含connectionString
变量、 提供程序级别的命令超时,以及可使在中执行的所有查询 EF Core 行为选择器DbContext
否跟踪默认情况下:
optionsBuilder
.UseSqlServer(connectionString, providerOptions=>providerOptions.CommandTimeout(60))
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
options = new DbContextOptionsBuilder<CholessContext>()
.UseInMemoryDatabase("choless") .Options; using (var context = new MyDBContext(options)) { ... }
Abp提供的实例DbContextOption实例,是DbContextOptionsFactory工厂方法提供,它封装了将DbContextOptionsBuilder,以及连接字符串,
所需要的全部参数封装在AbpDbContextConfigurationContext里面
在AbpDbContextConfigurationContext扩展方法就可以直接使用UseSqlServer方法(字符串)
而AbpDbContextOptions是再一层封装,目的是让应用上使用更加方便,即操作AbpDbContextConfigurationContext的委托方法。
使用的是Config,PreConfigure以及Config<DbContext>、PreConfigure<DbContext>方法
而它的使用,在DbContextOptionFactory方法,创建AbpDbContextConfigurationContext实例,再执行委托方法。
》单独创建DbContext的生命周期是TransientDependency,其依赖是DbContextOptions是工厂DbContextOptionFactory工厂模式进行创建的。
》如果使用仓储如IRespository创建,而默认使用EfCoreRepository<>创建,构造函数使用IDbContextProvider<TDbContext>进行创建,
由于EfCoreRepository是实现IUnitOfWorkEnabled的接口,其下的所有方法都使用拦截器,创建一个工作单元
进行注入IDbContextProvider是UnitOfWorkDbContextProvider,它需要在一个工作单元里,使用是IServiceScope,它是伴随着unitofwork的Dispose
销毁而销毁,进行注入创建DbContext.
它的在UnitOfWork的key值是"DbContext的FullName_连接字符串"
UnitOfWork.Option.IsTransational若是则使用key="EntityFrameWork_字符串",则使用创建一个事务
dbContext.database.BeginTransation()
而AbpDbContextRegisterOptions,则对仓储的实现类进行配置,默认值、使用哪一个仓储实现类,替换DbContext进行配置默认的IReadOnlyBasicRepostory(只有读方法),
IReadOnlyRepostory(添加WithDetails(表达树)方法)IQueryable方法
》IEumerable<T> --->GetEnumerator
>IEumerable ------>GetEumerator
IQueryable ------> Type, Expression,IQueryProvider
protected override IQueryable<TEntity> GetQueryable() { return DbSet.AsQueryable(); }
IBasicRepository:在IReadOnlyBasicRespostory基础上,添加增加、删除、修改方法
IRepostory:在IReadOnlyRepostory和IBasicRepository基础上,再增加了删除表达树方法
而EfCoreRepository<TDbContext,TEntity,Tkey>则包含了IRepository的全部实现方法
每一个数据库实现都继承实现RepositoryRegisterBase<AbpDbContextRegistrationOptions>,基类实现是AddDefaultRepositories()是增加
自定义的仓储和登记默认仓储(官方实现还是自定义)。
扩充是GetEntityTypes,GetRepositoryType方法
protected override Type GetRepositoryType(Type dbContextType, Type entityType) { return typeof(EfCoreRepository<,>).MakeGenericType(dbContextType, entityType); } protected override Type GetRepositoryType(Type dbContextType, Type entityType, Type primaryKeyType) { return typeof(EfCoreRepository<,,>).MakeGenericType(dbContextType, entityType, primaryKeyType); }
在EfCoreRepositoryRegistrar是关系数据库的实现方法
WithDetails()默认是Option.DefaultWithDetails(GetQueryable()),而
在AbpDbContextRegisterOptions下Entity<T>(Action<AbpEntityOptions<>>)配置
options.Entity<Person>(opt => { opt.DefaultWithDetailsFunc = q => q.Include(p => p.Phones); });
而在XXX.EntityFrameWorkCore,则应用SeviceConfigurationContext.services.AddAbpDbContext(option=>{???})
对每一个实体的仓储进行设置,对于默认仓储,默认仓储是优先使用Option配置的,若没有配置,则使用EfCoreRepository<>
protected virtual bool ShouldRegisterDefaultRepositoryFor(Type entityType)
{
//是否注册默认仓储,若false,则不注册 if (!Options.RegisterDefaultRepositories) { return false; } //如果自定义仓储包含,则不注册默认仓储 if (Options.CustomRepositories.ContainsKey(entityType)) { return false; } //如果设置不包括所有实体,而且当前不是聚合根类型,则返回false,(聚合根是默认要仓储的) if (!Options.IncludeAllEntitiesForDefaultRepositories && !typeof(IAggregateRoot).IsAssignableFrom(entityType)) { return false; } return true; }
1、Entity<>
2、AddDefaultRepositories===》登记了默认的仓储
3、AddRepository ========》自定义仓储 CustomRepositories[entitype]=repositoryType
3、SetDefaultRepositoryClass ===》默认是使用EfcoreRepository,可以自定义设置,分两个,一个是有key,一个无key的,
4、ReplaceDbContext => List<type> ReplaceDbContextTypes,就是将此DbContext替换成OriginDbContext(即AddAbpContext定义那个)
AddAbpDbContext中使用到
foreach (var dbContextType in options.ReplacedDbContextTypes) { services.Replace(ServiceDescriptor.Transient(dbContextType, typeof(TDbContext))); }
AbpContext:
1、ModelBuilder,配置审计、租户等数据映射
2、利用IEventChangeEventHelper进行事件发送
3、利用IEventHistoryHelper对日记的记录
4、数据筛选,主要是软删除、租户
ABP使用,引用Volo.Abp.EntityFrameworkCore,以及具体的数据库如Volo.Abp.EntityFrameworkCore.SqlServer
它的步骤在Configure(ServiceConfigurationContext context)方法
执行Configure<AbpDbContextOptions>(option=>option.UseSqlServer()),AbpDbContextOptions定义的方法Configure以及Configure<TDbContext>,
是对AbpDbContextConfigurationContext.DbContextOptions(
DbContextOptionsBuilder).UseSqlServer(字符串)进行设置,相应字符串,在工厂创建
时AbpDbContextConfigurationContext,引入IConnectionStringResolver,解析字符串
AbpDbContext上下文进行服务进行注册,包括默认仓储,特定仓储,以及DbContext替换,(委托方法)
对
将DbContextOptions<TDbContext>的工厂方法 注入容器里面,用于创建DbContext
而这个DbContext,在仓储方法是由UnitOfWorkDbContextProvider提供,
public static IServiceCollection AddAbpDbContext<TDbContext>(
this IServiceCollection services,
Action<IAbpDbContextRegistrationOptionsBuilder> optionsBuilder = null)
where TDbContext : AbpDbContext<TDbContext>
{
services.AddMemoryCache();
var options = new AbpDbContextRegistrationOptions(typeof(TDbContext), services);
optionsBuilder?.Invoke(options);
services.TryAddTransient(DbContextOptionsFactory.Create<TDbContext>);
foreach (var dbContextType in options.ReplacedDbContextTypes)
{
services.Replace(ServiceDescriptor.Transient(dbContextType, typeof(TDbContext)));
}
new EfCoreRepositoryRegistrar(options).AddRepositories();
return services;
}
配置
protected virtual void ConfigureExtraProperties<TEntity>(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) where TEntity : class { if (!typeof(IHasExtraProperties).IsAssignableFrom(typeof(TEntity))) { return; } modelBuilder.Entity<TEntity>(b => { b.Property(x => ((IHasExtraProperties) x).ExtraProperties) .HasConversion( d => JsonConvert.SerializeObject(d, Formatting.None), s => JsonConvert.DeserializeObject<Dictionary<string, object>>(s) ) .HasColumnName(nameof(IHasExtraProperties.ExtraProperties)); }); }
防并发的配置,IsConcurrencyToken
protected virtual void ConfigureConcurrencyStampProperty<TEntity>(ModelBuilder modelBuilder, IMutableEntityType mutableEntityType) where TEntity : class { if (!typeof(IHasConcurrencyStamp).IsAssignableFrom(typeof(TEntity))) { return; } modelBuilder.Entity<TEntity>(b => { b.Property(x => ((IHasConcurrencyStamp) x).ConcurrencyStamp) .IsConcurrencyToken() .HasColumnName(nameof(IHasConcurrencyStamp.ConcurrencyStamp)); }); }
更新了需要赋值旧值,新值 -------》ef的特性
protected virtual void UpdateConcurrencyStamp(EntityEntry entry) { var entity = entry.Entity as IHasConcurrencyStamp; if (entity == null) { return; } Entry(entity).Property(x => x.ConcurrencyStamp).OriginalValue = entity.ConcurrencyStamp; entity.ConcurrencyStamp = Guid.NewGuid().ToString("N"); }
AbpDbContextRegistrationOptions:利用IServicsCollection对默认仓储,特定实体的配置,以及DbContext的替换,这个功能已经很强大
AbpEntityOptions:
DbContextCreationContext:只有connectionStringName、connectionString的包装,有 Use方法(对旧的DbContextCreationContext释放)
AbpDbContextConfigurationContext:只有ConnectionString、ConnectionStringName、DbContextOptionsBuilder、DbConnection包装
AbpDbContextOptions: =>对AbpDbContextConfigurationContext委托 的配置, List<Action<AbpDbContextConfigurationContext >>,我们使用主要到这个AbpDbContextOptions对里面AbpDbContextConfigurationContext委托配置
它在DbContextOptionsFactory.Create方法,创建DbContextOptions<TDbContext>,而这个参数用于创建AbpDbContext实例
它的操作步骤是它获取DbContextCreationContext,使用到ConnectionStringNameAttribute上的connectionStringName,使用IConnectionStringResolver解析到字符串)
创建new AbpDbContextConfigurationContext<TDbContext>()
执行PreConfigure、Configure的方法(每个都是Default通用方法,以及具体的AbpDbContextConfigurationContext<TDbContext>的方法)
返回到AbpDbContextConfigurationContext.DbContextOptions.Options,即是 DbContextOptions<TDbContext>
而创建这个Option的工厂方法加入到IServiceCollection容器里面
而自身创建XXXDbContext,它需要它需要继承自 AbpDbContext<T>,此类的是派生于ITransientDependency,在依赖注入系统会自动注入XXXDbContext,
而仓储的调用注入是IDbContextProvider<TDbContext>,它的实现是UnitOfWorkDbContextProvider,
是提供具体的XXXDbContext,
要注意是一个工作单元里里面也存储一个实例字典实现方便使用,它的索引值是$DbContext全称_连接字符串,它是随着工作单元的释放也释放
context.Services.TryAddTransient(typeof(IDbContextProvider<>), typeof(UnitOfWorkDbContextProvider<>));
Add-Migration 添加一个新的迁移(名词),string是迁移的名称
remove-Migration 删除上一次的迁移
Update-Database 更新最近一次的迁移到数据库(有时候可能与原来的数据库产生冲突,所以可能并不能迁移到数据库)
Update-Database -Migration 0 表示清空数据库,回到空数据库状态
Script-Migration 查看迁移脚本
AbpModelBuilderConfigurationOptions:
AbpDbContextConfigurationContext :DbContextOptionsBuilder
扩展方法:UseUseMySQL
DbContextCreationContext
AbpDbContextRegistrationOptions:
AbpEntityOptions:
AbpDbContextOptions:
AbpModelBuilderConfigurationOptions:
AbpDbContextConfigurationContext
DbContextCreationContext
AbpDbContextRegistrationOptions:
AbpEntityOptions:
AbpDbContextOptions:
AbpModelBuilderConfigurationOptions:
AbpDbContextConfigurationContext
DbContextCreationContext