Удаление сгенерированных базой данных столбцов из набора столбцов перед записью - вот что вам нужно сделать.
Мы используем 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, но я не тот, кто написал большую часть этого, и я не прошел через это, чтобы все настроить.