Гость
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / Добавление записей в Master-Details DataSet / 7 сообщений из 7, страница 1 из 1
24.03.2004, 08:06
    #32454064
Yet another cat
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление записей в Master-Details DataSet
Столкнулся со следующей проблемой. У меня есть 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
24.03.2004, 11:14
    #32454404
Sa
Sa
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление записей в Master-Details DataSet
Тут можно обойтись без клона. Но проблема очевидно раньше привидите свой код.
...
Рейтинг: 0 / 0
24.03.2004, 13:04
    #32454694
Yet another cat
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление записей в Master-Details DataSet
Код обновления такой:

Код: 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
24.03.2004, 14:23
    #32454914
Sa
Sa
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление записей в Master-Details DataSet
Было бы хорошо если бы вы немножко комментировали код, а код constraintа привести можете?
...
Рейтинг: 0 / 0
24.03.2004, 15:43
    #32455159
Yet another cat
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление записей в Master-Details DataSet
Код констрайнта я сам не писал, так что комментарий будет перед кодом

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
25.03.2004, 08:01
    #32456014
Sa
Sa
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Добавление записей в Master-Details DataSet
Ну то что я хотел увидеть к сожалению не увидел :-(

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


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