将DbContext注入到服务层

35

我该如何将MyDbContext注入到我的数据库服务层 MyService中?

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<MyDbContext>(options =>
    options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
    services.AddMvc();
}

MyDbContext.cs

public partial class MyDbContext : DbContext
{
    public virtual DbSet<User> User { get; set; }

    public MyDbContext(DbContextOptions<MyDbContext> options)
    :base(options)
    {
    }
}

应用程序设置文件(appsettings.json)

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=MyServer;Database=MyDatabase;user id=MyUser;password=MyPassword;"
  }
}

我的服务.cs

public class MyService
{
    public User GetUser(string username)
    {
        // Should i remove this call and get the reference from the injection somehow?
        MyDbContext db_context = new MyDbContext(optionsBuilder.Options);
        using (db_context)
        {
            var users = from u in db_context.User where u.WindowsLogin == username select u;
            if (users.Count() == 1)
            {
                return users.First();
            }
            return null;
        }
    }
}

在我的GetUser方法中,我知道应该在这里使用注入的MyDbContext,但我不太确定如何获取它。我缺少哪个重要组成部分?

1个回答

60

您不必自己包含dbcontext,ASP.NET Core的依赖注入服务将为您完成此操作。

您只需在启动类中声明您的服务和数据库上下文,并在您的服务构造函数中放置您需要的dbcontext即可:

Startup.cs(您必须选择所需的服务生存期,在这里它是一个范围化的服务,每个请求一次):

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<MyDbContext>(options =>
    options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
    services.AddMvc();
    services.AddScoped<IMyService, MyService>();
}

你的服务类:

public class MyService : IMyService
{
    private readonly MyDbContext _context;

    public MyService(MyDbContext ctx){
         _context = ctx;
    }

    public User GetUser(string username)
    {
        var users = from u in _context.User where u.WindowsLogin == username select u;
        if (users.Count() == 1)
        {
            return users.First();
        }
        return null;
    }
}

public interface IMyService
{
    User GetUser(string username);
}
在您的控制器中,您需要以相同的方式声明您需要使用的服务(或数据库上下文):
public class TestController : Controller
{
     private readonly IMyService _myService;

      public TestController(IMyService serv)
      {
           _myService = serv;
      }

      public IActionResult Test()
      {
          return _myService.MyMethod(); // No need to instanciate your service here
      }
}

关于控制器的注意事项:您无需像对待数据库上下文或服务一样将它们添加到启动类中。只需实现它们的构造函数即可。

如果您需要更多关于.NET Core依赖注入的信息,官方文档非常明确和完整:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection


NB: 在startup.cs中,AddScoped行是一个选项。 您可以选择您想要的服务生命周期。 以下是您可以选择的不同生命周期:

Transient

暂时性生命周期服务在每次请求时创建。 此生命周期最适合轻量级,无状态服务。

Scoped

Scoped生命周期服务每个请求创建一次。

Singleton

单例生命周期服务在第一次请求时创建(或当在ConfigureServices中指定实例时运行), 然后每个后续请求都将使用相同的实例。

以上摘自:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection


注意:这里不是问题,但您的GetUser数据查询对我来说有点奇怪。如果count()==1的目标是检查用户的唯一性,则好的方法是在数据库中添加一个唯一性约束。如果count()==1的目标是检查您是否有数据以避免对象空引用异常,则可以使用.FirstOrDefault(),它将为您处理此操作。您可以简化此方法:

public User GetUser(string username) => (from u in _context.User where u.WindowsLogin == username select u).FirstOrDefault();

1
你不需要在控制器中自己实例化服务。我更新了我的答案以添加这个信息。 - AdrienTorris
1
我的答案中添加了控制器相关内容。依赖注入是.NET核心架构的一个非常核心的特性,我强烈建议您阅读相关文档。 - AdrienTorris
1
你在 GetUser 方法中删除了 using() 吗?你能否更新你的代码并添加控制器的代码? - AdrienTorris
1
已经排好序了,非常感谢Adrien。移除using(_context)就解决了问题。 - Danny Cullen
1
@AdrienTorris 我遇到过几个这样的情况,只是想知道这些讨论是被删除了(是由创建聊天的用户删除的吗?还是被管理员删除了?)还是根本没有发生过。 - user132748
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接