Привет всем! Тема типизированный датасет и транзакция несовместимы друг с другом и вот почему. Пусть есть одна таблица, в котрой мы модифицируем 1,2 и 3 строку. далее пишем код сохранения
SqlTransaction tran = DbConnection.BeginTransaction();
try
{
// Включеам команды адаптера в транзакцию
mdm_ColorTableAdapter.Settransaction(tran);
this.mdm_ColorTableAdapter.Update(tableColor);
tran.Commit();
}
catch (Exception err)
{
tran.Rollback();
MessageBox.Show(err.Message);
}
Теперь, внимание! Если в третей строке сервер даст ошибку (нам сейчас не важно почему) и произойдёт откат транзакции, то у первых двух строк Adapter выставит DataRowState.Unchanged!!! И всё!!! Хоть убейся значения 1 и 2 строки уже не передадутся никогда!!! Если, теперь исправив значения в 3 строке на правильно, запустить сохранение, то получим: 3 передалось, а 1 и 2 нет!!! Так как же нам быть?
С уважением, Михаил
MB> Теперь, внимание! Если в третей строке сервер даст ошибку (нам сейчас не важно почему) и произойдёт откат транзакции, то у первых двух строк Adapter выставит DataRowState.Unchanged!!! И всё!!! Хоть убейся значения 1 и 2 строки уже не передадутся никогда!!! Если, теперь исправив значения в 3 строке на правильно, запустить сохранение, то получим: 3 передалось, а 1 и 2 нет!!! Так как же нам быть?
модифицировать и записывать по одной записи.
Re: Я в шоке - данные нельзя сохранять в транзакции!
От:
Аноним
Дата:
12.11.05 15:35
Оценка:
Привет всем! Пример тестовый! В жизни это накладная = шапка+ детальная часть с большим количеством строк! Уж тут не по строчкам! Потом если так устроен "дот нет" то кому он нужен?
Re: Я в шоке - данные нельзя сохранять в транзакции!
От:
Аноним
Дата:
12.11.05 16:00
Оценка:
Можно попробовать установить своиство
DataAdapter.AcceptChangesDuringUpdate = false;
тогда он не скинет DataRowState, но при повторном выполнении облом — Concurrency violation дескать ждал от апдейта 1 строки что rowcount =1 а получил 0. Они считаетют дескать другой пользователь уже модифицирвал! Дурдом какой то!
Вы все локально делаете? Не через ремоутинг или веб-сервисы?
Если так, то нужно передавать адаптеру таблицу (или датасет) содержащую только изменения, т.е. GetChanges.
Потом, если все ок, на таблицу-источник и таблицу-изменений выполнить Merge.
Спасибо за сочуствие! Тигру special thank! Разор идёт о клиент ( Windows App) серверной (MS SQL 2000) системе. Проблема не куда "ложить" commit, а в том как в простую солдатскую душу DataAdaptera заронить подозрение, что не дождавшись конца всей транзакции нельзя скидывать DataRowState! Ну просто очевидно что должна быть симметрия- у транзакции 2 возможности Commit and RollBack, точно так и должно быть у адаптера, иначе кому он нужен!!!
Re: Я в шоке - данные нельзя сохранять в транзакции!
От:
Аноним
Дата:
17.11.05 10:18
Оценка:
2 BlackTigerAP.
Уважаемый Тигр. Блок finally исполняется ВСЕГДА. Если использовать Ваш совет, то при выбросе исключения отработает и tran.Rollback( ) и tran.Commit( ). Как-то не очень красиво получается.
Глубоко убежден, что фиксировать транзакцию надо в блоке try. А вот в finally надо помещать освобождение разнообразных ресурсов.
Данный подход, в общем то, идиоматический.
Всего наилучшего.
Re: Я в шоке - данные нельзя сохранять в транзакции!
От:
Аноним
Дата:
23.11.05 15:40
Оценка:
Вы на добавленных строках пробовали?
Потому-что на добавляемые строки AcceptChanges вызывается дважды — первыйраз добавления строки в БД и до обратного чтения той же строки, а второй раз после обратного чтения в строку, но уже в зависимости от AcceptChangesDuringUpdate.
Привет!
А можно по подробнее про метод TableAdapter.Settransaction(tran);
Как я понимаю, DataAdapter в TableAdapter является private, и вы до него не доберётесь, без расширения TypedDataSetGenerator как сказал Alex Leshkin в посте http://www.gotdotnet.ru/Forums/Data/277286.aspx.
Единственное, надо разобраться как модифицировать TypedDataSetGenerator, что-бы он генерировал Adapter с public.
Здравствуйте, black, Вы писали:
B>Привет! B>А можно по подробнее про метод TableAdapter.Settransaction(tran);
B>Как я понимаю, DataAdapter в TableAdapter является private, и вы до него не доберётесь, без расширения TypedDataSetGenerator как сказал Alex Leshkin в посте http://www.gotdotnet.ru/Forums/Data/277286.aspx.
B>Единственное, надо разобраться как модифицировать TypedDataSetGenerator, что-бы он генерировал Adapter с public.
Примерно так:
//Генерим адаптеры
// schema - схема датасетаprivate void createAdapters(DbProviderFactory providerFactory, string schema) {
CodeCompileUnit compileUnit = new CodeCompileUnit();
compileUnit.ReferencedAssemblies.Add("System.dll");
compileUnit.ReferencedAssemblies.Add("System.Data.dll");
compileUnit.ReferencedAssemblies.Add("System.Xml.dll");
CodeNamespace codeNamespace = new CodeNamespace("Tralyalya.Data.Access.Adapters");
CSharpCodeProvider codeProvider = new CSharpCodeProvider();
System.Data.Design.TypedDataSetGenerator.Generate(schema,
compileUnit,
codeNamespace,
codeProvider,
providerFactory);
List<string> propNames = new List<string>();
propNames.Add("Adapter");
propNames.Add("Connection");
propNames.Add("CommandCollection");
propNames.Add("ClearBeforeFill");
//Поправим адаптеры перед генерацией
PropertiesToPublic(propNames, compileUnit.Namespaces[0]);
CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions();
StringWriter writer = new StringWriter();
writer.WriteLine(@"#pragma warning disable 1591");
codeProvider.GenerateCodeFromCompileUnit(compileUnit, writer, codeGeneratorOptions);
writer.WriteLine(@"#pragma warning restore 1591");
File.WriteAllText("TableAdapters.Generated.cs", writer.ToString());
}
//А здесь правим адаптеры как нам надо перед генерацией в код
//propNames - это те проперти, которым надо public сделатьprivate void PropertiesToPublic(List<string> propNames, CodeNamespace ns) {
CodeTypeDeclaration[] types = new CodeTypeDeclaration[ns.Types.Count];
ns.Types.CopyTo(types, 0);
foreach (CodeTypeDeclaration type in types) {
CodeTypeMember[] members = new CodeTypeMember[type.Members.Count];
type.Members.CopyTo(members, 0);
foreach (CodeTypeMember member in members) {
if (member is CodeMemberField) {
CodeMemberField field = (CodeMemberField)member;
if (field.Name.ToUpper() == "_CONNECTION") {
field.Type = new CodeTypeReference(typeof(DbConnection));
}
if (field.Name.ToUpper() == "_ADAPTER") {
field.Type = new CodeTypeReference(typeof(DbDataAdapter));
}
if (field.Name.ToUpper() == "_COMMANDCOLLECTION") {
field.Type = new CodeTypeReference(typeof(DbCommand[]));
}
} else if (member is CodeMemberProperty) {
CodeMemberProperty property = (CodeMemberProperty)member;
//Вот именно здесь мы public и пропишемif (propNames.Contains(property.Name)) {
//Override нужен был исключительно в моем случае
//так что его можно убратьproperty.Attributes = MemberAttributes.Public | MemberAttributes.Override;
}
//Исправление приведения типа для проперти Connection
//Это делать не обязательно, но если есть базовый класс для адаптеров,
//может оказаться полезным.if (property.Name.ToUpper() == "CONNECTION") {
property.Type = new CodeTypeReference(typeof(DbConnection));
CodeIterationStatement codeIterationStatement = (CodeIterationStatement)property.SetStatements[4];
CodeConditionStatement codeConditionStatement = (CodeConditionStatement)codeIterationStatement.Statements[0];
CodeAssignStatement codeAssignStatement = (CodeAssignStatement)codeConditionStatement.TrueStatements[0];
CodePropertyReferenceExpression codePropertyReferenceExpression = (CodePropertyReferenceExpression)codeAssignStatement.Left;
CodeCastExpression codeCastExpression = (CodeCastExpression)codePropertyReferenceExpression.TargetObject;
codeCastExpression.TargetType.BaseType = "System.Data.Common.DbCommand";
}
if (property.Name.ToUpper() == "ADAPTER") {
property.Type = new CodeTypeReference(typeof(DbDataAdapter));
}
if (property.Name.ToUpper() == "COMMANDCOLLECTION") {
property.Type = new CodeTypeReference(typeof(DbCommand[]));
}
}
}
}
}
ну а потом можно делать partial class для адаптера, либо использовать базовый класс,
где реализовать SetTransaction, что мне кажется более удобным.