powered by simpleCommunicator - 2.0.34     © 2025 Programmizd 02
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / Добавление записей в Master-Details DataSet
7 сообщений из 7, страница 1 из 1
Добавление записей в Master-Details DataSet
    #32454064
Фотография Yet another cat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Столкнулся со следующей проблемой. У меня есть DataSet, содержащий 2 таблицы, одна из которых ссылается на другую. Обычный Master-Details. Задача состоит в том, чтобы добавить в таблицу details новые записи, которые могут ссылаться на новые записи в таблице master. При этом имеется осложняющее обстоятельство. Identity в master-таблице будет известно только после реального добавления записей в базу данных (backend - MS SQL 2K). При добавлении на клиенте просто используются временные ключи, чтобы обеспечить соблюдение ограничений.

Казалось бы все просто - получаем новые строки из master-таблицы, делаем Update на DataAdapter'e. Команда у нас - хранимая процедура, ключ в выходных параметрах, отображенных на нужные поля в таблице DataSet'a, значения в полях вторичного ключа во 2-й таблице обновляются каскадно, так что сгенерированное на сервере значение ключа оказывается во всех нужных полях... Вроде бы осталось только получить новые строки из подчиненной таблицы и натравить Update соответствующего адаптера на них... А фигушки - строки в таблице details после каскадного обновления находятся в состоянии Unchanged, естественно Update их игнорирует.

Пока нашел такое решение. Получаю набор новых строк из подчиненной таблицы до проведения обновления master-таблицы. Затем обновляю master-таблицу. После этого пробегаю по набору новых строк подчиненной таблицы и те из них, что имеют состояние Unchanged пихаю в клон этой подчиненной таблицы. Далее клон отправляется в Update, исходный набор новых строк также отправляется в Update, там могли остаться новые записи, ссылающиеся на уже существующие в master-таблице строки.

Это работает, но возможно существует более красивое и правильное решение. Поделитесь опытом, пожалуйста.

=====
Не дождетесь!
...
Рейтинг: 0 / 0
Добавление записей в Master-Details DataSet
    #32454404
Sa
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Тут можно обойтись без клона. Но проблема очевидно раньше привидите свой код.
...
Рейтинг: 0 / 0
Добавление записей в Master-Details DataSet
    #32454694
Фотография Yet another cat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код обновления такой:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
public void UpdateData()
		{
			// Required to write back record id
			System.Data.DataRow[] drUserAdded=setMain.cls_user.Select(null,null,DataViewRowState.Added);
			
			System.Data.DataTable tblIpAdded=setMain.ip_user.GetChanges(DataRowState.Added),
				tblUserModified=setMain.cls_user.GetChanges(DataRowState.Modified),
				tblIpModified=setMain.ip_user.GetChanges(DataRowState.Modified),
				tblUserDeleted=setMain.cls_user.GetChanges(DataRowState.Deleted),
				tblIpDeleted=setMain.ip_user.GetChanges(DataRowState.Deleted);

			if (drUserAdded.Length> 0 )
				sdaUser.Update(drUserAdded);
			if (tblIpAdded!=null) 
				sdaIP.Update(tblIpAdded);
			if (tblIpDeleted!=null) 
				sdaIP.Update(tblIpDeleted);
			if (tblUserDeleted!=null) 
				sdaUser.Update(tblUserDeleted);
			if (tblIpModified!=null) 
				sdaIP.Update(tblIpModified);
			if (tblUserModified!=null) 
				sdaUser.Update(tblUserModified);

			setMain.AcceptChanges();
		}


Больше ничего особенного там нету, DataSet, таблицы, 2 адаптера сделаны в дизайнере. Редактирование через грид.

Отклонения от умолчаний
- В схеме данных набора данных явно указаны UpdateRule и DeleteRule, выставлено значение Cascade
- Текст для команд в адаптере я сам написал

Собственно все. Я просто воспроизводил на своем примере содержание материала: Walkthrough: Creating a Master-Detail Windows Form

=====
Не дождетесь!
...
Рейтинг: 0 / 0
Добавление записей в Master-Details DataSet
    #32454914
Sa
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Было бы хорошо если бы вы немножко комментировали код, а код constraintа привести можете?
...
Рейтинг: 0 / 0
Добавление записей в Master-Details DataSet
    #32455159
Фотография Yet another cat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код констрайнта я сам не писал, так что комментарий будет перед кодом

1. Таблица cls_user, ключ user_id типа int. В схеме сделан автоинкремент, в реальной таблице на сервере его нет.
2. Таблица ip_user, ключ ip_ip varchar(255), поле ip_uid ссылается на ключ в таблице cls_user

Что касается, предыдущего фрагмента кода, то он (могу ошибаться) слишком прост для комментирования. Там всего лишь производится упорядочение внесения изменений с учетом связи между таблицами.

1. При добавлении записей строки в родительскую таблицу должны быть добавлены раньше, чем в подчиненную для поддержания ссылочной целостности. При удалении - наоборот, из дочерней таблицы строки надо удалить раньше. Изменение существующих записей - все равно когда проводить, у меня после вставки и перед удалением.

2. При добавлении записей в родительскую таблицу, строки получаются вызовом
Код: plaintext
setMain.cls_user.Select(null,null,DataViewRowState.Added);

потому что мне нужно вставить значения реального (а не клиентско-автоинкрементного ключа) обратно в таблицу набора данных. Select в отличие от GetChanges возвращает ссылки на оригинальные строки, а не клон таблицы. Очевидно, что если не заменить значения на реальные, после первого же вызова <имя адаптера>.Fill(setMain.cls_user) пользователь получит несуществующие дубли строк.

Собственно Constraint:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
private void InitClass() {
            this.DataSetName =  "setIPStatEdit" ;
            this.Prefix =  "";
            this.Namespace = " http://www.tempuri.org/setIPStatEdit.xsd ";
            this.Locale = new System.Globalization.CultureInfo(" ru-RU ");
            this.CaseSensitive = false;
            this.EnforceConstraints = true;
            this.tablecls_user = new cls_userDataTable();
            this.Tables.Add(this.tablecls_user);
            this.tableip_user = new ip_userDataTable();
            this.Tables.Add(this.tableip_user);
            ForeignKeyConstraint fkc;
            fkc = new ForeignKeyConstraint(" cls_userip_user ", new DataColumn[] {
                        this.tablecls_user.user_idColumn}, new DataColumn[] {
                        this.tableip_user.ip_uidColumn});
            this.tableip_user.Constraints.Add(fkc);
            fkc.DeleteRule = System.Data.Rule.Cascade;
            fkc.UpdateRule = System.Data.Rule.Cascade;
            this.relationcls_userip_user = new DataRelation(" cls_userip_user", new DataColumn[] {
                        this.tablecls_user.user_idColumn}, new DataColumn[] {
                        this.tableip_user.ip_uidColumn}, false);
            this.Relations.Add(this.relationcls_userip_user);
        }


Собственно, вот схема данных в DataSet:

1. Таблица cls_user, ключ user_id типа int. В схеме сделан автоинкремент, в реальной таблице на сервере его нет.
2. Таблица ip_user, ключ ip_ip varchar(255), поле ip_uid ссылается на ключ в таблице cls_user

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
<?xml version= "1 . 0 " standalone= "yes"  ?>
<xs:schema id= "setIPStatEdit"  targetNamespace= "http://www.tempuri.org/setIPStatEdit.xsd"  xmlns:mstns= "http://www.tempuri.org/setIPStatEdit.xsd" 
	xmlns= "http://www.tempuri.org/setIPStatEdit.xsd"  xmlns:xs= "http://www.w3.org/2001 /XMLSchema"
	xmlns:msdata= "urn:schemas-microsoft-com:xml-msdata"  attributeFormDefault= "qualified"  elementFormDefault= "qualified" >
	<xs:element name= "setIPStatEdit"  msdata:IsDataSet= "true"  msdata:Locale= "ru-RU" >
		<xs:complexType>
			<xs:choice maxOccurs= "unbounded" >
				<xs:element name= "cls_user" >
					<xs:complexType>
						<xs:sequence>
							<xs:element name= "user_id"  msdata:ReadOnly= "true"  msdata:AutoIncrement= "true"  type= "xs:int"  />
							<xs:element name= "user_host"  type= "xs:string"  minOccurs= "0 " nillable= "true"  />
							<xs:element name= "user_nick"  type= "xs:string"  minOccurs= "0 " nillable= "true"  />
							<xs:element name= "user_name"  type= "xs:string"  minOccurs= "0 " nillable= "true"  />
							<xs:element name= "user_desc"  type= "xs:string"  minOccurs= "0 " />
							<xs:element name= "user_limit"  type= "xs:int"  minOccurs= "0 " />
							<xs:element name= "user_dummy"  type= "xs:boolean"  />
						</xs:sequence>
					</xs:complexType>
				</xs:element>
				<xs:element name= "ip_user" >
					<xs:complexType>
						<xs:sequence>
							<xs:element name= "ip_ip"  type= "xs:string"  />
							<xs:element name= "ip_uid"  type= "xs:int"  minOccurs= "0 " />
						</xs:sequence>
					</xs:complexType>
				</xs:element>
			</xs:choice>
		</xs:complexType>
		<xs:unique name= "Constraint1"  msdata:PrimaryKey= "true" >
			<xs:selector xpath= ".//mstns:cls_user"  />
			<xs:field xpath= "mstns:user_id"  />
		</xs:unique>
		<xs:unique name= "ip_user_Constraint1"  msdata:ConstraintName= "Constraint1"  msdata:PrimaryKey= "true" >
			<xs:selector xpath= ".//mstns:ip_user"  />
			<xs:field xpath= "mstns:ip_ip"  />
		</xs:unique>
		<xs:keyref name= "cls_userip_user"  refer= "mstns:Constraint1"  msdata:DeleteRule= "Cascade"  msdata:UpdateRule= "Cascade" >
			<xs:selector xpath= ".//mstns:ip_user"  />
			<xs:field xpath= "mstns:ip_uid"  />
		</xs:keyref>
	</xs:element>
</xs:schema>




=====
Не дождетесь!
...
Рейтинг: 0 / 0
Добавление записей в Master-Details DataSet
    #32456014
Sa
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну то что я хотел увидеть к сожалению не увидел :-(

DataAdapter при Update меняет state Modified, Added, Deleted в Unchanged у Detail только в том случае когда у constraint стоит AcceptRejectRule = AcceptRejectRule.Cascade. По умолчанию должно быть None у вас же явно он не выставлен то есть по идее update не должен был коммитить записи Detail.

Одно из возможных решений:
Добавить следующую строчку, или сделать это через схему:
Код: plaintext
1.
2.
3.
4.
this.tableip_user.Constraints.Add(fkc);
fkc.DeleteRule = System.Data.Rule.Cascade;
fkc.UpdateRule = System.Data.Rule.Cascade;
fkc.AcceptRejectRule = AcceptRejectRule.None; // добавьте эту строку


Теперь после update Masterа ничего не должно коммититься, поставьте для проверки выдачу количество записей Modified, Deleted, Added до DataAdapter.Update , и после например так:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
string master =  "orders" , details =  "orderDetails" ;
DataTable masterModified, detailsModified, masterDeleted, detailsDeleted,masterAdded, detailsAdded;

//Modified
masterModified = ds.Tables[master].GetChanges(System.Data.DataRowState.Modified);
detailsModified = ds.Tables[details].GetChanges(System.Data.DataRowState.Modified);
				
if (masterModified != null)
     Console.WriteLine( "Before masterModified: {0 }", masterModified.Rows.Count);
				
if (detailsModified != null)
       Console.WriteLine( "Before detailModified: {0 }", detailsModified.Rows.Count);

// Deleted
masterDeleted = ds.Tables[master].GetChanges(System.Data.DataRowState.Deleted);
detailsDeleted = ds.Tables[details].GetChanges(System.Data.DataRowState.Deleted);
				
if (masterDeleted != null)
	Console.WriteLine( "Before masterDeleted: {0 }", masterDeleted.Rows.Count);
				
if (detailsDeleted != null)
	Console.WriteLine( "Before detailDeleted: {0 }", detailsDeleted.Rows.Count);

// и т.д для всех состояний для таблицы Master , и Detail

dataAdapter1.Update()

// тут выдача количество записей по всем состоянием таблицы Master и Detail после update 
.........


setMain.AcceptChanges() // можно убрать пока (но не навсегда :-) )

Тогда станет понятно что же происходит после очередного update, когда я говорил про комментарии к вашему коду как раз это и имел ввиду. Например вы могли его прокоменнтировать так: M - master table, D - detail table, "
До первого update - M ( 1 удал, 2 добавл, 0 модифи), D (2 удал, 4 добавл, 0 модифици)
После первого update - M (0, 0, 0 ), D(0, 0, 0 )
После второго update ......
или подобным образом.
"

Хотя приведенные в другом вашем посте комментарии тоже не лишние.

> Текст для команд в адаптере я сам написал
Можете для InsertCommand привести код?
...
Рейтинг: 0 / 0
Добавление записей в Master-Details DataSet
    #32456185
Фотография Yet another cat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Спасибо,
fkc.AcceptRejectRule = AcceptRejectRule.None;
помогло. Теперь буду знать
=====
Не дождетесь!
...
Рейтинг: 0 / 0
7 сообщений из 7, страница 1 из 1
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / Добавление записей в Master-Details DataSet
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]