Как можно использовать SQLBulkCopy для таблицы с первичным ключом GUID и по умолчанию newsequentialid ()? - PullRequest
6 голосов
/ 26 сентября 2008

При использовании SQLBulkCopy для таблицы с первичным ключом GUID и значением по умолчанию newsequentialid ()

* 1003 например *

CREATE TABLE [dbo].[MyTable](
[MyPrimaryKey] [uniqueidentifier] NOT NULL CONSTRAINT [MyConstraint]  DEFAULT (newsequentialid()),
[Status] [int] NULL,
[Priority] [int] NULL,
 CONSTRAINT [PK_MyTable] PRIMARY KEY NONCLUSTERED 
(
[MyPrimaryKey] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

с кодом C #

        tran = connection.BeginTransaction();
        SqlBulkCopy sqlCopy = new SqlBulkCopy(connection,SqlBulkCopyOptions.Default, tran);            

        sqlCopy.DestinationTableName = "MyTable";            
        sqlCopy.WriteToServer(dataTable);

выдает ошибку ...

В столбце 'MyPrimaryKey' не разрешено DBNull.Value

Я попытался поиграть в SqlBulkCopyOptions. Единственное, что работает, - это установить в поле MyPrimaryKey пустые значения и удалить первичный ключ.

Кто-нибудь знает, есть ли обходной путь для этой проблемы? Или вы можете проверить, что нет обходного пути (кроме изменения структуры таблицы)?

Ответы [ 3 ]

11 голосов
/ 26 сентября 2008

Вам необходимо настроить сопоставления столбцов. Первый звонок

sqlCopy.ColumnMappings.Clear();

Тогда звоните

sqlBulkCopy.ColumnMappings.Add("Status", "Status");
sqlBulkCopy.ColumnMappings.Add("Priority", "Priority");

Это означает, что массовая копия прекратит попытки вставки в столбец MyPrimaryKey и будет вставлена ​​только в столбцы статуса и приоритета.

0 голосов
/ 15 мая 2015

Удаление сгенерированных базой данных столбцов из набора столбцов перед записью - вот что вам нужно сделать.

Мы используем LINQ-to-SQL для большинства наших операций с базами данных, но используем другой метод для вставки множества записей одновременно, поскольку L2S немного медленен для этого.

У нас есть универсальный метод с именем BulkInsertAll<>, который мы можем использовать в любой таблице, которая использует SqlBulkCopy для внутреннего использования. Мы динамически генерируем столбцы, используя отражение на основе свойств универсального типа. ColumnAttribute находится в файле .cs, созданном из нашего файла .dbml, где мы указали столбец первичного ключа guid как IsDbGenerated="true".

public void BulkInsertAll<T>( IEnumerable<T> entities ) {
    entities = entities.ToArray();

    string cs = Connection.ConnectionString;
    var conn = new SqlConnection( cs );
    conn.Open();

    Type t = typeof( T );

    var tableAttribute = (TableAttribute) t.GetCustomAttributes(
        typeof( TableAttribute ), false
    ).Single();

    var bulkCopy = new SqlBulkCopy( conn ) {
        DestinationTableName = tableAttribute.Name
    };

    var properties = t.GetProperties().Where( EventTypeFilter );

    // This will prevent the bulk insert from attempting to update DBGenerated columns
    // Without, inserts with a guid pk will fail to get the generated sequential id
    // If uninitialized guids are passed to the DB, it will throw duplicate key exceptions
    properties = properties.Where( 
        x => !x.GetCustomAttributes( typeof( ColumnAttribute ), false )
            .Cast<ColumnAttribute>().Any( attr => attr.IsDbGenerated )
    );

    var table = new DataTable();

    foreach( var property in properties ) {
        Type propertyType = property.PropertyType;
        if( propertyType.IsGenericType &&
            propertyType.GetGenericTypeDefinition() == typeof( Nullable<> ) ) {
            propertyType = Nullable.GetUnderlyingType( propertyType );
        }

        table.Columns.Add( new DataColumn( property.Name, propertyType ) );
    }

    foreach( var entity in entities ) {
        table.Rows.Add( 
            properties.Select( 
                property => GetPropertyValue( property.GetValue( entity, null ) ) 
            ).ToArray()
        );
    }

    //specify the mapping for SqlBulk Upload
    foreach( var col in properties ) {
        bulkCopy.ColumnMappings.Add( col.Name, col.Name );
    }

    bulkCopy.WriteToServer( table );

    conn.Close();
}


private bool EventTypeFilter( System.Reflection.PropertyInfo p ) {
    var attribute = Attribute.GetCustomAttribute( p,
        typeof( AssociationAttribute ) ) as AssociationAttribute;

    if( attribute == null ) return true;
    if( attribute.IsForeignKey == false ) return true;

    return false;
}

private object GetPropertyValue( object o ) {
    if( o == null )
        return DBNull.Value;
    return o;
}

И это прекрасно работает. Сущности не будут обновлены с помощью вновь назначенного Guid, поэтому вам нужно будет сделать еще один запрос, чтобы получить их, но новые строки имеют сгенерированные свойства guid в базе данных.

Мы могли бы включить этот .Where фильтр в метод EventTypeFilter, но я не тот, кто написал большую часть этого, и я не прошел через это, чтобы все настроить.

0 голосов
/ 26 сентября 2008

Вы можете только удалить поле MyPrimaryKey из загружаемых данных или изменить структуру таблицы.

Если в поле нет значений, вы говорите SQL, что хотите принудительно ввести в поле ноль, что, очевидно, запрещено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...