1

Workflow:

I have a winform app with two forms, in the 1st form I query a liteDB and it manipulates an IEnumerable<T> instance inside a using block with retrieved data.

IEnumerable<student> searchResult;
using(var db = new LiteDatabase(@"C:\Temp\MyData.db"))
{
    var col = db.GetCollection<student>("students");
    col.EnsureIndex(x => x.contact.phone);
    searchResult = col.Find(x => x.contact.phone == "123456789");
}
Form2 frm2 = new Form2();
Form2.profileData = searchResult.AtElement(index);

Problem:

I then, need to send an element of searchResult<student> to 2nd form in order to show user, as you can see in the last 2 lines of above code.
But since it's inside using block, I get System.ObjectDisposedException.

Data types and exception:

studentCollection.Find(): studentCollection.Find() searchResult: enter image description here Exception: enter image description here Addition:

What I already though of as possible way is:
Override and nullify existing dispose() method then call my own implemented method after I'm done; Which is basically equals to not having a using block, except that I don't have to take care of disposing other objects in above using block, but only searchResult<student>.

P.S: I'm newbie at whole thing, appreciate the help and explanation

Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Shahaboddin
  • 136
  • 1
  • 9
  • 2
    and why not put the `Form2` into the `using`-statement as well? Apart from this your code seems pretty inefficient as you're running the query three times: once within `EnsureIndex`, once within `Find` and once for `AtElement`. You should meterialize the collection, e.g. by calling `ToList`. – MakePeaceGreatAgain Mar 25 '22 at 15:16
  • 3
    You have `var myIList = ...` inside the `using` block and `IList myIList;` outside. – Jimi Mar 25 '22 at 15:18
  • Overriding `Dispose` by doing nothing is a really bad idea. Either there **is** something to be disposed or not. In the first case you should implement `IDisposable` apropriately, in the latter not. Implementing it by an empty function is counter-intuitive. It implies: there's something to be disposed here - no, wait, it's not. – MakePeaceGreatAgain Mar 25 '22 at 15:21
  • 1
    remove var i think it will work – Negi Rox Mar 25 '22 at 15:21
  • @MakePeaceGreatAgain It's just a simplification, actually after `myIList` manipulated, there is a `foreach` for a `dataGridView` which has a buttonCell and when user clicks on each row's button, 2nd form shows up with the full profile (In `AtElement(index)`, the index is rowID of DGV) that way I have to do it inside `using` too! – Shahaboddin Mar 25 '22 at 15:22
  • @MakePeaceGreatAgain For edited comment, `EnsureIndex` makes an index of a field or do nothing if already exists according to official website example. And as for `AtElement`, I'm newbie, does linq query it again this way and `ToList` doesn't? Isn't the giving list also `IDispoable' so will be disposed? – Shahaboddin Mar 25 '22 at 15:27
  • @Jimi tnx for mentioning, fixed – Shahaboddin Mar 25 '22 at 15:28
  • 3
    `myIList = col.Find(...).ToList();` – Jimi Mar 25 '22 at 15:29
  • @NegiRox code edited and fixed, and sadly it won't bcs problem is that it's inside `using` block and will be disposed – Shahaboddin Mar 25 '22 at 15:29
  • @Jimi `ToList` gives non-IDisposable object? Otherwise it'll be disposed too as long as being inside `using` ? – Shahaboddin Mar 25 '22 at 15:32
  • 1
    I assume, from the code, that `db.GetCollection()` returns a collection of class objects (of Type `student`). Or `col.Find()` does. In this case, the elements in the collection are not disposed. You're disposing of the `LiteDatabase` object there, when the collection it generates is materialized, you won't gest *object disposed* exceptions -- If this is not the case, then write down the returned Type. – Jimi Mar 25 '22 at 15:41
  • @Jimi I apologize, since It's my first practical project I miscalled `IEnumerable<>` as `IList<>`. I looked in the code now and edited. gonna try my luck with `ToList()` rn. – Shahaboddin Mar 25 '22 at 15:49
  • 2
    @Shahaboddin: `ToList()` will *materialize* the results while the database connection is still active. So long as you've got all the data out, it should be fine. Note that a `using` statement doesn't dispose of anything other than the value in the "header" of the statement. So no, it's not going to try to dispose a `List`. – Jon Skeet Mar 25 '22 at 15:49
  • As a note, you keep on changing the code in the question, so a lot of comments become mute. Please, don't do that. Edit the question to present a *work in progress* (a sequence of tests and their different results), if necessary. – Jimi Mar 25 '22 at 15:57
  • @Jimi I understand and sorry for this behavior. it's finalized by now and screenshots added. Wont happen again – Shahaboddin Mar 25 '22 at 16:06
  • @Jimi declared as `student[] searchResult` and in order to avoid that triple query @MakePeaceGreatAgain said used `ToArray()` instead of `ToList()`, since the size is fixed this avoid `linq ElementAt()`. Please kindly write your comment as answer so I can mark it. tnx for dealing with my messy question editing and comments; learned a lot from ur kind behavior – Shahaboddin Mar 25 '22 at 16:22

2 Answers2

3

I'm not familliar with LiteDb, but I would assume it returns a proxy object for the database. So when the database is disposed, the proxy-object is no longer usable.

The simple method to avoid the problem is to add .ToList() after the .Find(...). This will convert the proxy-list to an actual List<T> in memory, and it can be used after the database is disposed. It is possible that the student objects inside the list are also proxies, and if that is the case this will fail.

If that is the case you either need to find some way to make the database return real, non-proxy objects, or extend the lifetime of the database to be longer than that of your form, for example

IList<student> myIList;
using(var db = new LiteDatabase(@"C:\Temp\MyData.db"))
{
    var col = db.GetCollection<student>("students");
    col.EnsureIndex(x => x.contact.phone);
    myIList = col.Find(x => x.contact.phone == "123456789");
    using(var frm2 = new Form2()){
       frm2.profileData = myIList.AtElement(index);
       frm2.ShowDialog(this);
    }
}

Note the usage of .ShowDialog, this will block until the second form has been closed. That is not strictly necessary, but it makes it much easier to manage the lifetime of the database.

JonasH
  • 28,608
  • 2
  • 10
  • 23
  • 1
    @JonasH, I understand you meant `.ToList()`, not `.ToString()` – Gian Paolo Mar 25 '22 at 16:13
  • 1
    @Gian Paolo Yes, Oups. – JonasH Mar 25 '22 at 18:16
  • `student` is simple **poco class** forms the LiteDB document written by myself. Form2 shows full details on a student object, like the name says `profileData` this why DB query should be done as form2 shows so user can query new student and open profiles at same. `ToList()` does the trick for proxy object, tnx – Shahaboddin Mar 25 '22 at 23:28
3

You need to access the element before exiting the using block.

using(var db = new LiteDatabase(@"C:\Temp\MyData.db"))
{
    var col = db.GetCollection<student>("students");
    col.EnsureIndex(x => x.contact.phone);
    var searchResult = col.Find(x => x.contact.phone == "123456789");
    Form2 frm2 = new Form2();
    Form2.profileData = searchResult.AtElement(index);
}
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • Thanks a lot for effort, but as said in the comments up there, it has to be used elsewhere in 2nd form. – Shahaboddin Mar 25 '22 at 23:17
  • I don't understand the problem. You assign it here. Then the second form can use it. later What am I missing? Are you saying that the item you are retrieving from the search result stops working in some way after the using block exits? Can you be more specific? – John Wu Mar 26 '22 at 00:25
  • I mean I need the process to be done (i.e: reach to the end of `using` block) so user can query another search (I should mention that like did in comments up there, that I have a `dataGridView` after the `using` block which I render `searchResult` in it, and it has a `ButtonCell` which cause the 2nd form to be shown); plus the second form shouldnt get closed after process is done, so we can have multiple student's profile open. – Shahaboddin Mar 26 '22 at 00:57
  • I am trying to understand but to me it sounds like that has nothing to do with anything. *Obtain* the instance within the using block; *use* it outside the using block, or anywhere else you want. What’s the problem? Once you have the instance assigned to a variable, you don’t need the search results anymore. – John Wu Mar 26 '22 at 01:42