简体   繁体   English

使用依赖注入访问DbContext

[英]Access to DbContext with dependency injection

I don't understand the official documentation , at the paragraph about dependency injection. 关于依赖注入的段落,我不明白官方文档

They say I can use a controller (but from here I know I don't need it because I'm using Razor pages) or I can access directly to ServiceProvider: 他们说我可以使用一个控制器(但从这里我知道我不需要它因为我正在使用Razor页面)或者我可以直接访问ServiceProvider:

using (var context = serviceProvider.GetService<BloggingContext>())
{
  // do stuff
}

but how to retrieve the reference to the ServiceProvider in a generic C# class of my project? 但是如何在我项目的通用C#类中检索对ServiceProvider的引用?

I setup the services in startup.cs: 我在startup.cs中设置服务:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<MyDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MyDbContext")));
    services.AddHangfire(options => options.UseSqlServerStorage(Configuration.GetConnectionString("MyDbContext")));
    services.AddOptions();
    services.Configure<MySettings>(options => Configuration.GetSection("MySettings").Bind(options));
    services.AddMvc().AddDataAnnotationsLocalization();
}

EDIT 编辑

To further clarify my confusion, what I'm trying to do is to add/get data from a Worker class. 为了进一步澄清我的困惑,我想要做的是从Worker类添加/获取数据。 Here I found an example how to do it: 在这里我找到了一个如何做到的例子:

using (var context = new BloggingContext())
{
    var blog = new Blog { Url = "http://sample.com" };
    context.Blogs.Add(blog);
    context.SaveChanges();

    Console.WriteLine(blog.BlogId + ": " +  blog.Url);
}

But I cannot use a constructor without the argument DbContext if I'm going to use dependency injection. 但是如果我要使用依赖注入,我不能使用没有参数DbContext的构造函数。 On the other side, if I add the argument, I have to pass the right value when I call the constructor as in the above example - and this is the initial question. 另一方面,如果我添加参数,我必须在调用构造函数时传递正确的值,如上例所示 - 这是最初的问题。

EDIT2 EDIT2

I'm going to post a "complete" example. 我将发布一个“完整”的例子。 It's hard to me to understand, but I'm trying anyway: 我很难理解,但无论如何我都在努力:

program.cs Program.cs中

using Hangfire;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Options;

namespace MyProject
{
    public class Program
    {

        public static void Main(string[] args)
        {
            IWebHost host = BuildWebHost(args);
            BackgroundJob.Enqueue<MyClass>(x => x.ImportOperatorList());
            host.Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();

    }
}

startup.cs startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Hangfire;
using MyProject.Models;

namespace MyProject
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<MyProjectContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MyProjectContext")));
            services.AddHangfire(options => options.UseSqlServerStorage(Configuration.GetConnectionString("MyProjectContext")));
            services.AddOptions();
            services.Configure<MySettings>(options => Configuration.GetSection("MySettings").Bind(options));
            services.AddMvc().AddDataAnnotationsLocalization();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Error");
            }

            app.UseStaticFiles();
            app.UseHangfireDashboard();
            app.UseHangfireServer();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller}/{action=Index}/{id?}");
            });
        }
    }
}

MyProjectContext.cs MyProjectContext.cs

using Microsoft.EntityFrameworkCore;

namespace MyProject.Models
{
    public class MyProjectContext : DbContext
    {
        public MyProjectContext(DbContextOptions<MyProjectContext> options) : base(options) { }

        public DbSet<Operator> Operators { get; set; }
    }

    public class Operator
    {
        public int Id { get; set; }
        [MaxLength(40)]
        public string Name { get; set; }
        public int Password { get; set; }
    }
}

MyClass.cs MyClass.cs

using MyProject.Models;
using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;

namespace MyProject
{
    public class MyClass
    {
        const string REGEX_OPERATORS = "^(?<Id>.{4})(?<Name>.{40})(?<Password>.{5})";
        private readonly Regex reOperators = new Regex(REGEX_OPERATORS, RegexOptions.Compiled);

        public void ImportOperatorList()
        {
            var path = @"F:\testdata.txt";
            string[] lines = File.ReadAllLines(path);

            foreach (var line in lines)
            {
                Match match = reOperators.Match(line);
                if (match.Success)
                {
                    string rawId = match.Groups["Id"].Value;
                    string rawName = match.Groups["Name"].Value;
                    string rawPassword = match.Groups["Password"].Value;

                    int Id;
                    try
                    {
                        Id = int.Parse(rawId, System.Globalization.NumberStyles.Integer);
                    }
                    catch (Exception)
                    {
                        throw;
                    }

                    string Name = rawName;

                    int Password;
                    try
                    {
                        Password = int.Parse(rawPassword, System.Globalization.NumberStyles.Integer);
                    }
                    catch (Exception)
                    {
                        throw;
                    }

                    using (var context = new MyProjectContext(/* ??? */))
                    {
                        var op = new Operator
                        {
                            Id = Id,
                            Name = Name,
                            Password = Password
                        };

                        context.Operators.Add(op);
                        Debug.WriteLine(context.SaveChanges());
                    }
                }
            }
        }
    }
}

Of course isn't complete nor compilable, because there are a lot of other files in the project (even without my own specific application). 当然不完整也不可编译,因为项目中有很多其他文件(即使没有我自己的特定应用程序)。

Building upon your self answer. 建立自己的答案。

Refactor MyClass to be dependent on abstractions and not too tightly coupled to concretions. 重构MyClass依赖于抽象而不是与结核紧密耦合。

Here is the refactored MyClass 这是重构的MyClass

public class MyClass {
    const string REGEX_OPERATORS = "^(?<Id>.{4})(?<Name>.{40})(?<Password>.{5})";
    private readonly Regex reOperators = new Regex(REGEX_OPERATORS, RegexOptions.Compiled);
    private readonly IFileSystem File;
    private readonly IProjectContext context;

    public MyClass(IFileSystem File, IProjectContext context) {
        this.File = File;
        this.context = context;
    }

    public void ImportOperatorList() {
        var path = @"F:\testdata.txt";
        var lines = File.ReadAllLines(path);
        foreach (var line in lines) {
            var match = reOperators.Match(line);
            if (match.Success) {
                string rawId = match.Groups["Id"].Value;
                string rawName = match.Groups["Name"].Value;
                string rawPassword = match.Groups["Password"].Value;
                var op = new Operator {
                    Id = int.Parse(rawId, System.Globalization.NumberStyles.Integer),
                    Name = rawName,
                    Password = int.Parse(rawPassword, System.Globalization.NumberStyles.Integer)
                };
                context.Operators.Add(op);
            }
        }
        if (lines.Length > 0)
            Debug.WriteLine(context.SaveChanges());
    }
}

With the following modifications 通过以下修改

public interface IFileSystem {
    string[] ReadAllLines(string path);
}

public class FileWrapper : IFileSystem {
    public string[] ReadAllLines(string path) {
        var lines = File.ReadAllLines(path);
        return lines;
    }
}

public interface IProjectContext : IDisposable {
    DbSet<Operator> Operators { get; set; }
    int SaveChanges();
    //...add other functionality that needs to be exposed as needed
    //eg: Database Database { get; }
    //...
}

public class MyProjectContext : DbContext, IProjectContext {
    public MyProjectContext(DbContextOptions<MyProjectContext> options) : base(options) { }

    public DbSet<Operator> Operators { get; set; }
}

You would make sure all the abstractions are registered with the service container at the composition root. 您将确保所有抽象都在组合根处注册到服务容器。

public void ConfigureServices(IServiceCollection services) {
    services.AddDbContext<MyProjectContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MyProjectContext")));
    services.AddHangfire(options => options.UseSqlServerStorage(Configuration.GetConnectionString("MyProjectContext")));
    services.AddOptions();
    services.Configure<MySettings>(options => Configuration.GetSection("MySettings").Bind(options));
    services.AddMvc().AddDataAnnotationsLocalization();

    //...adding additional services
    services.AddScoped<IProjectContext, MyProjectContext>();
    services.AddTransient<IFileSystem, FileWrapper>();
    services.AddTransient<MyClass, MyClass>();
}

Now when using the scoped service provider you can ask for your class and all the dependencies will be injected when resolving MyClass 现在,当使用作用域服务提供程序时,您可以请求您的类,并在解析MyClass时注入所有依赖项

using (var scope = host.Services.CreateScope()) {
    var services = scope.ServiceProvider;
    var myClass = services.GetRequiredService<MyClass>();
    myClass.ImportOperatorList();
}

As the above is scoped, the container will manage the disposal of any services created by the container when it goes out of scope. 由于上面是作用域,容器将管理容器超出范围时创建的任何服务的处置。

You have to pass the context argument to the function manually, dependency injection doesn't do this for you. 您必须手动将context参数传递给函数,依赖注入不会为您执行此操作。 Hence, in program.cs you might add: 因此,在program.cs中你可以添加:

using (var scope = host.Services.CreateScope())
{
    var services = scope.ServiceProvider;
    var context = services.GetRequiredService<MyProjectContext>();

    // pass context to relevant Classes/Functions, i.e.
    MyClass myClass = new MyClass();
    myClass.ImportOperatorList(context);
}

In MyClass.cs now you can directly use that variable: 在MyClass.cs中,您现在可以直接使用该变量:

public void ImportOperatorList(MyProjectContext context)
{
    // ...
    context.Operators.Add(op);
    context.SaveChanges();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM