【问题标题】:How to access the DbContext fom asp.net core service如何从 asp.net 核心服务访问 DbContext
【发布时间】:2020-09-26 15:15:03
【问题描述】:

我在使用 ASP.NET 核心和实体框架以及它的所有(其他)组件方面有点卡住了。 我正在开发一个简单的网络应用程序,您可以在其中输入一些数据并让它计算一些统计数据(基本上是 strava ultra-light)。

因此,如果我从 Visual Studio (2019) 打开默认的 blazor 应用程序,我会得到类似 Startup 的内容

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using BlazorApp1.Areas.Identity;
using BlazorApp1.Data;

namespace BlazorApp1
{
    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.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(
                    Configuration.GetConnectionString("DefaultConnection")));
            services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<ApplicationDbContext>();
            services.AddRazorPages();
            services.AddServerSideBlazor();
            services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
            services.AddSingleton<WeatherForecastService>();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseDatabaseErrorPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
            });
        }
    }
}

像这样的服务

using System;
using System.Linq;
using System.Threading.Tasks;

namespace BlazorApp1.Data
{
    public class WeatherForecastService
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
        {
            var rng = new Random();
            return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = startDate.AddDays(index),
                TemperatureC = rng.Next(-20, 55),
                Summary = Summaries[rng.Next(Summaries.Length)]
            }).ToArray());
        }
    }
}

所以我为我的数据添加了一个模型

using Microsoft.AspNetCore.Identity;
using BlazorApp1.Database.Types;
using System.ComponentModel.DataAnnotations;

namespace BlazorApp1.Database.Entity
{
    public class Activity
    {
        public string ActivityData { get; set; }
        public ActivityType ActivityType { get; set; }
        public float Distance { get; set; }

        [Key]
        public int Id { get; private set; }

        public IdentityUser User { get; set; }
    }
}

并将其添加到 ApplicationDbContext

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace BlazorApp1.Data
{
    public class ApplicationDbContext : IdentityDbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }

        public DbSet<Activity> Activities { get; set; }
    }
}

所以现在我想创建自己的类似于 WeatherForecastService 的服务,这就是我卡住的地方。

using log4net;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using BlazorApp1.Data.Model;
using BlazorApp1.Database;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BlazorApp1.Data
{
    public class LeaderBoardService
    {
        private readonly static ILog _logger = LogManager.GetLogger(typeof(LeaderBoardService));


        public Task<List<LeaderBoardItem>> GetOverallLeaderboard()
        {
            //I want to access the database context from here.
            return Task.FromResult(result);
        }
    }
}

另外,我需要将此服务添加到 Startup.ConfigureServices()。 从我目前发现的情况来看,我可以使用services.AddScoped&lt;LeaderBoardService&gt;()services.AddSingleton&lt;LeaderBoardService&gt;()services.AddTransient&lt;LeaderBoardService&gt;() 来完成此任务,而且似乎services.AddScoped&lt;LeaderBoardService&gt;() 是最好用的。

可能只有我有这个问题,但文档似乎缺少一些关于如何完成这个看似简单的任务的提示。

到目前为止,我查看了以下网站:

  1. https://docs.microsoft.com/en-us/aspnet/core/data/ef-rp/intro?view=aspnetcore-3.1&tabs=visual-studio
    • 但此示例中没有使用任何服务。
  2. https://docs.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext#using-dbcontext-with-dependency-injection
    • 虽然解决方案似乎只是将上下文作为参数添加到服务的构造函数中,但我似乎无法在任何调用期间找到如何添加此参数。
  3. https://stackoverflow.com/a/48698290
    • 我不能 100% 确定这是否是我正在寻找的,但如果结果是这样,我想就如何实现这一点提供一些提示。
  4. asp.net core access dbcontext within class
    • 这里使用了一些中间件,我想这不是我想要的。
  5. https://stackoverflow.com/a/44484724/2986756
    • 这是一个使 DbContext 瞬态的选项,也不是我想要的。
  6. https://stackoverflow.com/a/37511175
    • 这里的数据库被放置在一个单例中。虽然我可以使用它,但我认为我不应该使用它。

【问题讨论】:

  • LeaderBoardService(ApplicationDbContext context)LeaderBoardService添加一个构造函数,将上下文保存到成员变量_context中。将 LeaderBoardService 注册为 Transient 或 Scoped(检查它们之间的区别),从方法 _context.YourTable 访问上下文。
  • 另外,log4net 与Microsoft.Extensions.Logging 集成。您真的不应该直接使用ILogLogManager.GetLogger。相反,您应该添加log4net 服务并将ILogger&lt;LeaderBoardService&gt; 注入到您的构造函数中。 nuget.org/packages/…

标签: c# entity-framework asp.net-core blazor-server-side


【解决方案1】:

你的LeaderBoardService应该这样实现:

public class LeaderBoardService
{
    private readonly ApplicationDbContext dbContext;
    private readonly ILogger logger;

    public LeaderBoardService(ApplicationDbContext dbContext, ILogger<LeaderBoardService> logger)
    {
        this.dbContext = dbContext;
        this.logger = logger;
    }

    public async Task<List<LeaderBoardItem>> GetOverallLeaderboard()
    {
        return await dbContext.LeaderBoardItems.ToListAsync();
    }
}

关于您的服务生命周期,这取决于您的使用情况,但它绝不应该比其内部服务生命周期更粗略。因此,您的服务可以是scopedtransient,但不是singleton,因为您的DbContext 声明为scoped(当然,由于并发问题,您不应该将DbContext 声明为singleton)。

【讨论】:

  • 好的,成功了。但现在我想知道这是如何实现的。我猜想通过依赖注入的魔力。这是msdn 的正确文档还是在某处有更详细的描述?
  • @user2986756:是的,所有这些都是由 asp.net 核心 IoC 容器发生的。 msdn 在本主题中非常详细。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-21
  • 1970-01-01
  • 2019-12-19
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 2020-09-08
相关资源
最近更新 更多