@@ -202,7 +202,7 @@ private async Task UpsertRowsAsync(IList<T> rows, SqlAttribute attribute, IConfi
202202 {
203203 TelemetryInstance . TrackEvent ( TelemetryEventName . TableInfoCacheMiss , props ) ;
204204 // set the columnNames for supporting T as JObject since it doesn't have columns in the member info.
205- tableInfo = TableInformation . RetrieveTableInformation ( connection , fullTableName , this . _logger , GetColumnNamesFromItem ( rows . First ( ) ) , this . _serverProperties ) ;
205+ tableInfo = TableInformation . RetrieveTableInformation ( connection , fullTableName , this . _logger , this . _serverProperties ) ;
206206 var policy = new CacheItemPolicy
207207 {
208208 // Re-look up the primary key(s) after timeout (default timeout is 10 minutes)
@@ -244,7 +244,24 @@ private async Task UpsertRowsAsync(IList<T> rows, SqlAttribute attribute, IConfi
244244 }
245245
246246 var table = new SqlObject ( fullTableName ) ;
247- string mergeOrInsertQuery = tableInfo . QueryType == QueryType . Insert ? TableInformation . GetInsertQuery ( table , bracketedColumnNamesFromItem ) :
247+
248+ IEnumerable < string > objectColumnNames = GetColumnNamesFromItem ( rows . First ( ) ) ;
249+ IEnumerable < string > primaryKeysFromObject = objectColumnNames . Where ( f => tableInfo . PrimaryKeys . Any ( k => string . Equals ( k . Name , f , StringComparison . Ordinal ) ) ) ;
250+ IEnumerable < PrimaryKey > missingPrimaryKeysFromItem = tableInfo . PrimaryKeys
251+ . Where ( k => ! primaryKeysFromObject . Contains ( k . Name ) ) ;
252+ // If none of the primary keys are an identity column or have a default value then we require that all primary keys be present in the POCO so we can
253+ // generate the MERGE statement correctly
254+ if ( ! tableInfo . HasIdentityColumnPrimaryKeys && ! tableInfo . HasDefaultColumnPrimaryKeys && missingPrimaryKeysFromItem . Any ( ) )
255+ {
256+ string message = $ "All primary keys for SQL table { table } need to be found in '{ typeof ( T ) } .' Missing primary keys: [{ string . Join ( "," , missingPrimaryKeysFromItem ) } ]";
257+ var ex = new InvalidOperationException ( message ) ;
258+ TelemetryInstance . TrackException ( TelemetryErrorName . MissingPrimaryKeys , ex , connection . AsConnectionProps ( this . _serverProperties ) ) ;
259+ throw ex ;
260+ }
261+ // If any identity columns or columns with default values aren't included in the object then we have to generate a basic insert since the merge statement expects all primary key
262+ // columns to exist. (the merge statement can handle nullable columns though if those exist)
263+ QueryType queryType = ( tableInfo . HasIdentityColumnPrimaryKeys || tableInfo . HasDefaultColumnPrimaryKeys ) && missingPrimaryKeysFromItem . Any ( ) ? QueryType . Insert : QueryType . Merge ;
264+ string mergeOrInsertQuery = queryType == QueryType . Insert ? TableInformation . GetInsertQuery ( table , bracketedColumnNamesFromItem ) :
248265 TableInformation . GetMergeQuery ( tableInfo . PrimaryKeys , table , bracketedColumnNamesFromItem ) ;
249266
250267 var transactionSw = Stopwatch . StartNew ( ) ;
@@ -415,28 +432,28 @@ public class TableInformation
415432 public IEnumerable < string > ColumnDefinitions => this . Columns . Select ( c => $ "{ c . Key } { c . Value } ") ;
416433
417434 /// <summary>
418- /// Whether to use an insert query or merge query.
435+ /// Whether at least one of the primary keys on this table is an identity column
419436 /// </summary>
420- public QueryType QueryType { get ; }
437+ public bool HasIdentityColumnPrimaryKeys { get ; }
421438
422439 /// <summary>
423- /// Whether at least one of the primary keys on this table is an identity column
440+ /// Whether at least one of the primary keys on this table has a default value
424441 /// </summary>
425- public bool HasIdentityColumnPrimaryKeys { get ; }
442+ public bool HasDefaultColumnPrimaryKeys { get ; }
443+
426444 /// <summary>
427445 /// Settings to use when serializing the POCO into SQL.
428446 /// Only serialize properties and fields that correspond to SQL columns.
429447 /// </summary>
430448 public JsonSerializerSettings JsonSerializerSettings { get ; }
431449
432- public TableInformation ( List < PrimaryKey > primaryKeys , IEnumerable < PropertyInfo > primaryKeyProperties , IDictionary < string , string > columns , QueryType queryType , bool hasIdentityColumnPrimaryKeys )
450+ public TableInformation ( List < PrimaryKey > primaryKeys , IEnumerable < PropertyInfo > primaryKeyProperties , IDictionary < string , string > columns , bool hasIdentityColumnPrimaryKeys , bool hasDefaultColumnPrimaryKeys )
433451 {
434452 this . PrimaryKeys = primaryKeys ;
435453 this . PrimaryKeyProperties = primaryKeyProperties ;
436454 this . Columns = columns ;
437- this . QueryType = queryType ;
438455 this . HasIdentityColumnPrimaryKeys = hasIdentityColumnPrimaryKeys ;
439-
456+ this . HasDefaultColumnPrimaryKeys = hasDefaultColumnPrimaryKeys ;
440457 // Convert datetime strings to ISO 8061 format to avoid potential errors on the server when converting into a datetime. This
441458 // is the only format that are an international standard.
442459 // https://docs.microsoft.com/previous-versions/sql/sql-server-2008-r2/ms180878(v=sql.105)
@@ -546,10 +563,9 @@ WHEN NOT MATCHED THEN
546563 /// <param name="sqlConnection">An open connection with which to query SQL against</param>
547564 /// <param name="fullName">Full name of table, including schema (if exists).</param>
548565 /// <param name="logger">ILogger used to log any errors or warnings.</param>
549- /// <param name="objectColumnNames">Column names from the object</param>
550566 /// <param name="serverProperties">EngineEdition and Edition of the target Sql Server.</param>
551567 /// <returns>TableInformation object containing primary keys, column types, etc.</returns>
552- public static TableInformation RetrieveTableInformation ( SqlConnection sqlConnection , string fullName , ILogger logger , IEnumerable < string > objectColumnNames , ServerProperties serverProperties )
568+ public static TableInformation RetrieveTableInformation ( SqlConnection sqlConnection , string fullName , ILogger logger , ServerProperties serverProperties )
553569 {
554570 Dictionary < TelemetryPropertyName , string > sqlConnProps = sqlConnection . AsConnectionProps ( serverProperties ) ;
555571 var table = new SqlObject ( fullName ) ;
@@ -627,36 +643,20 @@ public static TableInformation RetrieveTableInformation(SqlConnection sqlConnect
627643
628644 // Match SQL Primary Key column names to POCO property objects. Ensure none are missing.
629645 IEnumerable < PropertyInfo > primaryKeyProperties = typeof ( T ) . GetProperties ( ) . Where ( f => primaryKeys . Any ( k => string . Equals ( k . Name , f . Name , StringComparison . Ordinal ) ) ) ;
630- IEnumerable < string > primaryKeysFromObject = objectColumnNames . Where ( f => primaryKeys . Any ( k => string . Equals ( k . Name , f , StringComparison . Ordinal ) ) ) ;
631- IEnumerable < PrimaryKey > missingPrimaryKeysFromItem = primaryKeys
632- . Where ( k => ! primaryKeysFromObject . Contains ( k . Name ) ) ;
633646 bool hasIdentityColumnPrimaryKeys = primaryKeys . Any ( k => k . IsIdentity ) ;
634647 bool hasDefaultColumnPrimaryKeys = primaryKeys . Any ( k => k . HasDefault ) ;
635- // If none of the primary keys are an identity column or have a default value then we require that all primary keys be present in the POCO so we can
636- // generate the MERGE statement correctly
637- if ( ! hasIdentityColumnPrimaryKeys && ! hasDefaultColumnPrimaryKeys && missingPrimaryKeysFromItem . Any ( ) )
638- {
639- string message = $ "All primary keys for SQL table { table } need to be found in '{ typeof ( T ) } .' Missing primary keys: [{ string . Join ( "," , missingPrimaryKeysFromItem ) } ]";
640- var ex = new InvalidOperationException ( message ) ;
641- TelemetryInstance . TrackException ( TelemetryErrorName . MissingPrimaryKeys , ex , sqlConnProps ) ;
642- throw ex ;
643- }
644-
645- // If any identity columns or columns with default values aren't included in the object then we have to generate a basic insert since the merge statement expects all primary key
646- // columns to exist. (the merge statement can handle nullable columns though if those exist)
647- QueryType queryType = ( hasIdentityColumnPrimaryKeys || hasDefaultColumnPrimaryKeys ) && missingPrimaryKeysFromItem . Any ( ) ? QueryType . Insert : QueryType . Merge ;
648648
649649 tableInfoSw . Stop ( ) ;
650650 var durations = new Dictionary < TelemetryMeasureName , double > ( )
651651 {
652652 { TelemetryMeasureName . GetColumnDefinitionsDurationMs , columnDefinitionsSw . ElapsedMilliseconds } ,
653653 { TelemetryMeasureName . GetPrimaryKeysDurationMs , primaryKeysSw . ElapsedMilliseconds }
654654 } ;
655- sqlConnProps . Add ( TelemetryPropertyName . QueryType , queryType . ToString ( ) ) ;
656- sqlConnProps . Add ( TelemetryPropertyName . HasIdentityColumn , hasIdentityColumnPrimaryKeys . ToString ( ) ) ;
655+ sqlConnProps . Add ( TelemetryPropertyName . HasIdentityColumnPrimaryKeys , hasIdentityColumnPrimaryKeys . ToString ( ) ) ;
656+ sqlConnProps . Add ( TelemetryPropertyName . HasDefaultColumnPrimaryKeys , hasDefaultColumnPrimaryKeys . ToString ( ) ) ;
657657 TelemetryInstance . TrackDuration ( TelemetryEventName . GetTableInfo , tableInfoSw . ElapsedMilliseconds , sqlConnProps , durations ) ;
658- logger . LogDebug ( $ "RetrieveTableInformation DB and Table: { sqlConnection . Database } .{ fullName } . Primary keys: [{ string . Join ( "," , primaryKeys . Select ( pk => pk . Name ) ) } ].\n SQL Column and Definitions: [{ string . Join ( "," , columnDefinitionsFromSQL ) } ]\n Object columns: [ { string . Join ( "," , objectColumnNames ) } ] ") ;
659- return new TableInformation ( primaryKeys , primaryKeyProperties , columnDefinitionsFromSQL , queryType , hasIdentityColumnPrimaryKeys ) ;
658+ logger . LogDebug ( $ "RetrieveTableInformation DB and Table: { sqlConnection . Database } .{ fullName } . Primary keys: [{ string . Join ( "," , primaryKeys . Select ( pk => pk . Name ) ) } ].\n SQL Column and Definitions: [{ string . Join ( "," , columnDefinitionsFromSQL ) } ]") ;
659+ return new TableInformation ( primaryKeys , primaryKeyProperties , columnDefinitionsFromSQL , hasIdentityColumnPrimaryKeys , hasDefaultColumnPrimaryKeys ) ;
660660 }
661661 }
662662
0 commit comments