Вставка DataRelation и ForeignKey - PullRequest
3 голосов
/ 08 мая 2009

У меня есть приложение winforms с двумя DataGridView, отображающими отношение master-detail из моих таблиц Person и Address. Таблица Person имеет поле PersonID, которое является автоинкрементным первичным ключом. Адрес имеет поле PersonID, которое является FK.

Я заполняю свои DataTables DataAdapter и устанавливаю AutoIncrement = true и AutoIncrementStep = -1 столбца Person.PersonID. Я могу вставить записи в Person DataTable из DataGridView. Столбец PersonID отображает уникальные отрицательные значения для PersonID. Я обновляю базу данных, вызывая DataAdapter.Update (PersonTable), и отрицательные PersonID автоматически преобразуются в положительные уникальные значения SQL Server.

Вот руб. Address DataGridView показывает таблицу адресов, которая имеет отношение DataRelation к Person by PersonID. Записи вставленного лица имеют временный отрицательный идентификатор PersonID. Теперь я могу вставлять записи в Address через DataGridView и Address.PersonID устанавливается в отрицательное значение из сопоставления DataRelation. Я вызываю Adapter.Update (AddressTable), и отрицательные PersonID попадают в таблицу Address, нарушая связь.

Как вы, ребята, справляетесь с первичными / внешними ключами, используя DataTables и master-detail DataGridViews?

Спасибо! Стив

EDIT:

После дополнительного поиска в Google я обнаружил, что событие SqlDataAdapter.RowUpdated дает мне то, что мне нужно. Я создаю новую команду для запроса последнего идентификатора, вставленного с помощью @@ IDENTITY. Это работает довольно хорошо. DataRelation обновляет поле Address.PersonID для меня, поэтому требуется сначала обновить таблицу Person, а затем обновить таблицу Address. Все новые записи вставляются правильно с правильными идентификаторами на месте!

            Adapter = new SqlDataAdapter(cmd);
            Adapter.RowUpdated += (s, e) => 
            {
                if (e.StatementType != StatementType.Insert) return;
                //set the id for the inserted record
                SqlCommand c = e.Command.Connection.CreateCommand();
                c.CommandText = "select @@IDENTITY id";
                e.Row[0] = Convert.ToInt32( c.ExecuteScalar() );
            };
            Adapter.Fill(this);
            SqlCommandBuilder sb = new SqlCommandBuilder(Adapter);
            sb.GetDeleteCommand();
            sb.GetUpdateCommand();
            sb.GetInsertCommand();
            this.Columns[0].AutoIncrement = true;
            this.Columns[0].AutoIncrementSeed = -1;
            this.Columns[0].AutoIncrementStep = -1;    

Ответы [ 2 ]

1 голос
/ 17 июня 2009

У меня была похожая проблема, но мое решение было немного другим.

@ Ноэль Кеннеди: Ваше решение не работает с SQL Server 2005 CE, поскольку оно не поддерживает несколько операторов, и TableAdapter не будет генерировать код обновления, необходимый для обновления столбцов автоинкремента в родительской таблице.

ПРИМЕЧАНИЕ: вам все еще нужны каскадные обновления в отношениях, чтобы дочерние таблицы обновлялись.

Я также добавляю метод в свой TableAdapter, который достаточно универсален, чтобы просто копировать / вставлять все ваши родительские TableAdapter. Единственное, что я изменяю, это тип строки идентификатора и индекс (при необходимости). Я также добавляю запрос к TableAdapter с именем GetIdentity (). Вы можете добавить его в TableAdapter в конструкторе набора данных, добавив скалярный запрос с помощью sql = "SELECT @@ IDENTITY;"

Теперь пользовательская функция:

public int InsertAndRefresh(System.Data.DataTable dataTable)
{
    int updated = 0;

    System.Data.DataRow[] updatedRows = dataTable.Select("", "", System.Data.DataViewRowState.Added);

    bool closed = (this.Connection.State == System.Data.ConnectionState.Closed);
    if (closed) 
        this.Connection.Open();

    foreach (System.Data.DataRow row in updatedRows)
    {
        updated+=this.Adapter.Update(new global::System.Data.DataRow[] { row });
        decimal identity = (decimal)this.GetIdentity();
        row[0] = System.Decimal.ToInt64(identity);
        row.AcceptChanges();
    }
    if (closed)
        this.Connection.Close();

    return updated;
}

Вы хотите сначала позвонить родителю. Затем выполните все как обычно (обновите родительский, а затем дочерний элементы).

Ура!

1 голос
/ 09 мая 2009

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

Вам не нужно делать что-либо из этого события RowUpdated. Он встроен в функциональность набора данных.

...