Third Party Lib disposes context - entity-framework

Without the first two lines of the following, the Email is saved to the database just fine.
However, when I send the email using SendGrid's lib, an exception is thrown when the repository tries to save it, as the Context has been disposed.
System.ObjectDisposedException
It makes no sense to me unless the library is somehow mishandling threads or some such.
var response = await this._emailSender.SendEmailAsync(email);
email.ResponseStatusCode = (int)response.StatusCode;
this._emailRepository.Save(email);
My workaround is to create a new context:
var context = new ApplicationDbContext(this._options, this._applicationUserProvider);
context.Add(email);
context.SaveChanges();
Is there a better way to resolve this?

unless the library is somehow mishandling threads or some such.
It's an async method. The line after await may run on a different thread at a later time.
The place to look is in the calling code. If you call the method containing this line and don't wait/await the returned task, then the calling code can Dispose your DbContext while the email-sending Task is still running.

Related

Flutter: "Binding has not yet been initialized" when accessing Flutter Secure Storage from an isolate

I'm working on a large Flutter project for Android/iOS/Windows. It will have multiple processor-intensive code segments, such as server data syncing, so to prevent the UI from being slowed down we're running those on separate isolates. One of the packages we're using is Flutter Secure Storage 7.0.0 to store data between sessions. However, whenever I try to access secure storage from within the isolate, I get the following error:
Binding has not yet been initialized.
The "instance" getter on the ServicesBinding binding mixin is only available once that binding has been initialized.
Typically, this is done by calling "WidgetsFlutterBinding.ensureInitialized()" or "runApp()" (the latter calls the former). Typically this call is done in the "void main()" method. The "ensureInitialized" method is idempotent; calling it multiple times is not harmful. After calling that method, the "instance" getter will return the binding.
In a test, one can call "TestWidgetsFlutterBinding.ensureInitialized()" as the first line in the test's "main()" method to initialize the binding.
If ServicesBinding is a custom binding mixin, there must also be a custom binding class, like WidgetsFlutterBinding, but that mixes in the selected binding, and that is the class that must be constructed before using the "instance" getter.
The error suggests calling WidgetsFlutterBinding.ensureInitialized(), however I've added that to the main function of my main isolate, and attempting to call it on the second isolate throws another error about running UI actions on non-root isolates.
One of the possible workarounds I'm aware of is to use send/receive ports to only use secure storage on the main isolate, and transfer data as needed. The problem is, one of the future plans for these isolates is to keep these isolates alive when the user closes the app, to allow us continuous background data syncing. My team has little experience on that kind of isolate usage, so I'm aware I may have a misunderstanding of how that part of our app may work in the future. But until we have a better grasp of that, we're looking to make the isolates as independent from the main isolate as possible.
Here's a minimal code sample needed to reproduce this issue:
//Run this function from main isolate
void runIsolateStorageTest() async {
//Create storage and write a value
const storage = FlutterSecureStorage();
await storage.write(key: "key", value: "value");
//Create and run second isolate. Main project uses a send/recieve port system, so I've replicated that here
var recievePort = ReceivePort();
await Isolate.spawn(isolateMain, recievePort.sendPort);
//Await the returned value from second isolate and print to console
var storageValue = await recievePort.first;
print(storageValue);
}
//Function to be run on second isolate
void isolateMain(SendPort sendPort) async {
//Several S.O. posts on similar issues have recommended this line. However, it seems to have no effect on my code
DartPluginRegistrant.ensureInitialized();
//Create storage and attempt to access written key
const storage = FlutterSecureStorage();
dynamic value = "";
try {
//Error is thrown when attempting to read value.
//Other storage functions such as .write or .containsKey throw the same error when used here
value = await storage.read(key: "key");
}
on Error catch (e) {
value = e;
}
//Return the resulting value to the main isolate
sendPort.send(value);
}
EDIT: After even more searching and digging through the flutter_secure_storage code, I've been able to more accurately identify the problem; This package uses platform channels to handle the platform specific storage implementations. According to the Flutter docs, channel methods must be invoked on the main thread. There may be a way around that using platform specific implementations, but I haven't figured that out yet.

Entity Framework Core: SaveChanges() NON-async throws "A second operation was started on this context before a previous operation completed."

I am fairly new to Entity Framework and everything has been moving smoothly, until I encountered this error. My code is attempting to save children of a parent table using SaveChanges() but I get this error:
A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext.
This message seems tied to async calls and having to use await - SaveChangesAsync(). However I am NOT calling the async version of the SaveChanges() method but still get a thread error message.
My code is fairly simple:
public void CreateRange(IList<Section> sections)
{
// Add new sections and save context.
_SqlRunnerContext.sectionsDbSet.AddRange(sections);
_SqlRunnerContext.SaveChanges(); // This line throws the error.
}
The error seems to occur when there are at least two entries in the list. Which makes me think it's the way that Entity Framework is handling the save internally.
The code that calls this method creates a new repository which in turn creates a new dao and SqlContext. Given this I wouldn't think it would be something outside of this code causing the issue. I have also tried a foreach loop and save each item individually with the same error.
If anyone could give me a suggestion or idea what to try, it would be much appreciated.
Thanks again,
Adam
Instead of deleting all records then re-inserting. I change the code to simply update if it exists and add if new. This has resolved the issue.

Async Issue for DbContext used in constructor of objects created via DI

I wonder if someone can clarify when to await and when not to. Consider this code
public Task<List<User>> GetUsersForParent(int someParentId)
{
var qry = Context.Users.Where(u=>u.parent = someParentId)
.OrderBy(u=>u.Surname)
return FilterActive(qry);
}
//Actually in a generic base class, but not important (I don't think)
protected Task<List<T>> FilterActive(IQueryable<T> query) where T: BaseEntity
{
return query.Where( q=>q.Active == true ).ToListAsync();
}
Then it is used like this
var users = await DbHandler.GetUsersForParent(1);
So the calling method is awaited, but the others are not. Is this correct?
Should the method calling the ToListAsync() be awaited? (this I assume is now doing the work)
My reason for this is I am getting the DbContext is being used by a second thread dreaded exception. I am running out of places to look. My understanding is the methods are building up the whole task which is executed, but could this be messing with the dbContext?
Edit re DbContext error
Having narrowed down the potential locations for the issue, via Debug.Print and SQL Query profiling (just in case that helps anyone else) I can see one statement being profiled (the next in profile is logging the exception) and I can see two methods being run via the debug print.
One of these methods is a PermissionsManager which, when constructed, initialises itself and loads the user data. This is constructed when requested via the DI framework.
The other method is the single query on the OnGet() method for the page. It is running a single query to get an entity by ID, it is awaited correctly.
My working theory at the moment is that the Thread running the DI construction and another thread running the Page initialise are colliding.
When I made the PermissionManager just _person = new Person() // await db.users.get(userid) the issue goes away. I could replicate the issue 1 in 2 or 3 times of refresh, and with that commented I could not replicate, despite refreshing the page 30+ times.
So my real question with async / await is probably more about DI injection and is that construction running on a different thread? if so, any best practice to avoid?
So the calling method is awaited, but the others are not. Is this correct?
I generally recommend using the async and await keywords, and only return the tasks directly if the method is extremely simple.
My reason for this is I am getting the DbContext is being used by a second thread dreaded exception. I am running out of places to look. My understanding is the methods are building up the whole task which is executed, but could this be messing with the dbContext?
No. At least, the code you posted cannot cause that exception. Whether the async/await keywords are used, or whether the tasks are returned directly, the methods are asynchronous and they do not attempt to do more than one thing on the dbcontext at once.
It's possible that your problem is further up the stack. Task.WhenAll is a good thing to search for when tracking this down.
Should the method calling the ToListAsync() be awaited? (this I assume is now doing the work)
If you await the contents of either method you will be returning the result type, not Task of result type which means the execution cannot be deferred.
Your error will be coming up because you either have multiple threads interacting with the same instance of DbContext, awaited or no this would cause problems, that or you have some code calling the ToListAsync()-containing method, or another async DbContext operation without awaiting.
Writing an EF data access layer returning Task is fairly dangerous which can shoot you in the foot very easily.
Given your code structure I would recommend a couple small changes:
public async Task<List<User>> GetUsersForParent(int someParentId)
{
var qry = Context.Users.Where(u=>u.parent = someParentId)
.OrderBy(u=>u.Surname);
qry = FilterActive(qry);
return await qry.ToListAsync();
}
protected IQueryable<T> FilterActive(IQueryable<T> query) where T: BaseEntity
{
return query.Where( q=> q.Active == true );
}
Notably here I would avoid returning Task to reduce risks of improper use and potentially intermittent bugs. The base-class method for FilterActive can return IQueryable<T> to apply the filter without triggering the execution of the operation. This way FilterActive can be applied whether you want a List, a Count, or simply do an Exists check.
Overall I would recommend exploring patterns that return IQueryable<TEntity> rather than List<TEntity> etc. as the later results in either a lot of limitations for performance and flexibility, or requires a lot of boiler-plate code to handle things like:
Sorting,
Pagination,
Getting just a Count,
Performing an Exists check,
Configurable filtering,
Selectively eager loading related data, or
Projection to generate efficient queries
Doing this with methods that return List<TEntity> either results in very complex code to support some of the above considerations, has these operations applied post-execution leading to heavier queries than would otherwise be needed, or requires a lot of near-duplicate code to handle each scenario.
So the constructor thing was a red herring. It was a missing await after all, just not where expected and in code that was unchanged.
I tracked down the culprit. There was a method in the basePage which hooked into the Filter of MVC pages. It took the user and loaded their permissions, however, since this loading of user permissions was made async, this method did not get awaited (it didn't need it before as was synchronous). I moved it to one of the async events on the page life cycle and all seems happy now (with a suitable await!). So it was a missing await, but the moral of the story is any time you make a sync method async, check what the heck is actually using it!

Multi-async in Entity Framework 6?

This is my code:
var banner = context.Banners.ToListAsync()
var newsGroup = context.NewsGroups.ToListAsync()
await Task.WhenAll(banner, newsGroup);
But when i called the function from controller. It showed error
A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
Please help me solve this issue.
The exception explains clearly that there is only one asynchronous operation per context allowed at a time.
So, you either have to await them one at a time as the error message suggests:
var banner = await context.Banners.ToListAsync();
var newsGroup = await context.NewsGroups.ToListAsync();
Or you can use multiple contexts:
var banner = context1.Banners.ToListAsync();
var newsGroup = context2.NewsGroups.ToListAsync();
await Task.WhenAll(banner, newsGroup);
If you are using IoC container for your Data Provider injection, consider to use "transient" or "PerWebRequest" type for your lifecycle.
For example: https://github.com/castleproject/Windsor/blob/master/docs/lifestyles.md
If you use Unity for dependency injection with for example repository pattern you will get the following error using two or more contexts with create/update/delete:
The relationship between the two objects cannot be defined because
they are attached to different ObjectContext objects.
This can be solved using PerRequestLifetimeManager. More info here:
C# EF6 make multiple async calls to one context using Unity - Asp.Net Web Api
container.RegisterType<DbContext>(new PerRequestLifetimeManager());
container.RegisterType<ISupplierRepository, SupplierRepository>();
container.RegisterType<IContactRepository, ContactRepository>();

How to properly use Object Contexts in Entity Framework using BackgroundWorker

I am developing using Entity Framework and WPF, and I am encountering some errors and I don't know why. When saving a record (using a BackgroundWorker), I set the entities change tracker to nothing (null), attach the record to a new disposable context, save it, detach, and dispose of the context.
Saving a record fires and event in theMainViewModel of the program that the other ViewModels (including the one that is saving) need to refresh their entities to reflect changes.
Private Sub _saveRecordWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles _saveRecordWorker.DoWork
Using MyContext As New RVShippingEntities
Dim MyShipment = CType(ShipmentRecord, IEntityWithChangeTracker)
MyShipment.SetChangeTracker(Nothing)
MyContext.Attach(MyShipment)
MyContext.Detach(ShipmentRecord)
End Using
End Sub
The Refresh background worker is similar, but it has a Do While block to keep it from interfering with the save worker (which doesn't appear to be working; hence the post). When I save (and it subsequently refreshes) I get the following error:
The calling thread cannot access this object because a different thread owns it.
I thought that with theDoWhile block, it would wait (and when I step through it does) until the save thread finished, and all would be good. But it would seem that something (either the main thread or the save thread) is still doing something that is interfering.
Is there a better way of doing this? Am I doing it in a goofy kludgey fashion? Any help would be appreciated.
(Apparently Firefox recognized kludgey as a word. Interesting)
So, 3+ months and nary an exception so far in relation to Entity Framework. I am going to call this the answer.
Parent Views (in my case Company, Customer, Shipment) have a context which is passed to child Views as necessary (Addresses, Phone Nums, Email Addresses, for Company and Customer; Packages, Contents, for Shipments). Anytime a context can't save changes or what have you (db disconnection is most common cause), the context is disposed, a new one instanced, the entities are re-attached, set to modified (based on custom change tracking which I do for UI), and changes are saved.