Hi,
I create an object, bind it to some controls, a user clicks 'Save' and the object gets added to the DLinq DataContext. A call to SubmitChanges() throws the exception. Is this a known issue Or am I just doing something wrong
Thanks,
Rana
--
System.InvalidOperationException was caught
Message="There is already an open DataReader associated with this Command which must be closed first."
Source="System.Data"
StackTrace:
at System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)
at System.Data.SqlClient.SqlInternalConnection.BeginSqlTransaction(IsolationLevel iso, String transactionName)
at System.Data.SqlClient.SqlConnection.BeginTransaction(IsolationLevel iso, String transactionName)
at System.Data.SqlClient.SqlConnection.BeginTransaction(IsolationLevel iso)
at System.Data.DLinq.SqlClient.SqlConnectionManager.LocalTxShim.System.Transactions.IPromotableSinglePhaseNotification.Initialize() in c:\PdcBuild\query\DLinq\Engine\SqlConnectionManager.cs:line 162
at System.Transactions.TransactionStatePSPEOperation.PSPEInitialize(InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification)
at System.Transactions.TransactionStateActive.EnlistPromotableSinglePhase(InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification, Transaction atomicTransaction)
at System.Transactions.Transaction.EnlistPromotableSinglePhase(IPromotableSinglePhaseNotification promotableSinglePhaseNotification)
at System.Data.DLinq.SqlClient.SqlConnectionManager.LocalTxShim.TryEnlistSinglePhase() in c:\PdcBuild\query\DLinq\Engine\SqlConnectionManager.cs:line 151
at System.Data.DLinq.SqlClient.SqlConnectionManager.Enlist() in c:\PdcBuild\query\DLinq\Engine\SqlConnectionManager.cs:line 125
at System.Data.DLinq.SqlClient.SqlConnectionManager.UseConnection(ISqlConnectionUser user) in c:\PdcBuild\query\DLinq\Engine\SqlConnectionManager.cs:line 86
at System.Data.DLinq.SqlClient.SqlContext.get_IsServer2KOrEarlier() in c:\PdcBuild\query\DLinq\Engine\SqlContext.cs:line 338
at System.Data.DLinq.SqlClient.SqlContext.Execute(Expression query) in c:\PdcBuild\query\DLinq\Engine\SqlContext.cs:line 242
at System.Data.DLinq.Model.SimpleChangeDirector.ExecuteInsertWithFacts(Statement cmd, Object item, StringBuilder changeText) in c:\PdcBuild\query\DLinq\Model\ChangeDirector.cs:line 233
at System.Data.DLinq.Model.SimpleChangeDirector.ExecuteInsert(Object item, StringBuilder changeText) in c:\PdcBuild\query\DLinq\Model\ChangeDirector.cs:line 201
at System.Data.DLinq.Model.SimpleChangeDirector.Insert(Object item) in c:\PdcBuild\query\DLinq\Model\ChangeDirector.cs:line 53
at System.Data.DLinq.Model.ChangeProcessor.SubmitChanges() in c:\PdcBuild\query\DLinq\Model\ChangeProcessor.cs:line 41
at System.Data.DLinq.DataContext.SubmitChanges() in c:\PdcBuild\query\DLinq\DataContext.cs:line 219
at Trtiya.Controls.DLinq.EntityPanel.m_btnSave_Click(Object sender, RoutedEventArgs e) in C:\Projects\ThousandSunsRising\Trtiya.Controls\DLinq\EntityPanel.cs:line 393

InvalidOperationException: open DataReader ...
Nikolaus Brennig
Miha,
Indeed, why not show you some code.
The offending line of code is in assigning a reflected Table<T> to a ListBox.ItemsSource. The assignment is made prior to displaying a Page. Removing the Table<T> assignment removes the exceptional behavior seen during the 'Save' button click handler.
...
// used when setting up gui controls
Type[]
types = { enumerationType };Type
targetType = typeof(Table<>).MakeGenericType(types); // get the table containing the enumeration entityType in modelFieldInfo[]
fields = this.Model.GetType().GetFields(); foreach (FieldInfo fiCandidate in fields){
if (fiCandidate.FieldType == targetType){
object oEnumerable = fiCandidate.GetValue(this.Model);// When the next line is commented the exception disappears
//listBox.ItemsSource = (IEnumerable)oEnumerable;
listBox.ItemTemplate = listBoxDataTemplate; break;}
}
...
// The event handler
void
m_btnSave_Click(object sender, RoutedEventArgs e){
try{
// added new entity to DLinq DataContext if (m_bIsNewEntity){
Type[]
types = { m_Type };Type
targetType = typeof(Table<>).MakeGenericType(types); // get the table fieldFieldInfo[]
fields = this.Model.GetType().GetFields(); foreach (FieldInfo fiCandidate in fields){
if (fiCandidate.FieldType == targetType){
object oGenericTable = fiCandidate.GetValue(this.Model);MethodInfo
miAdd = targetType.GetMethod("Add"); object[] oEntityParameter = new object[] { this.Entity }; // add the entity to the DataContext Table miAdd.Invoke(oGenericTable, oEntityParameter); break;}
}
}
// save changes this.Model.SubmitChanges(); // Raise EntitySaved event this.OnEntitySaved(m_oEntity);}
catch (Exception ex){
this.Model.RejectChanges();}
}
Is listBox.ItemsSource simply being assigned improperly
Cheers,
Rana
Chrisww2
Tom ODonnell
There aren't any open DataReaders, DataSets, instances of DataReaders or instances of DataSets. Only DLinq is used.
The database table which is queried for the ListBox.ItemsSource does not have any data in it. Possibly DLinq maintains an open reader until some data is returned
JohnEwing
Do you have another open database reader elsewhere in your application If so, you must close that reader first. Even if your database is configured for MARS (multiple active result sets), it is not enabled by default, and is not currently supported by DLinq.
synthkid
Ok - I've confirmed that this is a bug in our connection management. Below is a simple repro for the issue. The workaround in your case is to buffer the data you are assigning to ListBox.ItemsSource (as seen in the ToArray() call below). Whats happening is that when ListBox executes the query by iterating over the items, it opens a reader on the database. Later, SubmitChanges also attempts to open a reader on the connection (to retrieve database properties); normally our connection management forces any other open readers to buffer, but it isn't working in this case. I'll log a bug
Northwind db = new Northwind("Server=<yourserver>");
IEnumerable<Customer> enumerable = db.Customers;
IEnumerator<Customer> enumerator = enumerable.GetEnumerator();
// Use this instead as workaround
Customer newCust = new Customer();//IEnumerable<Customer> enumerable = db.Customers.ToArray();
newCust.CustomerID = "ABCD";
newCust.CompanyName = "EFGH";
db.Customers.Add(newCust);
db.SubmitChanges();
markybark