3

I have a problem with my asp.net application.

My application is supposed to webscrape a site once every day.

I'm trying to fire of a method with a timer and this method I'm trying to process needs my dbcontext to save the new data.

My method works fine if i run my application and go to the page that calls this method on request, but when the timer tries to use it my dbcontext is disposed.

My question is.. How do i configure my asp.net application so i can reuse my dbcontext in the background and not dependent to a request from a web browser?

Here is some code:

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();
            }                
        }            
    }
}
1
  • just FYI, you can set timer.AutoReset = false; to save yourself the hassle calling timer.Stop();. It will fire the Elapsed event just once when set to false. Commented Mar 4, 2019 at 11:02

1 Answer 1

1

I think the best aproach is to instantiate the context each ellapsed time, inject the DbContextOptions<FundContext> instead of FundContext in the FundDataService constructor and do a using/new for better control:

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

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

...
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you! this solved my problem. I need to read a little bit more about DbContextOptions just so i know what more it does.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.