【问题标题】:Binding autofac with webapi using generic repository使用通用存储库将 autofac 与 webapi 绑定
【发布时间】:2014-12-06 21:48:22
【问题描述】:

我正在尝试将 autofac 与存储库一起使用,并且我正在尝试添加一些泛型以尝试减少我正在编写的重复代码的数量。但是我正在兜圈子试图让 autofac 为我工作

所以我创建了一个域服务和接口来处理我们的标准 crud 操作

public class DomainService<T>:IDomainService<T>
{
    protected readonly IDomainService<T> Repository;

    public DomainService(IDomainService<T> repository)
    {
        Repository = repository;
    }



    public IQueryable<T> GetQueryable()
    {
        return Repository.GetQueryable();
    }

    public virtual Task<T> Add(T entity)
    {
       return Repository.Add(entity);
    }

界面:

public interface IDomainService<T>
{
    IQueryable<T> GetQueryable();
    Task<T> Add(T entity);
    Task<bool> Delete(T entity);
    Task<T> Update(T entity);
    Task<T> GetById(int id);
    Task<T> GetByUID(Guid id);
}

我使用我的 repo 没什么特别的

public class SkillRepository : DomainService<Skill>, ISkill
{
    private DataContext _db = new DataContext();
    private readonly ILogger _log = null;

    public SkillRepository(IDomainService<Skill> repository, ILogger log) : base(repository)
    {
        _log = log;
    }
}

最后我连接 autofac:

 var builder = new ContainerBuilder();

// Register the Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

// Register other dependencies.
builder.Register(c => new Logger()).As<ILogger>().InstancePerApiRequest();

builder.RegisterType<SkillRepository>()
    .As<IDomainService<Skill>>()
    .As<ISkill>()
    .InstancePerRequest();

// Build the container.
var container = builder.Build();

// Create the depenedency resolver.
var resolver = new AutofacWebApiDependencyResolver(container);

// Configure Web API with the dependency resolver.
GlobalConfiguration.Configuration.DependencyResolver = resolver;

我的 web api 控制器看起来像

public class SkillsController : BaseController<Skill>
{
    private readonly ISkill _skillRepository;

    public SkillsController(SkillRepository skillRepository) : base(skillRepository)
    {
        _skillRepository = skillRepository;
    }
}

基础控制器

public abstract class BaseController<TEntity> : ApiController
    where TEntity : new()
{
    protected readonly IDomainService<TEntity> DomainService;

    protected BaseController(IDomainService<TEntity> domainService)
    {
        DomainService = domainService;
    }

我得到一个例外:

"没有找到与 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' 类型 'Api.EndPoints.Skills.SkillsController' 可以用 可用的服务和参数:\ \ 无法解析参数 'Domain.Repository.SkillRepository SkillRepository' 的构造函数 'Void .ctor(Domain.Repository.SkillRepository)'。”

有什么明显的我做错了吗?

【问题讨论】:

    标签: c# asp.net-web-api dependency-injection autofac


    【解决方案1】:

    异常明确指出:

    无法解析构造函数“Void .ctor(Domain.IDomainService`1[Model.Skill], Domain.Interfaces.ISkill)”的参数“Domain.Interfaces.ISkill SkillRepository”。

    您只注册了IDomainService。但没有ISkill(该行已注释)。

    还有为什么 ctor 需要 2 个参数? SkillRepository 实现了 IDomainService&lt;Skill&gt;ISkill 所以你应该能够传递它:

    public SkillsController(SkillRepository skillRepository) : base(skillRepository)
    

    附言

    我会这样命名:

    public class SkillRepository : ISkillRepository, IDomainService<Skill>
    

    而且我更喜欢一切都是复数(SkillsControllers、SkillsRepository)或一切都是单数(SkillController、SkillRepository)。

    【讨论】:

    • 嗨 @abatishchev 我对上面的代码 global.asax 进行了修改。你能看出哪里不对吗?我仍然没有设法解决它
    【解决方案2】:

    它无法解决依赖关系,因为它正在寻找具体类型,但您从未将 SkillsRepository 注册为该类型。现在您可以更改注册以注册具体类型,但这不是最好的方法。

    更好的方法是将 SkillsRepository 注册为其接口:

    builder.RegisterType<SkillRepository>()
        .As<ISkillsRepository>()
        .InstancePerRequest();
    

    并定义ISkillsRepository 以继承您想要的所有其他接口,例如ISkill

    public interface ISkillsRepository : ISkill, IDomainService<Skill> { }
    

    不要将对象注册为具体类型,也不要依赖于构造函数中的具体类型。

      public SkillsController(ISkillRepository skillRepository) : 
          base(skillRepository) ...
    

    如果您使用具体类型作为依赖项,您创建的类无法使用模拟框架进行测试。

    您对SkillRepository : DomainService&lt;Skill&gt;, ISkill 的使用也令人困惑。为什么它既是技能又是技能的领域服务?没有多大意义。

    【讨论】:

    • 感谢伊恩的详细解释。听起来我做的事情有点不对。我已经更新了上述代码域服务,以尝试更多地展示我正在尝试做的事情。本质上,我试图使用 DomainService 作为我必须在每个存储库上实现 IDomainService 的一种方式。因为每个 repo 的代码都是相同且无聊的。这有意义吗?
    • 有道理,但使用组合而不是继承可能会更好。 en.wikipedia.org/wiki/Composition_over_inheritance
    • 此外,当您避免使用“胖控制器”(这通常是正确的做法)时,您很少需要控制器的基类。
    • 再次感谢伊恩。我会看看组合...希望不难使用继承
    • 好的抱歉这么多问题。我应该在我的 SkillRepository 中哪里使用组合而不是继承?我只真正使用过继承,所以在尝试使用存储库时我真的不知道最好的方法
    【解决方案3】:

    在我看来,您应该首先为您的类整理名称,这会使代码本身难以理解。其次,您的存储库正在实现域服务接口和 ISkill,诸如此类的事情增加了更多的混乱。我敢肯定,如果您正确地组织课程,那么您会找到解决问题的方法。 例如 ApiController 应该使用域服务,域服务应该使用存储库,存储库应该处理实体。

    public class SkillsDomainService:ISkillsDomainService
    {
      public void AddSkill(string name){}
      public void DeleteSkillById(int id){}
      ..... etc
    }
    
    public class Repository:IRepository
    {
       public T Get(int id){}
      public IEnumerable<T>GetAll(){}
    }
    

    然后您需要将您的接口绑定到在 ioc 中分类的具体。我很确定事情应该这样工作。

    【讨论】:

      猜你喜欢
      • 2011-11-06
      • 1970-01-01
      • 1970-01-01
      • 2012-05-07
      • 1970-01-01
      • 2012-09-05
      • 2018-01-03
      • 2021-03-17
      • 2020-01-13
      相关资源
      最近更新 更多