views:

8352

answers:

16

I am having a really hard time attempting to debug LINQ to SQL and submitting changes.

I have been using http://weblogs.asp.net/scottgu/archive/2007/07/31/linq-to-sql-debug-visualizer.aspx, which works great for debugging simple queries.

I'm working in the DataContext Class for my project with the following snippet from my application:

JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID = "New Project";
this.UpdateJobMaster(newJobToCreate);
this.SubmitChanges();

I will catch some very odd exceptions when I run this.SubmitChanges;

Index was outside the bounds of the array.

The stack trace goes places I cannot step into:

at System.Data.Linq.IdentityManager.StandardIdentityManager.MultiKeyManager`3.TryCreateKeyFromValues(Object[] values, MultiKey`2& k)
   at System.Data.Linq.IdentityManager.StandardIdentityManager.IdentityCache`2.Find(Object[] keyValues)
   at System.Data.Linq.IdentityManager.StandardIdentityManager.Find(MetaType type, Object[] keyValues)
   at System.Data.Linq.CommonDataServices.GetCachedObject(MetaType type, Object[] keyValues)
   at System.Data.Linq.ChangeProcessor.GetOtherItem(MetaAssociation assoc, Object instance)
   at System.Data.Linq.ChangeProcessor.BuildEdgeMaps()
   at System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode)
   at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
   at System.Data.Linq.DataContext.SubmitChanges()
   at JobTrakDataContext.CreateNewJob(NewJob job, String userName) in D:\JobTrakDataContext.cs:line 1119

Does anyone have any tools or techniques they use? Am I missing something simple?

EDIT: I've setup .net debugging using Slace's suggestion, however the .net 3.5 code is not yet available: http://referencesource.microsoft.com/netframework.aspx

EDIT2: I've changed to InsertOnSubmit as per sirrocco's suggestion, still getting the same error.

EDIT3: I've implemented Sam's suggestions trying to log the SQL generated and to catch the ChangeExceptoinException. These suggestions do not shed any more light, I'm never actually getting to generate SQL when my exception is being thrown.

EDIT4: I found an answer that works for me below. Its just a theory but it has fixed my current issue.

A: 

A simple solution could be to run a trace on your database and inspect the queries run against it - filtered ofcourse to sort out other applications etc. accessing the database.

That ofcourse only helps once you get past the exceptions...

Per Hornshøj-Schierbeck
A: 

Hrm.

Taking a WAG (Wild Ass Guess), it looks to me like LINQ - SQL is trying to find an object with an id that doesn't exist, based somehow on the creation of the JobMaster class. Are there foreign keys related to that table such that LINQ to SQL would attempt to fetch an instance of a class, which may not exist? You seem to be setting the ProjectID of the new object to a string - do you really have an id that's a string? If you're trying to set it to a new project, you'll need to create a new project and get its id.

Lastly, what does UpdateJobMaster do? Could it be doing something such that the above would apply?

John Christensen
+1  A: 

VS 2008 has the ability to debug though the .NET framework (http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx)

This is probably your best bet, you can see what's happening and what all the properties are at the exact point in time

Slace
As of today http://referencesource.microsoft.com/netframework.aspx the source code for .net 3.5 is not yet available.
ben
+2  A: 

The error you are referring to above is usually caused by associations pointing in the wrong direction. This happens very easily when manually adding associations to the designer since the association arrows in the L2S designer point backwards when compared to data modelling tools.

It would be nice if they threw a more descriptive exception, and maybe they will in a future version. (Damien / Matt...?)

KristoferA - Huagati.com
+1  A: 

Why do you do UpdateJobMaster on a new instance ? Shouldn't it be InsertOnSubmit ?

JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID = "New Project";
this.InsertOnSubmit(newJobToCreate);
this.SubmitChanges();
sirrocco
I've changed to InsertOnSubmit, still getting the same error
ben
+2  A: 

You can create a partial class for your DataContext and use the Created or what have you partial method to setup the log to the console.out wrapped in an #if DEBUG.. this will help you to see the queries executed while debugging any instance of the datacontext you are using.

I have found this useful while debugging LINQ to SQL exceptions..

partial void OnCreated()
{
#if DEBUG
      this.Log = Console.Out;
#endif
}
Quintin Robinson
+3  A: 

My first debugging action would be to look at the generated SQL:

JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID = "New Project";
this.UpdateJobMaster(newJobToCreate);
this.Log = Console.Out; // prints the SQL to the debug console
this.SubmitChanges();

The second would be to capture the ChangeConflictException and have a look at the details of failure.

  catch (ChangeConflictException e)
  {
    Console.WriteLine("Optimistic concurrency error.");
    Console.WriteLine(e.Message);
    Console.ReadLine();
    foreach (ObjectChangeConflict occ in db.ChangeConflicts)
    {
      MetaTable metatable = db.Mapping.GetTable(occ.Object.GetType());
      Customer entityInConflict = (Customer)occ.Object;
      Console.WriteLine("Table name: {0}", metatable.TableName);
      Console.Write("Customer ID: ");
      Console.WriteLine(entityInConflict.CustomerID);
      foreach (MemberChangeConflict mcc in occ.MemberConflicts)
      {
        object currVal = mcc.CurrentValue;
        object origVal = mcc.OriginalValue;
        object databaseVal = mcc.DatabaseValue;
        MemberInfo mi = mcc.Member;
        Console.WriteLine("Member: {0}", mi.Name);
        Console.WriteLine("current value: {0}", currVal);
        Console.WriteLine("original value: {0}", origVal);
        Console.WriteLine("database value: {0}", databaseVal);
      }
    }
  }
Sam
My app is never actually generating SQL and it is not generating any ChangeConflictExceptions, it is bombing out before this step.
ben
+5  A: 

First, thanks everyone for the help, I finally found it.

The solution was to drop the .dbml file from the project, add a blank .dbml file and repopulate it with the tables needed for my project from the 'Server Explorer'.

I noticed a couple of things while I was doing this:

  • There are a few tables in the system named with two words and a space in between the words, i.e. 'Job Master'. When I was pulling that table back into the .dbml file it would create a table called 'Job_Master', it would replace the space with an underscore.
  • In the orginal .dbml file one of my developers had gone through the .dbml file and removed all of the underscores, thus 'Job_Master' would become 'JobMaster' in the .dbml file. In code we could then refer to the table in a more, for us, standard naming convention.
  • My theory is that somewhere, the translation from 'JobMaster' to 'Job Master' while was lost while doing the projection, and I kept coming up with the array out of bounds error.

It is only a theory. If someone can better explain it I would love to have a concrete answer here.

ben
Somebody had created an association from one entity to another where the foreign end was not the primary key. This is a bug in 3.5 SP1.
DamienG
A: 

We have actually stopped using the Linq to SQL designer for our large projects and this problem is one of the main reasons. We also change a lot of the default values for names, data types and relationships and every once in a while the designer would lose those changes. I never did find an exact reason, and I can't reliably reproduce it.

That, along with the other limitations caused us to drop the designer and design the classes by hand. After we got used to the patterns, it is actually easier than using the designer.

amcoder
A: 

I posted a similar question earlier today here: http://stackoverflow.com/questions/191690/strange-linq-exception-index-out-of-bounds.

It's a different use case - where this bug happens during a SubmitChanges(), mine happens during a simple query, but it is also an Index out of range error.

Cross posting in this question in case the combination of data in the questions helps a good Samaritan answer either :)

Matt
A: 

Check that all the "primary key" columns in your dbml actually relate to the primary keys on the database tables. I just had a situation where the designer decided to put an extra PK column in the dbml, which meant LINQ to SQL couldn't find both sides of a foreign key when saving.

Neil Barnwell
A: 

I recently encountered the same issue: what I did was

Proce proces = unit.Proces.Single(u => u.ProcesTypeId == (from pt in context.ProcesTypes
                                                                         where pt.Name == "Fix-O"
                                                                         select pt).Single().ProcesTypeId &&
                                                                      u.UnitId == UnitId);

Instead of:

Proce proces = context.Proces.Single(u => u.ProcesTypeId == (from pt in context.ProcesTypes
                                                                     where pt.Name == "Fix-O"
                                                                     select pt).Single().ProcesTypeId &&
                                                                  u.UnitId == UnitId);

Where context was obviously the DataContext object and "unit" an instance of Unit object, a Data Class from a dbml file.

Next, I used the "proce" object to set a property in an instance of another Data Class object. Probably the LINQ engine could not check whether the property I was setting from the "proce" object, was allowed in the INSERT command that was going to have to be created by LINQ to add the other Data Class object to the database.

craziac
+11  A: 

I always found useful to know exactly what changes are being sent to the DataContext in the SubmitChanges() method.

I use the DataContext.GetChangeSet() method, it returns a ChangeSet object instance that holds 3 read-only IList's of objects which have either been added, modified, or removed.

You can place a breakpoint just before the SubmitChanges method call, and add a Watch (or Quick Watch) containing:

ctx.GetChangeSet();

Where ctx is the current instance of your DataContext, and then you'll be able to track all the changes that will be effective on the SubmitChanges call.

CMS
Thank you, never noticed that, incredibly helpful.
Ash Machine
You're welcome!
CMS
Exactly what I was looking for.
lambacck
A: 

I had the same non speaking error.

I had a foreign key relation to a column of a table that was not the primary key of the table, but a unique column. When I changed the unique column to be the primary key of the table the problem went away.

Hope this helps anyone!

A: 

Posted my experiences with this exception in an answer to SO# 237415

magnifico
A: 

This almost certainly won't be everyone's root cause, but I encountered this exact same exception in my project - and found that the root cause was that an exception was being thrown during construction of an entity class. Oddly, the true exception is "lost" and instead manifests as an ArgumentOutOfRange exception originating at the iterator of the Linq statement that retrieves the object/s.

If you are receiving this error and you have introduced OnCreated or OnLoaded methods on your POCOs, try stepping through those methods.

Mark