我正在将Entity Framework Core for MySql与数据库优先方法结合使用。
搭建数据库后,它将生成上下文类:sakilaContext.cs(sakila是MySql中的内置数据库/方案,我可以在其中玩耍)。
我将数据库注入了Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
var connectionString = @"server=localhost;port=1123;user=root;password=xxx;database=sakila";
services.AddDbContext<sakilaContext>(options => options.UseMySql(connectionString));
}
在我的api控制器中,我这样做如下:
private sakilaContext dbCtx;
public SakilaController(sakilaContext dbContext)
{
dbCtx = dbContext;
}
想知道,
谢谢
答案 0 :(得分:2)
在这种情况下注入具体实现是错误的吗?
依赖。如果您正在编写具有不同关注点(业务逻辑,验证,授权)的多层应用程序,或者您正在编写一个5页的应用程序以显示有限数量的用户的杂项信息?
我已经为小型的(大多是只读的)应用程序完成了此操作,因为只有很小的更改,我不需要Over design and architect the solution。
如果是这样,我怎么能只注入一个接口而不是注入一个具体的实现?
当然可以,但是某个类需要一个dbcontext,可以由需要它的对象对其进行DI或创建。如果确实将数据访问抽象到单独的类/接口中,则我不建议使用DI(将上下文注入到具体的数据访问中),因为具体的类型将紧密地与EF耦合。这意味着如果您决定切换到Dapper,NHibernate或其他ORM,则无论如何都必须重写所有方法。
快速而肮脏的方式(对于小型/学习型体验,我永远不会推荐给数百万个用户系统使用)提取DbContext
的方法很简单:
public interface IUserDA
{
IQueryable<User> Users { get; }
}
public class MyDbContext : IUserDA
{
public DbSet<User> Users { get; set; }
IQueryable<User> IUserDA.Users
{
get
{
return Users; // references the DbSet
}
}
}
现在,您的MyDbContext实现了未与实体框架耦合的接口。
但是,如果我们注入一个具体的类,它是否违反了DI的目的?
取决于您的目的。您到底想通过使用DI实现什么?您实际上是要编写真实的单元测试吗?老实说,您是否看到自己正在更改或具有多个安装/租户的接口实例?
答案 1 :(得分:1)
我不建议抽象出Entity Framework,因为它本身已经是一个抽象(您可以在不了解使用类的情况下,将SQL Server的数据库提供程序从SQL Server换成PostgreSQL)。我也不建议将上下文插入控制器。
我想做的是,我建议您亲自研究一下是否合适,以利用Command Pattern,并利用Jimmy Bogard令人惊叹的AutoMapper和{{ 3}}。
为您提供一个使用这种架构设计选择时控制器外观的基本示例:
[Route("api/[controller]")]
[ApiController]
public class SomeTypeController : ControllerBase
{
private readonly IMediator mediator;
private readonly IMapper mapper;
public SomeTypeController(IMediator mediator, IMapper mapper)
{
this.mediator = mediator;
this.mapper = mapper;
}
[HttpGet]
public async Task<ActionResult<IEnumerable<SomeTypeContract>>> GetAll(CancellationToken cancellationToken)
{
var result = await mediator.Send(new GetAllSomeTypeRequest(), cancellationToken);
return Ok(mapper.Map<IEnumerable<SomeTypeContract>>(result));
}
}
我真的很喜欢这样,因为现在您的控制器充当业务逻辑的路由器。他们幸福地没有意识到引擎盖下到底发生了什么(或怎么回事)。
了解您的问题的重点:
在这种情况下注入具体的实现是错误的吗?
对此的回答有些固执己见,但许多人认为,这不仅是可以接受的选择,而且是正确的选择。在某个时间点,为了进行实际的数据库查询,您将希望了解您的DBContext。
通过抽象它,您将通过编写数百种不同的方法以各种方式查询数据,执行各种selects
和includes
来使自己感到痛苦。要么,要么您将拥有“笨重”的方法,这些方法返回的数据远远超出您的实际需要。
通过对DBContext有具体的了解(如果使用命令模式,可能在请求处理程序中),可以完全控制数据的获取方式,从而实现查询优化以及强制分离问题。
免责声明:我提出了上述库,因为它们很棒而且很有帮助,但是我知道很多人都看不起他们将它们包含在答案中。我只想明确地说,它们绝对不是执行命令模式的要求,但是如果您要采用它,为什么还要重新发明轮子呢?