为什么我的通用服务会出现“无法实例化实现类型”的错误?

68

我有一个通用存储库,一直以来都在我的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);
    }
}

我们需要在StartUp中配置DI,如下:

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

现在我刚刚重构了代码,并尝试在控制器和通用仓库之间添加服务层。

这是我的第二个依赖注入(DI)语句,位于 StartUp 文件中:

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

现在我有这两条依赖注入的代码:

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

以下是我当前的代码:
从通用仓库开始:
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);
    }
}

我不确定如何连接中间服务层。

8个回答

151

最近我经历了这件事情。我将实现类标记为“抽象”,这是早期设计的产物。我移除了它,容器就可以实例化该类而没有任何问题。


1
这也是我的情况。 - SuperPoney
1
特阿拉姆。谁他妈写了个抽象类在class前面。我们真是丢脸。 - undefined

124

我是个白痴。 我拥有实现的界面。 我改变了:

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

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

2
同样的错误。我使用了一个别名,认为这是导致错误的原因,例如 using shortName= Foo.Repository.Repositories.Contacts; 实际上这是没有问题的,问题在于混淆了接口/实现顺序。 - VSO
6
我添加了一个抽象实现类。这是如此普遍的错误。 - Ordoshsen
刚刚我也遇到了这个问题,这条评论帮助我解决了它! :) - Toby Fieldgroove
我也是个傻瓜 - 今天我做了太多的布线,弄得一团糟。谢谢你的提示! - Robb Sadler
这是因为Visual Studio IntelliSense自动完成功能如此,容易忽视。 - Niksr

12

很好的问题和很好的答案,但是...

如果你的问题不同但错误信息相同

...而且你正在为默认实现注册服务,没有指定实现类型,就像这样:

services.AddScoped<IMyDependency>();

如果错误信息没有提供详细的原因,那么这个错误对你来说就没有什么用处:

为服务类型 'MyApp.IMyDependency' 实例化实现类型 'MyApp.IMyDependency' 失败。

在这种情况下,可以尝试更改服务注册方式:

services.AddScoped<IMyDependency, MyDependency>();

这将改进错误消息,并告诉您问题所在,如下所示:

某些服务无法构建(验证服务描述符时出错 'ServiceType: MyApp.IMyDependency Lifetime: Transient ImplementationType: MyApp.MyDependency': 在尝试激活“MyApp.IMyDependency”时无法解析类型为'MyApp.ILowerDependency'的服务。

在默认的ASP.NET核心DI容器中,整个依赖关系图需要明确注册(除了像ILogger之类的一些例外)。


你读懂了我的想法。对我来说,只需要在服务注册中添加具体类型就足够了。 - Design.Garden

6

继Bartosz的答案之后,此消息出现的另一个原因是,如果您尝试使用类似以下方式的通用具体类型来满足非通用接口:

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

这样不起作用,因为依赖注入容器在实例化MyDependency时不知道要提供什么泛型参数。要么将接口转换为通用类型,要么需要一个工厂来手动实例化它。


4
根据David的回答,我不得不在我的服务实现类中移除'abstract'。然而,该类中还有一些抽象方法和属性。因此,将它们改为非抽象的并创建一个公共构造函数后,我成功地注册了该服务。

3

当我的service类没有继承IService接口时,我遇到了这个问题。


0
builder.Services.AddTransient<Istudent , StudentService>();

StudentService类中实现了Istudent接口。


0

现在我弄清楚了,这似乎很明显,但如果你正在进行汇编扫描注册,你会想要排除抽象类和接口:

var assembly = AppDomain.CurrentDomain.Load("My.Namespace");
if (assembly == null)
    return;

var types = assembly.GetTypes().Where(x => x?.Namespace != null &&
                                           x.Namespace.Contains("My.Namespace.ViewModels") &&
                                           x.Name.Contains("ViewModel") &&
                                           !x.Name.Contains(nameof(MyAbstractViewModelBase)) &&
                                           !x.Name.Contains(nameof(IMyAbstractViewModel)));

foreach (var thing in types)
    services.AddScoped(thing);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接