Viewer to Server
This commit is contained in:
36
Server/Repositories/AppSettingsRepository.cs
Normal file
36
Server/Repositories/AppSettingsRepository.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using OI.Metrology.Server.Models;
|
||||
using OI.Metrology.Shared.Models.Stateless;
|
||||
|
||||
namespace OI.Metrology.Server.Repository;
|
||||
|
||||
public class AppSettingsRepository : IAppSettingsRepository<Models.Binder.AppSettings>
|
||||
{
|
||||
|
||||
private readonly AppSettings _AppSettings;
|
||||
|
||||
public AppSettingsRepository(AppSettings appSettings) => _AppSettings = appSettings;
|
||||
|
||||
internal Models.Binder.AppSettings GetAppSettings()
|
||||
{
|
||||
Models.Binder.AppSettings result = new()
|
||||
{
|
||||
BuildNumber = _AppSettings.BuildNumber,
|
||||
Company = _AppSettings.Company,
|
||||
GitCommitSeven = _AppSettings.GitCommitSeven,
|
||||
IsDevelopment = _AppSettings.IsDevelopment,
|
||||
IsStaging = _AppSettings.IsStaging,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
Models.Binder.AppSettings IAppSettingsRepository<Models.Binder.AppSettings>.GetAppSettings() => GetAppSettings();
|
||||
|
||||
internal string GetBuildNumberAndGitCommitSeven()
|
||||
{
|
||||
string result = string.Concat(_AppSettings.BuildNumber, '-', _AppSettings.GitCommitSeven);
|
||||
return result;
|
||||
}
|
||||
|
||||
string IAppSettingsRepository<Models.Binder.AppSettings>.GetBuildNumberAndGitCommitSeven() => GetBuildNumberAndGitCommitSeven();
|
||||
|
||||
}
|
36
Server/Repositories/ClientSettingsRepository.cs
Normal file
36
Server/Repositories/ClientSettingsRepository.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using OI.Metrology.Server.Models;
|
||||
using OI.Metrology.Shared.Models.Stateless;
|
||||
using System.Net;
|
||||
|
||||
namespace OI.Metrology.Server.Repository;
|
||||
|
||||
public class ClientSettingsRepository : IClientSettingsRepository
|
||||
{
|
||||
|
||||
private readonly AppSettings _AppSettings;
|
||||
|
||||
public ClientSettingsRepository(AppSettings appSettings) => _AppSettings = appSettings;
|
||||
|
||||
internal List<string> GetClientSettings(IPAddress? remoteIpAddress)
|
||||
{
|
||||
List<string> results = new();
|
||||
if (remoteIpAddress is null)
|
||||
results.Add(nameof(remoteIpAddress));
|
||||
else
|
||||
results.Add(remoteIpAddress.ToString());
|
||||
if (!_AppSettings.IsDevelopment)
|
||||
throw new Exception("Shouldn't expose!");
|
||||
return results;
|
||||
}
|
||||
|
||||
List<string> IClientSettingsRepository.GetClientSettings(IPAddress? remoteIpAddress) => GetClientSettings(remoteIpAddress);
|
||||
|
||||
internal static string GetIpAddress(IPAddress? remoteIpAddress)
|
||||
{
|
||||
string result = remoteIpAddress is null ? string.Empty : remoteIpAddress.ToString();
|
||||
return result;
|
||||
}
|
||||
|
||||
string IClientSettingsRepository.GetIpAddress(IPAddress? remoteIpAddress) => GetIpAddress(remoteIpAddress);
|
||||
|
||||
}
|
88
Server/Repositories/InboundRepository.cs
Normal file
88
Server/Repositories/InboundRepository.cs
Normal file
@ -0,0 +1,88 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OI.Metrology.Shared.DataModels;
|
||||
using OI.Metrology.Shared.Models;
|
||||
using OI.Metrology.Shared.Models.Stateless;
|
||||
using OI.Metrology.Shared.Services;
|
||||
using System.Net;
|
||||
|
||||
namespace OI.Metrology.Server.Repository;
|
||||
|
||||
public class InboundRepository : IInboundRepository
|
||||
{
|
||||
|
||||
private readonly Serilog.ILogger _Log;
|
||||
|
||||
public InboundRepository() => _Log = Serilog.Log.ForContext<InboundRepository>();
|
||||
|
||||
bool IInboundRepository.IsIPAddressAllowed(string inboundApiAllowedIPList, IPAddress? remoteIP)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(inboundApiAllowedIPList))
|
||||
return true;
|
||||
if (remoteIP is null)
|
||||
return false;
|
||||
byte[] remoteIPBytes = remoteIP.GetAddressBytes();
|
||||
string[] allowedIPs = inboundApiAllowedIPList.Split(';');
|
||||
foreach (string ip in allowedIPs)
|
||||
{
|
||||
IPAddress? parsedIP;
|
||||
if (IPAddress.TryParse(ip, out parsedIP))
|
||||
{
|
||||
if (parsedIP.GetAddressBytes().SequenceEqual(remoteIPBytes))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// this is the main endpoint, it accepts a JSON message that contains both the header and data records together
|
||||
// tooltype is the ToolTypeName column from the ToolType table
|
||||
// JToken is how you can accept a JSON message without deserialization.
|
||||
// Using "string" doesn't work because ASP.NET Core will expect a json encoded string, not give you the actual string.
|
||||
DataResponse IInboundRepository.Data(IMetrologyRepository metrologyRepository, IInboundDataService inboundDataService, string tooltype, JToken jsonbody)
|
||||
{
|
||||
DataResponse result = new();
|
||||
ToolType? toolType = metrologyRepository.GetToolTypeByName(tooltype);
|
||||
if (toolType is null)
|
||||
result.Errors.Add("Invalid tool type: " + tooltype);
|
||||
else
|
||||
{
|
||||
List<ToolTypeMetadata> metaData = metrologyRepository.GetToolTypeMetadataByToolTypeID(toolType.ID).ToList();
|
||||
if (metaData is null)
|
||||
result.Errors.Add("Invalid metadata for tool type: " + tooltype);
|
||||
else if (jsonbody is null)
|
||||
result.Errors.Add("Invalid json");
|
||||
else
|
||||
inboundDataService.ValidateJSONFields(jsonbody, 0, metaData, result.Errors, result.Warnings);
|
||||
if (metaData is not null && jsonbody is not null && !result.Errors.Any())
|
||||
{
|
||||
try
|
||||
{
|
||||
result.HeaderID = inboundDataService.DoSQLInsert(jsonbody, toolType, metaData);
|
||||
result.Success = result.HeaderID > 0;
|
||||
}
|
||||
catch (Exception ex) { result.Errors.Add(ex.Message); }
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// this is the endpoint for attaching a field. It is not JSON, it is form-data/multipart like an HTML form because that's the normal way.
|
||||
// header ID is the ID value from the Header table
|
||||
// datauniqueid is the Title value from the Data/Detail table
|
||||
string? IInboundRepository.AttachFile(IMetrologyRepository metrologyRepository, IAttachmentsService attachmentsService, string tooltype, long headerid, string datauniqueid, string fileName, object uploadedFile)
|
||||
{
|
||||
string? result = null;
|
||||
ToolType toolType = metrologyRepository.GetToolTypeByName(tooltype);
|
||||
if (toolType is null)
|
||||
result = $"Invalid tool type: {tooltype}";
|
||||
string filename = Path.GetFileName(fileName);
|
||||
if (string.IsNullOrWhiteSpace(filename))
|
||||
result = "Empty filename";
|
||||
if (filename.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
|
||||
result = "Invalid filename";
|
||||
if (result is null && toolType is not null)
|
||||
attachmentsService.SaveAttachment(toolType, headerid, datauniqueid, filename, uploadedFile);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
823
Server/Repositories/MetrologyRepository.cs
Normal file
823
Server/Repositories/MetrologyRepository.cs
Normal file
@ -0,0 +1,823 @@
|
||||
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 IMemoryCache _Cache;
|
||||
private readonly IDbConnectionFactory _DBConnectionFactory;
|
||||
|
||||
public MetrologyRepository(AppSettings appSettings, IDbConnectionFactory dbConnectionFactory, IMemoryCache memoryCache)
|
||||
{
|
||||
_Cache = memoryCache;
|
||||
_MockRoot = appSettings.MockRoot;
|
||||
_DBConnectionFactory = dbConnectionFactory;
|
||||
}
|
||||
|
||||
private DbConnection GetDbConnection() => _DBConnectionFactory.GetDbConnection();
|
||||
|
||||
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);
|
||||
|
||||
_ = _Cache.Set(key, v, new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromHours(1)));
|
||||
}
|
||||
|
||||
internal IEnumerable<ToolType> GetToolTypes()
|
||||
{
|
||||
IEnumerable<ToolType> cached;
|
||||
string cacheKey = "GetToolTypes";
|
||||
if (_Cache.TryGetValue(cacheKey, out cached))
|
||||
return cached;
|
||||
|
||||
using DbConnection conn = GetDbConnection();
|
||||
IEnumerable<ToolType> r = conn.Query<ToolType>("SELECT * FROM ToolType");
|
||||
|
||||
CacheItem(cacheKey, r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
internal ToolType GetToolTypeByName(string name)
|
||||
{
|
||||
ToolType cached;
|
||||
string cacheKey = "GetToolTypeByName_" + name;
|
||||
if (_Cache.TryGetValue(cacheKey, out cached))
|
||||
return cached;
|
||||
|
||||
using DbConnection conn = GetDbConnection();
|
||||
ToolType r = conn.QueryFirstOrDefault<ToolType>(
|
||||
"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 (_Cache.TryGetValue(cacheKey, out cached))
|
||||
return cached;
|
||||
|
||||
using DbConnection conn = GetDbConnection();
|
||||
ToolType r = conn.QueryFirstOrDefault<ToolType>(
|
||||
"SELECT * FROM ToolType WHERE ID = @id",
|
||||
new { id });
|
||||
|
||||
CacheItem(cacheKey, r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
internal IEnumerable<ToolTypeMetadata> GetToolTypeMetadataByToolTypeID(int id)
|
||||
{
|
||||
IEnumerable<ToolTypeMetadata> cached;
|
||||
string cacheKey = "GetToolTypeMetadataByToolTypeID_" + id.ToString();
|
||||
if (_Cache.TryGetValue(cacheKey, out cached))
|
||||
return cached;
|
||||
|
||||
using DbConnection conn = GetDbConnection();
|
||||
IEnumerable<ToolTypeMetadata> r = conn.Query<ToolTypeMetadata>(
|
||||
"SELECT * FROM ToolTypeMetadata WHERE ToolTypeID = @id",
|
||||
new { id });
|
||||
|
||||
CacheItem(cacheKey, r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
internal long InsertToolDataJSON(JToken jsonrow, long headerId, List<ToolTypeMetadata> metaData, string tableName)
|
||||
{
|
||||
long r = -1;
|
||||
using (DbConnection conn = GetDbConnection())
|
||||
{
|
||||
bool isHeader = headerId <= 0;
|
||||
|
||||
// get fields from metadata
|
||||
List<ToolTypeMetadata> fields = metaData.Where(md => md.Header == isHeader).ToList();
|
||||
|
||||
// maps ApiName to ColumnName
|
||||
Dictionary<string, string> fieldmap = new();
|
||||
|
||||
// store property name of container field
|
||||
string containerField = null;
|
||||
|
||||
// maps container ApiName to ColumnName
|
||||
Dictionary<string, string> 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<JProperty>().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<string, string> fieldmap,
|
||||
long headerId,
|
||||
JToken containerrow,
|
||||
Dictionary<string, string> 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<JProperty>())
|
||||
{
|
||||
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<JProperty>())
|
||||
{
|
||||
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 = 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<ToolTypeMetadata> 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, DateTime? startTime, DateTime? endTime, int? pageNo, int? pageSize, long? headerId, out long totalRecords)
|
||||
{
|
||||
ToolType tt = GetToolTypeByID(toolTypeId);
|
||||
if (tt is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
IEnumerable<ToolTypeMetadata> md = GetToolTypeMetadataByToolTypeID(toolTypeId);
|
||||
if (md is null)
|
||||
throw new Exception("Invalid tool type metadata");
|
||||
|
||||
DataTable dt = new();
|
||||
using (DbConnection conn = 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.HasValue)
|
||||
{
|
||||
whereClause = "[Date] >= @StartTime ";
|
||||
DateTime startTimeLocal = startTime.Value.ToLocalTime();
|
||||
AddParameter(cmd, "@StartTime", startTimeLocal);
|
||||
}
|
||||
|
||||
if (endTime.HasValue)
|
||||
{
|
||||
DateTime endTimeLocal = endTime.Value.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (tt is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
IEnumerable<ToolTypeMetadata> md = GetToolTypeMetadataByToolTypeID(toolTypeId);
|
||||
if (md is null)
|
||||
throw new Exception("Invalid tool type metadata");
|
||||
|
||||
DataTable dt = new();
|
||||
using (DbConnection conn = 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);
|
||||
if (tt is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
using DbConnection conn = 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<Guid>(sql, param: new { HeaderID = headerId });
|
||||
}
|
||||
internal string GetHeaderInsertDate(int toolTypeId, long headerId)
|
||||
{
|
||||
ToolType tt = GetToolTypeByID(toolTypeId);
|
||||
if (tt is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
using DbConnection conn = 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<string>(sql, param: new { HeaderID = headerId });
|
||||
}
|
||||
internal string GetAttachmentInsertDateByGUID(string tableName, Guid attachmentId)
|
||||
{
|
||||
using DbConnection conn = 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<string>(sql, param: new { AttachmentID = attachmentId });
|
||||
}
|
||||
internal void SetHeaderDirName(string tableName, long headerId, string dateDir)
|
||||
{
|
||||
using DbConnection conn = 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);
|
||||
if (tt is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
using DbConnection conn = 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<Guid>(sql, param: new { HeaderID = headerId, Title = title });
|
||||
}
|
||||
// J Ouellette Added
|
||||
internal string GetDataInsertDate(int toolTypeId, long headerId, string title)
|
||||
{
|
||||
ToolType tt = GetToolTypeByID(toolTypeId);
|
||||
if (tt is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
using DbConnection conn = 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<string>(sql, param: new { HeaderID = headerId, Title = title });
|
||||
}
|
||||
internal void SetDataDirName(string tableName, long headerId, string title, string dateDir)
|
||||
{
|
||||
using DbConnection conn = 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 = GetDbConnection();
|
||||
_ = conn.Execute("PurgeExistingData", param: new { ToolTypeID = toolTypeId, Title = title }, commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
internal DataSet GetOIExportData(int toolTypeId, long headerid)
|
||||
{
|
||||
ToolType tt = GetToolTypeByID(toolTypeId);
|
||||
if (tt is null)
|
||||
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 = 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<HeaderCommon> results;
|
||||
ToolType[] toolTypes = GetToolTypes().ToArray();
|
||||
|
||||
if (!toolTypes.Any() || toolTypes.FirstOrDefault() is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
ToolType tt;
|
||||
StringBuilder stringBuilder = new();
|
||||
using DbConnection conn = 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<HeaderCommon>(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);
|
||||
if (tt is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
using DbConnection conn = 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<HeaderCommon>(sql, param: new { PageNum = pageNo.Value, PageSize = pageSize.Value }).ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
headers = conn.Query<HeaderCommon>(sql).ToArray();
|
||||
}
|
||||
|
||||
sql = $"SELECT COUNT(*) FROM [{tt.HeaderTableName}] ";
|
||||
|
||||
totalRecords = Convert.ToInt64(conn.ExecuteScalar(sql));
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
internal IEnumerable<KeyValuePair<string, string>> GetHeaderFields(int toolTypeId, long headerid)
|
||||
{
|
||||
ToolType tt = GetToolTypeByID(toolTypeId);
|
||||
if (tt is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
IEnumerable<ToolTypeMetadata> md = GetToolTypeMetadataByToolTypeID(toolTypeId);
|
||||
if (md is null)
|
||||
throw new Exception("Invalid tool type metadata");
|
||||
|
||||
List<KeyValuePair<string, string>> r = new();
|
||||
|
||||
using (DbConnection conn = 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<string, string> kvp = new(m.DisplayTitle, v);
|
||||
r.Add(kvp);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
internal IEnumerable<AwaitingDisposition> GetAwaitingDisposition()
|
||||
{
|
||||
IEnumerable<AwaitingDisposition>? r;
|
||||
if (!string.IsNullOrEmpty(_MockRoot))
|
||||
{
|
||||
string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), "GetAwaitingDispositionApi.json"));
|
||||
r = System.Text.Json.JsonSerializer.Deserialize<IEnumerable<AwaitingDisposition>>(json);
|
||||
if (r is null)
|
||||
throw new NullReferenceException(nameof(r));
|
||||
}
|
||||
else
|
||||
{
|
||||
using DbConnection conn = GetDbConnection();
|
||||
r = conn.Query<AwaitingDisposition>("GetAwaitingDispo", commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
internal int UpdateReviewDate(int toolTypeId, long headerId, bool clearDate)
|
||||
{
|
||||
ToolType tt = GetToolTypeByID(toolTypeId);
|
||||
if (tt is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
using DbConnection conn = 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<int>(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);
|
||||
if (tt is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
using DbConnection conn = GetDbConnection();
|
||||
string sql =
|
||||
$"SELECT TOP 1 AttachmentID FROM [{tt.HeaderTableName}] WHERE Title = @Title ORDER BY InsertDate DESC";
|
||||
return conn.ExecuteScalar<Guid>(sql, param: new { Title = title });
|
||||
}
|
||||
|
||||
internal Guid GetDataAttachmentIDByTitle(int toolTypeId, string title)
|
||||
{
|
||||
ToolType tt = GetToolTypeByID(toolTypeId);
|
||||
if (tt is null)
|
||||
throw new Exception("Invalid tool type ID");
|
||||
|
||||
using DbConnection conn = GetDbConnection();
|
||||
string sql =
|
||||
$"SELECT TOP 1 AttachmentID FROM [{tt.DataTableName}] WHERE Title = @Title ORDER BY InsertDate DESC";
|
||||
return conn.ExecuteScalar<Guid>(sql, param: new { Title = title });
|
||||
}
|
||||
|
||||
DataTable IMetrologyRepository.GetDataSharePoint(int toolTypeId, string headerId) => throw new NotImplementedException();
|
||||
|
||||
IEnumerable<ToolType> IMetrologyRepository.GetToolTypes() => GetToolTypes();
|
||||
ToolType IMetrologyRepository.GetToolTypeByName(string name) => GetToolTypeByName(name);
|
||||
ToolType IMetrologyRepository.GetToolTypeByID(int id) => GetToolTypeByID(id);
|
||||
IEnumerable<ToolTypeMetadata> 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<ToolTypeMetadata> 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, DateTime? startTime, DateTime? 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<KeyValuePair<string, string>> IMetrologyRepository.GetHeaderFields(int toolTypeId, long headerid) => GetHeaderFields(toolTypeId, headerid);
|
||||
IEnumerable<AwaitingDisposition> IMetrologyRepository.GetAwaitingDisposition() => GetAwaitingDisposition();
|
||||
int IMetrologyRepository.UpdateReviewDate(int toolTypeId, long headerId, bool clearDate) => UpdateReviewDate(toolTypeId, headerId, clearDate);
|
||||
|
||||
}
|
120
Server/Repositories/PinRepository.cs
Normal file
120
Server/Repositories/PinRepository.cs
Normal file
@ -0,0 +1,120 @@
|
||||
using OI.Metrology.Shared.DataModels;
|
||||
using OI.Metrology.Shared.Models.Stateless;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace OI.Metrology.Server.Repository;
|
||||
|
||||
public class PinRepository : IPinRepository
|
||||
{
|
||||
|
||||
private readonly string _MockRoot;
|
||||
private readonly Serilog.ILogger _Log;
|
||||
private readonly Dictionary<string, Dictionary<long, HeaderCommon>> _RdsToHeaderCommonCollection;
|
||||
|
||||
public PinRepository(string mockRoot)
|
||||
{
|
||||
_MockRoot = mockRoot;
|
||||
_RdsToHeaderCommonCollection = new();
|
||||
_Log = Serilog.Log.ForContext<PinRepository>();
|
||||
}
|
||||
|
||||
void IPinRepository.SetPinnedTable(HeaderCommon headerCommon)
|
||||
{
|
||||
Dictionary<long, HeaderCommon>? toolIdToHeader;
|
||||
if (!string.IsNullOrEmpty(headerCommon.RDS))
|
||||
{
|
||||
if (!_RdsToHeaderCommonCollection.TryGetValue(headerCommon.RDS, out toolIdToHeader))
|
||||
{
|
||||
_RdsToHeaderCommonCollection.Add(headerCommon.RDS, new());
|
||||
if (!_RdsToHeaderCommonCollection.TryGetValue(headerCommon.RDS, out toolIdToHeader))
|
||||
throw new Exception();
|
||||
}
|
||||
if (toolIdToHeader.ContainsKey(headerCommon.ToolTypeID))
|
||||
toolIdToHeader[headerCommon.ToolTypeID] = headerCommon;
|
||||
else
|
||||
toolIdToHeader.Add(headerCommon.ToolTypeID, headerCommon);
|
||||
}
|
||||
}
|
||||
|
||||
private (HeaderCommon?, HeaderCommon?) GetBoth(string rds, long bioRadId, long cdeId)
|
||||
{
|
||||
Dictionary<long, HeaderCommon>? toolIdToHeader;
|
||||
if (!_RdsToHeaderCommonCollection.TryGetValue(rds, out toolIdToHeader))
|
||||
{
|
||||
_RdsToHeaderCommonCollection.Add(rds, new());
|
||||
if (!_RdsToHeaderCommonCollection.TryGetValue(rds, out toolIdToHeader))
|
||||
throw new Exception();
|
||||
}
|
||||
_ = toolIdToHeader.TryGetValue(cdeId, out HeaderCommon? cdeHeader);
|
||||
_ = toolIdToHeader.TryGetValue(bioRadId, out HeaderCommon? bioRadHeader);
|
||||
return new(bioRadHeader, cdeHeader);
|
||||
}
|
||||
|
||||
private static Pinned? GetPinned(IMetrologyRepository metrologyRepository, HeaderCommon headerCommon, int points, int column)
|
||||
{
|
||||
Pinned? result;
|
||||
List<string> values;
|
||||
System.Data.DataTable dataTable = metrologyRepository.GetData((int)headerCommon.ToolTypeID, headerCommon.ID);
|
||||
if (dataTable.Rows.Count <= points || dataTable.Columns.Count <= column)
|
||||
result = null;
|
||||
else
|
||||
{
|
||||
values = new();
|
||||
for (int i = 0; i < dataTable.Rows.Count - 1; i++)
|
||||
{
|
||||
if (dataTable.Rows[i]?.ItemArray[column] is null)
|
||||
break;
|
||||
values.Add(string.Concat(dataTable.Rows[i].ItemArray[column]));
|
||||
}
|
||||
if (values.Count <= points)
|
||||
result = null;
|
||||
else
|
||||
result = new(headerCommon, values);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Result<Pinned[]> IPinRepository.GetPinnedTable(IMetrologyRepository metrologyRepository, int id, string? biorad_id, string? cde_id, string? rds)
|
||||
{
|
||||
Result<Pinned[]>? r;
|
||||
if (!string.IsNullOrEmpty(_MockRoot))
|
||||
{
|
||||
string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), "GetPinnedTableApi.json"));
|
||||
r = JsonSerializer.Deserialize<Result<Pinned[]>>(json);
|
||||
if (r is null)
|
||||
throw new NullReferenceException(nameof(r));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(rds) || !long.TryParse(biorad_id, out long bioRadId) || !long.TryParse(cde_id, out long cdeId))
|
||||
r = new() { Results = Array.Empty<Pinned>(), TotalRows = 0 };
|
||||
else
|
||||
{
|
||||
const int points = 9;
|
||||
List<Pinned> results = new();
|
||||
(HeaderCommon? bioRadHeader, HeaderCommon? cdeHeader) = GetBoth(rds, bioRadId, cdeId);
|
||||
if (bioRadHeader is not null)
|
||||
{
|
||||
const int thickness = 5;
|
||||
Pinned? pinned = GetPinned(metrologyRepository, bioRadHeader, points, column: thickness);
|
||||
if (pinned is not null)
|
||||
results.Add(pinned);
|
||||
}
|
||||
if (cdeHeader is not null)
|
||||
{
|
||||
const int rs = 6;
|
||||
Pinned? pinned = GetPinned(metrologyRepository, cdeHeader, points, column: rs);
|
||||
if (pinned is not null)
|
||||
results.Add(pinned);
|
||||
}
|
||||
r = new()
|
||||
{
|
||||
Results = results.ToArray(),
|
||||
TotalRows = results.Count,
|
||||
};
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
}
|
56
Server/Repositories/ServiceShopOrderRepository.cs
Normal file
56
Server/Repositories/ServiceShopOrderRepository.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using OI.Metrology.Shared.Models.Stateless;
|
||||
using OI.Metrology.Shared.ViewModels;
|
||||
using Serilog.Context;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace OI.Metrology.Server.Repository;
|
||||
|
||||
public class ServiceShopOrderRepository : IServiceShopOrderRepository
|
||||
{
|
||||
|
||||
private readonly Serilog.ILogger _Log;
|
||||
|
||||
public ServiceShopOrderRepository() => _Log = Serilog.Log.ForContext<ServiceShopOrderRepository>();
|
||||
|
||||
private static ServiceShopOrder[] GetServiceShopOrders(Shared.Models.ServiceShop? serviceShop)
|
||||
{
|
||||
ServiceShopOrder[] result = IServiceShopOrder.GetServiceShopOrders(serviceShop);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Shared.Models.ServiceShop? GetServiceShopOrders()
|
||||
{
|
||||
Shared.Models.ServiceShop? result;
|
||||
string jsonFile;
|
||||
string checkDirectory = Path.Combine(AppContext.BaseDirectory, "Data/Mike");
|
||||
if (Directory.Exists(checkDirectory))
|
||||
jsonFile = Path.Combine(checkDirectory, "service-shop.json");
|
||||
else
|
||||
jsonFile = Path.Combine(AppContext.BaseDirectory, "service-shop.json");
|
||||
string json = File.ReadAllText(jsonFile);
|
||||
result = JsonSerializer.Deserialize<Shared.Models.ServiceShop>(json);
|
||||
return result;
|
||||
}
|
||||
|
||||
async Task<ServiceShopOrder[]> IServiceShopOrderRepository.GetAllServiceShopOrders()
|
||||
{
|
||||
ServiceShopOrder[] results;
|
||||
string? methodName = IMethodName.GetActualAsyncMethodName();
|
||||
using (LogContext.PushProperty("MethodName", methodName))
|
||||
{
|
||||
_Log.Debug("() => ...");
|
||||
Shared.Models.ServiceShop? serviceShop = await Task.Run(GetServiceShopOrders);
|
||||
results = GetServiceShopOrders(serviceShop);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
|
||||
async Task<ServiceShopOrder[]> IServiceShopOrderRepository.GetServiceShopOrders(string id)
|
||||
{
|
||||
ServiceShopOrder[] results;
|
||||
Shared.Models.ServiceShop? serviceShop = await Task.Run(GetServiceShopOrders);
|
||||
results = GetServiceShopOrders(serviceShop).Where(l => l.Id == id).ToArray();
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
340
Server/Repositories/ToolTypesRepository.cs
Normal file
340
Server/Repositories/ToolTypesRepository.cs
Normal file
@ -0,0 +1,340 @@
|
||||
using OI.Metrology.Shared.DataModels;
|
||||
using OI.Metrology.Shared.Models.Stateless;
|
||||
using OI.Metrology.Shared.Services;
|
||||
using System.Data;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace OI.Metrology.Server.Repository;
|
||||
|
||||
public class ToolTypesRepository : IToolTypesRepository
|
||||
{
|
||||
|
||||
private readonly string _MockRoot;
|
||||
private readonly Serilog.ILogger _Log;
|
||||
|
||||
public ToolTypesRepository(string mockRoot)
|
||||
{
|
||||
_MockRoot = mockRoot;
|
||||
_Log = Serilog.Log.ForContext<ToolTypesRepository>();
|
||||
}
|
||||
|
||||
// Get a list of tooltypes, returns just Name and ID
|
||||
Result<ToolTypeNameId[]> IToolTypesRepository.Index(IMetrologyRepository metrologyRepository)
|
||||
{
|
||||
Result<ToolTypeNameId[]>? r;
|
||||
if (!string.IsNullOrEmpty(_MockRoot))
|
||||
{
|
||||
string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), "IndexApi.json"));
|
||||
r = JsonSerializer.Deserialize<Result<ToolTypeNameId[]>>(json);
|
||||
if (r is null)
|
||||
throw new NullReferenceException(nameof(r));
|
||||
}
|
||||
else
|
||||
{
|
||||
ToolTypeNameId[] toolTypeNameIdCollection = metrologyRepository.GetToolTypes().Select(tt => new ToolTypeNameId() { ToolTypeName = tt.ToolTypeName, ID = tt.ID }).ToArray();
|
||||
r = new()
|
||||
{
|
||||
Results = toolTypeNameIdCollection,
|
||||
TotalRows = toolTypeNameIdCollection.Length,
|
||||
};
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Gets the metadata for a tooltype, accepts a parameter which sorts the results
|
||||
// This is used to setup the grid displays on the UI
|
||||
Result<ToolTypeMetadataResult> IToolTypesRepository.GetToolTypeMetadata(IMetrologyRepository metrologyRepository, int id, string sortby)
|
||||
{
|
||||
Result<ToolTypeMetadataResult>? r;
|
||||
if (!string.IsNullOrEmpty(_MockRoot))
|
||||
{
|
||||
string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), "GetToolTypeMetadataApi.json"));
|
||||
r = JsonSerializer.Deserialize<Result<ToolTypeMetadataResult>>(json);
|
||||
if (r is null)
|
||||
throw new NullReferenceException(nameof(r));
|
||||
}
|
||||
else
|
||||
{
|
||||
ToolType tt = metrologyRepository.GetToolTypeByID(id);
|
||||
IEnumerable<ToolTypeMetadata> md = metrologyRepository.GetToolTypeMetadataByToolTypeID(id);
|
||||
|
||||
if (string.Equals(sortby, "grid", StringComparison.OrdinalIgnoreCase))
|
||||
md = md.OrderBy(f => f.GridDisplayOrder).ToList();
|
||||
if (string.Equals(sortby, "table", StringComparison.OrdinalIgnoreCase))
|
||||
md = md.OrderBy(f => f.GridDisplayOrder).ToList();
|
||||
|
||||
ToolTypeMetadataResult toolTypeMetadataResult = new(tt, md.ToArray());
|
||||
int totalRows = toolTypeMetadataResult.Metadata is not null ? toolTypeMetadataResult.Metadata.Length : 0;
|
||||
r = new()
|
||||
{
|
||||
Results = toolTypeMetadataResult,
|
||||
TotalRows = totalRows,
|
||||
};
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Gets headers, request/response format is to allow paging with igniteUI
|
||||
// The headerid parameter is used for navigating directly to a header, when the button is clicked in Awaiting Dispo
|
||||
Result<DataTable> IToolTypesRepository.GetHeaders(IMetrologyRepository metrologyRepository, int id, DateTime? datebegin, DateTime? dateend, int? page, int? pagesize, long? headerid)
|
||||
{
|
||||
Result<DataTable>? r;
|
||||
if (!string.IsNullOrEmpty(_MockRoot))
|
||||
{
|
||||
string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), "GetHeadersApi.json"));
|
||||
r = Newtonsoft.Json.JsonConvert.DeserializeObject<Result<DataTable>>(json);
|
||||
if (r is null)
|
||||
throw new NullReferenceException(nameof(r));
|
||||
}
|
||||
else
|
||||
{
|
||||
long totalRecs;
|
||||
DataTable dataTable = metrologyRepository.GetHeaders(id, datebegin, dateend, page, pagesize, headerid, out totalRecs);
|
||||
r = new()
|
||||
{
|
||||
Results = dataTable,
|
||||
TotalRows = totalRecs,
|
||||
};
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Gets header titles, used in the Run Headers UI
|
||||
Result<HeaderCommon[]> IToolTypesRepository.GetHeaderTitles(IMetrologyRepository metrologyRepository, int id, int? page, int? pagesize)
|
||||
{
|
||||
Result<HeaderCommon[]>? r;
|
||||
if (!string.IsNullOrEmpty(_MockRoot))
|
||||
{
|
||||
string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), "GetHeaderTitlesApi.json"));
|
||||
r = JsonSerializer.Deserialize<Result<HeaderCommon[]>>(json);
|
||||
if (r is null)
|
||||
throw new NullReferenceException(nameof(r));
|
||||
}
|
||||
else
|
||||
{
|
||||
long totalRecs;
|
||||
int? toolTypeId = id > -1 ? id : null;
|
||||
HeaderCommon[] headerCommonCollection = metrologyRepository.GetHeaderTitles(toolTypeId, page, pagesize, out totalRecs).ToArray();
|
||||
r = new()
|
||||
{
|
||||
Results = headerCommonCollection,
|
||||
TotalRows = totalRecs,
|
||||
};
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Get all of the fields for a header, used with the Run Header UI
|
||||
Result<ColumnValue[]> IToolTypesRepository.GetHeaderFields(IMetrologyRepository metrologyRepository, int id, long headerid)
|
||||
{
|
||||
Result<ColumnValue[]>? r;
|
||||
if (!string.IsNullOrEmpty(_MockRoot))
|
||||
{
|
||||
string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), "GetHeaderFieldsApi.json"));
|
||||
r = JsonSerializer.Deserialize<Result<ColumnValue[]>>(json);
|
||||
if (r is null)
|
||||
throw new NullReferenceException(nameof(r));
|
||||
}
|
||||
else
|
||||
{
|
||||
ColumnValue[] columnValueCollection = metrologyRepository.GetHeaderFields(id, headerid).Select(x => new ColumnValue() { Column = x.Key, Value = x.Value }).ToArray();
|
||||
r = new()
|
||||
{
|
||||
Results = columnValueCollection,
|
||||
TotalRows = columnValueCollection.Length,
|
||||
};
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Get the data for a header, used with the Run Info UI
|
||||
Result<DataTable> IToolTypesRepository.GetData(IMetrologyRepository metrologyRepository, int id, long headerid)
|
||||
{
|
||||
Result<DataTable>? r;
|
||||
if (!string.IsNullOrEmpty(_MockRoot))
|
||||
{
|
||||
string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), "GetDataApi.json"));
|
||||
r = Newtonsoft.Json.JsonConvert.DeserializeObject<Result<DataTable>>(json);
|
||||
if (r is null)
|
||||
throw new NullReferenceException(nameof(r));
|
||||
}
|
||||
else
|
||||
{
|
||||
DataTable dataTable = metrologyRepository.GetData(id, headerid);
|
||||
r = new()
|
||||
{
|
||||
Results = dataTable,
|
||||
TotalRows = dataTable.Rows.Count,
|
||||
};
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// Display an attachment, used for Run Info - note it is by tool type ID and attachment GUID, so it is best for internal use
|
||||
(string?, string?, Stream?) IToolTypesRepository.GetAttachment(IMetrologyRepository metrologyRepository, IAttachmentsService attachmentsService, int toolTypeId, string tabletype, string attachmentId, string filename)
|
||||
{
|
||||
Stream? stream = null;
|
||||
string? message = null;
|
||||
Guid attachmentIdParsed;
|
||||
string? contenttype = null;
|
||||
ToolType tt = metrologyRepository.GetToolTypeByID(toolTypeId);
|
||||
bool header = !string.Equals(tabletype.Trim(), "data", StringComparison.OrdinalIgnoreCase);
|
||||
if (!Guid.TryParse(attachmentId, out attachmentIdParsed))
|
||||
message = "Invalid attachment id";
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
// figure out what content type to use. this is very simple because there are only two types being used
|
||||
contenttype = "application/pdf";
|
||||
if (filename.ToLower().TrimEnd().EndsWith(".txt"))
|
||||
contenttype = "text/plain";
|
||||
// Get attachment stream and feed it to the client
|
||||
stream = attachmentsService.GetAttachmentStreamByAttachmentId(tt, header, attachmentIdParsed, filename);
|
||||
}
|
||||
catch (Exception ex) { message = ex.Message; }
|
||||
}
|
||||
return new(message, contenttype, stream);
|
||||
}
|
||||
|
||||
// This endpoint triggers writing of the OI Export file
|
||||
Exception? IToolTypesRepository.OIExport(IMetrologyRepository metrologyRepository, string oiExportPath, int toolTypeId, long headerid)
|
||||
{
|
||||
Exception? result = null;
|
||||
// Call the export stored procedure
|
||||
_Log.Debug($"Exporting to <{oiExportPath}>");
|
||||
DataSet ds = metrologyRepository.GetOIExportData(toolTypeId, headerid);
|
||||
try
|
||||
{
|
||||
// The SP must return 3 result tables
|
||||
if (ds.Tables.Count != 3)
|
||||
throw new Exception("Error exporting, invalid results");
|
||||
// The first table has just one row, which is the export filename
|
||||
if (ds.Tables[0].Rows.Count != 1)
|
||||
throw new Exception("Error exporting, invalid filename");
|
||||
string? filename = Convert.ToString(ds.Tables[0].Rows[0][0]);
|
||||
// The second table has the header data
|
||||
if (ds.Tables[1].Rows.Count != 1)
|
||||
throw new Exception("Error exporting, invalid header data");
|
||||
StringBuilder sb = new();
|
||||
foreach (object? o in ds.Tables[1].Rows[0].ItemArray)
|
||||
{
|
||||
if ((o is not null) && (!Convert.IsDBNull(o)))
|
||||
_ = sb.Append(Convert.ToString(o));
|
||||
_ = sb.Append('\t');
|
||||
}
|
||||
// The third table has the detail data
|
||||
foreach (DataRow dr in ds.Tables[2].Rows)
|
||||
{
|
||||
foreach (object? o in dr.ItemArray)
|
||||
{
|
||||
if ((o is not null) && (!Convert.IsDBNull(o)))
|
||||
_ = sb.Append(Convert.ToString(o));
|
||||
_ = sb.Append('\t');
|
||||
}
|
||||
}
|
||||
_ = sb.AppendLine();
|
||||
// The output file will only have one line, the header columns are output first
|
||||
// Then each detail rows has it's columns appended
|
||||
// H1, H2, H3, D1.1, D1.2, D1.3, D2.1, D2.2, D2.3, etc
|
||||
// Write the file
|
||||
File.WriteAllText(Path.Join(oiExportPath, filename), sb.ToString());
|
||||
}
|
||||
catch (Exception ex) { result = ex; }
|
||||
return result;
|
||||
}
|
||||
|
||||
Result<DataTable> IToolTypesRepository.GetExportData(IMetrologyRepository metrologyRepository, int toolTypeId, DateTime? datebegin, DateTime? dateend)
|
||||
{
|
||||
Result<DataTable>? r;
|
||||
if (!string.IsNullOrEmpty(_MockRoot))
|
||||
{
|
||||
string json = File.ReadAllText(Path.Combine(string.Concat(AppContext.BaseDirectory, _MockRoot), "GetExportDataApi.json"));
|
||||
r = Newtonsoft.Json.JsonConvert.DeserializeObject<Result<DataTable>>(json);
|
||||
if (r is null)
|
||||
throw new NullReferenceException(nameof(r));
|
||||
}
|
||||
else
|
||||
{
|
||||
dateend ??= DateTime.Now;
|
||||
datebegin ??= dateend.Value.AddMonths(-1);
|
||||
ToolType tt = metrologyRepository.GetToolTypeByID(toolTypeId);
|
||||
if (string.IsNullOrEmpty(tt.ExportSPName))
|
||||
throw new NullReferenceException(nameof(tt.ExportSPName));
|
||||
DataTable dataTable = metrologyRepository.ExportData(tt.ExportSPName, datebegin.Value, dateend.Value);
|
||||
r = new()
|
||||
{
|
||||
Results = dataTable,
|
||||
TotalRows = dataTable.Rows.Count,
|
||||
};
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
protected static string GetRowData(DataRow dr)
|
||||
{
|
||||
StringBuilder result = new();
|
||||
for (int i = 0; i < dr.Table.Columns.Count; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
_ = result.Append(',');
|
||||
object v = dr[i];
|
||||
if (v is not null && !Convert.IsDBNull(v))
|
||||
_ = result.Append(FormatForCSV(string.Concat(Convert.ToString(v))));
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
protected static string GetColumnHeaders(DataTable dataTable)
|
||||
{
|
||||
StringBuilder result = new();
|
||||
for (int i = 0; i < dataTable.Columns.Count; i++)
|
||||
{
|
||||
if (i > 0)
|
||||
_ = result.Append(',');
|
||||
_ = result.Append(FormatForCSV(dataTable.Columns[i].ColumnName.TrimEnd('_')));
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
protected static string FormatForCSV(string v)
|
||||
{
|
||||
StringBuilder result = new(v.Length + 2);
|
||||
bool doubleQuoted = false;
|
||||
if (v.StartsWith(' ') || v.EndsWith(' ') || v.Contains(',') || v.Contains('"'))
|
||||
{
|
||||
_ = result.Append('"');
|
||||
doubleQuoted = true;
|
||||
}
|
||||
foreach (char c in v)
|
||||
{
|
||||
_ = c switch
|
||||
{
|
||||
'\r' or '\n' => result.Append(' '),
|
||||
'"' => result.Append("\"\""),
|
||||
_ => result.Append(c),
|
||||
};
|
||||
}
|
||||
if (doubleQuoted)
|
||||
_ = result.Append('"');
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
byte[] IToolTypesRepository.GetCSVExport(IMetrologyRepository metrologyRepository, int toolTypeId, DateTime? datebegin, DateTime? dateend)
|
||||
{
|
||||
byte[] results;
|
||||
Result<DataTable> result;
|
||||
IToolTypesRepository repository = this;
|
||||
result = repository.GetExportData(metrologyRepository, toolTypeId, datebegin, dateend);
|
||||
if (result.Results is null)
|
||||
throw new NullReferenceException(nameof(result.Results));
|
||||
StringBuilder stringBuilder = new();
|
||||
_ = stringBuilder.AppendLine(GetColumnHeaders(result.Results));
|
||||
foreach (DataRow dr in result.Results.Rows)
|
||||
_ = stringBuilder.AppendLine(GetRowData(dr));
|
||||
results = Encoding.UTF8.GetBytes(stringBuilder.ToString());
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user