EFCore, keeping entity after disposing of scope/context - c#

Im having troubles figuring out how to keep an entity after disposing of a scope:
public async Task<Camera> GetCamera(string cameraGuid)
{
DirectCamera directCamera;
using (var scope = _scopeFactory.CreateScope())
{
var _accessor = scope.ServiceProvider.GetService<CamerasAccessor>();
//CameraAccessor is just a DbContext, registered with `services.AddDbContext<CamerasAccessor>();`
directCamera = _accessor.Cameras.Single(can => can.Id == cameraGuid);
return directCamera;
}
}
This ofc won't work cause the accessor/dbcontext will be disposed, with all proxies included, before the value will be handled.
My question is: "How can I keep/get entity without it being wrapped in a proxy, that wont be disposed as soon as the scope will be disposed?"
My first thought was to use AsNoTracking() but that doesnt give you a non proxy object.
Any help is appreciated :)

Related

LinqToDB DataConnection not disposed upon exiting using block

This issue has been perplexing me for quite a while. I'm trying to dispose of a connection to a database, but it keeps leaking no matter how I try to dispose of it. Worse yet, it's supposed to dispose automatically.
Long story short, I have a HostedService that starts with the following code:
public async Task StartAsync(CancellationToken cancellationToken)
{
using (var scope = _serviceProvider.CreateScope())
{
_logger.LogDebug($"Инициализация операторов");
using (var operatorRepo = scope.ServiceProvider.GetService<IOperatorRepository>()) {
var availableOperators = operatorRepo.GetAllOperatorsWithSkills();
_logger.LogDebug($"Инициализовано {availableOperators.Count()} операторов");
foreach (OperatorRow opRow in availableOperators)
{
OperatorMeta opMeta = new(opRow.Idr, opRow.Name, opRow.Number, opRow.OperatorSkills.Select(skill => (skill.SkillId, skill.Experience)));
if (!TryAddOperator(opMeta))
throw new ArgumentException($"Оператор с id {opRow.Idr} уже был добавлен!");
}
if (Convert.ToBoolean(_configuration.GetSection("isOperatorTesting")?.Value))
{
_globalOperatorPool.TryAdd(
-1,
new CampaignOperator(
new OperatorMeta(-1, "Test operator", _configuration.GetSection("testOperatorNumber").Value, Enumerable.Empty<(int, int)>()),
OperatorState.Unassigned)
);
}
}
}
}
I have tried to wrap the insides of the using in a try finally blocks just in case but I honestly don't see how that would be an issue because the using isn't inside of one. Still, connection doesn't close.
The underlying class of IOperatorRepository extends from a chain of two classes which extend from LinqToDB.DataConnection. It's the only IDisposable in the chain.
Honestly, I'm stumped. Any help would be appreciated.
Edit: I feel like I should specify that the connection class does indeed return ObjectDisposedException, but the connection stays open in the database, and more are made constantly as if they're not being reused.

Why does DI return the same DbContext for two different scopes?

I'm trying to add a test to an ASP.NET Core project where an object is created in one scope and then read in another scope. This is to simulate a user creating an object in one POST request and then reading it in another GET Request. However, I'm having trouble properly simulating this scenario.
I have this in my test code
SomeDbContext firstContext;
bool isSame;
using (var scope = someServiceProvider.CreateScope()) {
firstContext = someServiceProvider.GetService<SomeDbContext>();
}
using (var scope = someServiceProvider.CreateScope()) {
var secondContext = someServiceProvider.GetService<SomeDbContext>();
isSame = firstContext == secondContext; //should be false, right?
}
I expect isSame to have a value of false when the code above executes but it's actually true. Why is that? SomeDbContext has a lifetime of scoped when registering it with AddDbContext() so it should be destroyed when its scope is disposed and recreated in the second scope.
Your test is incorrect. Although you are creating two separate scopes, you're not actually using them. Here's a working version:
SomeDbContext firstContext;
bool isSame;
using (var scope = someServiceProvider.CreateScope()) {
firstContext = scope.ServiceProvider.GetService<SomeDbContext>();
}
using (var scope = someServiceProvider.CreateScope()) {
var secondContext = scope.ServiceProvider.GetService<SomeDbContext>();
isSame = firstContext == secondContext; //should be false, right?
}
Note how scope.ServiceProvider is used instead of someServiceProvider when resolving dependencies.
The closest thing I can find in the docs is Call services from main. Although the example shows the Main method, it does also demonstrate how the IServiceProvider that gets used comes from the scope itself.

Two nested Entity Framework contexts, sharing a transaction

I have code that looks like the example below. There's an explicit transaction involved because of some database tomfoolery that needs to be done via a SP, and a save changes in the middle of it all. (Exception handling, rollbacks, etc.. omitted):
void OuterMethod(MyDatbase context)
{
using(var dbTrans = context.Database.BeginTransaction())
{
// some stuff, the save puts the data where the SP can see it
Stuff(context);
context.SaveChanges();
// now some SP stuff
context.Database.ExecuteSqlCommand(#"spFoo", params);
// more stuff
MoreStuff(context);
AlmostUnrelatedCode(context);
context.SaveChanges();
dbTrans.Commit();
}
}
Right now the method AlmostUnrelatedCode() -- which is only marginally related to the process above -- needs a nice, fast, disposable read-only context 99% of the time. I have a factory that will serve me up the right kind of context when I need it. The 1% of the time it's called from the middle of that block above.
MyDatabase localReadOnlyContext;
void AlmostUnrelatedCode(MyDatabase context)
{
if ( context.Database.CurrentTransaction != null )
{
// Must use the context passed or everything deadlocks :(
localReadOnlyContext = context;
disposeContextLater = false;
}
else
{
// I just want to do this all the time
localReadOnlyContext = _contextFactory.CreateReadOptimized();
disposeContextLater = true;
}
// Do many, many things with my read-optimized context...
// The Dispose() on the class will check for disposeContextLater
}
What I'd like to do is to get rid of that transaction check, and in fact not need to pass the outer context at all if I can help it.
What I've tried:
Just ignoring what's going on in the outer transaction and using the context I generate all the time. Problem: deadlocks.
Trying to get the outermost transaction into the EF context I create with the _contextFactory. Problem: EF context constructors don't allow you to pass an existing transaction; also Database.CurrentTransaction has no setter.
Pulling the whole transaction out into a TransactionScope that wraps everything up. Problem: the method OuterMethod passes in the context, and I don't have control of the caller.
What I can't try:
Dirty reads/nolock. AlmostUnrelatedCode() needs the data as written so far.
I'd rather not:
Just keep using the outer context while inside of AlmostUnrelatedCode. AlmostUnrelatedCode deals with a lot of data trees and that context gets fat and unhappy really fast. It pollutes its context with crap really fast, and I'd rather just dispose of it when I'm done.
you can prevent the deadlocks by using one connection for multiple contexts.
example
var efConnectionString = ConfigurationManager.ConnectionStrings["SomeEntities"].ConnectionString;
// note EntityConnection, not SqlConnection
using (var conn = new EntityConnection(efConnectionString)) {
// important to prevent escalation
await conn.OpenAsync();
using (var c1 = new SomeEntities(conn, contextOwnsConnection: false)) {
//Use some stored procedures etc.
count1 = await c1.SomeEntity1.CountAsync();
}
using (var c2 = new SomeEntities(conn, contextOwnsConnection: false)) {
//Use some stored procedures etc.
count2 = await c2.SomeEntity21.CountAsync();
}
}
in your case just get the connection from the context and reuse it
context.Database.Connection
Can't you separate things done in AlmostUnrelatedCode like this:
void AlmostUnrelatedCode()
{
var context = _contextFactory.CreateReadOptimized();
AlmostUnrelatedCode(context);
context.Dispose();
}
void AlmostUnrelatedCode(MyDatabase context)
{
// Do many, many things with context...
}
Now you can call AlmostUnrelatedCode(with param) from your OuterMethod. And maybe there is even more to be separated. Consider SOLID.

Should I keep an instance of DbContext in a separate thread that performs periodic job

I have a class Worker which sends emails periodically,I start in Global.asax.cs on App_start()
public static class Worker
{
public static void Start()
{
ThreadPool.QueueUserWorkItem(o => Work());
}
public static void Work()
{
var r = new DbContext();
var m = new MailSender(new SmtpServerConfig());
while (true)
{
Thread.Sleep(600000);
try
{
var d = DateTime.Now.AddMinutes(-10);
var ns = r.Set<Notification>().Where(o => o.SendEmail && !o.IsRead && o.Date < d);
foreach (var n in ns)
{
m.SendEmailAsync("noreply#example.com", n.Email, NotifyMailTitle(n) + " - forums", NotifyMailBody(n));
n.SendEmail = false;
}
r.SaveChanges();
}
catch (Exception ex)
{
ex.Raize();
}
}
}
}
So I keep this dbcontext alive for the entire lifetime of the application is this a good practice ?
DbContext is a very light-weight object.
It doesn't matter whether your DbContext stays alive or you instantiate it just before making the call because the actual DB Connection only opens when you SubmitChanges or Enumerate the query (in that case it is closed on end of enumeration).
In your specific case. It doesn't matter at all.
Read Linq DataContext and Dispose for details on this.
I would wrap it in a using statement inside of Work and let the database connection pool do it's thing:
using (DbContext r = new DbContext())
{
//working
}
NOTE: I am not 100% sure how DbContext handles the db connections, I am assuming it opens one.
It is not good practice to keep a database connection 'alive' for the lifetime of an application. You should use a connection when needed and close it via the API(using statement will take care of that for you). The database connection pool will actually open and close connections based on connection demands.
I agree with #rick schott that you should instantiate the DbContext when you need to use it rather than keep it around for the lifetime of the application. For more information, see Working with Objects (Entity Framework 4.1), especially the section on Lifetime:
When working with long-running context consider the following:
As you load more objects and their references into memory, the
memory consumption of the context may increase rapidly. This may cause
performance issues.
If an exception causes the context to be in an unrecoverable state,
the whole application may terminate.

Linq2Sql: Manage DataContext

In the following code doesn't work as
public void Foo()
{
CompanyDataContext db = new CompanyDataContext();
Client client = (select c from db.Clients ....).Single();
Bar(client);
}
public void Bar(Client client)
{
CompanyDataContext db = new CompanyDataContext();
db.Client.Attach(client);
client.SomeValue = "foo";
db.SubmitChanges();
}
This doens't work, I get error msg. "An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported."
How do you work with DataContexts throughout an application so you don't need to pass around a reference?
What
They really mean it with 'This is not supported.'. Attaching to an object fetched from another data context is not implemented.
There are a number of workarounds to the problem, the recommended way is by serializing objects, however this is not easy nor a clean approach.
The most simple approach I found is to use a readonly DataContext for fetching objects like this:
MyDataContext dataContext = new MyDataContext()
{
DeferredLoadingEnabled = false,
ObjectTrackingEnabled = false
};
The objects obtained from this context can be attached to another context but only applies to some scenarios.
The PLINQO framework generates detach for all entities making it easy to detach and reattach objects without receiving that error.
public void Foo()
{
CompanyDataContext db = new CompanyDataContext();
Client client = (select c from db.Clients ....).Single();
// makes it possible to call detach here
client.Detach();
Bar(client);
}
public void Bar(Client client)
{
CompanyDataContext db = new CompanyDataContext();
db.Client.Attach(client);
client.SomeValue = "foo";
db.SubmitChanges();
}
Here is the article that describing how the detach was implemented.
http://www.codeproject.com/KB/linq/linq-to-sql-detach.aspx
Yep. That's how it works.
You have tagged this asp.net so I guess it's a web app. Maybe you want one datacontext per request?
http://blogs.vertigo.com/personal/keithc/Blog/archive/2007/06/28/linq-to-sql-and-the-quote-request-scoped-datacontext-quote-pattern.aspx
(P.S. It's a lot harder in WinForms!)
I've created data access classes that encapsulate all the communication with Linq2Sql.
These classes have their own datacontext that they use on their objects.
public class ClientDataLogic
{
private DataContext _db = new DataContext();
public Client GetClient(int id)
{
return _db.Clients.SingleOrDefault(c => c.Id == id);
}
public void SaveClient(Client c)
{
if (ChangeSetOnlyIncludesClient(c))
_db.SubmitChanges();
}
}
Ofcourse you will need to keep this object instantiated as long as you need the objects.
Checking if only the rigth object has been changed is altso somewhat bothersom, you could make methods like
void ChangeClientValue(int clientId, int value);
but that can become a lot of code.
Attaching and detaching is a somewhat missing feature from Linq2Sql, if you need to use that a lot, you sould probably use Linq2Entities.
I took a look at this and found that it appears to work fine as long as the original DataContext has been disposed.
Try wrapping the DataContext with using() and make sure your changes occur after you've attached to the second DataContext? It worked for me..
public static void CreateEntity()
{
User user = null;
using (DataClassesDataContext dc = new DataClassesDataContext())
{
user = (from u in dc.Users
select u).FirstOrDefault();
}
UpdateObject(user);
}
public static void UpdateObject(User user)
{
using (DataClassesDataContext dc = new DataClassesDataContext())
{
dc.Users.Attach(user);
user.LastName = "Test B";
dc.SubmitChanges();
}
}
You need to handle object versioning.
An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.
So, if there's no timestamp member or other 'versioning' mechanism provided there's no way for LINQ to determine whether that data has changed - hence the error you are seeing.
I resolved this issue by adding a timestamp column to my tables but there are other ways around it. Rick Strahl has written some decent articles about exactly this issue.
Also, see this and this for a bit more info.

Categories

Resources