in src/SqlAsyncCollector.cs [569:659]
public static TableInformation RetrieveTableInformation(SqlConnection sqlConnection, string fullName, ILogger logger, ServerProperties serverProperties)
{
Dictionary<TelemetryPropertyName, string> sqlConnProps = sqlConnection.AsConnectionProps(serverProperties);
var table = new SqlObject(fullName);
var tableInfoSw = Stopwatch.StartNew();
// Get all column names and types
var columnDefinitionsFromSQL = new Dictionary<string, string>();
var columnDefinitionsSw = Stopwatch.StartNew();
try
{
string getColumnDefinitionsQuery = GetColumnDefinitionsQuery(table);
var cmdColDef = new SqlCommand(getColumnDefinitionsQuery, sqlConnection);
using (SqlDataReader rdr = cmdColDef.ExecuteReaderWithLogging(logger))
{
while (rdr.Read())
{
string columnName = rdr[ColumnName].ToString();
columnDefinitionsFromSQL.Add(columnName, rdr[ColumnDefinition].ToString());
}
columnDefinitionsSw.Stop();
TelemetryInstance.TrackDuration(TelemetryEventName.GetColumnDefinitions, columnDefinitionsSw.ElapsedMilliseconds, sqlConnProps);
}
}
catch (Exception ex)
{
TelemetryInstance.TrackException(TelemetryErrorName.GetColumnDefinitions, ex, sqlConnProps);
// Throw a custom error so that it's easier to decipher.
string message = $"Encountered exception while retrieving column names and types for table {table}. Cannot generate upsert command without them.";
throw new InvalidOperationException(message, ex);
}
if (columnDefinitionsFromSQL.Count == 0)
{
string message = $"Table {table} does not exist.";
var ex = new InvalidOperationException(message);
TelemetryInstance.TrackException(TelemetryErrorName.GetColumnDefinitionsTableDoesNotExist, ex, sqlConnProps);
throw ex;
}
// Query SQL for table Primary Keys
var primaryKeys = new List<PrimaryKey>();
var primaryKeysSw = Stopwatch.StartNew();
try
{
string getPrimaryKeysQuery = GetPrimaryKeysQuery(table);
var cmd = new SqlCommand(getPrimaryKeysQuery, sqlConnection);
using (SqlDataReader rdr = cmd.ExecuteReaderWithLogging(logger))
{
while (rdr.Read())
{
string columnName = rdr[ColumnName].ToString();
primaryKeys.Add(new PrimaryKey(columnName, bool.Parse(rdr[IsIdentity].ToString()), bool.Parse(rdr[HasDefault].ToString())));
}
primaryKeysSw.Stop();
TelemetryInstance.TrackDuration(TelemetryEventName.GetPrimaryKeys, primaryKeysSw.ElapsedMilliseconds, sqlConnProps);
}
}
catch (Exception ex)
{
TelemetryInstance.TrackException(TelemetryErrorName.GetPrimaryKeys, ex, sqlConnProps);
// Throw a custom error so that it's easier to decipher.
string message = $"Encountered exception while retrieving primary keys for table {table}. Cannot generate upsert command without them.";
throw new InvalidOperationException(message, ex);
}
if (primaryKeys.Count == 0)
{
string message = $"Did not retrieve any primary keys for {table}. Cannot generate upsert command without them.";
var ex = new InvalidOperationException(message);
TelemetryInstance.TrackException(TelemetryErrorName.NoPrimaryKeys, ex, sqlConnProps);
throw ex;
}
// Match SQL Primary Key column names to POCO property objects. Ensure none are missing.
IEnumerable<PropertyInfo> primaryKeyProperties = typeof(T).GetProperties().Where(f => primaryKeys.Any(k => string.Equals(k.Name, f.Name, StringComparison.Ordinal)));
bool hasIdentityColumnPrimaryKeys = primaryKeys.Any(k => k.IsIdentity);
bool hasDefaultColumnPrimaryKeys = primaryKeys.Any(k => k.HasDefault);
tableInfoSw.Stop();
var durations = new Dictionary<TelemetryMeasureName, double>()
{
{ TelemetryMeasureName.GetColumnDefinitionsDurationMs, columnDefinitionsSw.ElapsedMilliseconds },
{ TelemetryMeasureName.GetPrimaryKeysDurationMs, primaryKeysSw.ElapsedMilliseconds }
};
sqlConnProps.Add(TelemetryPropertyName.HasIdentityColumnPrimaryKeys, hasIdentityColumnPrimaryKeys.ToString());
sqlConnProps.Add(TelemetryPropertyName.HasDefaultColumnPrimaryKeys, hasDefaultColumnPrimaryKeys.ToString());
TelemetryInstance.TrackDuration(TelemetryEventName.GetTableInfo, tableInfoSw.ElapsedMilliseconds, sqlConnProps, durations);
logger.LogDebug($"RetrieveTableInformation DB and Table: {sqlConnection.Database}.{fullName}. Primary keys: [{string.Join(",", primaryKeys.Select(pk => pk.Name))}].\nSQL Column and Definitions: [{string.Join(",", columnDefinitionsFromSQL)}]");