VB.NET How to "Refresh" data DbContext in winform - vb.net

I have a DataDridview filled with objects queried with my DbContext.
I show some basic informations about my objects in this form.
I would like to query the most recent infos from the BD every time I select a different object in the DataDridview. I also want to be able to modify these objects.
I'm already able to do that. To modify an object, for example, I will do these steps:
-create a new DbContext
-get the object from my Datagridview using .Selectedrows(0).DatabountItem
-with that object's id I will query the (most recent) record in the DB using my new DbContext
-assign the old object (modified) properties to the new ones, one by one
-.SaveChanges on my new Dbcontext.
But there has to a better way, right? :/
If I understand correctly, by doing it this way I end up with a ton of unused Dbcontext and I doubt it's best practices.
But whenever I .Dispose my context, all EF navigation properties are broken and I get exceptions popping all over the place... So the ideal solution seems to me to just, refresh the data in a unique DbContext that I would use for the entire form. But is it even possible?
Maybe the solutions is obvious but I just can't see it.
If I'm unclear, please let me know I'll do my best to reformulate.
Any help would be appreciated, thanks!
** Edit **
In the end, here's the solution that worked best for me:
I do create a new DbContext for every logical operation, then immediatly .Dispose it...
Except for when I show informations about a specific row in my datagridview.
Instead, I create a new Context and leave it open. I will .Dispose only when the datagridview.SelectionChanged event fires.
The reason for not Disposing this context immediatly is: If a user saves his changes, but in the meantime someone else also saved changes on the same record, the (not-synced) context will hit a concurrency issue.. and I can let the user know about it, instead of overriding that row, which would be bad.
If I need these navigations properties from EF elsewhere, I can simply do eager loding by .Include("MyOtherTable") everything I need.( because navigation properties stop working when a context is Disposed)

I would use only one context in this case. You must just pay attention for accesing context from another threads - it can result in transaction errors ("New transaction is not allowed because there are other threads running in the session")
If you use DataGridView with DataSource from EF Context, all you have to do is just reload entities.
I wrote extension method in my context:
public partial class MyContext : testEntities1
{
public void Refresh(IEnumerable entities)
{
var ctx = ((IObjectContextAdapter)this).ObjectContext;
ctx.Refresh(System.Data.Entity.Core.Objects.RefreshMode.StoreWins, entities);
}
}
it is much faster than entry.Reload();
F.E.:
List<MyEntities> items = new List<MyEntities>();
foreach(DataGridViewRow row in dataGridView1.SelectedRows)
{
items.Add((MyEntities)row.DataBoundItem);
}
context.Refresh(items);

Related

Permanently disable saving of a model in Yii

Is it possible to create an AR model in yii in such a way as to disable the save() function? I am using the models to display data that is entered into the DB from another source and will never need to update it.
UPDATE:
So which methods do I override, which methods in the base class actually write something to DB?
Simply override save and have it throw an appropriate exception. For example:
public function save(bool $runValidation=true, array $attributes=NULL)
{
throw new \LogicException("This kind of model does not support saving.");
}
This way it's also clear to anyone that mistakenly calls the method what is going on.
Don't forget to also override saveAttributes since the two methods are unfortunately completely independent.

StaleStateException when saving a entity deleted by other session (concurrency)

I´m using NHibernate to data access Layer.
I have an entity in memory previously loaded, and Im making changes in order to save after on database. The problem comes when my application is running in some machines at the same time, and other user has deleted from database the same object that I have in memory and I want to save. When I try save the changes or delete this entity, a StaleStateException in fired.
I check if an entity exists on database calling session.Get<T> of this way (it´s get a null succesfully):
using (var session = NHibernateSessionHelper.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var entity = session.Get<T>(persistObject.Id);
return entity == null ? false : true;
}
}
The problem comes when I can´t differentiate between when the entity has been deleted by other session/user (therefore my entity in memory is obsolete) or the entity has been recently created and is able to save.
I think that the unique solution is implement a mechanism to check if the entity has already been saved or loaded from database, in order to discard the entity or save when proceed.
Is there a way to check this behaviour by using nhibernate? Im tried with session.Refresh() and session.Get<T> but I still without know if the object is new and ready to save or obsolete.
Help very appreciated.
The situaltion you have is a tipical error handling one. Because a user have dicided to delete an object in your database and an other user want save a change to the same object you can't say what the right state should be. The user is the one who should dicide what to do with situation. You can make your error handling smarter by giving him some chooses. For situations where you have multiple users which write to the same object you should also implement a meganism like optimistic locking to prevent an other user to override a change which he did not see. If you have many of these situation you should think about redesign your db/object structure to edit less data at once or rethink the work/processes your users do with the system.

Simultaneous LINQ data retrieval problem

I came across a very strange problem which never happened to me before.
In the login code:
var sqlLogin = db.LoginRetrieve(loginID, archived).SingleOrDefault();
//(db is the linq data context)
--problem:
If two users login at the same time, this line of code will throw an exception which is "The required column 'UserLoginID' does not exist in the results."
But if a single user logs in or two users don't click the button at the same time, it will have no exception.
Is there anyone can share some lights on this? Thanks in advance.
Han
I suspect that your DataContext is shared between requests.
Don't do that.
You should create a separate DataContext for each request.
Otherwise, you'll get nasty threading issues like this one. (DataContext isn't thread-safe)
In general, you should be very careful when sharing objects between requests (eg, statics or Application / Session state).
Unless you know specifically otherwise, you should assume that the object is not thread-safe and cannot be shared.

NHibernate, Databinding to DataGridView, Lazy Loading, and Session managment - need advice

My main application form (WinForms) has a DataGridView, that uses DataBinding and Fluent NHibernate to display data from a SQLite database. This form is open for the entire time the application is running.
For performance reasons, I set the convention DefaultLazy.Always() for all DB access.
So far, the only way I've found to make this work is to keep a Session (let's call it MainSession) open all the time for the main form, so NHibernate can lazy load new data as the user navigates with the grid.
Another part of the application can run in the background, and Save to the DB. Currently, (after considerable struggle), my approach is to call MainSession.Disconnect(), create a disposable Session for each Save, and MainSession.Reconnect() after finishing the Save. Otherwise SQLite will throw "The database file is locked" exceptions.
This seems to be working well so far, but past experience has made me nervous about keeping a session open for a long time (I ran into performance problems when I tried to use a single session for both Saves and Loads - the cache filled up, and bogged down everything - see Commit is VERY slow in my NHibernate / SQLite project).
So, my question - is this a good approach, or am I looking at problems down the road?
If it's a bad approach, what are the alternatives? I've considered opening and closing my main session whenever the user navigates with the grid, but it's not obvious to me how I would do that - hook every event from the grid that could possibly cause a lazy load?
I have the nagging feeling that trying to manage my own sessions this way is fundamentally the wrong approach, but it's not obvious what the right one is.
Edit
It's been more than a year since I asked this question...and it turns out that keeping a main session open for the lifetime of the app has indeed led to performance problems.
There seem to be a lot more NH users on SO these days - anyone want to suggest a better approach?
yeah it's me again. ;-)
stumbling upon your new question reminds me of the following: Did you understand the principle of lazy loading or are you mistaking lazy loading for pagination? NHibernate also provides functionality for that.
If you just want to display some defined properties within your grid that are of course within the object graph i think you should retrieve the whole data at once using 'fetched joins'. If the rowcount of the data is to high you can think about pagination, as far as i know its also possible using DataGridView and Binding.
Lazy Loading results in multiple database calls - in your case i'ld think at least one per row. This seems not to be the best performing solution.
If instead you are using paging with FetchType.Join you can get rid of the long running session and all your problems should be solved. So how about that?
I had a project where there was a main grid for selection.
I had a class which paged through the set and i called session.Clear() everytime when i got a new page.
class MyList : IList<Data>
{
private int _pagesize = 50;
private int _session; // from ctor
private int _firstresult = int.MinValue;
private IList<Data> cached;
public Data this[int index]
get
{
if (!index.Between(_firstresult, _firstresult + cached.Count))
{
_firstresult = index;
GetData();
}
if (!index.Between(_firstresult, _firstresult + cached.Count))
throw new IndexOutOfRangeException();
return cachedData[index - _firstresult];
}
void GetData()
{
Session.Clear();
cached = Session.QueryOver<Data>()
.Skip(_firstresult)
.Take(_pagesize)
.List();
}
}
If you need Databinding maybe implement IBindingList

Unloading a data reference in Entity Framework

Currently I am struggling with a Entity Framework issue. I have a wcf service that sits on top of the ef framework, and allows queries to the framework. At some point the user is able to request a file from the framework. The files are referenced by solution entries, so when you request a file from a solution, the reference is loaded to gain access to the file store.
That all works fine, but from that point onward, whenever you do another query that returns that solution entry, the whole file gets attached to the return result. I need some way of detaching or unloading the reference, such that the result entries will only contain an unloaded reference to the file store again.
I have tried to create a new context and query that context to retrieve information from, but when I do that, the entity in the original context is also changed.
I have tried to detach the entity from the original context and then query from the new context. That does not work either.
I have found one way of doing this. For all the non file-download queries, I detach the result entity, and send that over the wire. I am not sure if that is the best way to go about it though.
I hope someone might be able to provide some insight, thanks for the effort.
The issue you are experiencing is probably do to Change Tracking, which is on by default.
Possible Solution:
Disable Change Tracking with MergeOption.NoTracking
using (MyEntities _context = new MyEntities())
{
_context.Widgets.MergeOption = MergeOption.NoTracking;
return _context.Widgets.ToList();
}
This article may help to point you in the right direction on how to handle this issue if the solution above does not work.
I struggled with a similar issue recently. The problem was the result of the context maintaining a reference to the object I was using (obviously). Every time I made a change to an object of the same type, even when obtained with a new context (so I thought), the object was being changed.
With help from one of my peers, we determined the context was hanging around due to the way I was registering it with my IoC container (lifestyle per web request). When I changed the lifestyle to transient (which definitively provided a new instance) then changes to objects of the same type were unaffected.
Hope this helps.