简体   繁体   中英

How to use ApplicationDbContext in a Class / Separate Thread ASP.Net Core

I am attempting to have a separate thread start a looping process to collect and import data from an api call and insert it into a local db. I attempt to create the database connection the same way I do in my controllers but it keeps giving me various errors.

I have tried multiple ways. For the setup below it gives me this error:

System.ObjectDisposedException: 'Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.'

The setup is for testing purposes.

public class HomeController : Controller
{
    public ApplicationDbContext db;

    public HomeController(ApplicationDbContext context)
    {
        db = context;
    }
    public IActionResult Index()
    {
        // This executes the code and inserts the test row no problem.
        BinanceData binanceData = new BinanceData();
        binanceData.Symbol = "Test";
        binanceData.RealTime = DateTime.Now;
        db.BinanceData.Add(binanceData);
        db.SaveChanges();
        // End

        // Where I create the new thread and start the process
        Thread doThis = new Thread(delegate ()
        {
            DataCollection dataCollection = new DataCollection(db);
            dataCollection.InsertData(DateTime.Now);
        });
        doThis.Start();
        // End

        return View();
    }

This is the class where I attempt to create a connection (or pass existing connection) and to begin the loop and collect/insert data.

    public class DataCollection
{
    public ApplicationDbContext db;

    public DataCollection(ApplicationDbContext context)
    {
        db = context;
    }
    public void InsertData(DateTime nextTime)
    {
        List<string> tokens = new List<string> { "ETHBTC", "LTCBTC", "BNBBTC", "NEOBTC", "GASBTC", "BTCUSDT", "MCOBTC", "WTCBTC", "LRCBTC", "QTUMBTC", "YOYOBTC", "OMGBTC", "ZRXBTC", "STRATBTC", "SNGLSBTC", "BQXBTC", "KNCBTC", "FUNBTC", "SNMBTC", "IOTABTC", "LINKBTC", "XVGBTC", "SALTBTC", "MDABTC", "MTLBTC", "SUBBTC", "EOSBTC", "SNTBTC", "ETCBTC", "MTHBTC", "ENGBTC", "DNTBTC", "ZECBTC", "BNTBTC", "ASTBTC", "DASHBTC", "OAXBTC", "BTGBTC", "EVXBTC", "REQBTC", "VIBBTC", "TRXBTC", "POWRBTC", "ARKBTC", "XRPBTC", "MODBTC", "ENJBTC", "STORJBTC", "KMDBTC", "RCNBTC", "NULSBTC", "RDNBTC", "XMRBTC", "DLTBTC", "AMBBTC", "BATBTC", "BCPTBTC", "ARNBTC", "GVTBTC", "CDTBTC", "GXSBTC", "POEBTC", "QSPBTC", "BTSBTC", "XZCBTC", "LSKBTC", "TNTBTC", "FUELBTC", "MANABTC", "BCDBTC", "DGDBTC", "ADXBTC", "ADABTC", "PPTBTC", "CMTBTC", "XLMBTC", "CNDBTC", "LENDBTC", "WABIBTC", "TNBBTC", "WAVESBTC", "GTOBTC", "ICXBTC", "OSTBTC", "ELFBTC", "AIONBTC", "NEBLBTC", "BRDBTC", "EDOBTC", "WINGSBTC", "NAVBTC", "LUNBTC", "APPCBTC", "VIBEBTC", "RLCBTC", "INSBTC", "PIVXBTC", "IOSTBTC", "STEEMBTC", "NANOBTC", "VIABTC", "BLZBTC", "AEBTC", "NCASHBTC", "POABTC", "ZILBTC", "ONTBTC", "STORMBTC", "XEMBTC", "WANBTC", "WPRBTC", "QLCBTC", "SYSBTC", "GRSBTC", "CLOAKBTC", "GNTBTC", "LOOMBTC", "REPBTC", "TUSDBTC", "ZENBTC", "SKYBTC", "CVCBTC", "THETABTC", "IOTXBTC", "QKCBTC", "AGIBTC", "NXSBTC", "DATABTC", "SCBTC", "NPXSBTC", "KEYBTC", "NASBTC", "MFTBTC", "DENTBTC", "ARDRBTC", "HOTBTC", "VETBTC", "DOCKBTC", "POLYBTC", "PHXBTC", "HCBTC", "GOBTC", "PAXBTC", "RVNBTC", "DCRBTC", "USDCBTC", "MITHBTC", "BCHABCBTC" };

        foreach (string token in tokens)
        {
            BinanceData binance = new BinanceData();
            using (var client = new BinanceClient())
            {
                var data = client.GetKlines(token, Binance.Net.Objects.KlineInterval.OneMinute, null, null, 2);
                var kline = data.Data[0];
                binance.Symbol = token;
                binance.Close = kline.Close;
                binance.CloseTime = kline.CloseTime;
                binance.High = kline.High;
                binance.Low = kline.Low;
                binance.Open = kline.Open;
                binance.OpenTime = kline.OpenTime;
                binance.QuoteAssetVolume = kline.QuoteAssetVolume;
                binance.TakerBuyBaseAssetVolume = kline.TakerBuyBaseAssetVolume;
                binance.TakerBuyQuoteAssetVolume = kline.TakerBuyQuoteAssetVolume;
                binance.TradeCount = kline.TradeCount;
                binance.Volume = kline.Volume;
                binance.RealTime = DateTime.Now;
            }
        db.BinanceData.Add(binance);
        db.SaveChanges();
        }
        CountUntilNextMin(nextTime);
    }

    public void CountUntilNextMin(DateTime nextTime)
    {
        while (DateTime.Now < nextTime)
        {
            System.Threading.Thread.Sleep(10000);
        }
        DateTime passTime = DateTime.Now.AddMinutes(1);
        InsertData(passTime);
    }
}

Here is my ApplicationDbContext Class

    public class ApplicationDbContext : IdentityDbContext
{
    public DbSet<ApplicationUser> ApplicationUsers { get; set; }
    public DbSet<BinanceData> BinanceData { get; set; }
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public ApplicationDbContext()
        : base()
    {
    }
}

And my StartUp.cs ConfigureServices Method

        public void ConfigureServices(IServiceCollection services)
    {
        services.Configure<CookiePolicyOptions>(options =>
        {
            // This lambda determines whether user consent for non-essential cookies is needed for a given request.
            options.CheckConsentNeeded = context => true;
            options.MinimumSameSitePolicy = SameSiteMode.None;
        });

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));
        services.AddIdentity<IdentityUser, IdentityRole>()
            .AddDefaultUI()
            .AddDefaultTokenProviders()
            .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

By default your DI container is scoping the DbContext to the request. If you want to use one in a long-running background process, just create it directly. EG:

Thread doThis = new Thread(delegate ()
{
    using (var db = new ApplicationDbContext())
    {
      DataCollection dataCollection = new DataCollection();
      dataCollection.InsertData(DateTime.Now);
    }
});

The DbContext will need to be configured, and the simplest way to do that is to just override OnConfiguring to connect to your database. Minimally, like this:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
      optionsBuilder.UseSqlServer("Server=YourServer;Database=YourDatabase;Trusted_Connection=True;MultipleActiveResultSets=true");
}

Although you'll probably want to read the connection string from your configuration.

i using this by calling CreateScope try it

public static async Task InitializeDatabaseAsync(IServiceProvider serviceProvider, IHostingEnvironment env)
{
    var result = false;

    using (var scope1 = serviceProvider.CreateScope())
    using (var db1 = scope1.ServiceProvider.GetService<MainContext>())
    {
         result = await db1.Database.EnsureCreatedAsync();
        if (result)
        {

            InsertTestData(serviceProvider, env);
        }
    }
}

then

using (var scope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
using (var db = scope.ServiceProvider.GetService<MainContext>())
{
    existingData = db.Set<TEntity>().ToList();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM