Should I trust LinqDataSource to clean up properly? - linq

Taking my first stab as using the OnSelecting method of LinqDataSource so that I can specify a more complex query, I wrote this:
protected void CategoriesDataSource_OnSelecting(object sender, LinqDataSourceSelectEventArgs e)
{
using (DataLayerDataContext db = new DataLayerDataContext())
{
e.Result = (from feed in db.Feeds
where feed.FeedName.StartsWith("Google")
select feed.MainCategory).Distinct();
}
}
The problem, of course, is that the using clause will Dispose the DataLayerDataContext. The 'solution' is to write it without it, but I'm afraid then that the context won't be disposed of in a timely fashion, that it will leave a bunch of connections open until garbage collection runs, and so on.
I'm no expert in this area, so any comments on whether this is a real problem, or am I worried for naught?

Ah, another person burned by the unfortunate side effects of deferred loading...
protected void CategoriesDataSource_OnSelecting(object sender, LinqDataSourceSelectEventArgs e)
{
using (DataLayerDataContext db = new DataLayerDataContext())
{
e.Result = (from feed in db.Feeds
where feed.FeedName.StartsWith("Google")
select feed.MainCategory).Distinct().ToList();
// ^^^^^^^^^
}
}
This will force the DataLayerDataContext to immediately run the query and create an in-memory list that's not dependent on the context or connection. That way you get your results immediately and you can dispose the context whenever you like.
The only problem (per Steven's comment) is the lazy-loaded navigation properties; even if you force the query to evaluate, if you try to query any properties that are references to other LINQ objects (or lists of LINQ objects) they will fail to load unless you specify what properties are immediately required in the DataLoadOptions of the DataContext. See the below for an example:
protected void CategoriesDataSource_OnSelecting(object sender, LinqDataSourceSelectEventArgs e)
{
using (DataLayerDataContext db = new DataLayerDataContext())
{
DataLoadOptions loadOptions = new DataLoadOptions();
loadOptions.LoadWith<Category>(c => c.SomeReference);
loadOptions.LoadWith<Category>(c => c.SomeOtherReferences);
db.LoadOptions = loadOptions;
e.Result = (from feed in db.Feeds
where feed.FeedName.StartsWith("Google")
select feed.MainCategory).Distinct().ToList();
}
}
Once you manually specify these associations, LINQ to SQL will eagerly load them into memory when the query executes, instead of leaving them to deferred loading (which will cause an exception when the properties are used after the DataContext is disposed.)

Related

EF Object entity State moves to Unchanged when attached to new Context

I have go the oddest problem, I get an object from EF and pass it back to the Business Logic for manipulation, when finished i'm trying to save the object back to the DB, when the object is passed into the following method it's EntityState is Modified but as soon as the attach line of code is run it is set to UnChanged hence the Save will not work.
Does anyone know why EF would do this?
public void Save(IEntity entity)
{
using (var context = new eDocumentEntities())
{
using (var scope = new TransactionScope())
{
if (entity.Id != 0)
context.AttachTo(entity.EntitySet, entity);
else
context.AddObject(entity.EntitySet, entity);
context.SaveChanges();
scope.Complete();
}
}
}
Since entity change tracking is encapsulated in Context, naturally the entity loses state and other tracking stuff when it gets detached from context.
Ok I found the solution to the problem here but Ii'm more interested in an explanation, why would EF do this, seems very counter productive!?
http://geekswithblogs.net/michelotti/archive/2009/11/27/attaching-modified-entities-in-ef-4.aspx

Trying to change value cells in a background worker thread

I'm trying to update a Ultragridrow cell in a background worker, but this is throwing a InvalidOperation Exception when this is called more than 1 time.
Here you have the method that starts the RunWorkerAsync.
private void RefreshGridCacheStart()
{
try
{
if (this.uGridCache.Rows.Count == 0)
{
return;
}
if(!workerThread.IsBusy)
{
workerThread.DoWork += LookUpHostnames;
workerThread.ProgressChanged += UpdateCacheHostCell;
workerThread.RunWorkerCompleted += WorkerCompleted;
workerThread.WorkerReportsProgress = true;
workerThread.RunWorkerAsync();
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message + "\n" + ex.Source + "\n" + ex.ToString());
}
}
This is the DoWork method:
private void LookUpHostnames(object sender, DoWorkEventArgs e)
{
var rowValues = new object[2];
try
{
foreach (UltraGridRow row in uGridCache.Rows)//here is were I get an invalid operation exception
{
string cellValue = row.Cells["Host"].Text;
if (Globals.cNet.isValidIP(cellValue))
{
rowValues[0] = row;
rowValues[1] = cellValue;
workerThread.ReportProgress(0, rowValues);
string resolvedHostname = Globals.cIPLookup.LookupHostFromIP(cellValue);
rowValues[1] = resolvedHostname;
workerThread.ReportProgress(0, rowValues);
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message + "\n" + ex.Source + "\n" + ex.ToString());
}
}
And this is the Report Progress method:
private void UpdateCacheHostCell(object sender, ProgressChangedEventArgs e)
{
var rowValues = e.UserState as object[];
var row = (UltraGridRow) rowValues[0];
var sMesage = (string) rowValues[1];
row.Cells["Host"].Value = sMesage;
}
You can find your answer here Different question but ultimately the same problem. Your are changing data inside a foreach loop which invalidates the enumerator.
There are 2 possible solutions I see from reading your code
Save all changes that need to be made to an List of changes and only report progress once after the foreach loop. This might not be a very good solution though since you are processing in the background. If there is other code running that could also change the data in the grid you would get the same error again.
Since you are not adding rows you could easily change the foreach to a for loop. This might also lead to an issue if code on the main thread could add, or worse, remove rows
Sounds like something must be changing the underlying row collection hence invalidating your enumerable.
If you convert your enumerable to a list using .ToList() (this will cause the enumerable to iterate and give you a new list containing the items in the original) you will be able to iterate over this new enumerable and changes in the source won't affect you.
foreach (UltraGridRow row in uGridCache.Rows.ToList())
{
....
workerThread.ReportProgress(0, rowValues);
}
You will have to be aware that if something else is changing the rows on the grid, your ReportProgress might be reporting progress of something that no longer exists in the grid, you might want to check in your ReportProgress handler whether reporting on progress for that item is still valid before doing whatever you do.
The MSDN documentation on DoWork states the following:
"You must be careful not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the BackgroundWorker events.".
You can view the full details of the DoWork method here:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.dowork.aspx
Accessing the UltraGridRows from this event is causing you to access the UltraGrid from another thread and windows forms controls aren't thread safe.
Note that this isn't limited to accessing properties of the control. If you were to set values in the data source that the UltraGrid is bound to you would have the same issue as the change notifications would then happen on the background thread and the UI would still be manipulated from the background thread.
Note that there are only a few members that are actually thread safe on windows forms controls and these are documented in the section on Thread Safety for Control on MSDN: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.aspx
Safe, Simple Multithreading in Windows Forms is a good resource for threading in windows forms even though it is older:
Part 1: http://msdn.microsoft.com/en-us/library/ms951089.aspx
Part 2: http://msdn.microsoft.com/en-us/library/ms951109.aspx
Part 3: http://msdn.microsoft.com/en-us/library/ms993020.aspx
How to: Make Thread-Safe Calls to Windows Forms Controls is also a good resource
http://msdn.microsoft.com/en-us/library/ms171728.aspx

In LINQ-SQL, wrap the DataContext is an using statement - pros cons

Can someone pitch in their opinion about pros/cons between wrapping the DataContext in an using statement or not in LINQ-SQL in terms of factors as performance, memory usage, ease of coding, right thing to do etc.
Update: In one particular application, I experienced that, without wrapping the DataContext in using block, the amount of memory usage kept on increasing as the live objects were not released for GC. As in, in below example, if I hold the reference to List of q object and access entities of q, I create an object graph that is not released for GC.
DataContext with using
using (DBDataContext db = new DBDataContext())
{
var q =
from x in db.Tables
where x.Id == someId
select x;
return q.toList();
}
DataContext without using and kept alive
DBDataContext db = new DBDataContext()
var q =
from x in db.Tables
where x.Id == someId
select x;
return q.toList();
Thanks.
A DataContext can be expensive to create, relative to other things. However if you're done with it and want connections closed ASAP, this will do that, releasing any cached results from the context as well. Remember you're creating it no matter what, in this case you're just letting the garbage collector know there's more free stuff to get rid of.
DataContext is made to be a short use object, use it, get the unit of work done, get out...that's precisely what you're doing with a using.
So the advantages:
Quicker closed connections
Free memory from the dispose (Cached objects in the content)
Downside - more code? But that shouldn't be a deterrent, you're using using properly here.
Look here at the Microsoft answer: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/2625b105-2cff-45ad-ba29-abdd763f74fe
Short version of if you need to use using/.Dispose():
The short answer; no, you don't have to, but you should...
Well, It's an IDisposable, so I guess it's not a bad idea. The folks at MSFT have said that they made DataContexts as lightweight as possible so that you may create them with reckless abandon, so you're probably not gaining much though.....
First time DataContext will get object from DB.
Next time you fire a query to get the same object (same parameters).: You’ll see query in a profiler but your object in DataContext will not be replaced with new one from DB !!
Not to mention that behind every DataContext is identity map of all objects you are asking from DB (you don’t want to keep this around).
Entire idea of DataContext is Unit Of Work with Optimistic Concurrency.
Use it for short transaction (one submit only) and dispose.
Best way to not forget dispose is using ().
I depends on the complexity of your Data Layer. If every call is a simple single query, then each call can be wrapped in the Using like in your question and that would be fine.
If, on the other hand, your Data Layer can expect multiple sequential calls from the Business Layer, the you'd wind up repeatedly creating/disposing the DataContext for each larger sequence of calls. not ideal.
What I've done is to create my Data Layer object as IDisposible. When it's created, the DataContext is created (or really, once the first call to a method is made), and when the Data Layer object disposes, it closes and disposes the DataContext.
here's what it looks like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
namespace PersonnelDL
{
public class PersonnelData : IDisposable
{
#region DataContext management
/// <summary>
/// Create common datacontext for all data routines to the DB
/// </summary>
private PersonnelDBDataContext _data = null;
private PersonnelDBDataContext Data
{
get
{
if (_data == null)
{
_data = new PersonnelDBDataContext(ConfigurationManager.ConnectionStrings["PersonnelDB"].ToString());
_data.DeferredLoadingEnabled = false; // no lazy loading
//var dlo = new DataLoadOptions(); // dataload options go here
}
return _data;
}
}
/// <summary>
/// close out data context
/// </summary>
public void Dispose()
{
if (_data != null)
_data.Dispose();
}
#endregion
#region DL methods
public Person GetPersonByID(string userid)
{
return Data.Persons.FirstOrDefault(p => p.UserID.ToUpper().Equals(userid.ToUpper()));
}
public List<Person> GetPersonsByIDlist(List<string> useridlist)
{
var ulist = useridlist.Select(u => u.ToUpper().Trim()).ToList();
return Data.Persons.Where(p => ulist.Contains(p.UserID.ToUpper())).ToList();
}
// more methods...
#endregion
}
}
In one particular application, I experienced that, without wrapping the DataContext in using block, the amount of memory usage kept on increasing as the live objects were not released for GC. As in, in below example, if I hold the reference to List<Table> object and access entities of q, I create an object graph that is not released for GC.
DBDataContext db = new DBDataContext()
var qs =
from x in db.Tables
where x.Id == someId
select x;
return qs.toList();
foreach(q in qs)
{
process(q);
// cannot dispose datacontext here as the 2nd iteration
// will throw datacontext already disposed exception
// while accessing the entity of q in process() function
//db.Dispose();
}
process(Table q)
{
// access entity of q which uses deferred execution
// if datacontext is already disposed, then datacontext
// already disposed exception is thrown
}
Given this example, I cannot dispose the datacontext because all the Table instances in list variable qs **share the same datacontext. After Dispose(), accessing the entity in process(Table q) throws a datacontext already disposed exception.
The ugly kluge, for me, was to remove all the entity references for q objects after the foreach loop. The better way is to of course use the using statement.
As far as my experience goes, I would say use the using statement.

How to rollback changes to WPF DataGrid control using LINQ-to-SQL?

I was able to set up a WPF Datagrid, display a Northwind database table via linq-to-sql, and handle the TheDataGrid_RowEditEnding event so that it saves back the the database.
However, when a CustomerID was changed, it gets an error from the database which I handle, but how do I now either (1) rollback the Datagrid control or (2) refetch the original data from the database view LINQ-to-SQL (the refetching that I do below via LINQ seems to have some kind of caching, it doesn't refresh):
<Grid DockPanel.Dock="Bottom">
<toolkit:DataGrid x:Name="TheDataGrid"
AutoGenerateColumns="True"
RowEditEnding="TheDataGrid_RowEditEnding"/>
</Grid>
private void TheDataGrid_RowEditEnding(object sender, Microsoft.Windows.Controls.DataGridRowEditEndingEventArgs e)
{
try
{
_db.SubmitChanges();
}
catch (Exception ex)
{
RefreshData();
Message.Text = ex.Message;
}
}
public void RefreshData()
{
var customers = from c in _db.Customers
select c;
TheDataGrid.ItemsSource = customers;
}
ANSWER:
Thanks Denis, I used your suggestions to get what I was after:
private void TheDataGrid_RowEditEnding(object sender, Microsoft.Windows.Controls.DataGridRowEditEndingEventArgs e)
{
try
{
_db.SubmitChanges();
}
catch (Exception ex)
{
Customer customer = e.Row.Item as Customer;
_db.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, customer);
Message.Text = ex.Message;
}
}
First off, is it a requirement to be able to change the CustomerID? If that generates an error I would imagine that is not the case, so you should have that column marked as ReadOnly (IsReadonly="True" I believe).
Second, if you need to refresh an object from the database, you need to call the Refresh() on the datacontext, passing your object as a parameter. This will pull back the current values from the database.
Third, to deal with edit and rollbacks on whole object, the DataGrid supports the IEditableObject interface, which allows an object to expose local transaction-like methods (basically it makes internal copies of the data whenever BeginEdit is called and can revert the changes if needed). This is all a manual process, but it is an interface that has existed for a long time in Windows Forms if I am not mistaken, and you should be able to find tons of information about it (or someone better versed than me here can give you some examples).
You could implement that interface on your objects (through partial classes, since your objects are LinqToSQL classes, presumably generated by the designer).
Here's an article about the DataGrid and some options you have with it. It is a bit old, but it might give you some more pointers. Here is another, shorter one.
By the way, check out the WPF toolkit again, they just released a new version (March 2009 version) which might have more support for edition/validation than it used too.

How can I reject all changes in a Linq to SQL's DataContext?

On Linq to SQL's DataContext I am able to call SubmitChanges() to submit all changes.
What I want is to somehow reject all changes in the datacontext and rollback all changes (preferable without going to the database).
Is this possible?
Why not discard the data context and simply replace it with a new instance?
public static class DataContextExtensions
{
/// <summary>
/// Discard all pending changes of current DataContext.
/// All un-submitted changes, including insert/delete/modify will lost.
/// </summary>
/// <param name="context"></param>
public static void DiscardPendingChanges(this DataContext context)
{
context.RefreshPendingChanges(RefreshMode.OverwriteCurrentValues);
ChangeSet changeSet = context.GetChangeSet();
if (changeSet != null)
{
//Undo inserts
foreach (object objToInsert in changeSet.Inserts)
{
context.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
}
//Undo deletes
foreach (object objToDelete in changeSet.Deletes)
{
context.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
}
}
}
/// <summary>
/// Refreshes all pending Delete/Update entity objects of current DataContext according to the specified mode.
/// Nothing will do on Pending Insert entity objects.
/// </summary>
/// <param name="context"></param>
/// <param name="refreshMode">A value that specifies how optimistic concurrency conflicts are handled.</param>
public static void RefreshPendingChanges(this DataContext context, RefreshMode refreshMode)
{
ChangeSet changeSet = context.GetChangeSet();
if (changeSet != null)
{
context.Refresh(refreshMode, changeSet.Deletes);
context.Refresh(refreshMode, changeSet.Updates);
}
}
}
Refer to Linq to SQL - Discard Pending Changes
In .net 3.0 use the db.GetChangeSet().Updates.Clear() for updated, db.GetChangeSet().Inserts.Clear() for new or db.GetChangeSet().Deletes.Clear() for deleted items.
In .net 3.5 and above the result of GetChangeSet() is now readonly, loop the collection in for or foreach and refresh every ChangeSet table like also macias wrote in his comment.
As Haacked said, just drop the data context.
You probably shouldn't keep the data context alive for a long time. They're designed to be used in a transactional manner (i.e. one data context per atomic work unit). If you keep a data context alive for a long time, you run a greater risk of generating a concurrency exception when you update a stale entity.
Calling Clear() on the Updates, Deletes and Inserts collection does not work.
GetOriginalEntityState() can be useful, but it only gives the IDs for foreign key relationships, not the actual entities so you're left with a detached object.
Here's an article that explains how to discard changes from the data context: http://graemehill.ca/discard-changes-in-linq-to-sql-datacontext
EDIT: Calling Refresh() will undo updates, but not deletes and inserts.
The Refresh will work, however you have to give the entities you want to reset.
For example
dataContext.Refresh(RefreshMode.OverwriteCurrentValues, someObject);
You can use the GetOriginalEntityState(..) to get the original values for the objects e.g. Customers using the old cached values.
You can also iterate through the changes e.g. updates and refresh the specific objects only and not the entire tables because the performance penalty will be high.
foreach (Customer c in MyDBContext.GetChangeSet().Updates)
{
MyDBContext.Refresh(System.Data.Linq.RefreshMode.OverwriteCurrentValues, c);
}
this will revert the changes using persisted data in the database.
Another solution is to dump the datacontext you use, using Dispose().
In any case it is a good practice to override the Insert and Remove methods in the collection of e.g. Customers you use and add e.g. an InsertOnSubmit() call. This will resolve your issue with pending Insertions and Deletions.
My application is outlook style with a icon to select an active form (ListBox). Before allowing the user to change their context they have to accept changes or discard them.
var changes = db.GetChangeSet();
if ((changes.Updates.Count > 0) || (changes.Inserts.Count > 0) || (changes.Deletes.Count > 0))
{
if (MessageBox.Show("Would you like to save changes?", "Save Changes", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
db.SubmitChanges();
} else
{
//Rollback Changes
foreach (object objToInsert in changes.Inserts)
{
db.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
}
foreach (object objToDelete in changes.Deletes)
{
db.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
}
foreach (object objToUpdate in changes.Updates)
{
db.Refresh(RefreshMode.OverwriteCurrentValues, objToUpdate);
}
CurrentForm.SetObject(null); //Application Code to Clear active form
RefreshList(); //Application Code to Refresh active list
}
}
Excellent write up on here, but here is a copy and paste of the code used.
Public Sub DiscardInsertsAndDeletes(ByVal data As DataContext)
' Get the changes
Dim changes = data.GetChangeSet()
' Delete the insertions
For Each insertion In changes.Inserts
data.GetTable(insertion.GetType).DeleteOnSubmit(insertion)
Next
' Insert the deletions
For Each deletion In changes.Deletes
data.GetTable(deletion.GetType).InsertOnSubmit(deletion)
Next
End Sub
Public Sub DiscardUpdates(ByVal data As DataContext)
' Get the changes
Dim changes = data.GetChangeSet()
' Refresh the tables with updates
Dim updatedTables As New List(Of ITable)
For Each update In changes.Updates
Dim tbl = data.GetTable(update.GetType)
' Make sure not to refresh the same table twice
If updatedTables.Contains(tbl) Then
Continue For
Else
updatedTables.Add(tbl)
data.Refresh(RefreshMode.OverwriteCurrentValues, tbl)
End If
Next
End Sub
Here is how I did it. I just followed Teddy's example above and simplified it. I have one question though, why even bother with the refresh on the DELETES?
public static bool UndoPendingChanges(this NtsSuiteDataContext dbContext)
{
if (dbContext.ChangesPending())
{
ChangeSet dbChangeSet = dbContext.GetChangeSet();
dbContext.Refresh(RefreshMode.OverwriteCurrentValues, dbChangeSet.Deletes);
dbContext.Refresh(RefreshMode.OverwriteCurrentValues, dbChangeSet.Updates);
//Undo Inserts
foreach (object objToInsert in dbChangeSet.Inserts)
{
dbContext.GetTable(objToInsert.GetType()).DeleteOnSubmit(objToInsert);
}
//Undo deletes
foreach (object objToDelete in dbChangeSet.Deletes)
{
dbContext.GetTable(objToDelete.GetType()).InsertOnSubmit(objToDelete);
}
}
return true;
}
//This works for me, 13 years later using Dotnet (Core) 6:
dbContext.ChangeTracker.Clear();

Resources