Object Tracking

Discussion of open issues, suggestions and bugs regarding ADO.NET provider for Oracle
Post Reply
dcoracle600pro
Posts: 51
Joined: Mon 09 Apr 2012 09:57

Object Tracking

Post by dcoracle600pro » Tue 03 Dec 2013 22:17

What does this error message mean?

"The operation is not valid because object tracking is disabled."

I have found the ObjectTrackingEnabled property in the Devart.Data.Linq.DataContext class, but nowhere do I change this value, and when I am stepping through the code, the property is set to true anyway.

This error is sporadic and inconsistent, so it's hard for me to give accurate conditions, but it has only started happening since our upgrade to 8.1.45 from 6.7.

MariiaI
Devart Team
Posts: 1472
Joined: Mon 13 Feb 2012 08:17

Re: Object Tracking

Post by MariiaI » Wed 04 Dec 2013 10:57

"The operation is not valid because object tracking is disabled."
This error means that the object tracking is disabled for current DataContext object and deletes/updates/inserts are not allowed. ObjectTrackingEnabled controls whether your DataContext will track changes to the entities after they're loaded. Please refer to
http://www.devart.com/linqconnect/docs/ ... abled.html

Please try to localize the issue and send us the model and part of the code, in which this error occurs, so that we are able to investigate this issue in more details and find the reason.
This error is sporadic and inconsistent, so it's hard for me to give accurate conditions, but it has only started happening since our upgrade to 8.1.45 from 6.7.
We have made a major refactoring of the LinqConnect engine since version 4.0 (dotConnect for Oracle 7.0.6) and since this version the references to System.Data.Linq are removed, and LinqConnect uses only its own classes. So it is necessary to replace all occurrences of "System.Data.Linq." with "Devart.Data.Linq.". Please check that your code (*.Designer.cs file) doesn't contain "System.Data.Linq." and there is no reference to the System.Data.Linq assembly.

dcoracle600pro
Posts: 51
Joined: Mon 09 Apr 2012 09:57

Re: Object Tracking

Post by dcoracle600pro » Wed 04 Dec 2013 16:56

I found one reference to System.Data.Linq and removed it, but it did not fix the problem. I have isolated a couple of errors out of our error logging system:

The first I have mentioned:

Code: Select all

Message: The operation is not valid because object tracking is disabled.
StackTrace: at Devart.Data.Linq.Engine.c5.p()
 at Devart.Data.Linq.Table.f(Object A_0)
 at NAVIDAS.Data.Oracle.Linq.StatusRepository.UpdateCurrentStep(Procurement proc, Nullable`1 stepID)
 at NAVIDAS.Presenter.Procurement.RequestWorkflowPresenter._view_Submit(Object sender, EventArgs e)
A second error that I have never seen also occurs sometimes: "Invalid attempt to access column data when reader is closed"

This is the function that is getting the error:

Code: Select all

		public void UpdateCurrentStep(Procurement proc, long? stepID)
		{
			REQUESTSTATUS status = _context.REQUESTSTATUS.FirstOrDefault(s => s.PROCUREMENTID == proc.ProcurementID);

			if (status == null)
			{
				status = new REQUESTSTATUS();

				status.PROCUREMENTID = proc.ProcurementID;
				status.ORGID = proc.OrganizationID;
				status.CURRENTSTEPID = stepID;
				status.RETURNEDSTEPID = null;
				status.LASTCSRSTEPID = null;

				_context.REQUESTSTATUS.InsertOnSubmit(status);
			}
			else
				status.CURRENTSTEPID = stepID;

			LinqContext.SubmitChanges(_context);
		}
Most of our Submit changes goes through the class below to clear concurrency errors. I did have to make a couple of changes to this function after the upgrade to 8.1.45 from 6.7, so perhaps I have modified it incorrectly (ie. MetaTable to MetaType, and removing the System.Linq references):

Code: Select all

	public static class LinqContext
	{
		/// <summary>
		/// Resolve "Row not found or change" errors.
		/// Caused by concurrency issues, which are not always issues, depending on the operation
		/// This will cause it to be a warning rather than a fatal error
		/// </summary>
		/// <param name="context">The data context</param>
		public static void SubmitChanges(ORMDataContext context)
		{
			Staple.Core.ILog log = null;

			try
			{
				// ContinueOnConflict will cause operation to continue if more than one conflict exists
				context.SubmitChanges(ConflictMode.ContinueOnConflict);
			}
			catch (ChangeConflictException) // Other DB errors should still be thrown
			{
				// Write a message to the log that actually provides some useful information
				StringBuilder exceptionMessage = new StringBuilder();

				// After testing this, I decided putting the exception into the log is useless, since the stack trace only
				// shows the .SubmitChanges() line above, which we already would know, so just put a nice header on instead.
				exceptionMessage.Append("A conflict has been detected and resolved. Details below:\r\n");

				// Cycle through all conflicts and give DB information on where the conflict occurred
				foreach (ObjectChangeConflict occ in context.ChangeConflicts)
				{
					// Log table that caused the conflict
					MetaType metatype = context.Mapping.GetMetaType(occ.Object.GetType());
					exceptionMessage.AppendFormat("\r\nTable name: {0}\r\n", metatype.TableName);
					foreach (MemberChangeConflict mcc in occ.MemberConflicts)
					{
						exceptionMessage.AppendFormat("\r\nColumn name: {0}", mcc.Member.Name);
						exceptionMessage.AppendFormat("\r\n\tCurrent  value: {0}", mcc.CurrentValue);
						exceptionMessage.AppendFormat("\r\n\tOriginal value: {0}", mcc.OriginalValue);
						exceptionMessage.AppendFormat("\r\n\tDatabase value: {0}\r\n", mcc.DatabaseValue);
					}

					// This marks the conflict as resolved, and essentially results in a "last action wins" scenario
					occ.Resolve(RefreshMode.KeepChanges);
				}
				// Create log for error message
				log = LogFactory.MakeGenericInfoLog(exceptionMessage);

				// Redo the submit changes after the conflicts are resolved
				context.SubmitChanges();
			}
			finally
			{
				if (log != null)
				{
					IAuditLogFacade _auditLogFacade = FacadeFactory.Instance.MakeAuditLogFacade();
					_auditLogFacade.ConnectLogger(new AuditLogRepository(context));
					_auditLogFacade.WriteLog(log);
				}
			}
		}
	}

MariiaI
Devart Team
Posts: 1472
Joined: Mon 13 Feb 2012 08:17

Re: Object Tracking

Post by MariiaI » Thu 05 Dec 2013 11:40

Unfortunately, this information is not enough to reproduce this issue, find the reason and the solution for you. Please provide us with your sample project so that we are able to investigate this issue in more details. It is not necessary to send us the whole project, you could exclude those parts of the project that should not affect the main scenario. In this case, we will find the most suitable solution for you in a shorter time.

dcoracle600pro
Posts: 51
Joined: Mon 09 Apr 2012 09:57

Re: Object Tracking

Post by dcoracle600pro » Thu 05 Dec 2013 20:43

I will try to provide a sample project, but I cannot reproduce the error from localhost, and our project is large as is the operation that is going on in this function, so I am having trouble making a reliable sample.

I did narrow down the issue somewhat. FirstOrDefault() returns null, because at this point the record does not exist. Commenting out the call to FirstOrDefault() resolves the issue for the test case as in the code below:

Code: Select all

		public void UpdateCurrentStep(Procurement proc, long? stepID)
		{
			//REQUESTSTATUS status = _context.REQUESTSTATUS.FirstOrDefault(s => s.PROCUREMENTID == proc.ProcurementID);
			REQUESTSTATUS otherStatus = new REQUESTSTATUS();

			//if (status == null)
			//{
			otherStatus = new REQUESTSTATUS();

			otherStatus.PROCUREMENTID = proc.ProcurementID;
			otherStatus.ORGID = proc.OrganizationID;
			otherStatus.CURRENTSTEPID = stepID;
			otherStatus.RETURNEDSTEPID = null;
			otherStatus.LASTCSRSTEPID = null;

			_context.REQUESTSTATUS.InsertOnSubmit(otherStatus);
			//}
			//else
			//	status.CURRENTSTEPID = stepID;

			LinqContext.SubmitChanges(_context);
		}
Also I have noticed that when I call two repository functions that references the same table, and the first repository calls FirstOrDefault() on the table, the second function fails with either the "Invalid attempt to access column data when reader is closed"
or "Object is disposed" error message.

I tried changing the EntityCachingMode property as I thought it might be related to the way we handle our data context since we use the same instance of it for multiple calls. I found that adding "_context.EntityCachingMode = Devart.Data.Linq.EntityCachingMode.None;" causes the error to always occur on localhost for me, but on a different function call.

dcoracle600pro
Posts: 51
Joined: Mon 09 Apr 2012 09:57

Re: Object Tracking

Post by dcoracle600pro » Mon 09 Dec 2013 20:19

We have come to a resolution to the problem listed. While our main function was happening, separate threads were being spawned to write records for our email notification system. It seems that the problems occurred when both threads were using the same context and our theory is that one thread was disposing of the context while the other was between a read and an update. (Things get weird when you multi-thread)

Anyway, my current emergency is resolved.

MariiaI
Devart Team
Posts: 1472
Joined: Mon 13 Feb 2012 08:17

Re: Object Tracking

Post by MariiaI » Tue 10 Dec 2013 09:23

Yes, this issue could be related to that fact that your DataContext object is accessed from different threads simultaneously (insert, updates, etc.) and in such cases the errors you get are expected.
The DataContext objects are not thread-safe, at least because a DataContext instance uses a single connection, which is not thread-safe itself. We recommend you to create a new instance of DataContext to work with each set of the objects. DataContext is a lightweight object, and it is recommended to use a new DataContext for each unit of work instead of keeping it for a long time.

Post Reply