我有一个使用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();
}
}
答案 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语法进行注册。但结构和一般概念应该是相同的。