using Dapper; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json.Linq; using OI.Metrology.Server.Models; using OI.Metrology.Shared.DataModels; using OI.Metrology.Shared.Models.Stateless; using OI.Metrology.Shared.Repositories; using System.Data; using System.Data.Common; using System.Text; using System.Transactions; #pragma warning disable CS8600, CS8602, CS8603, CS8604, CS8625 namespace OI.Metrology.Server.Repositories; public class MetrologyRepository : IMetrologyRepository { private readonly string _MockRoot; private readonly string _RepositoryName; private readonly IMemoryCache _MemoryCache; private readonly IDbConnectionFactory _DBConnectionFactory; public MetrologyRepository(AppSettings appSettings, IDbConnectionFactory dbConnectionFactory, IMemoryCache memoryCache) { _MemoryCache = memoryCache; _MockRoot = appSettings.MockRoot; _DBConnectionFactory = dbConnectionFactory; _RepositoryName = nameof(MetrologyRepository)[..^10]; } protected DbProviderFactory GetDbProviderFactory(IDbConnection conn) => DbProviderFactories.GetFactory(conn.GetType().Namespace); internal static TransactionScope StartTransaction() => new(); protected void CacheItem(string key, object v) { System.Diagnostics.Debug.WriteLine("CacheItem: " + key); _ = _MemoryCache.Set(key, v, new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromHours(1))); } internal IEnumerable GetToolTypes() { IEnumerable cached; string cacheKey = "GetToolTypes"; if (_MemoryCache.TryGetValue(cacheKey, out cached)) return cached; using DbConnection conn = _DBConnectionFactory.GetDbConnection(); IEnumerable r = conn.Query("SELECT * FROM ToolType"); CacheItem(cacheKey, r); return r; } internal ToolType GetToolTypeByName(string name) { ToolType cached; string cacheKey = "GetToolTypeByName_" + name; if (_MemoryCache.TryGetValue(cacheKey, out cached)) return cached; using DbConnection conn = _DBConnectionFactory.GetDbConnection(); ToolType r = conn.QueryFirstOrDefault( "SELECT * FROM ToolType WHERE ToolTypeName = @name", new { name }); CacheItem(cacheKey, r); return r; } internal ToolType GetToolTypeByID(int id) { ToolType cached; string cacheKey = "GetToolTypeByID_" + id.ToString(); if (_MemoryCache.TryGetValue(cacheKey, out cached)) return cached; using DbConnection conn = _DBConnectionFactory.GetDbConnection(); ToolType r = conn.QueryFirstOrDefault( "SELECT * FROM ToolType WHERE ID = @id", new { id }); CacheItem(cacheKey, r); return r; } internal IEnumerable GetToolTypeMetadataByToolTypeID(int id) { IEnumerable cached; string cacheKey = "GetToolTypeMetadataByToolTypeID_" + id.ToString(); if (_MemoryCache.TryGetValue(cacheKey, out cached)) return cached; using DbConnection conn = _DBConnectionFactory.GetDbConnection(); IEnumerable r = conn.Query( "SELECT * FROM ToolTypeMetadata WHERE ToolTypeID = @id", new { id }); CacheItem(cacheKey, r); return r; } internal long InsertToolDataJSON(JToken jsonrow, long headerId, List metaData, string tableName) { long r = -1; using (DbConnection conn = _DBConnectionFactory.GetDbConnection()) { bool isHeader = headerId <= 0; // get fields from metadata List fields = metaData.Where(md => md.Header == isHeader).ToList(); // maps ApiName to ColumnName Dictionary fieldmap = new(); // store property name of container field string containerField = null; // maps container ApiName to ColumnName Dictionary containerFieldMap = new(); // build field map foreach (ToolTypeMetadata f in fields) { if ((f.ApiName is not null) && f.ApiName.Contains('\\')) { string n = f.ApiName.Split('\\')[0].Trim().ToUpper(); if (containerField is null) containerField = n; else if (!string.Equals(containerField, n)) throw new Exception("Only one container field is allowed"); string pn = f.ApiName.Split('\\')[1].Trim().ToUpper(); containerFieldMap.Add(pn, f.ColumnName.Trim()); } else if (!string.IsNullOrWhiteSpace(f.ApiName) && !string.IsNullOrWhiteSpace(f.ColumnName)) { fieldmap.Add(f.ApiName.Trim().ToUpper(), f.ColumnName.Trim()); } } if (containerField is null) { // No container field, just insert a single row r = InsertRowFromJSON(conn, tableName, jsonrow, fieldmap, headerId, null, null); } else { // Find the container field in the json JProperty contJP = jsonrow.Children().Where(c => string.Equals(c.Name.Trim(), containerField, StringComparison.OrdinalIgnoreCase)).SingleOrDefault(); if ((contJP is not null) && (contJP.Value is JArray array)) { JArray contRows = array; // Insert a row for each row in the container field foreach (JToken contRow in contRows) { r = InsertRowFromJSON(conn, tableName, jsonrow, fieldmap, headerId, contRow, containerFieldMap); } } else throw new Exception("Invalid container field type"); } } return r; } protected void AddParameter(IDbCommand cmd, string name, object value) { IDbDataParameter p = cmd.CreateParameter(); p.ParameterName = name; p.Value = value; _ = cmd.Parameters.Add(p); } protected long InsertRowFromJSON( IDbConnection conn, string tableName, JToken jsonrow, Dictionary fieldmap, long headerId, JToken containerrow, Dictionary containerFieldmap) { // Translate the json into a SQL INSERT using the field map IDbCommand cmd = conn.CreateCommand(); string columns = "INSERT INTO [" + tableName + "]("; string parms = ") SELECT "; int parmnumber = 1; if (headerId > 0) { columns += "HeaderID,"; parms += "@HeaderID,"; AddParameter(cmd, "@HeaderID", headerId); } foreach (JProperty jp in jsonrow.Children()) { string apifield = jp.Name.Trim().ToUpper(); if (fieldmap.TryGetValue(apifield, out string value)) { string parmname = string.Format("@p{0}", parmnumber); columns += string.Format("[{0}],", value); parms += parmname; parms += ","; parmnumber += 1; object sqlValue = ((JValue)jp.Value).Value; sqlValue ??= DBNull.Value; AddParameter(cmd, parmname, sqlValue); } } if ((containerrow is not null) && (containerFieldmap is not null)) { foreach (JProperty jp in containerrow.Children()) { string apifield = jp.Name.Trim().ToUpper(); if (containerFieldmap.TryGetValue(apifield, out string value)) { string parmname = string.Format("@p{0}", parmnumber); columns += string.Format("[{0}],", value); parms += parmname; parms += ","; parmnumber += 1; object sqlValue = ((JValue)jp.Value).Value; sqlValue ??= DBNull.Value; AddParameter(cmd, parmname, sqlValue); } } } if (parmnumber == 1) throw new Exception("JSON had no fields"); cmd.CommandText = columns.TrimEnd(',') + parms.TrimEnd(',') + ";SELECT SCOPE_IDENTITY();"; object o = cmd.ExecuteScalar(); if ((o is null) || Convert.IsDBNull(o)) throw new Exception("Unexpected query result"); return Convert.ToInt64(o); } internal DataTable ExportData(string spName, DateTime startTime, DateTime endTime) { DataTable dt = new(); DateTime endTimeLocal = endTime.ToLocalTime(); DateTime startTimeLocal = startTime.ToLocalTime(); using (DbConnection conn = _DBConnectionFactory.GetDbConnection()) { DbProviderFactory factory = GetDbProviderFactory(conn); DbCommand cmd = factory.CreateCommand(); cmd.Connection = conn; cmd.CommandText = spName; cmd.CommandType = CommandType.StoredProcedure; AddParameter(cmd, "@StartTime", startTimeLocal); AddParameter(cmd, "@EndTime", endTimeLocal); DbDataAdapter da = factory.CreateDataAdapter(); da.SelectCommand = cmd; _ = da.Fill(dt); } return dt; } protected string FormDynamicSelectQuery(IEnumerable fields, string tableName) { StringBuilder sb = new(); _ = sb.Append("SELECT "); bool firstField = true; foreach (ToolTypeMetadata f in fields) { if (!string.IsNullOrWhiteSpace(f.ColumnName)) { if (!firstField) _ = sb.Append(','); if (f.GridAttributes is not null && f.GridAttributes.Contains("isNull")) { _ = sb.AppendFormat("{0}", "ISNULL(" + f.ColumnName + ", '')[" + f.ColumnName + "]"); } else { _ = sb.AppendFormat("[{0}]", f.ColumnName); } firstField = false; } } _ = sb.AppendFormat(" FROM [{0}] ", tableName); return sb.ToString(); } internal DataTable GetHeaders(int toolTypeId, string? startTime, string? endTime, int? pageNo, int? pageSize, long? headerId, out long totalRecords) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); IEnumerable md = GetToolTypeMetadataByToolTypeID(toolTypeId) ?? throw new Exception("Invalid tool type metadata"); DataTable dt = new(); using (DbConnection conn = _DBConnectionFactory.GetDbConnection()) { StringBuilder sb = new(); _ = sb.Append( FormDynamicSelectQuery( md.Where(m => m.Header == true).ToList(), tt.HeaderTableName) ); DbProviderFactory factory = GetDbProviderFactory(conn); DbCommand cmd = factory.CreateCommand(); string whereClause = ""; if (headerId.HasValue && headerId.Value > 0) { whereClause = "ID = @HeaderID "; AddParameter(cmd, "@HeaderID", headerId.Value); } else { if (startTime is not null) { whereClause = "[Date] >= @StartTime "; DateTime startTimeLocal = DateTime.Parse(startTime).ToLocalTime(); AddParameter(cmd, "@StartTime", startTimeLocal.ToString("yyyy-MM-dd HH:mm:ss")); } if (endTime is not null) { DateTime endTimeLocal = DateTime.Parse(endTime).ToLocalTime(); TimeSpan timeSpan = new(DateTime.Now.Ticks - endTimeLocal.Ticks); if (timeSpan.TotalMinutes > 5) { if (whereClause.Length > 0) whereClause += "AND "; whereClause += "[Date] <= @EndTime "; AddParameter(cmd, "@EndTime", endTimeLocal.ToString("yyyy-MM-dd HH:mm:ss")); } } } if (whereClause.Length > 0) { _ = sb.Append("WHERE "); _ = sb.Append(whereClause); } if (pageNo.HasValue && pageSize.HasValue) { _ = sb.Append("ORDER BY [Date] DESC OFFSET @PageNum * @PageSize ROWS FETCH NEXT @PageSize ROWS ONLY"); AddParameter(cmd, "@PageNum", pageNo.Value); AddParameter(cmd, "@PageSize", pageSize.Value); } else { _ = sb.Append("ORDER BY [Date] DESC"); } cmd.Connection = conn; cmd.CommandText = sb.ToString(); cmd.CommandType = CommandType.Text; DbDataAdapter da = factory.CreateDataAdapter(); da.SelectCommand = cmd; _ = da.Fill(dt); cmd.CommandText = "SELECT COUNT(*) FROM [" + tt.HeaderTableName + "] "; if (whereClause.Length > 0) { cmd.CommandText += "WHERE "; cmd.CommandText += whereClause; } totalRecords = Convert.ToInt64(cmd.ExecuteScalar()); } return dt; } internal DataTable GetData(int toolTypeId, long headerid) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); IEnumerable md = GetToolTypeMetadataByToolTypeID(toolTypeId) ?? throw new Exception("Invalid tool type metadata"); DataTable dt = new(); using (DbConnection conn = _DBConnectionFactory.GetDbConnection()) { StringBuilder sb = new(); _ = sb.Append( FormDynamicSelectQuery( md.Where(m => m.Header == false).OrderBy(m => m.GridDisplayOrder).ToList(), tt.DataTableName) ); DbProviderFactory factory = GetDbProviderFactory(conn); DbCommand cmd = factory.CreateCommand(); _ = sb.Append("WHERE [HeaderID] = @HeaderID "); if (!string.IsNullOrWhiteSpace(tt.DataGridSortBy)) { _ = sb.AppendFormat("ORDER BY {0} ", tt.DataGridSortBy); } AddParameter(cmd, "@HeaderID", headerid); cmd.Connection = conn; cmd.CommandText = sb.ToString(); cmd.CommandType = CommandType.Text; DbDataAdapter da = factory.CreateDataAdapter(); da.SelectCommand = cmd; _ = da.Fill(dt); } // this code will add a couple of rows with stats calculations if (!string.IsNullOrWhiteSpace(tt.DataGridStatsColumn)) { if (dt.Columns.Contains(tt.DataGridStatsColumn)) { double sumAll = 0; double sumAllQ = 0; foreach (DataRow dr in dt.Rows) { try { object v = dr[tt.DataGridStatsColumn]; if (!Convert.IsDBNull(v)) { double d = Convert.ToDouble(v); sumAll += d; sumAllQ += d * d; } } catch { } } double length = Convert.ToDouble(dt.Rows.Count); double meanAverage = Math.Round(sumAll / length, 4); double stdDev = Math.Sqrt((sumAllQ - sumAll * sumAll / length) * (1.0d / (length - 1))); string stdDevStr = ""; if (tt.DataGridStatsStdDevType == "%") stdDevStr = Math.Round(stdDev / meanAverage * 100.0d, 2).ToString("0.####") + "%"; else stdDevStr = Math.Round(stdDev, 4).ToString("0.####"); int labelIndex = dt.Columns[tt.DataGridStatsColumn].Ordinal - 1; DataRow newrow = dt.NewRow(); newrow["ID"] = -1; newrow[labelIndex] = "Average"; newrow[tt.DataGridStatsColumn] = meanAverage; dt.Rows.Add(newrow); newrow = dt.NewRow(); newrow["ID"] = -2; newrow[labelIndex] = "Std Dev"; newrow[tt.DataGridStatsColumn] = stdDevStr; dt.Rows.Add(newrow); } } return dt; } internal Guid GetHeaderAttachmentID(int toolTypeId, long headerId) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = $"UPDATE [{tt.HeaderTableName}] SET AttachmentID = NEWID() WHERE ID = @HeaderID AND AttachmentID IS NULL; " + $"SELECT AttachmentID FROM [{tt.HeaderTableName}] WHERE ID = @HeaderID"; return conn.ExecuteScalar(sql, param: new { HeaderID = headerId }); } internal string GetHeaderInsertDate(int toolTypeId, long headerId) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = $"SELECT CONVERT(varchar, case when [InsertDate] < [Date] or [Date] is null then [InsertDate] else [Date] end, 120) d FROM[{tt.HeaderTableName}] where ID = @HeaderID"; return conn.ExecuteScalar(sql, param: new { HeaderID = headerId }); } internal string GetAttachmentInsertDateByGUID(string tableName, Guid attachmentId) { using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = ""; if (tableName is "SP1RunData" or "TencorRunData") { sql = $"SELECT [InsertDate] FROM[{tableName}] where AttachmentID = @AttachmentID"; } else { sql = $"SELECT [InsertDate] FROM[{tableName}] where AttachmentID = @AttachmentID"; } return conn.ExecuteScalar(sql, param: new { AttachmentID = attachmentId }); } internal void SetHeaderDirName(string tableName, long headerId, string dateDir) { using DbConnection conn = _DBConnectionFactory.GetDbConnection(); _ = conn.Execute($"UPDATE [{tableName}] SET AttachDirName = @AttachDirName WHERE ID = @HeaderID;", new { HeaderID = headerId, AttachDirName = dateDir }); } internal Guid GetDataAttachmentID(int toolTypeId, long headerId, string title) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = $"UPDATE [{tt.DataTableName}] SET AttachmentID = NEWID() WHERE HeaderID = @HeaderID AND Title = @Title AND AttachmentID IS NULL; " + $"SELECT AttachmentID FROM [{tt.DataTableName}] WHERE HeaderID = @HeaderID AND Title = @Title"; return conn.ExecuteScalar(sql, param: new { HeaderID = headerId, Title = title }); } // J Ouellette Added internal string GetDataInsertDate(int toolTypeId, long headerId, string title) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = ""; if (tt.DataTableName is "" or "") { sql = $"SELECT InsertDate FROM [{tt.DataTableName}] WHERE HeaderID = @HeaderID AND Title = @Title"; } else { sql = $"SELECT [InsertDate] FROM[{tt.DataTableName}] WHERE HeaderID = @HeaderID AND Title = @Title"; } return conn.ExecuteScalar(sql, param: new { HeaderID = headerId, Title = title }); } internal void SetDataDirName(string tableName, long headerId, string title, string dateDir) { using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = $"UPDATE [{tableName}] SET AttachDirName = @AttachDirName WHERE HeaderID = @HeaderID AND Title = @Title;"; _ = conn.Execute(sql, param: new { HeaderID = headerId, Title = title, AttachDirName = dateDir }); } internal void PurgeExistingData(int toolTypeId, string title) { using DbConnection conn = _DBConnectionFactory.GetDbConnection(); _ = conn.Execute("PurgeExistingData", param: new { ToolTypeID = toolTypeId, Title = title }, commandType: CommandType.StoredProcedure); } internal DataSet GetOIExportData(int toolTypeId, long headerid) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); if (string.IsNullOrWhiteSpace(tt.OIExportSPName)) throw new Exception("OpenInsight export not available for " + tt.ToolTypeName); DataSet ds = new(); using (DbConnection conn = _DBConnectionFactory.GetDbConnection()) { DbProviderFactory factory = GetDbProviderFactory(conn); DbCommand cmd = factory.CreateCommand(); cmd.Connection = conn; cmd.CommandText = tt.OIExportSPName; cmd.CommandType = CommandType.StoredProcedure; AddParameter(cmd, "@ID", headerid); DbDataAdapter da = factory.CreateDataAdapter(); da.SelectCommand = cmd; _ = da.Fill(ds); } return ds; } private HeaderCommon[] GetHeaderTitles() { IEnumerable results; ToolType[] toolTypes = GetToolTypes().ToArray(); if (toolTypes.Length == 0 || toolTypes.FirstOrDefault() is null) throw new Exception("Invalid tool type ID"); ToolType tt; StringBuilder stringBuilder = new(); using DbConnection conn = _DBConnectionFactory.GetDbConnection(); _ = stringBuilder.Append(" SELECT * FROM ( "); for (int i = 0; i < toolTypes.Length; i++) { tt = toolTypes[i]; _ = stringBuilder.Append($" SELECT ID, InsertDate, AttachmentID, Title, [Date], {tt.ID} AS ToolTypeID, '{tt.ToolTypeName}' AS ToolTypeName, Reactor, RDS, PSN FROM {tt.HeaderTableName} "); if (i != toolTypes.Length - 1) _ = stringBuilder.Append(" UNION ALL "); } _ = stringBuilder.Append(" ) AS A ORDER BY A.[Date] DESC "); results = conn.Query(stringBuilder.ToString()).ToArray(); return results.ToArray(); } internal HeaderCommon[] GetHeaderTitles(int? toolTypeId, int? pageNo, int? pageSize, out long totalRecords) { HeaderCommon[] headers; if (toolTypeId is not null && (pageNo is not null || pageSize is not null)) throw new Exception(); if (toolTypeId is null) { headers = GetHeaderTitles(); totalRecords = headers.Length; return headers; } ToolType tt = GetToolTypeByID(toolTypeId.Value) ?? throw new Exception("Invalid tool type ID"); using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = $"SELECT ID, InsertDate, AttachmentID, Title, [Date], {tt.ID} AS ToolTypeID, '{tt.ToolTypeName}' AS ToolTypeName, Reactor, RDS, PSN FROM {tt.HeaderTableName} ORDER BY [Date] DESC "; if (pageNo.HasValue && pageSize.HasValue) { sql += "OFFSET @PageNum * @PageSize ROWS FETCH NEXT @PageSize ROWS ONLY"; headers = conn.Query(sql, param: new { PageNum = pageNo.Value, PageSize = pageSize.Value }).ToArray(); } else { headers = conn.Query(sql).ToArray(); } sql = $"SELECT COUNT(*) FROM [{tt.HeaderTableName}] "; totalRecords = Convert.ToInt64(conn.ExecuteScalar(sql)); return headers; } internal IEnumerable> GetHeaderFields(int toolTypeId, long headerid) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); IEnumerable md = GetToolTypeMetadataByToolTypeID(toolTypeId) ?? throw new Exception("Invalid tool type metadata"); List> r = new(); using (DbConnection conn = _DBConnectionFactory.GetDbConnection()) { DbProviderFactory factory = GetDbProviderFactory(conn); DbCommand cmd = factory.CreateCommand(); cmd.Connection = conn; cmd.CommandText = $"SELECT * FROM [{tt.HeaderTableName}] WHERE ID = @HeaderID"; AddParameter(cmd, "@HeaderID", headerid); DataTable dt = new(); DbDataAdapter da = factory.CreateDataAdapter(); da.SelectCommand = cmd; _ = da.Fill(dt); DataRow dr = null; if (dt.Rows.Count > 0) dr = dt.Rows[0]; foreach (ToolTypeMetadata m in md.Where(m => m.Header == true && m.TableDisplayOrder > 0).OrderBy(m => m.TableDisplayOrder)) { string v = ""; if (dr is not null) { object o = dr[m.ColumnName]; if (o is not null && !Convert.IsDBNull(o)) v = Convert.ToString(o); } KeyValuePair kvp = new(m.DisplayTitle, v); r.Add(kvp); } } return r; } internal IEnumerable GetAwaitingDisposition() { IEnumerable? r; if (!string.IsNullOrEmpty(_MockRoot)) { string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), $"{_RepositoryName}-{nameof(IMetrologyRepository.GetAwaitingDisposition)}.json")); r = System.Text.Json.JsonSerializer.Deserialize>(json); if (r is null) throw new NullReferenceException(nameof(r)); } else { using DbConnection conn = _DBConnectionFactory.GetDbConnection(); r = conn.Query("GetAwaitingDispo", commandType: CommandType.StoredProcedure); } return r; } internal int UpdateReviewDate(int toolTypeId, long headerId, bool clearDate) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); using DbConnection conn = _DBConnectionFactory.GetDbConnection(); if (clearDate) { // if it's already past the 6 hour window, then it won't show in queue anyway, so need to return value so we can show that string sql = $"SELECT DATEDIFF(HH, INSERTDATE, GETDATE()) FROM [{tt.HeaderTableName}] WHERE ID = @HeaderID"; int hrs = conn.ExecuteScalar(sql, param: new { HeaderId = headerId }); _ = conn.Execute($"UPDATE [{tt.HeaderTableName}] SET ReviewDate = NULL WHERE ID = @HeaderID", new { HeaderID = headerId }); return hrs; } else { _ = conn.Execute($"UPDATE [{tt.HeaderTableName}] SET ReviewDate = GETDATE() WHERE ID = @HeaderID", new { HeaderID = headerId }); return 1; } } internal Guid GetHeaderAttachmentIDByTitle(int toolTypeId, string title) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = $"SELECT TOP 1 AttachmentID FROM [{tt.HeaderTableName}] WHERE Title = @Title ORDER BY InsertDate DESC"; return conn.ExecuteScalar(sql, param: new { Title = title }); } internal Guid GetDataAttachmentIDByTitle(int toolTypeId, string title) { ToolType tt = GetToolTypeByID(toolTypeId) ?? throw new Exception("Invalid tool type ID"); using DbConnection conn = _DBConnectionFactory.GetDbConnection(); string sql = $"SELECT TOP 1 AttachmentID FROM [{tt.DataTableName}] WHERE Title = @Title ORDER BY InsertDate DESC"; return conn.ExecuteScalar(sql, param: new { Title = title }); } DataTable IMetrologyRepository.GetDataSharePoint(int toolTypeId, string headerId) => throw new NotImplementedException(); IEnumerable IMetrologyRepository.GetToolTypes() => GetToolTypes(); ToolType IMetrologyRepository.GetToolTypeByName(string name) => GetToolTypeByName(name); ToolType IMetrologyRepository.GetToolTypeByID(int id) => GetToolTypeByID(id); IEnumerable IMetrologyRepository.GetToolTypeMetadataByToolTypeID(int id) => GetToolTypeMetadataByToolTypeID(id); TransactionScope IMetrologyRepository.StartTransaction() => StartTransaction(); void IMetrologyRepository.PurgeExistingData(int toolTypeId, string title) => PurgeExistingData(toolTypeId, title); long IMetrologyRepository.InsertToolDataJSON(JToken jsonbody, long headerId, List metaData, string tableName) => InsertToolDataJSON(jsonbody, headerId, metaData, tableName); DataTable IMetrologyRepository.ExportData(string spName, DateTime startTime, DateTime endTime) => ExportData(spName, startTime, endTime); DataTable IMetrologyRepository.GetHeaders(int toolTypeId, string? startTime, string? endTime, int? pageNo, int? pageSize, long? headerid, out long totalRecords) => GetHeaders(toolTypeId, startTime, endTime, pageNo, pageSize, headerid, out totalRecords); DataTable IMetrologyRepository.GetData(int toolTypeId, long headerId) => GetData(toolTypeId, headerId); HeaderCommon[] IMetrologyRepository.GetHeaderTitles(int? toolTypeId, int? pageNo, int? pageSize, out long totalRecords) => GetHeaderTitles(toolTypeId, pageNo, pageSize, out totalRecords); Guid IMetrologyRepository.GetHeaderAttachmentIDByTitle(int toolTypeId, string title) => GetHeaderAttachmentIDByTitle(toolTypeId, title); Guid IMetrologyRepository.GetDataAttachmentIDByTitle(int toolTypeId, string title) => GetDataAttachmentIDByTitle(toolTypeId, title); Guid IMetrologyRepository.GetHeaderAttachmentID(int toolTypeId, long headerId) => GetHeaderAttachmentID(toolTypeId, headerId); string IMetrologyRepository.GetHeaderInsertDate(int toolTypeId, long headerId) => GetHeaderInsertDate(toolTypeId, headerId); string IMetrologyRepository.GetAttachmentInsertDateByGUID(string tableName, Guid attachmentId) => GetAttachmentInsertDateByGUID(tableName, attachmentId); void IMetrologyRepository.SetHeaderDirName(string tableName, long headerId, string dateDir) => SetHeaderDirName(tableName, headerId, dateDir); Guid IMetrologyRepository.GetDataAttachmentID(int toolTypeId, long headerId, string title) => GetDataAttachmentID(toolTypeId, headerId, title); string IMetrologyRepository.GetDataInsertDate(int toolTypeId, long headerId, string title) => GetDataInsertDate(toolTypeId, headerId, title); void IMetrologyRepository.SetDataDirName(string tableName, long headerId, string title, string dateDir) => SetDataDirName(tableName, headerId, title, dateDir); DataSet IMetrologyRepository.GetOIExportData(int toolTypeId, long headerid) => GetOIExportData(toolTypeId, headerid); IEnumerable> IMetrologyRepository.GetHeaderFields(int toolTypeId, long headerid) => GetHeaderFields(toolTypeId, headerid); IEnumerable IMetrologyRepository.GetAwaitingDisposition() => GetAwaitingDisposition(); int IMetrologyRepository.UpdateReviewDate(int toolTypeId, long headerId, bool clearDate) => UpdateReviewDate(toolTypeId, headerId, clearDate); }