Devexpress FormReport ve GridReport Optimize
This commit is contained in:
parent
cf90472088
commit
ecb32a502a
4 changed files with 363 additions and 601 deletions
|
|
@ -4,7 +4,6 @@ using System.Data;
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using DevExpress.DataAccess.ConnectionParameters;
|
||||
using DevExpress.DataAccess.Sql;
|
||||
|
|
@ -95,7 +94,7 @@ public class DynamicFormReport : XtraReport
|
|||
var queryParams = new QueryParameters
|
||||
{
|
||||
ListFormCode = listFormCode,
|
||||
Filter = GetParameter(parameters, "filter") ?? CreateKeyFilter(listForm.KeyFieldName, id),
|
||||
Filter = DynamicReportImageHelper.GetParameter(parameters, "filter") ?? CreateKeyFilter(listForm.KeyFieldName, id),
|
||||
Take = 1,
|
||||
Skip = 0
|
||||
};
|
||||
|
|
@ -103,7 +102,9 @@ public class DynamicFormReport : XtraReport
|
|||
selectQueryManager.PrepareQueries(listForm, fields, dataSourceType, customizations, queryParams);
|
||||
|
||||
var reportFields = GetReportFields(listForm, fields, selectQueryManager, defaultValueHelper);
|
||||
var selectQuery = StripReportPaging(ApplyQueryParameters(selectQueryManager.SelectQuery, selectQueryManager.SelectQueryParameters));
|
||||
var selectQuery = DynamicReportImageHelper.StripReportPaging(DynamicReportImageHelper.ApplyQueryParameters(
|
||||
selectQueryManager.SelectQuery,
|
||||
selectQueryManager.SelectQueryParameters));
|
||||
selectQuery = DynamicReportSqlBuilder.ApplyLookupTextColumns(
|
||||
selectQuery,
|
||||
reportFields,
|
||||
|
|
@ -114,7 +115,7 @@ public class DynamicFormReport : XtraReport
|
|||
|
||||
ConfigurePage();
|
||||
ConfigureDataSource(selectQuery, connectionString, dataSourceType);
|
||||
await BuildLayoutAsync(listForm, reportFields, row, localizer, GetImageBaseUrl(configuration));
|
||||
await BuildLayoutAsync(listForm, reportFields, row, localizer, DynamicReportImageHelper.GetImageBaseUrl(configuration));
|
||||
RequestParameters = false;
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +140,8 @@ public class DynamicFormReport : XtraReport
|
|||
|
||||
private void ConfigureDataSource(string selectQuery, string connectionString, DataSourceTypeEnum dataSourceType)
|
||||
{
|
||||
var dataSource = new SqlDataSource(new CustomStringConnectionParameters(ToDevExpressConnectionString(connectionString, dataSourceType)));
|
||||
var dataSource = new SqlDataSource(new CustomStringConnectionParameters(
|
||||
DynamicReportImageHelper.ToDevExpressConnectionString(connectionString, dataSourceType)));
|
||||
dataSource.Queries.Add(new CustomSqlQuery(QueryName, selectQuery));
|
||||
ComponentStorage.Add(dataSource);
|
||||
DataSource = dataSource;
|
||||
|
|
@ -154,18 +156,20 @@ public class DynamicFormReport : XtraReport
|
|||
string imageBaseUrl)
|
||||
{
|
||||
Bands.Clear();
|
||||
var formGroups = GetFormGroups(listForm, fields).ToList();
|
||||
var listSubForms = GetListSubForms(listForm, row).ToList();
|
||||
|
||||
var topMargin = new TopMarginBand { HeightF = Margins.Top };
|
||||
var bottomMargin = new BottomMarginBand { HeightF = 32 };
|
||||
var reportHeader = new ReportHeaderBand { HeightF = 38 };
|
||||
var detail = new DetailBand { HeightF = CalculateDetailHeight(fields, listForm, row) };
|
||||
var detail = new DetailBand { HeightF = CalculateDetailHeight(formGroups, listSubForms.Count) };
|
||||
|
||||
Bands.AddRange([topMargin, reportHeader, detail, bottomMargin]);
|
||||
|
||||
var pageWidth = PageWidth - Margins.Left - Margins.Right;
|
||||
reportHeader.Controls.Add(new XRLabel
|
||||
{
|
||||
Text = GetLocalizedReportTitle(listForm, localizer),
|
||||
Text = DynamicReportImageHelper.GetLocalizedReportTitle(listForm, localizer),
|
||||
BoundsF = new System.Drawing.RectangleF(0, 0, pageWidth, 30),
|
||||
Font = new DXFont("Arial", 14F, DXFontStyle.Bold),
|
||||
TextAlignment = TextAlignment.MiddleLeft
|
||||
|
|
@ -188,13 +192,13 @@ public class DynamicFormReport : XtraReport
|
|||
});
|
||||
|
||||
var y = 0F;
|
||||
foreach (var group in GetFormGroups(listForm, fields))
|
||||
foreach (var group in formGroups)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(group.Caption))
|
||||
{
|
||||
detail.Controls.Add(new XRLabel
|
||||
{
|
||||
Text = Localize(localizer, group.Caption, group.Caption),
|
||||
Text = DynamicReportImageHelper.Localize(localizer, group.Caption, group.Caption),
|
||||
BoundsF = new System.Drawing.RectangleF(0, y, pageWidth, 22),
|
||||
Font = new DXFont("Arial", 9F, DXFontStyle.Bold),
|
||||
BackColor = System.Drawing.Color.FromArgb(238, 242, 247),
|
||||
|
|
@ -223,7 +227,7 @@ public class DynamicFormReport : XtraReport
|
|||
y += 8;
|
||||
}
|
||||
|
||||
foreach (var subForm in GetListSubForms(listForm, row))
|
||||
foreach (var subForm in listSubForms)
|
||||
{
|
||||
detail.Controls.Add(new XRSubreport
|
||||
{
|
||||
|
|
@ -246,7 +250,7 @@ public class DynamicFormReport : XtraReport
|
|||
{
|
||||
return new XRLabel
|
||||
{
|
||||
Text = GetLocalizedFieldCaption(field.Field, localizer),
|
||||
Text = DynamicReportImageHelper.GetLocalizedFieldCaption(field.Field, localizer),
|
||||
BoundsF = new System.Drawing.RectangleF(x, y, width, height),
|
||||
Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
|
||||
BackColor = System.Drawing.Color.FromArgb(248, 250, 252),
|
||||
|
|
@ -280,7 +284,7 @@ public class DynamicFormReport : XtraReport
|
|||
BorderColor = FieldBorderColor,
|
||||
BorderWidth = 0.25F,
|
||||
Padding = new PaddingInfo(5, 5, 2, 2),
|
||||
TextAlignment = ToTextAlignment(field.Field.Alignment, field.Field.SourceDbType),
|
||||
TextAlignment = DynamicReportImageHelper.ToTextAlignment(field.Field.Alignment, field.Field.SourceDbType),
|
||||
Multiline = true,
|
||||
CanGrow = true
|
||||
};
|
||||
|
|
@ -392,19 +396,13 @@ public class DynamicFormReport : XtraReport
|
|||
return null;
|
||||
}
|
||||
|
||||
if (TryGetRowValue(row, field.BindingName, out var value) && HasValue(value))
|
||||
if (DynamicReportImageHelper.TryGetRowValue(row, field.BindingName, out var value) &&
|
||||
DynamicReportImageHelper.HasValue(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return TryGetRowValue(row, field.Field.FieldName, out value) ? value : null;
|
||||
}
|
||||
|
||||
private static bool HasValue(object value)
|
||||
{
|
||||
return value != null &&
|
||||
value != DBNull.Value &&
|
||||
!string.IsNullOrWhiteSpace(Convert.ToString(value, CultureInfo.InvariantCulture));
|
||||
return DynamicReportImageHelper.TryGetRowValue(row, field.Field.FieldName, out value) ? value : null;
|
||||
}
|
||||
|
||||
private static bool TryGetFormattedValue(
|
||||
|
|
@ -413,7 +411,7 @@ public class DynamicFormReport : XtraReport
|
|||
out string text)
|
||||
{
|
||||
text = null;
|
||||
if (row == null || !TryGetRowValue(row, field.BindingName, out var rawValue))
|
||||
if (row == null || !DynamicReportImageHelper.TryGetRowValue(row, field.BindingName, out var rawValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -436,15 +434,14 @@ public class DynamicFormReport : XtraReport
|
|||
}
|
||||
|
||||
private static float CalculateDetailHeight(
|
||||
IReadOnlyList<FormReportField> fields,
|
||||
ListForm listForm,
|
||||
IDictionary<string, object> row)
|
||||
IReadOnlyList<FormGroup> groups,
|
||||
int subFormCount)
|
||||
{
|
||||
var groupHeight = GetFormGroups(listForm, fields)
|
||||
var groupHeight = groups
|
||||
.Sum(group => (string.IsNullOrWhiteSpace(group.Caption) ? 0 : 26) +
|
||||
group.Fields.Sum(field => GetFieldRowHeight(field) + 3) + 8);
|
||||
|
||||
var subFormHeight = GetListSubForms(listForm, row).Count() * 32;
|
||||
var subFormHeight = subFormCount * 32;
|
||||
return Math.Max(groupHeight + subFormHeight, RowHeight);
|
||||
}
|
||||
|
||||
|
|
@ -486,11 +483,12 @@ public class DynamicFormReport : XtraReport
|
|||
.Select(x => new FieldWithEditor(x, null))
|
||||
.ToList();
|
||||
|
||||
var selectFieldsByName = DynamicReportImageHelper.CreateSelectFieldMap(selectQueryManager.SelectFields);
|
||||
return orderedFields
|
||||
.Where(x => selectQueryManager.SelectFields.Any(s => s.FieldName == x.Field.FieldName))
|
||||
.Where(x => selectFieldsByName.ContainsKey(x.Field.FieldName))
|
||||
.Select(x =>
|
||||
{
|
||||
var selectField = selectQueryManager.SelectFields.First(s => s.FieldName == x.Field.FieldName);
|
||||
var selectField = selectFieldsByName[x.Field.FieldName];
|
||||
return CreateFormReportField(x.Field, selectField, x.EditorType, defaultValueHelper);
|
||||
})
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.BindingName) && x.BindingName != "undefined")
|
||||
|
|
@ -584,11 +582,12 @@ public class DynamicFormReport : XtraReport
|
|||
var relation = subForm.Relation?
|
||||
.FirstOrDefault(x => !string.IsNullOrWhiteSpace(x.ParentFieldName) &&
|
||||
!string.IsNullOrWhiteSpace(x.ChildFieldName) &&
|
||||
TryGetRowValue(row, x.ParentFieldName, out var value) &&
|
||||
DynamicReportImageHelper.TryGetRowValue(row, x.ParentFieldName, out var value) &&
|
||||
value != null &&
|
||||
value != DBNull.Value);
|
||||
|
||||
if (relation == null || !TryGetRowValue(row, relation.ParentFieldName, out var parentValue))
|
||||
if (relation == null ||
|
||||
!DynamicReportImageHelper.TryGetRowValue(row, relation.ParentFieldName, out var parentValue))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -612,8 +611,8 @@ public class DynamicFormReport : XtraReport
|
|||
string editorType,
|
||||
DefaultValueHelper defaultValueHelper)
|
||||
{
|
||||
var bindingName = GetReportFieldName(selectField);
|
||||
var lookup = GetLookup(field);
|
||||
var bindingName = DynamicReportImageHelper.GetReportFieldName(selectField);
|
||||
var lookup = DynamicReportImageHelper.GetLookup(field);
|
||||
|
||||
if (lookup == null || selectField?.JoinOptions != null)
|
||||
{
|
||||
|
|
@ -627,7 +626,7 @@ public class DynamicFormReport : XtraReport
|
|||
|
||||
if (lookup.DataSourceType == UiLookupDataSourceTypeEnum.StaticData)
|
||||
{
|
||||
var staticLookupValues = GetStaticLookupValues(lookup, defaultValueHelper);
|
||||
var staticLookupValues = DynamicReportImageHelper.GetStaticLookupValues(lookup, defaultValueHelper);
|
||||
return new FormReportField(
|
||||
field,
|
||||
staticLookupValues?.Count > 0 ? DynamicReportSqlBuilder.GetLookupBindingName(field) : bindingName,
|
||||
|
|
@ -661,238 +660,6 @@ public class DynamicFormReport : XtraReport
|
|||
return null;
|
||||
}
|
||||
|
||||
private static bool TryGetRowValue(IDictionary<string, object> row, string name, out object value)
|
||||
{
|
||||
foreach (var item in row)
|
||||
{
|
||||
if (string.Equals(item.Key, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
value = item.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string ToDevExpressConnectionString(string connectionString, DataSourceTypeEnum dataSourceType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(connectionString) ||
|
||||
connectionString.Contains("XpoProvider=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
var provider = dataSourceType switch
|
||||
{
|
||||
DataSourceTypeEnum.Mssql => "MSSqlServer",
|
||||
DataSourceTypeEnum.Postgresql => "Postgres",
|
||||
_ => null
|
||||
};
|
||||
|
||||
return string.IsNullOrWhiteSpace(provider)
|
||||
? connectionString
|
||||
: $"XpoProvider={provider};{connectionString}";
|
||||
}
|
||||
|
||||
private static string ApplyQueryParameters(string sql, Dictionary<string, object> parameters)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sql) || parameters == null || parameters.Count == 0)
|
||||
{
|
||||
return sql;
|
||||
}
|
||||
|
||||
foreach (var parameter in parameters.OrderByDescending(x => x.Key.Length))
|
||||
{
|
||||
var key = parameter.Key.StartsWith("@") ? parameter.Key : "@" + parameter.Key;
|
||||
sql = Regex.Replace(
|
||||
sql,
|
||||
Regex.Escape(key) + @"\b",
|
||||
ToSqlLiteral(parameter.Value),
|
||||
RegexOptions.CultureInvariant);
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
private static string StripReportPaging(string sql)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sql))
|
||||
{
|
||||
return sql;
|
||||
}
|
||||
|
||||
return Regex.Replace(
|
||||
sql,
|
||||
@"\s+ORDER\s+BY[\s\S]*?(?:\s+OFFSET\s+\d+\s+ROWS(?:\s+FETCH\s+NEXT\s+\d+\s+ROWS\s+ONLY)?)?\s*$",
|
||||
string.Empty,
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
}
|
||||
|
||||
private static string ToSqlLiteral(object value)
|
||||
{
|
||||
if (value == null || value == DBNull.Value)
|
||||
{
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
return value switch
|
||||
{
|
||||
string text => $"'{text.Replace("'", "''")}'",
|
||||
Guid guid => $"'{guid}'",
|
||||
DateTime date => $"'{date:yyyy-MM-dd HH:mm:ss.fff}'",
|
||||
DateTimeOffset date => $"'{date:yyyy-MM-dd HH:mm:ss.fff zzz}'",
|
||||
bool boolean => boolean ? "1" : "0",
|
||||
byte or sbyte or short or ushort or int or uint or long or ulong or float or double or decimal
|
||||
=> Convert.ToString(value, CultureInfo.InvariantCulture),
|
||||
_ => $"'{value.ToString()?.Replace("'", "''")}'"
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetReportFieldName(SelectField selectField)
|
||||
{
|
||||
if (selectField?.JoinOptions != null && !string.IsNullOrWhiteSpace(selectField.JoinOptions.FieldNameAlias))
|
||||
{
|
||||
return selectField.JoinOptions.FieldNameAlias;
|
||||
}
|
||||
|
||||
return selectField?.FieldName;
|
||||
}
|
||||
|
||||
private static LookupDto GetLookup(ListFormField field)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(field.LookupJson))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<LookupDto>(field.LookupJson);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> GetStaticLookupValues(
|
||||
LookupDto lookup,
|
||||
DefaultValueHelper defaultValueHelper)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(lookup.LookupQuery))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var lookupJson = defaultValueHelper.GetDefaultValue(lookup.LookupQuery);
|
||||
using var document = JsonDocument.Parse(lookupJson);
|
||||
if (document.RootElement.ValueKind != JsonValueKind.Array)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var valueExpr = lookup.ValueExpr ?? "Key";
|
||||
var displayExpr = lookup.DisplayExpr ?? "Name";
|
||||
var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var item in document.RootElement.EnumerateArray())
|
||||
{
|
||||
if (!TryGetProperty(item, valueExpr, out var valueElement) ||
|
||||
!TryGetProperty(item, displayExpr, out var displayElement))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var value = GetJsonScalar(valueElement);
|
||||
if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
values[value] = GetJsonScalar(displayElement) ?? value;
|
||||
}
|
||||
|
||||
return values.Count == 0 ? null : values;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryGetProperty(JsonElement element, string propertyName, out JsonElement value)
|
||||
{
|
||||
foreach (var property in element.EnumerateObject())
|
||||
{
|
||||
if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
value = property.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetJsonScalar(JsonElement element)
|
||||
{
|
||||
return element.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => element.GetString(),
|
||||
JsonValueKind.Number => element.GetRawText(),
|
||||
JsonValueKind.True => bool.TrueString,
|
||||
JsonValueKind.False => bool.FalseString,
|
||||
JsonValueKind.Null => null,
|
||||
_ => element.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
private static string GetLocalizedReportTitle(ListForm listForm, IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
var title = string.IsNullOrWhiteSpace(listForm.Title) ? listForm.Name : listForm.Title;
|
||||
return Localize(localizer, title, listForm.ListFormCode);
|
||||
}
|
||||
|
||||
private static string GetLocalizedFieldCaption(ListFormField field, IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
var caption = string.IsNullOrWhiteSpace(field.CaptionName) ? field.FieldName : field.CaptionName;
|
||||
return Localize(localizer, caption, field.FieldName);
|
||||
}
|
||||
|
||||
private static string Localize(IStringLocalizer<PlatformResource> localizer, string key, string fallback)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
var localized = localizer[key];
|
||||
return localized.ResourceNotFound || string.IsNullOrWhiteSpace(localized.Value)
|
||||
? key
|
||||
: localized.Value;
|
||||
}
|
||||
|
||||
private static TextAlignment ToTextAlignment(string alignment, DbType dbType)
|
||||
{
|
||||
return alignment?.ToLowerInvariant() switch
|
||||
{
|
||||
"center" => TextAlignment.MiddleCenter,
|
||||
"right" => TextAlignment.MiddleRight,
|
||||
"left" => TextAlignment.MiddleLeft,
|
||||
_ => IsNumeric(dbType) ? TextAlignment.MiddleRight : TextAlignment.MiddleLeft
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsNumeric(DbType dbType)
|
||||
{
|
||||
return dbType is DbType.Currency or DbType.Decimal or DbType.Double or DbType.Int16 or DbType.Int32
|
||||
or DbType.Int64 or DbType.Single or DbType.UInt16 or DbType.UInt32 or DbType.UInt64;
|
||||
}
|
||||
|
||||
private static bool IsBooleanEditor(FormReportField field)
|
||||
{
|
||||
return field.EditorType is PlatformConsts.EditorTypes.dxCheckBox or PlatformConsts.EditorTypes.dxSwitch ||
|
||||
|
|
@ -928,16 +695,6 @@ public class DynamicFormReport : XtraReport
|
|||
return false;
|
||||
}
|
||||
|
||||
private static string GetParameter(IDictionary<string, string> parameters, string name)
|
||||
{
|
||||
return parameters.TryGetValue(name, out var value) ? value : null;
|
||||
}
|
||||
|
||||
private static string GetImageBaseUrl(IConfiguration configuration)
|
||||
{
|
||||
return configuration["App:CdnUrl"] ?? configuration["App:SelfUrl"];
|
||||
}
|
||||
|
||||
private sealed record FieldWithEditor(ListFormField Field, string EditorType);
|
||||
private sealed record FormGroup(string Caption, IReadOnlyList<FormReportField> Fields);
|
||||
private sealed record FormReportField(
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using DevExpress.DataAccess.ConnectionParameters;
|
||||
using DevExpress.DataAccess.Sql;
|
||||
|
|
@ -100,11 +97,13 @@ public class DynamicGridReport : XtraReport
|
|||
};
|
||||
|
||||
selectQueryManager.PrepareQueries(listForm, fields, dataSourceType, customizations, queryParams);
|
||||
var selectFieldsByName = DynamicReportImageHelper.CreateSelectFieldMap(selectQueryManager.SelectFields);
|
||||
var editorTypesByField = DynamicReportImageHelper.CreateEditorTypeMap(listForm);
|
||||
|
||||
var visibleFields = fields
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName) && x.FieldName != "undefined")
|
||||
.Where(x => x.IsActive != false && x.Visible == true)
|
||||
.Where(x => selectQueryManager.SelectFields.Any(s => s.FieldName == x.FieldName))
|
||||
.Where(x => selectFieldsByName.ContainsKey(x.FieldName))
|
||||
.ToList();
|
||||
|
||||
if (visibleFields.Count == 0)
|
||||
|
|
@ -112,7 +111,7 @@ public class DynamicGridReport : XtraReport
|
|||
visibleFields = fields
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.FieldName) && x.FieldName != "undefined")
|
||||
.Where(x => x.IsActive != false)
|
||||
.Where(x => selectQueryManager.SelectFields.Any(s => s.FieldName == x.FieldName))
|
||||
.Where(x => selectFieldsByName.ContainsKey(x.FieldName))
|
||||
.Take(8)
|
||||
.ToList();
|
||||
}
|
||||
|
|
@ -120,24 +119,27 @@ public class DynamicGridReport : XtraReport
|
|||
var reportColumns = visibleFields
|
||||
.Select(field =>
|
||||
{
|
||||
var selectField = selectQueryManager.SelectFields.First(s => s.FieldName == field.FieldName);
|
||||
var selectField = selectFieldsByName[field.FieldName];
|
||||
editorTypesByField.TryGetValue(field.FieldName, out var editorType);
|
||||
return CreateReportColumn(
|
||||
field,
|
||||
selectField,
|
||||
defaultValueHelper,
|
||||
DynamicReportImageHelper.HasImageEditor(listForm, field.FieldName),
|
||||
HasHtmlEditor(listForm, field.FieldName));
|
||||
DynamicReportImageHelper.IsImageEditor(editorType),
|
||||
editorType == PlatformConsts.EditorTypes.dxHtmlEditor);
|
||||
})
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x.BindingName) && x.BindingName != "undefined")
|
||||
.ToList();
|
||||
|
||||
var selectQuery = StripReportPaging(ApplyQueryParameters(selectQueryManager.SelectQuery, selectQueryManager.SelectQueryParameters));
|
||||
var selectQuery = DynamicReportImageHelper.StripReportPaging(DynamicReportImageHelper.ApplyQueryParameters(
|
||||
selectQueryManager.SelectQuery,
|
||||
selectQueryManager.SelectQueryParameters));
|
||||
selectQuery = DynamicReportSqlBuilder.ApplyLookupTextColumns(
|
||||
selectQuery,
|
||||
reportColumns,
|
||||
defaultValueHelper,
|
||||
dataSourceType);
|
||||
var imageBaseUrl = GetImageBaseUrl(configuration);
|
||||
var imageBaseUrl = DynamicReportImageHelper.GetImageBaseUrl(configuration);
|
||||
reportColumns = await ApplyImageCountsAsync(reportColumns, dynamicDataRepository, selectQuery, connectionString, imageBaseUrl);
|
||||
|
||||
ConfigurePage(reportColumns);
|
||||
|
|
@ -172,87 +174,14 @@ public class DynamicGridReport : XtraReport
|
|||
|
||||
private void ConfigureDataSource(string selectQuery, string connectionString, DataSourceTypeEnum dataSourceType)
|
||||
{
|
||||
var dataSource = new SqlDataSource(new CustomStringConnectionParameters(ToDevExpressConnectionString(connectionString, dataSourceType)));
|
||||
var dataSource = new SqlDataSource(new CustomStringConnectionParameters(
|
||||
DynamicReportImageHelper.ToDevExpressConnectionString(connectionString, dataSourceType)));
|
||||
dataSource.Queries.Add(new CustomSqlQuery(QueryName, selectQuery));
|
||||
ComponentStorage.Add(dataSource);
|
||||
DataSource = dataSource;
|
||||
DataMember = QueryName;
|
||||
}
|
||||
|
||||
private static string ToDevExpressConnectionString(string connectionString, DataSourceTypeEnum dataSourceType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(connectionString) ||
|
||||
connectionString.Contains("XpoProvider=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
var provider = dataSourceType switch
|
||||
{
|
||||
DataSourceTypeEnum.Mssql => "MSSqlServer",
|
||||
DataSourceTypeEnum.Postgresql => "Postgres",
|
||||
_ => null
|
||||
};
|
||||
|
||||
return string.IsNullOrWhiteSpace(provider)
|
||||
? connectionString
|
||||
: $"XpoProvider={provider};{connectionString}";
|
||||
}
|
||||
|
||||
private static string ApplyQueryParameters(string sql, Dictionary<string, object> parameters)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sql) || parameters == null || parameters.Count == 0)
|
||||
{
|
||||
return sql;
|
||||
}
|
||||
|
||||
foreach (var parameter in parameters.OrderByDescending(x => x.Key.Length))
|
||||
{
|
||||
var key = parameter.Key.StartsWith("@") ? parameter.Key : "@" + parameter.Key;
|
||||
sql = Regex.Replace(
|
||||
sql,
|
||||
Regex.Escape(key) + @"\b",
|
||||
ToSqlLiteral(parameter.Value),
|
||||
RegexOptions.CultureInvariant);
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
private static string StripReportPaging(string sql)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sql))
|
||||
{
|
||||
return sql;
|
||||
}
|
||||
|
||||
return Regex.Replace(
|
||||
sql,
|
||||
@"\s+ORDER\s+BY[\s\S]*?(?:\s+OFFSET\s+\d+\s+ROWS(?:\s+FETCH\s+NEXT\s+\d+\s+ROWS\s+ONLY)?)?\s*$",
|
||||
string.Empty,
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
}
|
||||
|
||||
private static string ToSqlLiteral(object value)
|
||||
{
|
||||
if (value == null || value == DBNull.Value)
|
||||
{
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
return value switch
|
||||
{
|
||||
string text => $"'{text.Replace("'", "''")}'",
|
||||
Guid guid => $"'{guid}'",
|
||||
DateTime date => $"'{date:yyyy-MM-dd HH:mm:ss.fff}'",
|
||||
DateTimeOffset date => $"'{date:yyyy-MM-dd HH:mm:ss.fff zzz}'",
|
||||
bool boolean => boolean ? "1" : "0",
|
||||
byte or sbyte or short or ushort or int or uint or long or ulong or float or double or decimal
|
||||
=> Convert.ToString(value, CultureInfo.InvariantCulture),
|
||||
_ => $"'{value.ToString()?.Replace("'", "''")}'"
|
||||
};
|
||||
}
|
||||
|
||||
private static async Task<List<ReportColumn>> ApplyImageCountsAsync(
|
||||
IReadOnlyList<ReportColumn> columns,
|
||||
IDynamicDataRepository dynamicDataRepository,
|
||||
|
|
@ -292,34 +221,13 @@ public class DynamicGridReport : XtraReport
|
|||
|
||||
private static object GetImageRawValue(IDictionary<string, object> row, ReportColumn column)
|
||||
{
|
||||
if (TryGetRowValue(row, column.BindingName, out var value) && HasValue(value))
|
||||
if (DynamicReportImageHelper.TryGetRowValue(row, column.BindingName, out var value) &&
|
||||
DynamicReportImageHelper.HasValue(value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
return TryGetRowValue(row, column.Field.FieldName, out value) ? value : null;
|
||||
}
|
||||
|
||||
private static bool HasValue(object value)
|
||||
{
|
||||
return value != null &&
|
||||
value != DBNull.Value &&
|
||||
!string.IsNullOrWhiteSpace(Convert.ToString(value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
private static bool TryGetRowValue(IDictionary<string, object> row, string name, out object value)
|
||||
{
|
||||
foreach (var item in row)
|
||||
{
|
||||
if (string.Equals(item.Key, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
value = item.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
return DynamicReportImageHelper.TryGetRowValue(row, column.Field.FieldName, out value) ? value : null;
|
||||
}
|
||||
|
||||
private void BuildLayout(
|
||||
|
|
@ -347,7 +255,7 @@ public class DynamicGridReport : XtraReport
|
|||
var pageWidth = PageWidth - Margins.Left - Margins.Right;
|
||||
reportHeader.Controls.Add(new XRLabel
|
||||
{
|
||||
Text = GetLocalizedReportTitle(listForm, localizer),
|
||||
Text = DynamicReportImageHelper.GetLocalizedReportTitle(listForm, localizer),
|
||||
BoundsF = new System.Drawing.RectangleF(0, 0, pageWidth, 30),
|
||||
Font = new DXFont("Arial", 14F, DXFontStyle.Bold),
|
||||
TextAlignment = TextAlignment.MiddleLeft
|
||||
|
|
@ -419,7 +327,7 @@ public class DynamicGridReport : XtraReport
|
|||
var field = column.Field;
|
||||
return new XRTableCell
|
||||
{
|
||||
Text = GetLocalizedFieldCaption(field, localizer),
|
||||
Text = DynamicReportImageHelper.GetLocalizedFieldCaption(field, localizer),
|
||||
WidthF = width,
|
||||
Weight = width,
|
||||
Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
|
||||
|
|
@ -430,31 +338,6 @@ public class DynamicGridReport : XtraReport
|
|||
};
|
||||
}
|
||||
|
||||
private static string GetLocalizedReportTitle(ListForm listForm, IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
var title = string.IsNullOrWhiteSpace(listForm.Title) ? listForm.Name : listForm.Title;
|
||||
return Localize(localizer, title, listForm.ListFormCode);
|
||||
}
|
||||
|
||||
private static string GetLocalizedFieldCaption(ListFormField field, IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
var caption = string.IsNullOrWhiteSpace(field.CaptionName) ? field.FieldName : field.CaptionName;
|
||||
return Localize(localizer, caption, field.FieldName);
|
||||
}
|
||||
|
||||
private static string Localize(IStringLocalizer<PlatformResource> localizer, string key, string fallback)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
var localized = localizer[key];
|
||||
return localized.ResourceNotFound || string.IsNullOrWhiteSpace(localized.Value)
|
||||
? key
|
||||
: localized.Value;
|
||||
}
|
||||
|
||||
private static XRTableCell CreateDetailCell(ReportColumn column, float width, string imageBaseUrl, float height)
|
||||
{
|
||||
var field = column.Field;
|
||||
|
|
@ -479,7 +362,7 @@ public class DynamicGridReport : XtraReport
|
|||
Weight = width,
|
||||
Font = new DXFont("Segoe UI Emoji", 8F),
|
||||
Padding = new PaddingInfo(4, 4, 2, 2),
|
||||
TextAlignment = ToTextAlignment(field.Alignment, field.SourceDbType),
|
||||
TextAlignment = DynamicReportImageHelper.ToTextAlignment(field.Alignment, field.SourceDbType),
|
||||
Multiline = false,
|
||||
CanGrow = false
|
||||
};
|
||||
|
|
@ -521,7 +404,7 @@ public class DynamicGridReport : XtraReport
|
|||
Weight = width,
|
||||
Font = new DXFont("Arial", 8F, DXFontStyle.Bold),
|
||||
Padding = new PaddingInfo(4, 4, 2, 2),
|
||||
TextAlignment = ToTextAlignment(field.Alignment, field.SourceDbType)
|
||||
TextAlignment = DynamicReportImageHelper.ToTextAlignment(field.Alignment, field.SourceDbType)
|
||||
};
|
||||
|
||||
if (summaryType == null)
|
||||
|
|
@ -580,7 +463,7 @@ public class DynamicGridReport : XtraReport
|
|||
return 75;
|
||||
}
|
||||
|
||||
if (IsNumeric(field.SourceDbType))
|
||||
if (DynamicReportImageHelper.IsNumeric(field.SourceDbType))
|
||||
{
|
||||
return 70;
|
||||
}
|
||||
|
|
@ -622,16 +505,6 @@ public class DynamicGridReport : XtraReport
|
|||
return DynamicReportImageHelper.GetGridImageColumnWidth(column.ImageCount);
|
||||
}
|
||||
|
||||
private static string GetReportFieldName(SelectField selectField)
|
||||
{
|
||||
if (selectField?.JoinOptions != null && !string.IsNullOrWhiteSpace(selectField.JoinOptions.FieldNameAlias))
|
||||
{
|
||||
return selectField.JoinOptions.FieldNameAlias;
|
||||
}
|
||||
|
||||
return selectField?.FieldName;
|
||||
}
|
||||
|
||||
private static ReportColumn CreateReportColumn(
|
||||
ListFormField field,
|
||||
SelectField selectField,
|
||||
|
|
@ -639,8 +512,8 @@ public class DynamicGridReport : XtraReport
|
|||
bool isImage,
|
||||
bool isHtml)
|
||||
{
|
||||
var bindingName = GetReportFieldName(selectField);
|
||||
var lookup = GetLookup(field);
|
||||
var bindingName = DynamicReportImageHelper.GetReportFieldName(selectField);
|
||||
var lookup = DynamicReportImageHelper.GetLookup(field);
|
||||
|
||||
if (lookup == null || selectField?.JoinOptions != null)
|
||||
{
|
||||
|
|
@ -654,7 +527,7 @@ public class DynamicGridReport : XtraReport
|
|||
|
||||
if (lookup.DataSourceType == UiLookupDataSourceTypeEnum.StaticData)
|
||||
{
|
||||
var staticLookupValues = GetStaticLookupValues(lookup, defaultValueHelper);
|
||||
var staticLookupValues = DynamicReportImageHelper.GetStaticLookupValues(lookup, defaultValueHelper);
|
||||
return new ReportColumn(
|
||||
field,
|
||||
staticLookupValues?.Count > 0 ? DynamicReportSqlBuilder.GetLookupBindingName(field) : bindingName,
|
||||
|
|
@ -667,119 +540,6 @@ public class DynamicGridReport : XtraReport
|
|||
return new ReportColumn(field, bindingName, null, null, isImage, isHtml);
|
||||
}
|
||||
|
||||
private static bool HasHtmlEditor(ListForm listForm, string fieldName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(listForm?.EditingFormJson) || string.IsNullOrWhiteSpace(fieldName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var groups = JsonSerializer.Deserialize<List<EditingFormDto>>(listForm.EditingFormJson) ?? [];
|
||||
return groups
|
||||
.SelectMany(x => x.Items ?? [])
|
||||
.Any(x => string.Equals(x.DataField, fieldName, StringComparison.OrdinalIgnoreCase) &&
|
||||
x.EditorType2 == PlatformConsts.EditorTypes.dxHtmlEditor);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static LookupDto GetLookup(ListFormField field)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(field.LookupJson))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<LookupDto>(field.LookupJson);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> GetStaticLookupValues(
|
||||
LookupDto lookup,
|
||||
DefaultValueHelper defaultValueHelper)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(lookup.LookupQuery))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var lookupJson = defaultValueHelper.GetDefaultValue(lookup.LookupQuery);
|
||||
using var document = JsonDocument.Parse(lookupJson);
|
||||
if (document.RootElement.ValueKind != JsonValueKind.Array)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var valueExpr = lookup.ValueExpr ?? "Key";
|
||||
var displayExpr = lookup.DisplayExpr ?? "Name";
|
||||
var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var item in document.RootElement.EnumerateArray())
|
||||
{
|
||||
if (!TryGetProperty(item, valueExpr, out var valueElement) ||
|
||||
!TryGetProperty(item, displayExpr, out var displayElement))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var value = GetJsonScalar(valueElement);
|
||||
if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
values[value] = GetJsonScalar(displayElement) ?? value;
|
||||
}
|
||||
|
||||
return values.Count == 0 ? null : values;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryGetProperty(JsonElement element, string propertyName, out JsonElement value)
|
||||
{
|
||||
foreach (var property in element.EnumerateObject())
|
||||
{
|
||||
if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
value = property.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetJsonScalar(JsonElement element)
|
||||
{
|
||||
return element.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => element.GetString(),
|
||||
JsonValueKind.Number => element.GetRawText(),
|
||||
JsonValueKind.True => bool.TrueString,
|
||||
JsonValueKind.False => bool.FalseString,
|
||||
JsonValueKind.Null => null,
|
||||
_ => element.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private sealed record ReportColumn(
|
||||
ListFormField Field,
|
||||
string BindingName,
|
||||
|
|
@ -821,30 +581,6 @@ public class DynamicGridReport : XtraReport
|
|||
return null;
|
||||
}
|
||||
|
||||
private static TextAlignment ToTextAlignment(string alignment, DbType dbType)
|
||||
{
|
||||
return alignment?.ToLowerInvariant() switch
|
||||
{
|
||||
"center" => TextAlignment.MiddleCenter,
|
||||
"right" => TextAlignment.MiddleRight,
|
||||
"left" => TextAlignment.MiddleLeft,
|
||||
_ => IsNumeric(dbType) ? TextAlignment.MiddleRight : TextAlignment.MiddleLeft
|
||||
};
|
||||
}
|
||||
|
||||
private static bool IsNumeric(DbType dbType)
|
||||
{
|
||||
return dbType is DbType.Currency or DbType.Decimal or DbType.Double or DbType.Int16 or DbType.Int32
|
||||
or DbType.Int64 or DbType.Single or DbType.UInt16 or DbType.UInt32 or DbType.UInt64;
|
||||
}
|
||||
|
||||
private static string GetParameter(IDictionary<string, string> parameters, string name)
|
||||
{
|
||||
return parameters.TryGetValue(name, out var value) ? value : null;
|
||||
}
|
||||
|
||||
private static string GetImageBaseUrl(IConfiguration configuration)
|
||||
{
|
||||
return configuration["App:CdnUrl"] ?? configuration["App:SelfUrl"];
|
||||
}
|
||||
private static string GetParameter(IDictionary<string, string> parameters, string name) =>
|
||||
DynamicReportImageHelper.GetParameter(parameters, name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
|
@ -11,9 +12,14 @@ using DevExpress.Data.Filtering;
|
|||
using DevExpress.XtraPrinting;
|
||||
using DevExpress.XtraPrinting.Drawing;
|
||||
using DevExpress.XtraReports.UI;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Sozsoft.Platform;
|
||||
using Sozsoft.Platform.Entities;
|
||||
using Sozsoft.Platform.Enums;
|
||||
using Sozsoft.Platform.ListForms;
|
||||
using Sozsoft.Platform.Localization;
|
||||
using Sozsoft.Platform.Queries;
|
||||
|
||||
namespace Sozsoft.Reports.PredefinedReports;
|
||||
|
||||
|
|
@ -26,6 +32,9 @@ internal static class DynamicReportImageHelper
|
|||
private const int MaxFormImages = 8;
|
||||
private const string ImageUrlFunctionName = "SozReportImageUrl";
|
||||
private const string ImageSourceFunctionName = "SozReportImageSource";
|
||||
private static readonly Regex ReportPagingRegex = new(
|
||||
@"\s+ORDER\s+BY[\s\S]*?(?:\s+OFFSET\s+\d+\s+ROWS(?:\s+FETCH\s+NEXT\s+\d+\s+ROWS\s+ONLY)?)?\s*$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
||||
private static readonly HttpClient HttpClient = new(new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
|
||||
|
|
@ -40,6 +49,289 @@ internal static class DynamicReportImageHelper
|
|||
RegisterImageFunction(new ReportImageSourceFunction());
|
||||
}
|
||||
|
||||
public static Dictionary<string, SelectField> CreateSelectFieldMap(IEnumerable<SelectField> selectFields)
|
||||
{
|
||||
var map = new Dictionary<string, SelectField>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var selectField in selectFields ?? [])
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(selectField.FieldName))
|
||||
{
|
||||
map.TryAdd(selectField.FieldName, selectField);
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> CreateEditorTypeMap(ListForm listForm)
|
||||
{
|
||||
var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
if (string.IsNullOrWhiteSpace(listForm?.EditingFormJson))
|
||||
{
|
||||
return map;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var groups = JsonSerializer.Deserialize<List<EditingFormDto>>(listForm.EditingFormJson) ?? [];
|
||||
foreach (var item in groups.SelectMany(x => x.Items ?? []))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(item.DataField))
|
||||
{
|
||||
map[item.DataField] = item.EditorType2;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public static string GetReportFieldName(SelectField selectField)
|
||||
{
|
||||
if (selectField?.JoinOptions != null && !string.IsNullOrWhiteSpace(selectField.JoinOptions.FieldNameAlias))
|
||||
{
|
||||
return selectField.JoinOptions.FieldNameAlias;
|
||||
}
|
||||
|
||||
return selectField?.FieldName;
|
||||
}
|
||||
|
||||
public static string ToDevExpressConnectionString(string connectionString, DataSourceTypeEnum dataSourceType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(connectionString) ||
|
||||
connectionString.Contains("XpoProvider=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return connectionString;
|
||||
}
|
||||
|
||||
var provider = dataSourceType switch
|
||||
{
|
||||
DataSourceTypeEnum.Mssql => "MSSqlServer",
|
||||
DataSourceTypeEnum.Postgresql => "Postgres",
|
||||
_ => null
|
||||
};
|
||||
|
||||
return string.IsNullOrWhiteSpace(provider)
|
||||
? connectionString
|
||||
: $"XpoProvider={provider};{connectionString}";
|
||||
}
|
||||
|
||||
public static string ApplyQueryParameters(string sql, Dictionary<string, object> parameters)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(sql) || parameters == null || parameters.Count == 0)
|
||||
{
|
||||
return sql;
|
||||
}
|
||||
|
||||
foreach (var parameter in parameters.OrderByDescending(x => x.Key.Length))
|
||||
{
|
||||
var key = parameter.Key.StartsWith("@") ? parameter.Key : "@" + parameter.Key;
|
||||
sql = Regex.Replace(
|
||||
sql,
|
||||
Regex.Escape(key) + @"\b",
|
||||
ToSqlLiteral(parameter.Value),
|
||||
RegexOptions.CultureInvariant);
|
||||
}
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
public static string StripReportPaging(string sql)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(sql)
|
||||
? sql
|
||||
: ReportPagingRegex.Replace(sql, string.Empty);
|
||||
}
|
||||
|
||||
public static string ToSqlLiteral(object value)
|
||||
{
|
||||
if (value == null || value == DBNull.Value)
|
||||
{
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
return value switch
|
||||
{
|
||||
string text => $"'{text.Replace("'", "''")}'",
|
||||
Guid guid => $"'{guid}'",
|
||||
DateTime date => $"'{date:yyyy-MM-dd HH:mm:ss.fff}'",
|
||||
DateTimeOffset date => $"'{date:yyyy-MM-dd HH:mm:ss.fff zzz}'",
|
||||
bool boolean => boolean ? "1" : "0",
|
||||
byte or sbyte or short or ushort or int or uint or long or ulong or float or double or decimal
|
||||
=> Convert.ToString(value, CultureInfo.InvariantCulture),
|
||||
_ => $"'{value.ToString()?.Replace("'", "''")}'"
|
||||
};
|
||||
}
|
||||
|
||||
public static bool TryGetRowValue(IDictionary<string, object> row, string name, out object value)
|
||||
{
|
||||
if (row != null && !string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
if (row.TryGetValue(name, out value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var item in row)
|
||||
{
|
||||
if (string.Equals(item.Key, name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
value = item.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static LookupDto GetLookup(ListFormField field)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(field.LookupJson))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return JsonSerializer.Deserialize<LookupDto>(field.LookupJson);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> GetStaticLookupValues(
|
||||
LookupDto lookup,
|
||||
DefaultValueHelper defaultValueHelper)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(lookup.LookupQuery))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var lookupJson = defaultValueHelper.GetDefaultValue(lookup.LookupQuery);
|
||||
using var document = JsonDocument.Parse(lookupJson);
|
||||
if (document.RootElement.ValueKind != JsonValueKind.Array)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var valueExpr = lookup.ValueExpr ?? "Key";
|
||||
var displayExpr = lookup.DisplayExpr ?? "Name";
|
||||
var values = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var item in document.RootElement.EnumerateArray())
|
||||
{
|
||||
if (!TryGetLookupProperty(item, valueExpr, out var valueElement) ||
|
||||
!TryGetLookupProperty(item, displayExpr, out var displayElement))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var value = GetJsonScalar(valueElement);
|
||||
if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
values[value] = GetJsonScalar(displayElement) ?? value;
|
||||
}
|
||||
|
||||
return values.Count == 0 ? null : values;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetLocalizedReportTitle(ListForm listForm, IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
var title = string.IsNullOrWhiteSpace(listForm.Title) ? listForm.Name : listForm.Title;
|
||||
return Localize(localizer, title, listForm.ListFormCode);
|
||||
}
|
||||
|
||||
public static string GetLocalizedFieldCaption(ListFormField field, IStringLocalizer<PlatformResource> localizer)
|
||||
{
|
||||
var caption = string.IsNullOrWhiteSpace(field.CaptionName) ? field.FieldName : field.CaptionName;
|
||||
return Localize(localizer, caption, field.FieldName);
|
||||
}
|
||||
|
||||
public static string Localize(IStringLocalizer<PlatformResource> localizer, string key, string fallback)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
var localized = localizer[key];
|
||||
return localized.ResourceNotFound || string.IsNullOrWhiteSpace(localized.Value)
|
||||
? key
|
||||
: localized.Value;
|
||||
}
|
||||
|
||||
public static TextAlignment ToTextAlignment(string alignment, DbType dbType)
|
||||
{
|
||||
return alignment?.ToLowerInvariant() switch
|
||||
{
|
||||
"center" => TextAlignment.MiddleCenter,
|
||||
"right" => TextAlignment.MiddleRight,
|
||||
"left" => TextAlignment.MiddleLeft,
|
||||
_ => IsNumeric(dbType) ? TextAlignment.MiddleRight : TextAlignment.MiddleLeft
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsNumeric(DbType dbType)
|
||||
{
|
||||
return dbType is DbType.Currency or DbType.Decimal or DbType.Double or DbType.Int16 or DbType.Int32
|
||||
or DbType.Int64 or DbType.Single or DbType.UInt16 or DbType.UInt32 or DbType.UInt64;
|
||||
}
|
||||
|
||||
public static string GetParameter(IDictionary<string, string> parameters, string name)
|
||||
{
|
||||
return parameters.TryGetValue(name, out var value) ? value : null;
|
||||
}
|
||||
|
||||
public static string GetImageBaseUrl(IConfiguration configuration)
|
||||
{
|
||||
return configuration["App:CdnUrl"] ?? configuration["App:SelfUrl"];
|
||||
}
|
||||
|
||||
private static bool TryGetLookupProperty(JsonElement element, string propertyName, out JsonElement value)
|
||||
{
|
||||
foreach (var property in element.EnumerateObject())
|
||||
{
|
||||
if (string.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
value = property.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetJsonScalar(JsonElement element)
|
||||
{
|
||||
return element.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => element.GetString(),
|
||||
JsonValueKind.Number => element.GetRawText(),
|
||||
JsonValueKind.True => bool.TrueString,
|
||||
JsonValueKind.False => bool.FalseString,
|
||||
JsonValueKind.Null => null,
|
||||
_ => element.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
public static bool IsImageEditor(string editorType)
|
||||
{
|
||||
return editorType is PlatformConsts.EditorTypes.dxImageUpload or PlatformConsts.EditorTypes.dxImageViewer;
|
||||
|
|
@ -150,11 +442,11 @@ internal static class DynamicReportImageHelper
|
|||
}
|
||||
}
|
||||
|
||||
private static bool HasValue(object value)
|
||||
public static bool HasValue(object value)
|
||||
{
|
||||
return value != null &&
|
||||
value != DBNull.Value &&
|
||||
!string.IsNullOrWhiteSpace(Convert.ToString(value));
|
||||
!string.IsNullOrWhiteSpace(Convert.ToString(value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
|
||||
private static bool HasImageValue(object value)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Sozsoft.Platform;
|
||||
|
|
@ -14,6 +12,12 @@ internal static class DynamicReportSqlBuilder
|
|||
{
|
||||
private const string SourceAlias = "ReportSource";
|
||||
private const string LookupAlias = "LookupSource";
|
||||
private static readonly Regex OrderByRegex = new(
|
||||
@"(?<![A-Za-z0-9_])ORDER\s+BY\b[\s\S]*$",
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
||||
private static readonly Regex LookupParameterRegex = new(
|
||||
@"@\s*param\d+",
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
||||
|
||||
internal interface IReportLookupColumn
|
||||
{
|
||||
|
|
@ -81,7 +85,7 @@ internal static class DynamicReportSqlBuilder
|
|||
{
|
||||
var sourceField = GetSourceField(column.Field.FieldName);
|
||||
var cases = column.StaticLookupValues
|
||||
.Select(item => $"WHEN {ToSqlLiteral(item.Key)} THEN {ToSqlLiteral(item.Value)}");
|
||||
.Select(item => $"WHEN {DynamicReportImageHelper.ToSqlLiteral(item.Key)} THEN {DynamicReportImageHelper.ToSqlLiteral(item.Value)}");
|
||||
|
||||
return $"CASE {sourceField} {string.Join(" ", cases)} ELSE CAST({sourceField} AS VARCHAR(4000)) END AS {QuoteSqlIdentifier(column.BindingName)}";
|
||||
}
|
||||
|
|
@ -123,17 +127,9 @@ internal static class DynamicReportSqlBuilder
|
|||
}
|
||||
|
||||
var query = lookupQuery.Trim().TrimEnd(';');
|
||||
query = Regex.Replace(
|
||||
query,
|
||||
@"(?<![A-Za-z0-9_])ORDER\s+BY\b[\s\S]*$",
|
||||
string.Empty,
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
query = OrderByRegex.Replace(query, string.Empty);
|
||||
|
||||
return Regex.Replace(
|
||||
query,
|
||||
@"@\s*param\d+",
|
||||
"NULL",
|
||||
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
return LookupParameterRegex.Replace(query, "NULL");
|
||||
}
|
||||
|
||||
private static string NormalizeSql(string sql)
|
||||
|
|
@ -146,23 +142,4 @@ internal static class DynamicReportSqlBuilder
|
|||
return $"\"{identifier.Replace("\"", "\"\"")}\"";
|
||||
}
|
||||
|
||||
private static string ToSqlLiteral(object value)
|
||||
{
|
||||
if (value == null || value == DBNull.Value)
|
||||
{
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
return value switch
|
||||
{
|
||||
string text => $"'{text.Replace("'", "''")}'",
|
||||
Guid guid => $"'{guid}'",
|
||||
DateTime date => $"'{date:yyyy-MM-dd HH:mm:ss.fff}'",
|
||||
DateTimeOffset date => $"'{date:yyyy-MM-dd HH:mm:ss.fff zzz}'",
|
||||
bool boolean => boolean ? "1" : "0",
|
||||
byte or sbyte or short or ushort or int or uint or long or ulong or float or double or decimal
|
||||
=> Convert.ToString(value, CultureInfo.InvariantCulture),
|
||||
_ => $"'{value.ToString()?.Replace("'", "''")}'"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue