Почему я получаю сообщение об ошибке Не удается создать экземпляр типа реализации для моей универсальной службы?

У меня есть общий репозиторий, который я создавал прямо в своем контроллере WEB API, какое-то время без проблем.

Вот как выглядел мой контроллер:

[Route("api/[controller]")]
public class EmployeesController : Controller
{
    private IGenericRepository<Employee> _empRepo;

    public EmployeesController(IGenericRepository<Employee> employeeRepo)
    {
        _empRepo = employeeRepo;
    }

    // GET: api/employees
    [HttpGet]
    public async Task<IEnumerable<Employee>> GetEmployeesAsync(
            string firstName = null, string lastName = null)
    {
        //return await _empRepo.GetAll().Include("Organization").Include("PayPlan").Include("GradeRank").Include("PositionTitle").Include("Series").Include("BargainingUnit")
        //    .Where(e => (string.IsNullOrEmpty(firstName) || e.FirstName.Contains(firstName))
        //        && (string.IsNullOrEmpty(lastName) || e.LastName.Contains(lastName))
        //    )
        //    .ToListAsync();

        return await _empRepo.GetAllIncluding(
                a => a.Organization,
                b => b.PayPlan,
                c => c.GradeRank,
                d => d.PositionTitle,
                e => e.Series,
                f => f.BargainingUnit)
            .Where(e => (string.IsNullOrEmpty(firstName) || e.FirstName.Contains(firstName))
                && (string.IsNullOrEmpty(lastName) || e.LastName.Contains(lastName))
            )
            .ToListAsync();
    }

    // GET api/employees/5
    [HttpGet("{id}", Name = "GetEmployeeById")]
    public async Task<IActionResult> GetEmployeeByIdAsync(long id)
    {         
        //var employee = await _empRepo.Find(id).Include("Organization").Include("PayPlan").Include("GradeRank").Include("PositionTitle").Include("Series").Include("BargainingUnit").SingleAsync();
        var employee = await  _empRepo.GetSingleIncludingAsync(id,
            a => a.Organization,
            b => b.PayPlan,
            c => c.GradeRank, 
            d => d.PositionTitle,
            e => e.Series,
            f => f.BargainingUnit);

        if (employee == null)
        {
            return NotFound();
        }
        else
        {
            return new ObjectResult(employee);
        }
    }

    // PUT api/employees/id
    [HttpPut("{id}")]
    public async Task<IActionResult> PutEmployeeAsync([FromBody] Employee emp)
    {
        var employee = await _empRepo.UpdateAsync(emp);
        if (employee == null)
        {
            return NotFound();
        }

        await _empRepo.SaveAsync();
        return new ObjectResult(employee);
    }
}

и мы бы настроили DI в StartUp следующим образом:

services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));

Теперь я только что провел рефакторинг и попытался разместить уровень обслуживания между контроллером и общим репозиторием.

Итак, вот моя вторая строка DI в StartUp:

services.AddScoped(typeof(IGenericService<>), typeof(IGenericService<>));

Итак, теперь у меня есть две строки DI:

services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddScoped(typeof(IGenericService<>), typeof(IGenericService<>));

Вот мой текущий код:

Начиная с Generic Repo:

public enum FilteredSource
{
    All,
    GetAllIncluding,
}

public class GenericRepository<T> : IGenericRepository<T>
    where T: BaseEntity
{
    protected readonly ApplicationDbContext _context;
    protected DbSet<T> _dbSet;

    public GenericRepository(ApplicationDbContext context)
    {
        _context = context;
        _dbSet = context.Set<T>();
    }

    // no eager loading
    private IQueryable<T> All => _dbSet.Cast<T>();

    #region FIXME : DELETE
    // FIXME: Delete and use ALL instead.
    public IQueryable<T> GetAll() => _dbSet.AsQueryable();

    // FIXME: Delete and use GetSingleIncludingAsync instead.
    public IQueryable<T> Find(long id) =>
        _dbSet.Where(e => e.Id == id).AsQueryable();
    #endregion

    // eager loading
    private IQueryable<T> GetAllIncluding(
        params Expression<Func<T, object>>[] includeProperties) =>
         includeProperties.Aggregate(All, (currentEntity, includeProperty) => currentEntity.Include(includeProperty));

    // no eager loading
    public async Task<T> GetSingleIncludingAsync(long id)
    {
        return await _dbSet.SingleOrDefaultAsync(e => e.Id == id);
    }

    /// <summary>
    /// Takes in a lambda selector and let's you filter results from GetAllIncluding and All.
    /// </summary>
    /// <param name="selector">labmda expression to filter results by.</param>
    /// <param name="getFilteredSource">All or GetAllIncluding as the method to get results from.</param>
    /// <param name="includeProperties">array of eager load lamda expressions.</param>
    /// <returns></returns>
    public async Task<IEnumerable<T>> GetFiltered(
        Expression<Func<T, bool>> selector, FilteredSource filteredSource,
        Expression<Func<T, object>>[] includeProperties = null)
    {
        var results = default(IEnumerable<T>);
        switch (filteredSource)
        {
            case FilteredSource.All:
                results = All.Where(selector);
                break;
            case FilteredSource.GetAllIncluding:
                results = GetAllIncluding(includeProperties).Where(selector);
                break;
        }
        return await results.AsQueryable().ToListAsync();
    }

    // eager loading
    public async Task<T> GetSingleIncludingAsync(
        long id, params Expression<Func<T, object>>[] includeProperties)
    {
        IQueryable<T> entities = GetAllIncluding(includeProperties);
        //return await Filter<long>(entities, x => x.Id, id).FirstOrDefaultAsync();
        return await entities.SingleOrDefaultAsync(e => e.Id == id);
    }

    public async Task<T> InsertAsync(T entity)
    {
        if (entity == null)
        {
            throw new ArgumentNullException($"No {nameof(T)}  Entity was provided for Insert");
        }
        await _dbSet.AddAsync(entity);
        return entity;
    }

    public async Task<T> UpdateAsync(T entity)
    {
        T entityToUpdate = await
            _dbSet.AsNoTracking().SingleOrDefaultAsync(e => e.Id == entity.Id);
        if (entityToUpdate == null)
        {
            //return null;
            throw new ArgumentNullException($"No {nameof(T)}  Entity was provided for Update");
        }

        _dbSet.Update(entity);
        return entity;
    }

    public async Task<T> DeleteAsync(T entity)
    {
        _dbSet.Remove(entity);
        return await Task.FromResult(entity);
    }

    public Task SaveAsync() => _context.SaveChangesAsync();

Определение интерфейса:

public interface IGenericRepository<T>
    where T : BaseEntity
{
    #region FIXME : DELETE
    // FIXME: Delete and use ALL instead.
    IQueryable<T> GetAll();

    // FIXME: Delete and use GetSingleIncludingAsync instead.
    IQueryable<T> Find(long id);
    #endregion

    // eager loading
    Task<T> GetSingleIncludingAsync(
        long id, params Expression<Func<T, object>>[] includeProperties);

    Task<IEnumerable<T>> GetFiltered(
        Expression<Func<T, bool>> selector, FilteredSource filteredSource,
        Expression<Func<T, object>>[] includeProperties = null);

    Task<T> InsertAsync(T entity);
    Task<T> UpdateAsync(T entity);
    Task<T> DeleteAsync(T entity);

    #region Possible TODOs:
    //Task<IEnumerable<T>> FindBy(Expression<Func<T, bool>> predicate);
    //Task AddRange(IEnumerable<T> entities);
    //Task RemoveRange(IEnumerable<T> entities);
    #endregion

    Task SaveAsync();
}

Это вводится в мою общую службу:

public class GenericService<T> : IGenericService<T>
    where T : BaseEntity
{
    private IGenericRepository<T> _genericRepo;

    public GenericService(IGenericRepository<T> genericRepo)
    {
        _genericRepo = genericRepo;
    }

    public async Task<IEnumerable<T>> GetFiltered(
        Expression<Func<T, bool>> selector, FilteredSource filteredSource,
        Expression<Func<T, object>>[] includeProperties = null)
    {
        return await _genericRepo.GetFiltered(selector, filteredSource,
            includeProperties);
    }

    // eager loading
    public async Task<T> GetSingleIncludingAsync(long id, params Expression<Func<T, object>>[] includeProperties)
    {
        IEnumerable<T> entities = await _genericRepo.GetFiltered(null, FilteredSource.GetAllIncluding, includeProperties);
        //return await Filter<long>(entities, x => x.Id, id).FirstOrDefaultAsync();
        return entities.SingleOrDefault(e => e.Id == id);
    }

    public async Task<T> InsertAsync(T entity)
    {
        var result = await _genericRepo.InsertAsync(entity);
        await _genericRepo.SaveAsync();
        return entity;
    }

    public async Task<T> UpdateAsync(T entity)
    {
        var result = await _genericRepo.UpdateAsync(entity);
        if (result != null)
        {
            await _genericRepo.SaveAsync();
        }
        return result;
    }

    public async Task<T> DeleteAsync(T entity)
    {
        throw new NotImplementedException();
    }
}

Определение интерфейса для службы:

public interface IGenericService<T>
    where T : BaseEntity
{
    Task<IEnumerable<T>> GetFiltered(
        Expression<Func<T, bool>> selector, FilteredSource filteredSource,
        Expression<Func<T, object>>[] includeProperties = null);

    // eager loading
    Task<T> GetSingleIncludingAsync(long id, params Expression<Func<T, object>>[] includeProperties);

    Task<T> InsertAsync(T entity);
    Task<T> UpdateAsync(T entity);
    Task<T> DeleteAsync(T entity);
}

и, наконец, вот контроллер:

[Route("api/[controller]")]
public class EmployeesController : Controller
{
    private IGenericService<Employee> _genericService;

    public EmployeesController(IGenericService<Employee> genericService)
    {
        _genericService = genericService;
    }

    // GET: api/employees
    [HttpGet]
    public async Task<IEnumerable<Employee>> GetEmployeesAsync(
            string firstName = null, string lastName = null)
    {
        return await _genericService.GetFiltered(
                e => (string.IsNullOrEmpty(firstName) || e.FirstName.Contains(firstName))
                && (string.IsNullOrEmpty(lastName) || e.LastName.Contains(lastName)),
                FilteredSource.GetAllIncluding,
                new Expression<Func<Employee, object>>[] { a => a.Organization,
                b => b.PayPlan,
                c => c.GradeRank,
                d => d.PositionTitle,
                e => e.Series,
                f => f.BargainingUnit }
            );
    }

    // GET api/employees/5
    [HttpGet("{id}", Name = "GetEmployeeById")]
    public async Task<IActionResult> GetEmployeeByIdAsync(long id)
    {
        //var employee = await _empRepo.Find(id).Include("Organization").Include("PayPlan").Include("GradeRank").Include("PositionTitle").Include("Series").Include("BargainingUnit").SingleAsync();
        var employee = await _genericService.GetSingleIncludingAsync(id,
            a => a.Organization,
            b => b.PayPlan,
            c => c.GradeRank,
            d => d.PositionTitle,
            e => e.Series,
            f => f.BargainingUnit);

        if (employee == null)
        {
            return NotFound();
        }
        else
        {
            return new ObjectResult(employee);
        }
    }

    // PUT api/employees/id
    [HttpPut("{id}")]
    public async Task<IActionResult> PutEmployeeAsync([FromBody] Employee emp)
    {
        var employee = await _genericService.UpdateAsync(emp);
        if (employee == null)
        {
            return NotFound();
        }

        return new ObjectResult(employee);
    }
}

Я просто не знаю, как подключить средний уровень обслуживания.


person Sam    schedule 23.03.2017    source источник


Ответы (5)


Я идиот. У меня был интерфейс для реализации. Я изменился:

services.AddScoped(typeof(IGenericService<>), typeof(IGenericService<>));

to

services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
person Sam    schedule 23.03.2017
comment
Ты спас мои волосы. бедный меня services.AddSingleton<IXProvider, IXProvider>(); вместо services.AddSingleton<IXProvider, XProvider>(); - person waitforit; 21.11.2018
comment
Ты не единственный. Я только что сделал ‹type›, ‹interface›. Ура, про работу разработчиков! - person jeriley; 02.02.2019
comment
Та же ошибка. Я использовал псевдоним и подумал, что это вызвало ошибку, например using shortName= Foo.Repository.Repositories.Contacts; Это было действительно хорошо, и проблема заключалась в смешивании порядка интерфейса / реализации. - person VSO; 30.01.2020
comment
Спас меня. Незначительный, но может занять большую часть вашего времени - person NoloMokgosi; 23.02.2020
comment
Я добавил абстрактный класс реализации. Откуда такая популярная ошибка - person Ordoshsen; 08.07.2020
comment
Я сделал то же самое! - person niico; 31.12.2020
comment
Я тоже идиотка! Посмотрите, спустя почти 4 года после вашего ответа, люди все еще совершают эту ошибку. Забыл добавить реализацию ... - person Mister Q; 02.02.2021
comment
Я сделал ту же ошибку - person Gonzalo Méndez; 06.06.2021
comment
Спасибо: D Я сделал такое же копирование и вставку :) - person Harpal; 07.06.2021

Это случилось со мной недавно. У меня был класс реализации, помеченный как «абстрактный», артефакт более раннего дизайна. Я удалил его, и контейнер смог без проблем создать экземпляр класса.

person David Nutting    schedule 25.03.2019
comment
У меня была точно такая же проблема! Спасибо, что поделились этим! - person Fernando Moreira; 07.05.2019
comment
То же самое! Спасибо! - person dselgo; 12.12.2019
comment
То же самое. Удаление «абстрактного» исправило это для меня. Спасибо. - person Daniel Pavlovsky; 14.04.2021

Это случилось со мной, когда класс service не унаследовал от интерфейса IService

person Amirhossein Yari    schedule 09.02.2020

Однако пока отличный вопрос и отличные ответы ...

Если ваша проблема другая, но сообщение об ошибке такое же

... и вы регистрируете службу для реализации по умолчанию, без указания типа реализации, как здесь:

services.AddScoped<IMyDependency>();

Тогда ошибка бесполезна, потому что не дает подробностей о том, почему что-то не работает:

Невозможно создать экземпляр типа реализации MyApp.IMyDependency для типа службы MyApp.IMyDependency.

В этом случае измените регистрацию службы на:

services.AddScoped<IMyDependency, MyDependency>();

Это улучшит качество сообщения об ошибке и расскажет, в чем проблема, например:

Некоторые службы не могут быть созданы (Ошибка при проверке дескриптора службы ServiceType: MyApp.IMyDependency Lifetime: Transient ImplementationType: MyApp.MyDependency ': Невозможно разрешить службу для типа MyApp.ILowerDependency, пока пытается активировать MyApp.IMyDependency.)

В контейнере DI ядра ASP.NET по умолчанию весь граф зависимостей должен быть зарегистрирован явно (за исключением, возможно, некоторых исключений, таких как ILogger)

person Bartosz    schedule 04.02.2021

Следуя ответу Бартоша, еще одна причина этого сообщения заключается в том, что вы пытаетесь удовлетворить неуниверсальный интерфейс с помощью такого общего конкретного типа:

services.AddScoped(typeof(IMyDependency), typeof(MyDependency<,>));

Это не сработает, потому что контейнер DI не знает, какие общие аргументы предоставить при создании экземпляра MyDependency. Либо интерфейс необходимо преобразовать в универсальный, либо вам потребуется фабрика для его создания вручную.

person Skrymsli    schedule 31.03.2021