asp.net 核心 dbcontext 在计时器结束时被释放
asp.net core dbcontext is disposed when timer elapsed
我的 asp.net 应用程序有问题。
我的应用程序应该每天对网站进行一次网络抓取。
我正在尝试使用计时器触发一个方法,而我正在尝试处理的这个方法需要我的 dbcontext 来保存新数据。
如果我 运行 我的应用程序并根据请求转到调用此方法的页面,我的方法可以正常工作,但是当计时器尝试使用它时,我的 dbcontext 已被释放。
我的问题是.. 我如何配置我的 asp.net 应用程序以便我可以在后台重用我的 dbcontext 而不是依赖于来自网络浏览器的请求?
这是一些代码:
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();
}
}
}
}
我认为最好的方法是在每个经过的时间实例化上下文,在 FundDataService
构造函数中注入 DbContextOptions<FundContext>
而不是 FundContext
并为 using/new更好的控制:
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
...
using(var context = new FundContext(_options)){
GetFundFromWebAndSavetoDB(context);
PopulateFundLists(context);
}
...
}
我的 asp.net 应用程序有问题。
我的应用程序应该每天对网站进行一次网络抓取。
我正在尝试使用计时器触发一个方法,而我正在尝试处理的这个方法需要我的 dbcontext 来保存新数据。
如果我 运行 我的应用程序并根据请求转到调用此方法的页面,我的方法可以正常工作,但是当计时器尝试使用它时,我的 dbcontext 已被释放。
我的问题是.. 我如何配置我的 asp.net 应用程序以便我可以在后台重用我的 dbcontext 而不是依赖于来自网络浏览器的请求?
这是一些代码:
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();
}
}
}
}
我认为最好的方法是在每个经过的时间实例化上下文,在 FundDataService
构造函数中注入 DbContextOptions<FundContext>
而不是 FundContext
并为 using/new更好的控制:
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
...
using(var context = new FundContext(_options)){
GetFundFromWebAndSavetoDB(context);
PopulateFundLists(context);
}
...
}