带有DbContext的服务器端计时器

时间:2018-04-10 07:35:47

标签: c# asp.net entity-framework asp.net-core dbcontext

我想在每隔60秒运行一次方法()的服务器上运行一个计时器

现在我已经完成了 - 使用下面的代码

public class Alarm
{

    public Alarm(AppDbContext _db)
    {
        db = _db;
    }


    private static Timer aTimer;
    private AppDbContext db;

    public void StartTimerEvent()
    {
        // Create a timer and set a 60 second interval.
        aTimer = new Timer();
        aTimer.Interval = 60000;

        // Hook up the Elapsed event for the timer. 
        aTimer.Elapsed += (source, e) => CheckDBEvents(source, e);
        // Have the timer fire repeated events (true is the default)
        aTimer.AutoReset = true;
        // Start the timer
        aTimer.Enabled = true;

    }


    private void CheckDBEvents(Object source, ElapsedEventArgs e)
    {

        //get data from db with matching queries
        List<Grocery> DataList = db.Grocery.Where(G => G.basic).Select(G => new Grocery
        {
            Id = G.Id,
            Timeout = G.Timeout
        }).ToList();

    }

}

方法()是CheckDBEvents(),它的作用是访问dbcontext实例并查找一些数据以保存到本地常量变量Called DataList

问题:每次我尝试将上下文(Dbcontext)实例 - 在控制器或任何其他类中 - 传递给CheckDBEvents()方法时,都会释放上下文-DbContext Disposed Exception。 来电者

var t = new Alarm(db);
t.StartTimerEvent();

我的轮胎: -

  • 使警报成为静态类:

现在,如果我能做到这将是惊人的...但不能在DbContext上操作,因为你不能在静态类中调用DbContext上的实例,你必须从调用它的人那里传递它,这导致对于同样的问题:db已经处理并且没有通过

        public static void StartTimerEvent(AppDbContext db)
    {
    .....

     // Hook up the Elapsed event for the timer. 
     aTimer.Elapsed += (source, e) => CheckDBEvents(source, e, db 
     //DbContext is Disposed here and
     //don't get passed'
     );

也是不断上课,Dbcontext与我读过的内容并不相处。

长篇短篇   - 我想将dbContext的实例保留在另一个类而不是控制器中。 没有被处置。

如果有人知道如何做到这一点或者有源代码到服务器端计时器或类似的东西请评论,我已经被困2天

2 个答案:

答案 0 :(得分:0)

最后我经过多次测试后发现了问题, 我需要利用asp.net具有的强Debedenty注入,并将该类添加为服务,我还使用IHostedService作为我的服务类的接口,这里是服务FinalTest的一个示例(重命名为Alarm to FinalTest)

internal class FinalTest : IHostedService, IDisposable
{

    private Timer aTimer;
    public static List<Grocery> List;
    private AppDbContext db;
    // variable  to test that timer really works
    public static int test2;


    public FinalTest( AppDbContext _db )
    {
        db = _db;

    }

    //This method runs at the start of the application once only as FinalTest was set as Singleton in services
    public Task StartAsync(CancellationToken cancellationToken)
    {
        test2 = 1;
        aTimer =new Timer(CheckDBEvents, null , TimeSpan.Zero , TimeSpan.FromSeconds(10) );
        return Task.CompletedTask;
    }

    //Method runs every TimeSpan.FromSeconds(10)
    private void CheckDBEvents(object state)
    {

        var users = from u in db.Grocery where u.basic == true select u;
        List = users.ToList();
        //increase test2 To see changes in the real world
        test2++;
    }


    //--------shutdown operations---------//
    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

    public void Dispose()
    {
        aTimer?.Dispose();
    }


}

现在,如果我在服务services.AddSingleton(FinalTest)中注入了这个,我会得到一个范围异常,因为在SingleTon服务中使用AppDbContext是Scoped服务并不好并且有效地将AppDbContext提升为Singleton,这会导致问题未来所以我不得不为AppDbContext创建另一个构造函数

    public class AppDbContext : DbContext
{
    //Scoped constructor
    public AppDbContext(DbContextOptions<AppDbContext>  options) : base(options)
    {

    }

    //Singletone constructor
    public AppDbContext(DbContextOptions<AppDbContext> options,string connection)
    {
        connectionString = connection;
    }
    private string connectionString;
    //this is an override to OnConfiguring that's 
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {

        if (connectionString != null)
        {
            var config = connectionString;
            optionsBuilder.UseSqlServer(config);
        }

        base.OnConfiguring(optionsBuilder);
    }
    //DbSet

    public DbSet<Grocery> Grocery { get; set; }

}

最后将AppDbContextFinalTest添加到服务

 var connection = @"Server=(localdb)\mssqllocaldb;Database=FridgeServer.AspNetCore.NewDb;Trusted_Connection=True;ConnectRetryCount=0";
 services.AddDbContext<AppDbContext>(
            options => {
                //options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))
                options.UseSqlServer(connection);
                //var config = Configuration["Data:DefaultConnection:ConnectionString"];

                //options.UseInMemoryDatabase("Grocery")
            }
        );

  services.AddSingleton<IHostedService,FinalTest>(s => new FinalTest(new AppDbContext(null, connection) ));

现在这是我的经验,阅读所有关于依赖注入和Ioc以及其他编程概念和模式的所有内容都是一个有趣的体验 如果有人面对其中的一些问题甚至想了解更多关于asp.net的信息,这里有一些帮助,第一个是最重要的

http://deviq.com/category/principles/ http://deviq.com/dependency-inversion-principle/ https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1 Use DbContext in ASP .Net Singleton Injected Class https://blogs.msdn.microsoft.com/cesardelatorre/2017/11/18/implementing-background-tasks-in-microservices-with-ihostedservice-and-the-backgroundservice-class-net-core-2-x/

感谢@KienChu告诉我有关IHostedService的事情,我希望这可以帮助某人

答案 1 :(得分:0)

我会厌倦将 DbContext 直接注入托管服务。

我的建议是注入 IServiceScopeFactory。每次闹钟响起时,创建一个范围,并为该 CheckDBEvents 操作解析 DbContext。

甚至您指向其他问题的链接也建议您这样做。通过这种方式,您可以更优雅地管理 DbContext 生命周期。

像这样:

var scope = serviceFactory.CreateScope();
await using var dbContext = scope.ServiceProvider.GetService<MyDbContext>();