Отладка LINQ для SQL SubmitChanges()
-
01-07-2019 - |
Вопрос
Мне действительно трудно пытаться отладить LINQ для SQL и отправить изменения.
Я использовал http://weblogs.asp.net/scottgu/archive/2007/07/31/linq-to-sql-debug-visualizer.aspx, который отлично подходит для отладки простых запросов.
Я работаю в классе DataContext для своего проекта со следующим фрагментом из моего приложения:
JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID = "New Project";
this.UpdateJobMaster(newJobToCreate);
this.SubmitChanges();
Я поймаю несколько очень странных исключений, когда я запущу это.SubmitChanges;
Index was outside the bounds of the array.
Трассировка стека проходит там, куда я не могу ступить:
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
Есть ли у кого-нибудь какие-нибудь инструменты или методы, которые они используют?Я упускаю что-то простое?
Редактировать:Я настроил отладку .net, используя предложение Slace, однако код .net 3.5 пока недоступен: http://referencesource.microsoft.com/netframework.aspx
РЕДАКТИРОВАТЬ 2:Я перешел на InsertOnSubmit в соответствии с предложением сиррокко, по-прежнему получая ту же ошибку.
РЕДАКТИРОВАТЬ 3: Я реализовал предложения Сэма, пытаясь зарегистрировать сгенерированный SQL и перехватить ChangeExceptoinException.Эти предложения не проливают больше никакого света, на самом деле я никогда не смогу сгенерировать SQL, когда генерируется мое исключение.
РЕДАКТИРОВАТЬ 4: Ниже я нашел ответ, который подходит мне.Это всего лишь теория, но она исправила мою текущую проблему.
Решение 2
Во-первых, спасибо всем за помощь, я наконец-то нашел это.
Решение состояло в том, чтобы удалить файл .dbml из проекта, добавить пустой файл .dbml и повторно заполнить его таблицами, необходимыми для моего проекта, из "Проводника сервера".
Пока я этим занимался, я заметил пару вещей:
- В системе есть несколько таблиц, названных двумя словами с пробелом между словами, т.е."Мастер работы".Когда я перетаскивал эту таблицу обратно в файл .dbml, она создавала таблицу с именем 'Job_Master', заменяя пробел символом подчеркивания.
- В исходном файле .dbml один из моих разработчиков просмотрел файл .dbml и удалил все подчеркивания, таким образом, 'Job_Master' станет 'JobMaster' в файле .dbml.Затем в коде мы могли бы ссылаться на таблицу в более, для нас, стандартном соглашении об именовании.
- Моя теория заключается в том, что где-то перевод с 'JobMaster' на 'Job Master' while был потерян при выполнении проекции, и я продолжал выдавать ошибку array out of bounds.
Это всего лишь теория.Если кто-то может лучше объяснить это, я хотел бы получить конкретный ответ здесь.
Другие советы
Я всегда считал полезным точно знать, какие изменения отправляются в DataContext в методе SubmitChanges().
Я использую DataContext.GetChangeSet() метод, он возвращает Набор изменений экземпляр объекта, содержащий 3 списка объектов, доступных только для чтения, которые были либо добавлены, либо изменены, либо удалены.
Вы можете установить точку останова непосредственно перед вызовом метода SubmitChanges и добавить наблюдение (или Быстрое наблюдение), содержащее:
ctx.GetChangeSet();
Где ctx - это текущий экземпляр вашего DataContext, и тогда вы сможете отслеживать все изменения, которые вступят в силу при вызове SubmitChanges.
Моим первым отладочным действием было бы просмотреть сгенерированный 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();
Второй способ состоял бы в том, чтобы зафиксировать исключение ChangeConflictException и взглянуть на детали сбоя.
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);
}
}
}
Вы можете создать частичный класс для вашего DataContext и использовать созданный или какой у вас есть частичный метод для настройки журнала в console.out, завернутый в #if DEBUG..это поможет вам увидеть запросы, выполняемые при отладке любого экземпляра datacontext, который вы используете.
Я нашел это полезным при отладке исключений LINQ to SQL..
partial void OnCreated()
{
#if DEBUG
this.Log = Console.Out;
#endif
}
Ошибка, на которую вы ссылаетесь выше, обычно вызвана ассоциации, указывающие в неправильном направлении.Это происходит очень легко при ручном добавлении ассоциаций в конструктор, поскольку стрелки ассоциаций в конструкторе L2S указывают назад по сравнению с инструментами моделирования данных.
Было бы неплохо, если бы они выдали более описательное исключение, и, возможно, они сделают это в будущей версии.(Дэмиен / Мэтт...?)
Это то, что я сделал
...
var builder = new StringBuilder();
try
{
context.Log = new StringWriter(builder);
context.MY_TABLE.InsertAllOnSubmit(someData);
context.SubmitChanges();
}
finally
{
Log.InfoFormat("Some meaningful message here... ={0}", builder);
}
Простым решением могло бы стать выполнение трассировки в вашей базе данных и проверка выполняемых к ней запросов - разумеется, с фильтрацией для сортировки других приложений и т.д.доступ к базе данных.
Это, конечно, помогает только после того, как вы преодолеете исключения...
В VS 2008 есть возможность отладки через .NET framework (http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx)
Вероятно, это ваш лучший выбор, вы можете видеть, что происходит и каковы все свойства в конкретный момент времени
Почему вы делаете UpdateJobMaster на новом экземпляре?Разве это не должно быть InsertOnSubmit ?
JobMaster newJobToCreate = new JobMaster();
newJobToCreate.JobID = 9999
newJobToCreate.ProjectID = "New Project";
this.InsertOnSubmit(newJobToCreate);
this.SubmitChanges();
Это почти наверняка не будет основной причиной для всех, но я столкнулся с точно таким же исключением в своем проекте - и обнаружил, что основной причиной было то, что исключение создавалось во время создания класса сущностей.Как ни странно, истинное исключение "потеряно" и вместо этого проявляется как исключение ArgumentOutOfRange, возникающее на итераторе оператора Linq, который извлекает объект / ы.
Если вы получаете эту ошибку и ввели методы onCreated или OnLoaded в свой POCOs, попробуйте пошагово использовать эти методы.
Хрм.
Рискуя (Дикое предположение), мне кажется, что LINQ - SQL пытается найти объект с несуществующим идентификатором, основанный каким-то образом на создании класса JobMaster.Существуют ли внешние ключи, связанные с этой таблицей, так что LINQ to SQL попытался бы извлечь экземпляр класса, который, возможно, не существует?Кажется, вы устанавливаете ProjectID нового объекта в строку - у вас действительно есть идентификатор, который является строкой?Если вы пытаетесь установить его для нового проекта, вам нужно будет создать новый проект и получить его идентификатор.
Наконец, что делает UpdateJobMaster?Может ли это быть что-то такое, к чему было бы применимо вышесказанное?
Мы фактически прекратили использовать Linq to SQL designer для наших крупных проектов, и эта проблема является одной из основных причин.Мы также меняем множество значений по умолчанию для имен, типов данных и связей, и время от времени разработчик теряет эти изменения.Я так и не нашел точной причины и не могу достоверно воспроизвести ее.
Это, наряду с другими ограничениями, заставило нас отказаться от конструктора и создавать классы вручную.После того, как мы привыкли к шаблонам, это стало на самом деле проще, чем использовать конструктор.
Я опубликовал аналогичный вопрос ранее сегодня здесь: Странное исключение LINQ (индекс выходит за пределы).
Это другой вариант использования - когда эта ошибка возникает во время SubmitChanges(), моя происходит во время простого запроса, но это также ошибка, указывающая на выход индекса за пределы диапазона.
Перекрестный постинг в этом вопросе на случай, если комбинация данных в вопросах поможет доброму самаритянину ответить на любой из них :)
Убедитесь, что все столбцы "первичного ключа" в вашем dbml на самом деле относятся к первичным ключам в таблицах базы данных.У меня только что была ситуация, когда разработчик решил поместить дополнительный столбец PK в dbml, что означало, что LINQ to SQL не смог найти обе стороны внешнего ключа при сохранении.
Недавно я столкнулся с такой же проблемой:то, что я сделал, было
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);
Вместо того, чтобы:
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);
Где контекстом, очевидно, был объект DataContext, а "unit" - экземпляр объекта Unit, класса данных из файла dbml.
Затем я использовал объект "proce" для установки свойства в экземпляре другого объекта класса данных.Вероятно, движок LINQ не смог проверить, разрешено ли свойство, которое я устанавливал из объекта "proce", в команде INSERT, которая должна была быть создана LINQ для добавления другого объекта класса данных в базу данных.
У меня была такая же ошибка при не говорении.
У меня было отношение внешнего ключа к столбцу таблицы, которое было не первичным ключом таблицы, а уникальным столбцом.Когда я изменил уникальный столбец на первичный ключ таблицы, проблема исчезла.
Надеюсь, это кому-нибудь поможет!
Опубликовал свой опыт работы с этим исключением в ответе на SO# 237415
Я столкнулся с этим вопросом, пытаясь отладить мое LINQ ChangeConflictException.В конце концов я понял, что проблема заключалась в том, что я вручную добавил свойство к таблице в моем файле DBML, но я забыл установить такие свойства, как Обнуляемый (в моем случае это должно было быть правдой) и Тип данных сервера
Надеюсь, это кому-то поможет.
Это было давно, но у меня была такая же проблема, и ошибка была из-за триггера с оператором select.Что -то вроде
CREATE TRIGGER NAME ON TABLE1 AFTER UPDATE AS SELECT table1.key from table1
inner join inserted on table1.key = inserted.key
Когда linq-to-sql запускает команду update, он также запускает инструкцию select для получения автоматически сгенерированных значений в том же запросе и ожидает, что первая запись, установленная как содержащая столбцы, "запрошенные", но в этом случае первой строкой были столбцы из инструкции select в триггере.Таким образом, linq-to-sql ожидал два автоматически сгенерированных столбца, но получил только один столбец (с неверными данными), и это вызвало это исключение.