firstly let me explain that I fully understand why this InvalidOperationException
has been thrown and that I am simply looking for a way to avoid it from being thrown. There is a call to System.Linq.Enumerable.SingleOrDefault()
that I can see in the Visual Studio 2010 Call Stack window. However, the call is in external Linq2Sql
code, so I have no access to change it.
(Sorry, you may need to zoom in to see this image properly)
The last internal code call before execution goes external is found in the application dbml.designer.cs
file which I do have access to, but still cannot edit because it updates automatically and loses custom changes. It is in a (Linq2Sql
) property setter for one of the database tables used in the application and it appears that the problem is caused by the call to the _DbAudioTrackContributors.Entity
object:
public DbAudioTrackContributor DbAudioTrackContributors
{
get
{
return this._DbAudioTrackContributors.Entity;
}
set
{
// This is the last internal line before the Exception
DbAudioTrackContributor previousValue = this._DbAudioTrackContributors.Entity;
// Execution never reaches here
if (((previousValue != value) ||
(this._DbAudioTrackContributors.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._DbAudioTrackContributors.Entity = null;
previousValue.DbAudioTrack = null;
}
this._DbAudioTrackContributors.Entity = value;
if ((value != null))
{
value.DbAudioTrack = this;
}
this.SendPropertyChanged("DbAudioTrackContributors");
}
}
}
If anyone has a way to provide me with the internal code for the EntityRef<TEntity>
struct (or even just the Entity
property), this could possibly help me to work out what is causing the problem.
The last line of my own code before the InvalidOperationException
is thrown is a call to dataContext.SubmitChanges()
:
public int UpdateAudioTrack(AudioTrack audioTrack)
{
using (TransactionScope transactionScope = new TransactionScope())
{
using (MidasDataContext dataContext = DataContext)
{
DbAudioTrack dbAudioTrack = dataContext.DbAudioTracks.Where(
g => g.Id == audioTrack.Id).FirstOrDefault();
if (dbAudioTrack == null) return -1;
CopyToDbAudioTrack(audioTrack, dbAudioTrack);
UpdateAudioTrackContributors(dataContext, audioTrack);
// This is the last line of my code before the Exception
dataContext.SubmitChanges(ConflictMode.FailOnFirstConflict);
// Execution never reaches here
transactionScope.Complete();
return 0;
}
}
}
The CopyToDbAudioTrack
method simply copies all of the property values from the AudioTrack
object to the Linq2Sql
generated DbAudioTrack
object and the UpdateAudioTrackContributors
method is shown below.
private void UpdateAudioTrackContributors(MidasDataContext dataContext, AudioTrack audioTrack)
{
DataList<Label> labels = new DataList<Label>(
audioTrack.Labels.Except(audioTrack.OriginalState.Labels));
if (labels.Count > 0) AddAudioTrackContributors(dataContext, audioTrack, labels);
labels = new DataList<Label>(audioTrack.OriginalState.Labels.Except(audioTrack.Labels));
if (labels.Count > 0) DeleteAudioTrackContributors(dataContext, audioTrack, labels);
}
This method simply finds the Label
objects that have changed and either adds or deletes them in the AudioTrackContributors
database table. This table seems to be the root of the problem as it disappears if this code is commented out. However, this method correctly selects the objects to add or delete, so I am still confused. The AddAudioTrackContributors
method basically calls the code shown below and the DeleteAudioTrackContributors
code is shown below that:
List<DbAudioTrackContributor> dbAudioTrackContributors = new List<DbAudioTrackContributor>();
foreach (T dataListEntry in dataList)
{
DbAudioTrackContributor dbAudioTrackContributor = new DbAudioTrackContributor();
CopyToDbAudioTrackContributor(audioTrack, dataListEntry, dbAudioTrackContributor, contributorType);
dbAudioTrackContributors.Add(dbAudioTrackContributor);
}
dataContext.DbAudioTrackContributors.InsertAllOnSubmit(dbAudioTrackContributors);
DeleteAudioTrackContributors
:
List<DbAudioTrackContributor> dbAudioTrackContributors = new List<DbAudioTrackContributor>();
foreach (T dataListEntry in dataList)
{
DbAudioTrackContributor dbAudioTrackContributor = dataContext.DbAudioTrackContributors.Where(d => d.DataListId == dataListEntry.Id && d.AudioTrackId == audioTrack.Id).FirstOrDefault();
if (dbAudioTrackContributor != null) dbAudioTrackContributors.Add(dbAudioTrackContributor);
}
dataContext.DbAudioTrackContributors.DeleteAllOnSubmit(dbAudioTrackContributors);
Again, stepping through the code during execution shows that the above code also correctly selects the right objects to delete. As I can't see any problems in my code, I don't know where to look next.
If anyone has any ideas on how to proceed, I would be very glad to hear them. Many thanks in advance.