Base solution for your next web application
Open Closed

Custom repository exception #7627


User avatar
0
mhrabak created

Hi guys, We try to implement our own Repository (https://aspnetboilerplate.com/Pages/Documents/EntityFramework-Integration) but we are facing an issue. When I use piece of code above (TaskAppService) exception is raised, but when we change generic to TaskEntity type (EfCore entity) everything works fine. Unfortunately we need to use TaskDto instead of TaskEntity. TaskDto inherits from IEntity<TPrimaryKey> Exception is raised when we try to execute action GetTasks. WebAPI starts as usual without any problem. AutorepositoryTypes registration:

 [AutoRepositoryTypes(
        typeof(ICustomRepository<>)
        , typeof(ICustomRepository<,>)
        , typeof(AttisDemoRepository<>)
        , typeof(AttisDemoRepository<,>)
        , WithDefaultRepositoryInterfaces = true
        )]

Raised exception:

'AttisDemo.Tasks.TaskAppService' is waiting for the following dependencies:
- Service 'AttisDemo.ICustomRepository`1[[AttisDemo.Tasks.Dto.TaskDto, AttisDemo.Application.Shared, Version=7.1.0.0, Culture=neutral, PublicKeyToken=null]]' which was not registered.

TaskAppService:

 public class TaskAppService : AttisDemoAppServiceBase, ITaskAppService {
        [NotNull]
        private readonly ICustomRepository&lt;TaskDto&gt; _taskRepo;

        public TaskAppService([NotNull] ICustomRepository&lt;TaskDto&gt; taskRepo) {
            _taskRepo = taskRepo;
        }

        public async Task&lt;ListResultDto&lt;TaskDto&gt;> GetTasks() {
            var tasks = await _taskRepo.GetAll().ToListAsync();
            return new ListResultDto&lt;TaskDto&gt;(tasks);
        }

        public async Task&lt;TaskDto&gt; GetTaskById(TaskInput filter) {
            var task = await _taskRepo.GetAll().WhereIf(filter?.Id != null, tasks => tasks.Id == filter.Id)?.FirstOrDefaultAsync();
            return task;
        }

        public async Task CreateTask(TaskDto input) {
            //var taskEntity = ObjectMapper.Map&lt;TaskEntity&gt;(input);
            await _taskRepo.InsertAsync(input);
        }

        public async Task UpdateTask(int taskId, TaskDto input) {
            var taskEntity = await _taskRepo.GetAsync(taskId);
            taskEntity.Description = input.Description;
            taskEntity.Identification = input.Identification;
            await _taskRepo.UpdateAsync(taskEntity);
        }
    }

TaskRepo:

 public class TaskRepo : AttisDemoRepository&lt;TaskDto&gt; {
        public TaskRepo(IDbContextProvider&lt;AttisDemoDbContext&gt; dbContextProvider) : base(dbContextProvider) {
        }

        public override IQueryable&lt;TaskDto&gt; GetAsQuearyable(Expression&lt;Func&lt;TaskDto, bool&gt;> criteria) {
            var taskDtos = Context.Tasks.Select(a => new TaskDto {
                Id = a.Id,
                Description = a.Description,
                Identification = a.Identification
            });
            if (criteria != null) {
                return taskDtos.Where(criteria);
            }
            return taskDtos;
        }

        public override Task&lt;TaskDto&gt; GetByKeyAsync(int id) {
            return GetAll().FirstOrDefaultAsync(a => a.Id == id);
        }
    }

Thanks in advance.


3 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team

    Entities in the DbContext that meet the following conditions will be actively registered.

    https://github.com/aspnetboilerplate/aspnetboilerplate/blob/25335cd16354b5246f02352f3220b9861e7f3dca/src/Abp.EntityFrameworkCore/EntityFrameworkCore/EfCoreDbContextEntityFinder.cs#L17

  • User Avatar
    0
    mhrabak created

    Thanks I thought it will be something like that what cause the problem. Is there some way how to obtain this functionality and be able to use Repository without IEntity and keep your functionality behind the scenes? We don't know much about your structures yet but I presume Repository has some hidden functionality which we can loose when we start to create and register our own custom repositories and do not use yours IRepository or am I mistaken?

    We are thinking about something like this:

     public class TaskRepository : CustomRepo<TaskDto> {
            public TaskRepository(IDbContextProvider<AttisDemoDbContext> dbContextProvider) : base(dbContextProvider) {
            }
        }
    
      public abstract class CustomRepo<TDto, TPrimaryKey>
            where TDto : IDto<TPrimaryKey> {
            protected readonly AttisDemoDbContext Ctx;
            protected CustomRepo(IDbContextProvider<AttisDemoDbContext> dbContextProvider) {
                Ctx = dbContextProvider.GetDbContext();
            }
    
            public virtual Task<IQueryable<TDto>> GetAllAsync() {
                return Task.FromResult(GetAll());
            }
    
            public abstract IQueryable<TDto> GetAll();
            .
            .
            .
            etc.
        }
    
        public interface IDto<TPrimaryKey> : IDto {
            TPrimaryKey Id { get; set; }
        }
    
        public interface IDto {
            object KeyValue { get; }
        }
    

    Structure is similar to yours but with that change TaskDto is not an entity registered to DbContext - now I expect to override GetAll method get desired DbSet join another DBSets if desired, filter record/s I want and return them in structure of my choice. So I think the only problem is part of code you posted as answer the rest is OK I think. What I dont know is where to get MultitenancySIte for GetDbContext method. I guess this parameter is necessary if we want to support multitenancy and keep DbContext works correctly with tenancy.

    Hope you understand why we need this.

  • User Avatar
    0
    maliming created
    Support Team

    Why you don't create a separate service, use the abp standard repository internally. You can get the IQueryable and map it to Dto.(eg: automaper ProjectTo)