Server side timer with DbContext - c#

I want to make a timer running in the server that runs a method() every 60 seconds
now i have done -sort of- that using the code below
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();
}
}
the method() is CheckDBEvents() and what it does is it accesses the dbcontext instance and looks for some data to save to to a local constant variable Called DataList
problem : every time i try passing the context (Dbcontext) instance --in the controller or any other class-- to the CheckDBEvents() method, the context is disposed -DbContext Disposed Exception.
the caller
var t = new Alarm(db);
t.StartTimerEvent();
My tires :-
making alarm a static class :
Now if i can do that It would be amazing ... but can not operate on DbContext since you can't call instance on DbContext in a static class you have to pass it from who ever calls it, which leads to the same problem :db is Disposed and don't get passed
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'
);
also constant classes and Dbcontext don't get along very well from what i have read.
Long Story Short
-I want to keep instance of dbContext in another class rather than controllers.
without it being disposed.
if anyone have and idea what how to do this or have a source code to server side timer or anything like that please comment, I have been stuck for 2 days

finally I have found the problem after many test,
I needed to take advantage of the strong Dependency Injection that asp.net has, and add the class as a service. Also, I used IHostedService as an interface for my service class, here is an example of the service FinalTest (renamed 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();
}
}
Now if I injected this in the service services.AddSingleton(FinalTest) as it is I would get a scoped exception because using AppDbContext which is a scoped service in Singleton service is not good and effectively promote AppDbContext to Singleton which is goint to cause problems in the future, so I had to create another constructor for 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; }
}
finally the adding both AppDbContext and FinalTest to services
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) ));
Now that's my experience and it was all in all a fun experience reading all about Dependency injection and Ioc and other concept and pattern of programing
if anyone face some of those problem or even want to know more about asp.net, here are some that helped ,the first one is the most important
http://deviq.com/category/principles/
http://deviq.com/dependency-inversion-principle/
https://learn.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/
thanks to #KienChu for telling me about IHostedService , I Hope this helps someone

I would be weary of injecting DbContext directy into the hosted service.
My advice would be to inject IServiceScopeFactory. And every time your alarm goes off, create a scope, and resolve the DbContext just for that CheckDBEvents action.
Even your links to other questions recommend doing so. This way you manage the DbContext lifetime a bit more gracefully.
Something like this:
var scope = serviceFactory.CreateScope();
await using var dbContext = scope.ServiceProvider.GetService<MyDbContext>();

Related

EF Core: Create an object by passing an injected DbContext object as parameter

I have created a .Net Core MVC project and understand that how the dependency injection works for our MVC controller as shown below, but same like I wanted to create an object for my own class by calling the same injected interface/class as a parameter.
public class ShiftsController : BaseController
{
ShardingDbContext _dbContext;
public ShiftsController(ShardingDbContext ShardingDbContext) : base(ShardingDbContext)
{
_dbContext = ShardingDbContext;
ViewBag.Menu = BuildMenu();
}
I have injected the DbContext into my Startup.cs file as below,
//Entity Framework Core
services.AddDbContext<ShardingDbContext>(options => options.UseSqlServer(ConnectionString),
ServiceLifetime.Transient);
The ShiftsController is a C#-MVC controller and the DbContext is working perfectly when I run my app and go to Shift's page in my application, but when I try like below-given code, it's not working and gives an error. So I don't know how to pass the registered class's object while creating an object by using "new" keyword.
public class JobScheduler
{
ShardingDbContext _dbContext;
public JobScheduler(ShardingDbContext ShardingDbContext)
{
_dbContext = ShardingDbContext;
}...
This is my own class and tried to create an object for the class JobScheduler as shown below.
JobScheduler jobs = new JobScheduler();
So now I don't know how to pass the EF core's DbContext's object to the constructor JobScheduler, the DI works fine for the controller but not for a normal class. Can anyone help with this and I am eagerly waiting to understand this logic as well?.
Register your JobScheculer like this:
services.AddSingleton<JobScheduler>();
then use your dbContext like this:
public class JobScheduler
{
private readonly IServiceProvider provider;
public JobScheduler(IServiceProvider provider)
{
}...
public (or private etc) DoYourJob()
{
using (var scope = provider.CreateScope())
{
var dbContext = scope.GetService<ShardingDbContext>();
//use it here
}
}
At the end of the ConfigureServices method of the Startup.cs class, and I did not change anything in the JobSchedulerclass and passing the DbContext object from the service provider as shown below, thanks to everyone who tried to help with this question.
public void ConfigureServices(IServiceCollection services)
{
...
...
JobScheduler job = new
JobScheduler(services.BuildServiceProvider().CreateScope().ServiceProvider
.GetService<ShardingDbContext>());
job.Start();
}
You are right: Your DI works fine but your ShardingDbContext is not passed into your JobScheduler because you are not using DI to instanciate JobScheduler. Whenever you are explicitly creating an object instance using the new keyowrd you are not using DI.
You have two options:
Wherever you are calling new JobScheduler() let DI inject you a ShardingDbContext through the constructor and pass it to JobScheduler like so new JobScheduler(shardingDbContext)
Register JobScheduler to the dependency injection as well and let DI build up the whole chain so you don't need to call new JobScheduler() but rather get a JobScheduler injected directly wherever you need it
Edit
As requested here is the example for a timed job using a short lived DB context:
public class TimedBackgroundService : IHostedService, IDisposable
{
private readonly Timer timer;
private readonly IServiceProvider serviceProvider;
public TimedBackgroundService(IServiceProvider serviceProvider)
{
timer = new Timer(async state => await ExecuteAsync());
this.serviceProvider = serviceProvider;
}
public Task StartAsync(CancellationToken stoppingToken)
{
timer.Change(0, TimeSpan.FromMinutes(30));
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken stoppingToken)
{
timer.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose() => timer.Dispose();
private async Task ExecuteAsync()
{
try
{
using var scope = serviceProvider.CreateScope();
var job = scope.ServiceProvider.GetRequiredService<MyJob>();
await job.Execute();
}
catch (Exception exception)
{
// log error here
return;
}
}
}
The MyJob class wil look something like this:
public class MyJob
{
private readonly ShardingDbContext dbContext;
public MyJob(ShardingDbContext dbContext)
{
this.dbContext = dbContext;
}
public Task Execute()
{
// Your logic goes here
}
}
Then you register your classes in the startup like so:
services
.AddHostedService<TimedBackgroundService>()
.AddScoped<MyJob>();
Now you have a job which runs every 30 minutes and uses a short lived db context.

Consumer Producer- Producer thread never executes assigned function

I have .NET Core Web API solution. In each call, I need to perform some database operations. The issue is at a time multiple db connections get opened & close. So to avoid it, I want to implement Queue of objects to be sent to database and then want a separate thread to perform db operation.
I've tried some code as below. But here, Consumer thread never executes assigned function. There is no separate thread for Producer, I am simply feeding queue with object.
What modifications I should do? Need some guidance as I'm new to Threading stuff.
public static class BlockingQueue
{
public static Queue<WebServiceLogModel> queue;
static BlockingQueue()
{
queue = new Queue<WebServiceLogModel>();
}
public static object Dequeue()
{
lock (queue)
{
while (queue.Count == 0)
{
Monitor.Wait(queue);
}
return queue.Dequeue();
}
}
public static void Enqueue(WebServiceLogModel webServiceLog)
{
lock (queue)
{
queue.Enqueue(webServiceLog);
Monitor.Pulse(queue);
}
}
public static void ConsumerThread(IConfiguration configuration)
{
WebServiceLogModel webServiceLog = (WebServiceLogModel)Dequeue();
webServiceLog.SaveWebServiceLog(configuration);
}
public static void ProducerThread(WebServiceLogModel webServiceLog)
{
Enqueue(webServiceLog);
Thread.Sleep(100);
}
}
I've created and started thread in StartUp.cs:
public Startup(IConfiguration configuration)
{
Thread t = new Thread(() => BlockingQueue.ConsumerThread(configuration));
t.Start();
}
In Controller, I've written code to feed the queue:
[HttpGet]
[Route("abc")]
public IActionResult GetData()
{
BlockingQueue.ProducerThread(logModel);
return StatusCode(HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound, ApplicationConstants.Message.NoBatchHistoryInfo);
}
First of all, try to avoid static classes and methods. Use pattern singleton in that case (and if you really need this).
Second, try to avoid lock, Monitor - those concurrency primitives significantly lower your performance.
In such situation, you can use BlockingCollection<> as 'Adam G' mentioned above, or you can develop your own solution.
public class Service : IDisposable
{
private readonly BlockingCollection<WebServiceLogModel> _packets =
new BlockingCollection<WebServiceLogModel>();
private Task _task;
private volatile bool _active;
private static readonly TimeSpan WaitTimeout = TimeSpan.FromSeconds(1);
public Service()
{
_active = true;
_task = ExecTaskInternal();
}
public void Enqueue(WebServiceLogModel model)
{
_packets.Add(model);
}
public void Dispose()
{
_active = false;
}
private async Task ExecTaskInternal()
{
while (_active)
{
if (_packets.TryTake(out WebServiceLogModel model))
{
// TODO: whatever you need
}
else
{
await Task.Delay(WaitTimeout);
}
}
}
}
public class MyController : Controller
{
[HttpGet]
[Route("abc")]
public IActionResult GetData([FromServices] Service service)
{
// receive model form somewhere
WebServiceLogModel model = FetchModel();
// enqueue model
service.Enqueue(model);
// TODO: return what you need
}
}
And in Startup:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Service>();
// TODO: other init staffs
}
}
You even can add Start/Stop methods to the service instead of implementing IDisposable and start your service in the startup class in the method Configure(IApplicationBuilder app).
I think your consumer thread is executed just once if there is something in the queue and then immediately returns. If you want to have a thread doing work in background, which is started just once, it should never return and should catch all exceptions. Your thread from BlockingQueue.ConsumerThread is invoked once in Stratup and returns.
Also please be aware that doing such solution is not safe. ASP.NET doesn't guarantee background threads to be running if there are no requests coming in. Your application pool can recycle (and by default it recycles after 20 minutes of inactivity or every 27 hours), so there is a chance that your background code won't be executed for some queue items.
Also, while it doesn't solve all issues, I would suggest using https://www.hangfire.io/ to do background tasks in ASP.NET server. It has persistence layer, can retry jobs and has simple API's. In your request handler you can push new jobs to Hangfire and then have just 1 job processor thread.

Autofac Lifetime scopes with external application

It'll probably take a few more questions to understand this subject quite well.
I've read a lot of documentations, yet I can't figure it out completely.
Say for example :
Singleton1 = resolved in lifetimeScope1
Singleton1 handles messages coming from Tcp/Ip.
DataService handles messages coming from web controllers, and it handles messages from Singleton1 (meaning - messages from Tcp/Ip).
In DataService there is a component registered as PerLifetimeScope (DbContext).
The things I can't understand is:
How will DataService know if it currently handles Http request (and should use instance 1 of DbContext) or Tcp/Ip (and use instance 2 of DbContext)?
My final goal is to configure Autofac to create me a new lifetime scope for each Tcp/Ip request I get as well. Just like it automatically does with Http requests!
Note:
If it'll be easier to understand with code, I'll put it, I just thought it could complicate things.
If anything I said was stupid or incorrect please fix me. Having hard time learning this.
Thanks!
Edit:
Adding code to explain my problem better:
public class Singleton1 : ISingleton1
{
private IDataService _dataService;
public Singleton1(IDataService dataService)
{
_dataService = dataService;
}
public void HandleExternalAddItemMessage(AddItemMessage msg)
{
_dataService.AddItem(msg.Item);
}
}
public interface IUnitOfWork : IDisposable
{
void Commit();
}
public EFUnitOfWork : IUnitOfWork
{
private DbContext _context;
public EFUnitOfWork(DbContext context)
{
_context = context;
}
public void Commit()
{
_context.SaveChanges();
}
}
public class DataService
{
private Func<Owned<IUnitOfWork>> _unitOfWorkFactory;
public (Func<Owned<IUnitOfWork>> unitOfWorkFactory)
{
_unitOfWorkFactory = unitOfWorkFactory;
}
// This method is called from both controllers and external Tcp/Ip calls. How do I do it - how do I set the context that is in the unit of work???
public void AddItem(Item item)
{
using(unitOfWork = _unitOfWorkFactory())
{
...
}
}
}
Startup code:
{
_container.RegisterType<IDataService, DataService>().SingleInstance();
_container.RegisterType<Singleton1, ISingleton1>().SingleInstance();
_container.RegisterType<EFUnitOfWork, IUnitOfWork>().PerDepnendecny();
_container.RegisterType<DbContext, MyDbContext>().InstancePerLifetimeScope();
}
This is a great article by Nicholas Blumhardt touching on many issues surrounding object lifetime including common pitfalls by developers attempting to own the object disposal in Autofac.
http://nblumhardt.com/2011/01/an-autofac-lifetime-primer/
I think what you're attempting to do is probably overly complicated and the model can probably be simplified however it is possible to register a new instance of an object by manually creating your own LifetimeScope.
public class Singleton1 : ISingleton1
{
private readonly ILifetimeScope _lifetimeScope;
public Singleton1(ILifetimeScope lifetimeScope)
{
_lifetimeScope = lifetimeScope;
}
public void HandleExternalAddItemMessage(AddItemMessage msg)
{
using(var scope = _lifetimeScope.BeginLifetimeScope())
{
var dataService = scope.Resolve<IDataService>();
dataService.AddItem(msg.Item);
}
}
}
As the article will explain you're now brewing up a nice batch of "scope soup".
Pay close attention to Nested lifetime scopes and Scope Sharing. Your usage of singletons is likely to trip you up.

How to use Ninject in a multi-threaded Windows service to get new instances of a dependency (DbContext) on every tick?

I have inherited a Windows service where all the dependencies are created when the service starts and are injected in the transient scope.
We are having a number of problems with this service, not least we have a DbContext which lives for the whole time the service is running, and different instances of it are injected each time.
I would like to refactor so that each worker thread gets it’s own DbContext injected which will live for just the duration of each tick.
I have looked at the custom scope. It looks fine for a single threaded app, but not multi-threaded. I also considered InThreadScope. Whilst that would give each thread it’s own instance, they are singletons as far as the thread is concerned so it does not fulfil the per tick requirement.
My current thinking is to use the named scope extension and to inject a scope factory which I can use to create a new scope on every tick.
Is this the way to go? Any suggestions, tips or alternatives would be appreciated.
UPDATE
Due to a time constraint we ended up using the named scope, but it wasn't as clean as #BatteryBackupUnit's solution. There were some dependencies further down the graph which needed a DbContext and we had to inject the scope factory again to get it. Using #BatteryBackupUnit's solution we could have reused the same instance from the ThreadLocal storage instead.
Regarding Named Scope: Consider that when you are creating a DbContext from the same thread but from an object (p.Ex. factory) which was created before the scope was created, it won't work. Either it will fail because there is no scope, or it will inject another instance of DbContext because there is a different scope.
If you don't do this, then a scope like named scope or call scope can work for you.
We are doing the following instead:
When a DbContext is requested, we check a ThreadLocal
(http://msdn.microsoft.com/de-de/library/dd642243%28v=vs.110%29.aspx) whether there is already one. In case there is, we use that one. Otherwise, we create a new one and assign it to the ThreadLocal<DbContext>.Value.
Once all operations are done, we release the DbContext and reset the ThreadLocal<DbContext>.Value.
See this (simplified, not perfect) code for an example:
public interface IUnitOfWork
{
IUnitOfWorkScope Start();
}
internal class UnitOfWork : IUnitOfWork
{
public static readonly ThreadLocal<IUnitOfWorkScope> LocalUnitOfWork = new ThreadLocal<IUnitOfWorkScope>();
private readonly IResolutionRoot resolutionRoot;
public UnitOfWork(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public IUnitOfWorkScope Start()
{
if (LocalUnitOfWork.Value == null)
{
LocalUnitOfWork.Value = this.resolutionRoot.Get<IUnitOfWorkScope>();
}
return LocalUnitOfWork.Value;
}
}
public interface IUnitOfWorkScope : IDisposable
{
Guid Id { get; }
}
public class UnitOfWorkScope : IUnitOfWorkScope
{
public UnitOfWorkScope()
{
this.Id = Guid.NewGuid();
}
public Guid Id { get; private set; }
public void Dispose()
{
UnitOfWork.LocalUnitOfWork.Value = null;
}
}
public class UnitOfWorkIntegrationTest : IDisposable
{
private readonly IKernel kernel;
public UnitOfWorkIntegrationTest()
{
this.kernel = new StandardKernel();
this.kernel.Bind<IUnitOfWork>().To<UnitOfWork>();
this.kernel.Bind<IUnitOfWorkScope>().To<UnitOfWorkScope>();
}
[Fact]
public void MustCreateNewScopeWhenOldOneWasDisposed()
{
Guid scopeId1;
using (IUnitOfWorkScope scope = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId1 = scope.Id;
}
Guid scopeId2;
using (IUnitOfWorkScope scope = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId2 = scope.Id;
}
scopeId1.Should().NotBe(scopeId2);
}
[Fact]
public void NestedScope_MustReuseSameScope()
{
Guid scopeId1;
Guid scopeId2;
using (IUnitOfWorkScope scope1 = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId1 = scope1.Id;
using (IUnitOfWorkScope scope2 = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId2 = scope2.Id;
}
}
scopeId1.Should().Be(scopeId2);
}
[Fact]
public void MultipleThreads_MustCreateNewScopePerThread()
{
var unitOfWork = this.kernel.Get<IUnitOfWork>();
Guid scopeId1;
Guid scopeId2 = Guid.Empty;
using (IUnitOfWorkScope scope1 = unitOfWork.Start())
{
scopeId1 = scope1.Id;
Task otherThread = Task.Factory.StartNew(() =>
{
using (IUnitOfWorkScope scope2 = unitOfWork.Start())
{
scopeId2 = scope2.Id;
}
},
TaskCreationOptions.LongRunning);
if (!otherThread.Wait(TimeSpan.FromSeconds(5)))
{
throw new TimeoutException();
}
}
scopeId2.Should().NotBeEmpty();
scopeId1.Should().NotBe(scopeId2);
}
public void Dispose()
{
this.kernel.Dispose();
}
}
Note: i'm using nuget packages: ninject, xUnit.Net, Fluent Assertions
Also note, that you can replace the IUnitOfWork.Start with a ToProvider<IUnitOfWorkScope>() binding. Of course you need to implement the corresponding logic in the provider.
A proper unit-of-work scope, implemented in Ninject.Extensions.UnitOfWork, solves this problem.
Setup:
_kernel.Bind<IService>().To<Service>().InUnitOfWorkScope();
Usage:
using(UnitOfWorkScope.Create()){
// resolves, async/await, manual TPL ops, etc
}

Good way to manage DataContext in ASP.NET? SqlException: Server failed to resume the transaction

We create one Linq2Sql DataContext for every request using the following class:
public static class DataContextManager
{
private const string HTTPCONTEXT_KEY = "DataContextManagerKey";
private static CompanyDataContext _staticContext; //when there's no HttpContext (in test/debug situations).
public static CompanyDataContext Context
{
get
{
if (_Context == null)
{
_Context = NewContext();
}
return _Context;
}
}
private static CompanyDataContext _Context
{
get
{
return (CompanyDataContext)(HttpContext.Current != null ? HttpContext.Current.Items[HTTPCONTEXT_KEY] : _staticContext);
}
set
{
if (HttpContext.Current != null)
{
HttpContext.Current.Items[HTTPCONTEXT_KEY] = value;
}
else
{
DataContextManager._staticContext = value;
}
}
}
public static void Dispose()
{
CompanyDataContext context = _Context;
if (context != null)
{
if (Config.Instance.TestMode) context.Log.WriteLine("--- DISPOSING DATACONTEXT ---");
context.Dispose();
_Context = null;
}
}
public static CompanyDataContext NewContext()
{
CompanyDataContext db = new CompanyDataContext();
db.CommandTimeout = Config.SQL_COMMAND_TIMEOUT;
if (Config.Instance.TestMode)
{
db.Log = new ConsoleTextWriter();
db.Log.WriteLine("--- CREATING NEW DATACONTEXT ---");
}
return db;
}
}
And in Global.asax:
protected void Application_EndRequest(Object sender, EventArgs e)
{
DataContextManager.Dispose();
}
The reason why I'm asking is that we're suddenly getting random "SqlException: Server failed to resume the transaction" exceptions once or twice every day with code that used to work perfectly. After the exception we get a lot of other exceptions until we restart the web application. Anyone seen this behaviour before?
We're running ASP .Net 2.0 with SQL server 2005 on IIS 6.
UPDATE:
Just so no one else does the same horrible mistake as we did:
It turned out that some worker threads also used the DataContext but with no HttpContext they of course got the _staticContext (a feature in DataContextManager only to be used when testing). We rewrote the code in the worker threads to make sure one DataContext per thread and disposing it when done. And so far everything has worked for 2 weeks :)
This is a bad pattern. First you should never have a static data context it implements IDisposable so one thread could try to use the context while another is disposing of it plus many other potential problems. One data context per http request is no good either, data contexts are designed to be used for a single transaction then disposed of. You get problems if you retrieve update/insert/delete and retrieve using the same context, the second retrieve does not reflect the changes of the update/insert/delete. Remove the static context and just have the Context property return a new context every time. You could still dispose of the all at the end of a request by sticking them all in a List property and iterating through it.

Categories

Resources