Search code examples
repositoryabp-framework

How can I extend BasicRepositoryBase and add new base repository functions with abp framework?


I want to extend BasicRepositoryBase to add a new function for all my repositories.

I created my interface as follows

using System;
using System.Threading.Tasks;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;

namespace FMS.Domain.Repositories
{
    public interface IFMSRepository<TEntity, TKey> : IBasicRepository<TEntity,TKey>, IRepository<TEntity, TKey>
        where TEntity : class, IEntity<TKey>
    {
        Task<TEntity> CreateOrUpdate(TEntity entity);
    }
}

I created my repository as follows. I added a new function CreateOrUpdate.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;

namespace FMS.Domain.Repositories
{
    public abstract class FMSRepository<TEntity, TKey> : BasicRepositoryBase<TEntity, TKey>, IFMSRepository<TEntity, TKey>
        where TEntity : class, IEntity<TKey>
    {
        
        public async Task<TEntity> CreateOrUpdate(TEntity entity )
        {
            TEntity resultEntity;

            if (entity.Id == null)
            {
                resultEntity = await InsertAsync(entity);
            }
            else
            {
                var checkEntity = await GetAsync(entity.Id);

                if (checkEntity != null)
                    resultEntity = await UpdateAsync(entity);
                else
                    resultEntity = await InsertAsync(entity);
            }

            return resultEntity;
        }

        public abstract Task DeleteAsync(Expression<Func<TEntity, bool>> predicate, bool autoSave = false, CancellationToken cancellationToken = default);
        public abstract Task DeleteDirectAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
        public abstract Task<TEntity> FindAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default);
        public abstract Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate, bool includeDetails = true, CancellationToken cancellationToken = default);
        public abstract Task<IQueryable<TEntity>> GetQueryableAsync();
        public abstract IQueryable<TEntity> WithDetails();
        public abstract IQueryable<TEntity> WithDetails(params Expression<Func<TEntity, object>>[] propertySelectors);
        public abstract Task<IQueryable<TEntity>> WithDetailsAsync();
        public abstract Task<IQueryable<TEntity>> WithDetailsAsync(params Expression<Func<TEntity, object>>[] propertySelectors);
    }
}

To register new repository I tried two different approaches in ApplicationModule File

first approach

public override void ConfigureServices(ServiceConfigurationContext context)
    {
        Configure<AbpAutoMapperOptions>(options =>
        {
            options.AddMaps<FMSApplicationModule>();
        });

        context.Services.AddScoped(typeof(IFMSRepository<,>), typeof(FMSRepository<,>));

}

Second approach

public override void ConfigureServices(ServiceConfigurationContext context)
    {
        Configure<AbpAutoMapperOptions>(options =>
        {
            options.AddMaps<FMSApplicationModule>();
        });
        
        System.Reflection.Assembly.GetExecutingAssembly()
            .GetTypes()
            .Where(item => item.GetInterfaces()
            .Where(i => i.IsGenericType).Any(i => i.GetGenericTypeDefinition() == typeof(IFMSRepository<,>)) && !item.IsAbstract && !item.IsInterface)
            .ToList()
            .ForEach(assignedTypes =>
            {
                var serviceType = assignedTypes.GetInterfaces().First(i => i.GetGenericTypeDefinition() == typeof(IFMSRepository<,>));
                context.Services.AddScoped(serviceType, assignedTypes);
            });
        

}

Both option yield the same error in my data seeder class which I use the new repository method. Below is my data seeder class

using System;
using System.Threading.Tasks;
using FMS.Domain.Repositories;
using FMS.Fairs;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.MultiTenancy;

namespace FMS.DataSeed
{

    public class FMSDataSeederContributer
    : IDataSeedContributor, ITransientDependency
    {
        private readonly IFMSRepository<FairArea, Guid> _fairAreaRepository;
        private readonly ICurrentTenant _currentTenant;

        public FMSDataSeederContributer(
            IFMSRepository<FairArea,Guid> fairAreaRepository,
            ICurrentTenant currentTenant)
        {
            _fairAreaRepository = fairAreaRepository;
            _currentTenant = currentTenant;

        }

        public async Task SeedAsync(DataSeedContext context)
        {
            using (_currentTenant.Change(context?.TenantId))
            {
                if (context?.TenantId == null)
                {
                    return;
                }
                if (await _fairAreaRepository.GetCountAsync() > 0)
                {
                    return;
                }
                await _fairAreaRepository.CreateOrUpdate(
                    new FairArea
                    {
                        Name = "Name of Area",
                        Type = FairAreaType.ClosedArea,
                        Description = "Description of Area",
                        Address = "Country of Area",
                        TenantId = _currentTenant.Id
                    }
                ) ;
                
                

            }

        }
    }
}

Here is the DataSeeder program output.

[23:12:10 INF] Started database migrations...
[23:12:10 INF] Migrating schema for host database...
[23:12:11 INF] Executing host database seed...
Unhandled exception. Autofac.Core.DependencyResolutionException: An exception was thrown while activating FMS.DataSeed.FMSDataSeederContributer.
 ---> Autofac.Core.DependencyResolutionException: None of the constructors found on type 'FMS.DataSeed.FMSDataSeederContributer' can be invoked with the available services and parameters:
Cannot resolve parameter 'FMS.Domain.Repositories.IFMSRepository`2[FMS.Fairs.FairArea,System.Guid] fairAreaRepository' of constructor 'Void .ctor(FMS.Domain.Repositories.IFMSRepository`2[FMS.Fairs.FairArea,System.Guid], Volo.Abp.MultiTenancy.ICurrentTenant)'.

See https://autofac.rtfd.io/help/no-constructors-bindable for more info.
   at Autofac.Core.Activators.Reflection.ReflectionActivator.<>c__DisplayClass14_0.<UseSingleConstructorActivation>b__0(ResolveRequestContext ctxt, Action`1 next)
   at Autofac.Core.Resolving.Middleware.DelegateMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.DisposalTrackingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Builder.RegistrationBuilder`3.<>c__DisplayClass41_0.<PropertiesAutowired>b__0(ResolveRequestContext ctxt, Action`1 next)
   at Autofac.Core.Resolving.Middleware.DelegateMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   --- End of inner exception stack trace ---
   at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.RegistrationPipelineInvokeMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.SharingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.Middleware.CircularDependencyDetectorMiddleware.Execute(ResolveRequestContext context, Action`1 next)
   at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
   at Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext ctxt)
   at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ResolveRequest request)
   at Autofac.Core.Resolving.ResolveOperation.ExecuteOperation(ResolveRequest request)
   at Autofac.Core.Resolving.ResolveOperation.Execute(ResolveRequest request)
   at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(ResolveRequest request)
   at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
   at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType, IEnumerable`1 parameters)
   at Autofac.ResolutionExtensions.Resolve(IComponentContext context, Type serviceType)
   at Autofac.Extensions.DependencyInjection.AutofacServiceProvider.GetRequiredService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Volo.Abp.Data.DataSeeder.SeedAsync(DataSeedContext context)
   at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous(IInvocation invocation, IInvocationProceedInfo proceedInfo)
   at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapter.ProceedAsync()
   at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
   at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed)
   at FMS.Data.FMSDbMigrationService.SeedDataAsync(Tenant tenant) in /Users/serhatonal/Documents/Codes/FMS/FMS/src/FMS.Domain/Data/FMSDbMigrationService.cs:line 103
   at FMS.Data.FMSDbMigrationService.MigrateAsync() in /Users/serhatonal/Documents/Codes/FMS/FMS/src/FMS.Domain/Data/FMSDbMigrationService.cs:line 53
   at FMS.DbMigrator.DbMigratorHostedService.StartAsync(CancellationToken cancellationToken) in /Users/serhatonal/Documents/Codes/FMS/FMS/src/FMS.DbMigrator/DbMigratorHostedService.cs:line 36
   at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at FMS.DbMigrator.Program.Main(String[] args) in /Users/serhatonal/Documents/Codes/FMS/FMS/src/FMS.DbMigrator/Program.cs:line 30
   at FMS.DbMigrator.Program.<Main>(String[] args)
bash: line 1: 28828 Abort trap: 6           "/usr/local/share/dotnet/dotnet" "/Users/serhatonal/Documents/Codes/FMS/FMS/src/FMS.DbMigrator/bin/Debug/net7.0/FMS.DbMigrator.dll"

Press any key to continue...^A
Saving session...
...copying shared history...
...saving history...truncating history files...
...completed.

[Process completed]

Any help is appreciated.


Solution

  • You are trying to register an abstract class, instead you need to register a concrete class. You can inherit from the EfCoreRepository class. Or in an easier way, you can write it as an extensions method.