在ABP實際使用過程有時候需要對IRepository進行擴展,增加一些自定義方法。
先創建一個自定義倉儲接口對IRepository<TEntity, TPrimaryKey>進行擴展。
using System; using System.Linq.Expressions; using Abp.Dependency; using Abp.Domain.Entities; using Abp.Domain.Repositories; namespace MyProject.Domain { /// <summary> /// 定義倉儲模型中的擴展操作 /// </summary> public interface IMyRepository<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey> where TEntity : class, IEntity<TPrimaryKey> { /// <summary> /// 為<see cref="IRepository{TEntity, TPrimaryKey}"/>擴展一個可以獲取關聯實體的FirstOrDefault操作 /// </summary> /// <param name="predicate"></param> /// <param name="includeExpression"></param> /// <returns></returns> TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includeExpression); } public interface IMyRepository<TEntity> : IMyRepository<TEntity, int>, IRepository, ITransientDependency where TEntity : class, IEntity<int> { } }
創建IMyRepository的實現類MyRepository,它繼承自ABP自動創建的MyProjectRepositoryBase的這個基類。
using System; using System.Linq; using System.Linq.Expressions; using Abp.Domain.Entities; using Abp.EntityFrameworkCore; using Abp.EntityFrameworkCore.Repositories; using MyProject.Domain; namespace MyProject.EntityFrameworkCore.Repositories { /// <summary> /// 實現倉儲模型中的擴展操作 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <typeparam name="TPrimaryKey"></typeparam> public class MyRepository<TEntity, TPrimaryKey> : MyProjectRepositoryBase<TEntity, TPrimaryKey>, IMyRepository<TEntity, TPrimaryKey> where TEntity : class, IEntity<TPrimaryKey> { public MyRepository(IDbContextProvider<MyProjectDbContext> dbContextProvider) : base(dbContextProvider) { } public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate, params Expression<Func<TEntity, object>>[] includeExpression) { return GetAllIncluding(includeExpression).FirstOrDefault(predicate); } } public class MyRepository<TEntity> : MyRepository<TEntity, int>, IMyRepository<TEntity> where TEntity : class, IEntity<int> { public MyRepository(IDbContextProvider<MyProjectDbContext> dbContextProvider) : base(dbContextProvider) { } } }
創建MyEfCoreAutoRepositoryTypes用於關聯倉儲接口和其默認實現。
using System; using Abp.Domain.Repositories; using MyProject.Domain; namespace MyProject.EntityFrameworkCore.Repositories { /// <summary> /// 關聯倉儲接口和其默認實現。 /// </summary> public class MyEfCoreAutoRepositoryTypes { public static AutoRepositoryTypesAttribute Default { get; } static MyEfCoreAutoRepositoryTypes() { Default = new AutoRepositoryTypesAttribute( typeof(IMyRepository<>), typeof(IMyRepository<,>), typeof(MyRepository<>), typeof(MyRepository<,>) ); } } }
修改MyProjectEntityFrameworkModule為其添加RegisterGenericRepositoriesAndMatchDbContexes方法為DbContext中每個實體注冊一個IMyRepository的默認實現,並在Initialize中調用這個注冊方法。
using System; using System.Reflection; using Abp.Collections.Extensions; using Abp.Dependency; using Abp.EntityFramework; using Abp.EntityFramework.Repositories; using Abp.EntityFrameworkCore; using Abp.EntityFrameworkCore.Configuration; using Abp.Modules; using Abp.Orm; using Abp.Reflection; using Abp.Reflection.Extensions; using Abp.Zero.EntityFrameworkCore; using Castle.MicroKernel.Registration; using MyProject.EntityFrameworkCore.Repositories; using MyProject.EntityFrameworkCore.Seed; namespace MyProject.EntityFrameworkCore { [DependsOn( typeof(MyProjectCoreModule), typeof(AbpZeroCoreEntityFrameworkCoreModule))] public class MyProjectEntityFrameworkModule : AbpModule { /* Used it tests to skip dbcontext registration, in order to use in-memory database of EF Core */ public bool SkipDbContextRegistration { get; set; } public bool SkipDbSeed { get; set; } private readonly ITypeFinder _typeFinder; public MyProjectEntityFrameworkModule(ITypeFinder typeFinder) { _typeFinder = typeFinder; } public override void PreInitialize() {
... } public override void Initialize() { IocManager.RegisterAssemblyByConvention(typeof(MyProjectEntityFrameworkModule).GetAssembly()); //調用注冊方法 RegisterGenericRepositoriesAndMatchDbContexes(); } public override void PostInitialize() { ... } /// <summary> /// 為DbContext中表達的實體注冊通用的Repository /// </summary> private void RegisterGenericRepositoriesAndMatchDbContexes() { //獲取所有注入的數據庫上下文 var dbContextTypes = _typeFinder.Find(type => { var typeInfo = type.GetTypeInfo(); return typeInfo.IsPublic && !typeInfo.IsAbstract && typeInfo.IsClass && typeof(AbpDbContext).IsAssignableFrom(type); }); if (dbContextTypes.IsNullOrEmpty()) { Logger.Warn("No class found derived from AbpDbContext."); return; } using (IScopedIocResolver scope = IocManager.CreateScope()) { foreach (var dbContextType in dbContextTypes) { Logger.Debug("Registering DbContext: " + dbContextType.AssemblyQualifiedName); //為數據上下文中的所有實體注冊MyEfCoreAutoRepositoryTypes的定義的Repository。 scope.Resolve<IEfGenericRepositoryRegistrar>().RegisterForDbContext(dbContextType, IocManager, MyEfCoreAutoRepositoryTypes.Default); IocManager.IocContainer.Register( Component.For<ISecondaryOrmRegistrar>() .Named(Guid.NewGuid().ToString("N")) .Instance(new EfCoreBasedSecondaryOrmRegistrar(dbContextType, scope.Resolve<IDbContextEntityFinder>())) .LifestyleTransient() ); } } } } }
寫個單元測試使用IMyRepository調用自定義的FirstOrDefault對User進行查詢,並且Roles把作為關聯實體進行預加載。
public class MyRepository_Test:MyProjectTestBase { private readonly IMyRepository<User, long> _myRepository; public MyRepository_Test() { //MyRepository<User, long>已經被自動注冊 _myRepository = Resolve<IMyRepository<User, long>>(); } [Fact] public void GetUsers_IncludeRoles_Test() { // Act var User = _myRepository.FirstOrDefault(t => t.Name == "admin", t => t.Roles); //Assert User.Roles.ShouldNotBeNull(); } }