当计时器到期时,ASP.NET Core的DbContext被释放。

3

我有一个关于我的asp.net应用程序的问题。

我的应用程序每天需要从网站上进行一次数据抓取。

我正在尝试使用定时器来触发一个方法,这个方法需要使用我的dbcontext来保存新数据。

如果我运行我的应用程序并访问调用此方法的页面,则我的方法可以正常工作,但是当定时器尝试使用它时,我的dbcontext被释放了。

我的问题是..如何配置我的asp.net应用程序,以便在后台重复使用我的dbcontext,而不依赖于Web浏览器的请求?

以下是一些代码:

Startup.cs

public void ConfigureServices(IServiceCollection services)
    {
        services.AddDbContext<FundContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("FundContext")));

        services.AddTransient<IFundDataService, FundDataService>();

        services.AddMvc();            
    }

FundContext.cs

    public class FundContext : DbContext, IFundContext
    {
    public FundContext(DbContextOptions<FundContext> options)
        : base(options)
    {

    }

    public DbSet<Fund> Funds { get; set; }
}

FundsModel.cshtml.cs

public class FundsModel : PageModel
{
    private IFundDataService fundDataService;

    public FundsModel(IFundDataService fundDataService)
    {
        this.fundDataService = fundDataService;            
    }

    public void OnGet()
    {

    }

    public List<Fund> TodaysFundList { get { return fundDataService.TodaysFundList; } }

    public List<Fund> YesterdaysFundList { get { return fundDataService.YesterdaysFundList; } }
}

FundDataService.cs

public class FundDataService : Controller, IFundDataService
{
    private FundContext fundContext;
    private List<Fund> todaysFundList;
    private List<Fund> yesterdaysFundList;

    private static Timer timer;

    public FundDataService(FundContext fundContext)
    {
        this.fundContext = fundContext;

        GetFundFromWebAndSavetoDB();
        PopulateFundLists();

        InitializeTimer();
    }

    public List<Fund> TodaysFundList { get { return todaysFundList; } }

    public List<Fund> YesterdaysFundList{ get { return yesterdaysFundList; } }

    private void InitializeTimer()
    {
        DateTime timeNow = DateTime.Now;
        DateTime scheduledTime = new DateTime(timeNow.Year, timeNow.Month, timeNow.Day, 00, 01, 00);

        if(timeNow > scheduledTime)
        {
            scheduledTime = scheduledTime.AddDays(1);
        }

        double tickTime = 10000;/*(double)(scheduledTime - DateTime.Now).TotalMilliseconds;*/
        timer = new Timer(tickTime);
        timer.Elapsed += Timer_Elapsed;
        timer.Start();
    }

    private void Timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        timer.Stop();

        GetFundFromWebAndSavetoDB();
        PopulateFundLists();

        InitializeTimer();
    }

    private void PopulateFundLists()
    {
        todaysFundList = new List<Fund>();
        yesterdaysFundList = new List<Fund>();

        foreach (var fund in fundContext.Funds)
        {
            if(fund.DateAddedToDB == DateTime.Now.Date)
            {
                todaysFundList.Add(new Fund
                {
                    ID = fund.ID,
                    Name = fund.Name,
                    RateLastDay = fund.RateLastDay,
                    RateThisYear = fund.RateThisYear,
                    LastUpdate = fund.LastUpdate,
                    DateAddedToDB = fund.DateAddedToDB
                });
            }
            if (fund.DateAddedToDB == DateTime.Now.Date.AddDays(-1))
            {
                yesterdaysFundList.Add(new Fund
                {
                    ID = fund.ID,
                    Name = fund.Name,
                    RateLastDay = fund.RateLastDay,
                    RateThisYear = fund.RateThisYear,
                    LastUpdate = fund.LastUpdate,
                    DateAddedToDB = fund.DateAddedToDB
                });
            }               
        }

        todaysFundList.Sort(delegate (Fund a, Fund b)
        {
            return b.RateThisYear.CompareTo(a.RateThisYear);
        });

        yesterdaysFundList.Sort(delegate (Fund a, Fund b)
        {
            return b.RateThisYear.CompareTo(a.RateThisYear);
        });
    }

    private void GetFundFromWebAndSavetoDB()
    {
        var rawData = WebScrapingService.Instance.WebScrapeSiteAndReturnCollection(
            "url"
            , "//tbody/tr");            

        foreach (var fund in rawData)
        {
            decimal rateLastDay;
            bool rateLastDayOK = decimal.TryParse(fund.ChildNodes[5].InnerText, out rateLastDay);

            decimal rateThisYear;
            bool rateThisYearOK = decimal.TryParse(fund.ChildNodes[11].InnerText, out rateThisYear);

            var newFund = new Fund
            {
                Name = fund.ChildNodes[3].InnerText,
                RateLastDay = rateLastDay,
                RateThisYear = rateThisYear,
                LastUpdate = Convert.ToDateTime(fund.ChildNodes[21].InnerText),
                DateAddedToDB = DateTime.Now.Date
            };

            var NumberOfFundsAddedToday = (from x in fundContext.Funds where x.DateAddedToDB == DateTime.Now.Date select x).Count();

            if(NumberOfFundsAddedToday < 5)
            {
                fundContext.Funds.Add(newFund);
                fundContext.SaveChanges();
            }                
        }            
    }
}

只是提供信息,你可以设置 timer.AutoReset = false; 来避免调用 timer.Stop(); 的麻烦。当设置为 false 时,它将仅触发一次 Elapsed 事件。 - lenny
1个回答

1
我认为最好的方法是在每个经过的时间内实例化上下文,将FundContext替换为DbContextOptions<FundContext>注入到FundDataService构造函数中,并使用using/new进行更好的控制:
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
...

    using(var context = new FundContext(_options)){
        GetFundFromWebAndSavetoDB(context);
        PopulateFundLists(context);  
    }

...
}

谢谢!这解决了我的问题。我需要多读一些关于DbContextOptions的内容,以便了解它的更多功能。 - s0ckan

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