我有一个通用的存储库,我已经在我的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<>));
现在我刚重构并试图在控制器和通用仓库之间放置一个服务层。
所以这是我在StartUp中的第二个DI行:
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);
}
}
我只是不确定如何插入中间服务层。
答案 0 :(得分:30)
services.AddScoped(typeof(IGenericService<>), typeof(IGenericService<>));
到
services.AddScoped(typeof(IGenericService<>), typeof(GenericService<>));
答案 1 :(得分:2)
到目前为止,很好的问题和很好的答案,但是......
...并且您正在为默认实现注册服务,没有指定实现类型,如下所示:
services.AddScoped<IMyDependency>();
那么该错误没有帮助,因为它没有详细说明为什么事情不起作用:
<块引用>无法为服务类型“MyApp.IMyDependency”实例化实现类型“MyApp.IMyDependency”。
在这种情况下,将服务注册更改为:
services.AddScoped<IMyDependency, MyDependency>();
这将改善错误信息,并告诉您问题出在哪里,如下所示:
<块引用>无法构建某些服务(验证服务描述符“ServiceType: MyApp.IMyDependency Lifetime: Transient ImplementationType: MyApp.MyDependency”时出错:无法解析“MyApp.ILowerDependency”类型的服务 在尝试激活“MyApp.IMyDependency”时。)
在默认的 ASP.NET 核心 DI 容器中,需要显式注册整个依赖图(可能除了一些像 ILogger 的异常)
答案 2 :(得分:1)
这最近发生在我身上。我将实现类标记为“抽象”,工件或早期设计。我将其删除,容器可以实例化该类而没有问题。
答案 3 :(得分:1)
当 service 类没有从 IService 继承时,这发生在我身上 界面
答案 4 :(得分:0)
根据 Bartosz 的回答,此消息的另一个原因是,如果您尝试使用这样的通用具体类型来满足非通用接口:
services.AddScoped(typeof(IMyDependency), typeof(MyDependency<,>));
这将不起作用,因为 DI 容器在实例化 MyDependency 时不知道要提供哪些通用参数。要么必须将接口转换为通用接口,要么您需要一个工厂来手动实例化它。