首页 文章

使用依赖注入访问DbContext

提问于
浏览
3

关于依赖注入的段落,我不明白官方的documentation .

他们说我可以使用控制器(但是我知道我不会使用Razor页面)或者我可以直接访问ServiceProvider:

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

但是如何在我项目的通用C#类中检索对ServiceProvider的引用?

我在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();
}

编辑

为了进一步澄清我的困惑,我想要做的是从Worker类添加/获取数据 . Here我找到了一个如何做到的例子:

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);
}

但是如果我要使用依赖注入,我不能使用没有参数DbContext的构造函数 . 另一方面,如果我添加参数,我必须在调用构造函数时传递正确的值,如上例所示 - 这是最初的问题 .

EDIT2

我将发布一个“完整”的例子 . 我很难理解,但无论如何我都在努力:

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

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

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

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());
                    }
                }
            }
        }
    }
}

当然不完整也不可编译,因为项目中有很多其他文件(即使没有我自己的特定应用程序) .

2 回答

  • 3

    Build 自己的答案 .

    重构 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());
        }
    }
    

    通过以下修改

    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; }
    }
    

    您将确保所有抽象都在组合根处注册到服务容器 .

    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>();
    }
    

    现在,当使用作用域服务提供程序时,您可以请求您的类,并在解析 MyClass 时注入所有依赖项

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

    由于上面是作用域,容器将管理容器超出范围时创建的任何服务的处置 .

  • 1

    您必须手动将context参数传递给函数,依赖注入不会为您执行此操作 . 因此,在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);
    }
    

    在MyClass.cs中,您现在可以直接使用该变量:

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

相关问题