如何将DI(AutoFac)与服务层一起使用

时间:2018-02-19 15:10:39

标签: c# asp.net entity-framework dependency-injection autofac

我有一个使用3层模式的MVC应用程序:DAL< - >服务< - > UI和我试图添加AutoFac来测试我的MVC控制器和服务。

几个问题:

1)如何使用AutoFac告诉我的服务层使用特定的上下文? 目前我正在使用builder.Register(c => c.Resolve<IRepo>()).As<Repo>();,但这会将IRepo暴露给用户界面,这将允许他们绕过服务层验证和业务规则。

2)除了构造函数之外,Repo和MockRepo是相同的,用于标识要使用的上下文。它存在,因为UI没有引用DAL,因此我无法传入IDataContext。有没有办法删除冗余的代码/类,仍然可以使用DI?

/* DAL */
public interface IDataContext
{
    T GetById<T>(int Id);
}

// Entity Framework context
public class EFContext : DbContext, IDataContext
{
    public EFContext() : base("MyConnectionString")
    {...}

    public T GetById<T>(int Id)
    {...}
}

// In-memory context for testing
public class MockContext : IDataContext
{
    public MockContext()
    {...}

    public T GetById<T>(int Id)
    {...}
}

/* Service */
public interface IRepo
{
    T GetById<T>(int id);
}

// Entity Framework Repo
public class Repo
{
    private IRepo _repo;
    public Repo()
    {
        _repo = new EFContext();
    }

    public T GetById<T>(int Id)
    {
        return _repo.GetById<T>(Id);
    }
}

// In-memory Repo
public class MockRepo : RepoBase
{
    private IRepo _repo;
    public MockRepo()
    {
        _repo = new MockContext();
    }

    public T GetById<T>(int Id)
    {
        return _repo.GetById<T>(Id);
    }
}

// Service to be used in the UI
public class StudentService
{
    public StudentService(IRepo repo)
    {
        _repo = repo;
    }

    public void ExpelStudent(int studentId)
    {
        var student = _repo.GetById(studentId);
        ...
        _repo.Save();
    }
}

1 个答案:

答案 0 :(得分:1)

根据您的评论,您将按以下方式构建项目。

解决方案项目(参考链):

  • 示例(服务,域名,数据)
  • 服务(域名,数据)
  • 数据(域名)

因此,您将主项目与所有其他.dll相关联。服务层不关心任何事情,除了充当数据层的中介,因此依赖。然后,您的数据层不应该关心除域模型之外的任何内容。

除非您执行Clean Architecture / Onion Architecture,否则您将松散地加载.dll而不是存储硬引用。

使用上述结构,你有一个基础,除非你这样做。

public class SampleContext : ISampleRepository
{
     public IEnumerable<SampleModel> GetAllSamples() => dbConnection.ExecuteQuery(query); // Dapper syntax, but you get the idea

     // Implement Dispose
}

public interface ISampleRepository : IDispose
{
     IEnumerable<SampleModel> GetAllSamples();
}

然后你建造工厂。

public class SampleFactory : ISampleFactory
{
     public ISampleRepository CreateSample() => new SampleContext();
}

public interface ISampleFactory
{
     ISampleRepository CreateSample();
}

关键是,如果您使用DI Container,那么框架需要将DI Framework注册到具有相关配置的全局文件中。这存在于您的顶级结构中。因此,如果没有容器,您的服务层就可以完成。

public class SampleService
{
     public IEnumerable<SampleModel> GetAllSamplesFromDb() 
     {
          using(var context = new SampleFactory().CreateSample())
               return context.GetAllSamples();
     }
}

没有依赖注入,但这需要服务层对关联哪个工厂有一些具体的了解。因此,每种方法都会创建一个工厂,这可能是多余的。

因此,您的DI Framework容器将使上述服务看起来如下所示。

public class SampleService
{
     private ISampleFactory factory;

     public SampleService(ISampleFactory factory) => this.factory = factory;

     public IEnumerable<SampleModel> GetAllSamplesFromDb()
     {
          using(var context = factory.CreateSample())
              return context.GetAllSamples();
     {
}

因此,在您的DI容器配置中,您将按照这些行注册一些内容。

builder.RegisterType<SampleFactory>().As<ISampleFactory>();
var container = builder.Build();   

现在,我对Autofac并不熟悉,因为我在Unity中使用Unity,Ninject或默认值。但你会告诉顶级解决方案中的容器,所以你可以像我显示的那样引用。

希望这会对你有所帮助。但是您需要稍微比较我的语法,以确保使用正确的Autofac语法进行注册。但结构和一般概念应该是相同的。