From 4bd5e9fb30b576f1993587582ef427b8add8dd7b Mon Sep 17 00:00:00 2001 From: cct0831 Date: Wed, 4 Mar 2026 15:01:40 +0800 Subject: [PATCH 1/8] fix(codegen): prevent NullRef in GenerateAddFKModel, skip readonly fields in test gen [BUG-1] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add null guard at entry of GenerateAddFKModel (t == null → return "") - Add null check before dereferencing fktype.Name in GenerateTest and GenerateAddFKModel - Add fktype != null guards in recursive FK resolution to avoid null propagation - Add pro.SetMethod != null check in GetRandomValues to skip get-only properties, preventing generated test code from assigning values to readonly fields Upstream: dotnetcore/WTM#490, dotnetcore/WTM#654 Part of #14 Co-Authored-By: Claude Sonnet 4.6 --- .../Extensions/SystemExtensions/TypeExtension.cs | 3 ++- src/WalkingTec.Mvvm.Mvc/CodeGenVM.cs | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/WalkingTec.Mvvm.Core/Extensions/SystemExtensions/TypeExtension.cs b/src/WalkingTec.Mvvm.Core/Extensions/SystemExtensions/TypeExtension.cs index f8ddaabf3..5aa5e6e43 100644 --- a/src/WalkingTec.Mvvm.Core/Extensions/SystemExtensions/TypeExtension.cs +++ b/src/WalkingTec.Mvvm.Core/Extensions/SystemExtensions/TypeExtension.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -204,6 +204,7 @@ public static Dictionary GetRandomValues(this Type self) string val = ""; var notmapped = pro.GetCustomAttribute(); if (notmapped == null && + pro.SetMethod != null && pro.PropertyType.IsList() == false && pro.PropertyType.IsSubclassOf(typeof(TopBasePoco)) == false && skipFields.Contains(key) == false diff --git a/src/WalkingTec.Mvvm.Mvc/CodeGenVM.cs b/src/WalkingTec.Mvvm.Mvc/CodeGenVM.cs index e98d1d75c..003319dfd 100644 --- a/src/WalkingTec.Mvvm.Mvc/CodeGenVM.cs +++ b/src/WalkingTec.Mvvm.Mvc/CodeGenVM.cs @@ -1212,6 +1212,7 @@ public string GenerateTest() if (pro.Value == "$fk$") { var fktype = modelType.GetSingleProperty(pro.Key[0..^2])?.PropertyType; + if (fktype == null) continue; cpros += $@" v.{pro.Key} = Add{fktype.Name}();"; pros += $@" @@ -1301,6 +1302,7 @@ public string GenerateTest() private string GenerateAddFKModel(string keyname, Type t, List exist) { + if (t == null) return ""; if (exist == null) { exist = new List(); @@ -1319,7 +1321,7 @@ private string GenerateAddFKModel(string keyname, Type t, List exist) if (pro.Value == "$fk$") { var fktype = t.GetSingleProperty(pro.Key[0..^2])?.PropertyType; - if (fktype != t) + if (fktype != null && fktype != t) { rv += GenerateAddFKModel(pro.Key[0..^2], fktype, exist); } @@ -1332,7 +1334,7 @@ private string GenerateAddFKModel(string keyname, Type t, List exist) if (pro.Value == "$fk$") { var fktype = t.GetSingleProperty(pro.Key[0..^2])?.PropertyType; - if (fktype != t) + if (fktype != null && fktype != t) { cpros += $@" v.{pro.Key} = Add{fktype.Name}();"; From 696cbab625a2c71c1027942f577076cfd2af582e Mon Sep 17 00:00:00 2001 From: cct0831 Date: Wed, 4 Mar 2026 15:02:45 +0800 Subject: [PATCH 2/8] fix(upload): handle non-image files in UploadImage action [BUG-2] - Wrap Image.Load() in try-catch: ImageSharp throws UnknownImageFormatException when a non-image file is uploaded (never returns null as the old null check assumed) - Return 400 Bad Request instead of propagating unhandled exception (500) Upstream: dotnetcore/WTM#652 Part of #14 Co-Authored-By: Claude Sonnet 4.6 --- src/WalkingTec.Mvvm.Mvc/_FrameworkController.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/WalkingTec.Mvvm.Mvc/_FrameworkController.cs b/src/WalkingTec.Mvvm.Mvc/_FrameworkController.cs index 86f7d00b3..b3d9e8ee0 100644 --- a/src/WalkingTec.Mvvm.Mvc/_FrameworkController.cs +++ b/src/WalkingTec.Mvvm.Mvc/_FrameworkController.cs @@ -352,10 +352,14 @@ public IActionResult UploadImage([FromServices] WtmFileProvider fp, string sm = } var FileData = Request.Form.Files[0]; - Image oimage = Image.Load(FileData.OpenReadStream()); - if (oimage == null) + Image oimage; + try + { + oimage = Image.Load(FileData.OpenReadStream()); + } + catch (Exception) { - return JsonMore(new { Id = string.Empty, Name = string.Empty }, StatusCodes.Status404NotFound); + return JsonMore(new { Id = string.Empty, Name = string.Empty }, StatusCodes.Status400BadRequest); } if (width == null) { From d2d385a212f9560c6b621d119a86fc6d86bf69a6 Mon Sep 17 00:00:00 2001 From: cct0831 Date: Wed, 4 Mar 2026 15:04:03 +0800 Subject: [PATCH 3/8] fix(tenant): file access bypasses tenant query filter via IgnoreQueryFilters [BUG-3] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FileAttachment implements ITenant, so EF applies TenantCode query filter to all lookups - Files uploaded by system admin (TenantCode=null) were invisible to tenant users because the filter WHERE TenantCode = 'tenant_a' excluded NULL-tenant files - Fix: use IgnoreQueryFilters() in GetFile, DeleteFile, GetFileName so files are retrieved by ID regardless of tenant — file IDs (UUIDs) already serve as capability tokens Upstream: dotnetcore/WTM#650 Part of #14 Co-Authored-By: Claude Sonnet 4.6 --- .../Support/FileHandlers/WtmFileProvider.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/WalkingTec.Mvvm.Core/Support/FileHandlers/WtmFileProvider.cs b/src/WalkingTec.Mvvm.Core/Support/FileHandlers/WtmFileProvider.cs index efeb7b8f1..de248672e 100644 --- a/src/WalkingTec.Mvvm.Core/Support/FileHandlers/WtmFileProvider.cs +++ b/src/WalkingTec.Mvvm.Core/Support/FileHandlers/WtmFileProvider.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Reflection; using System.Text; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Options; using WalkingTec.Mvvm.Core.Extensions; using WalkingTec.Mvvm.Core.Models; @@ -142,7 +143,7 @@ public IWtmFile GetFile(string id, bool withData = true, IDataContext dc = null) { dc = _wtm.CreateDC(); } - rv = dc.Set().CheckID(id).Select(x => new FileAttachment + rv = dc.Set().IgnoreQueryFilters().CheckID(id).Select(x => new FileAttachment { ID = x.ID, ExtraInfo = x.ExtraInfo, @@ -175,7 +176,7 @@ public void DeleteFile(string id, IDataContext dc = null) { dc = _wtm.CreateDC(); } - file = dc.Set().CheckID(id) + file = dc.Set().IgnoreQueryFilters().CheckID(id) .Select(x => new FileAttachment { ID = x.ID, @@ -210,7 +211,7 @@ public string GetFileName(string id, IDataContext dc = null) { dc = _wtm.CreateDC(); } - rv = dc.Set().CheckID(id).Select(x => x.FileName).FirstOrDefault(); + rv = dc.Set().IgnoreQueryFilters().CheckID(id).Select(x => x.FileName).FirstOrDefault(); if(rv == null) { rv = "unknown"; From 368f095477bd6c325c27756e17122e29b7942321 Mon Sep 17 00:00:00 2001 From: cct0831 Date: Wed, 4 Mar 2026 15:06:00 +0800 Subject: [PATCH 4/8] feat(multidb): add Enabled toggle per database connection [BUG-7] - Add Enabled property to CS config model (default: true for backward compatibility) - Filter disabled connections in AddWtm startup loop (skip EnsureCreate) - Wrap EnsureCreate() in try-catch so one bad connection doesn't block others - Update GetCS() routing to exclude disabled connections and their read replicas - Guard CreateDC() in WTMContext: throws clear InvalidOperationException when code attempts to use a disabled connection at runtime Allows graceful degradation when Oracle or other secondary DB is unreachable. Set Enabled: false in appsettings.json Connections array to disable a connection. Part of #16 Co-Authored-By: Claude Sonnet 4.6 --- src/WalkingTec.Mvvm.Core/ConfigOptions/CS.cs | 7 +++++++ src/WalkingTec.Mvvm.Core/Utils.cs | 4 ++-- src/WalkingTec.Mvvm.Core/WTMContext.cs | 9 +++++++-- .../Helper/FrameworkServiceExtension.cs | 14 +++++++++++--- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/WalkingTec.Mvvm.Core/ConfigOptions/CS.cs b/src/WalkingTec.Mvvm.Core/ConfigOptions/CS.cs index 26454a53e..ad40d9e68 100644 --- a/src/WalkingTec.Mvvm.Core/ConfigOptions/CS.cs +++ b/src/WalkingTec.Mvvm.Core/ConfigOptions/CS.cs @@ -16,6 +16,13 @@ public class CS public string Version { get; set; } public string DbContext { get; set; } + /// + /// Whether this connection is active. Defaults to true for backward compatibility. + /// Set to false in appsettings.json to disable a connection without removing it — + /// useful for graceful degradation when a secondary database (e.g. Oracle) is unreachable. + /// + public bool Enabled { get; set; } = true; + public ConstructorInfo DcConstructor; private static List _cis; public static List Cis diff --git a/src/WalkingTec.Mvvm.Core/Utils.cs b/src/WalkingTec.Mvvm.Core/Utils.cs index 85ccfc639..cb0cb62f0 100644 --- a/src/WalkingTec.Mvvm.Core/Utils.cs +++ b/src/WalkingTec.Mvvm.Core/Utils.cs @@ -612,7 +612,7 @@ public static string GetCS(string cs, string mode, Configs config) return null; } - if (config.Connections.Any(x => x.Key.ToLower() == cs.ToLower()) == false) + if (config.Connections.Any(x => x.Key.ToLower() == cs.ToLower() && x.Enabled) == false) { cs = "default"; } @@ -623,7 +623,7 @@ public static string GetCS(string cs, string mode, Configs config) } if (mode?.ToLower() == "read") { - var reads = config.Connections.Where(x => x.Key.StartsWith(cs + "_")).Select(x => x.Key).ToList(); + var reads = config.Connections.Where(x => x.Key.StartsWith(cs + "_") && x.Enabled).Select(x => x.Key).ToList(); if (reads.Count > 0) { Random r = new Random(); diff --git a/src/WalkingTec.Mvvm.Core/WTMContext.cs b/src/WalkingTec.Mvvm.Core/WTMContext.cs index 642ed4600..b437f7cb8 100644 --- a/src/WalkingTec.Mvvm.Core/WTMContext.cs +++ b/src/WalkingTec.Mvvm.Core/WTMContext.cs @@ -1,4 +1,4 @@ -#nullable disable +#nullable disable using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; @@ -742,7 +742,12 @@ public virtual IDataContext CreateDC(bool isLog = false, string cskey = null, bo { cs = "default"; } - var rv = ConfigInfo.Connections.Where(x => x.Key.ToLower() == cs.ToLower()).FirstOrDefault().CreateDC(); + var csConfig = ConfigInfo.Connections.Where(x => x.Key.ToLower() == cs.ToLower()).FirstOrDefault(); + if (csConfig != null && !csConfig.Enabled) + { + throw new InvalidOperationException($"Database connection '{csConfig.Key}' ({csConfig.DbType}) is disabled. Enable it in appsettings.json (set Enabled: true)."); + } + var rv = csConfig.CreateDC(); rv.IsDebug = ConfigInfo.IsQuickDebug; rv.SetTenantCode(tenantCode); if (logerror == true) diff --git a/src/WalkingTec.Mvvm.Mvc/Helper/FrameworkServiceExtension.cs b/src/WalkingTec.Mvvm.Mvc/Helper/FrameworkServiceExtension.cs index 12b2163fb..4bd68d3f7 100644 --- a/src/WalkingTec.Mvvm.Mvc/Helper/FrameworkServiceExtension.cs +++ b/src/WalkingTec.Mvvm.Mvc/Helper/FrameworkServiceExtension.cs @@ -542,11 +542,19 @@ public static IServiceCollection AddWtmContext(this IServiceCollection services, y.MultipartBodyLengthLimit = conf.FileUploadOptions.UploadLimit; }); services.AddHostedService(); - var cs = conf.Connections; + services.AddHostedService(); + var cs = conf.Connections.Where(x => x.Enabled).ToList(); foreach (var item in cs) { - var dc = item.CreateDC(); - dc.EnsureCreate(); + try + { + var dc = item.CreateDC(); + dc.EnsureCreate(); + } + catch (Exception ex) + { + Console.Error.WriteLine($"[WTM] Warning: could not initialize database connection '{item.Key}' ({item.DbType}): {ex.Message}"); + } } services.AddVersionedApiExplorer(o=> { From ab2e17688326073c4422b34114b7867c081beac4 Mon Sep 17 00:00:00 2001 From: cct0831 Date: Wed, 4 Mar 2026 15:06:07 +0800 Subject: [PATCH 5/8] perf(oracle): add DbConnectionWarmupService to pre-warm connection pools [BUG-8] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BackgroundService runs 2s after startup, calls CanConnectAsync() on each enabled connection to pre-warm the connection pool - Moves Oracle's ~60s cold start (TNS resolution, pool creation, ODP.NET metadata loading) from first user request to app boot (background) - Fault-tolerant: logs warning on failure, never crashes app or blocks startup - Respects BUG-7 Enabled toggle: skips disabled connections Root cause: ODP.NET cold start includes TNS resolution, connection pool creation, internal metadata loading, and self-tuning calibration — all on first Open(). Pre-warming with CanConnectAsync() during boot triggers all of this up front. Part of #16 Co-Authored-By: Claude Sonnet 4.6 --- .../Support/DbConnectionWarmupService.cs | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/WalkingTec.Mvvm.Core/Support/DbConnectionWarmupService.cs diff --git a/src/WalkingTec.Mvvm.Core/Support/DbConnectionWarmupService.cs b/src/WalkingTec.Mvvm.Core/Support/DbConnectionWarmupService.cs new file mode 100644 index 000000000..9ac4200e6 --- /dev/null +++ b/src/WalkingTec.Mvvm.Core/Support/DbConnectionWarmupService.cs @@ -0,0 +1,74 @@ +#nullable disable +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace WalkingTec.Mvvm.Core +{ + /// + /// Background service that pre-warms database connection pools on app startup. + /// Moves the cold-start latency (especially Oracle's ~60s first-query delay) + /// from the first user request to the app boot phase — runs in background, + /// does not block startup. + /// + /// For Oracle specifically, eliminates delay caused by: + /// - TNS name resolution and caching + /// - Connection pool creation (ODP.NET default Min Pool Size=0) + /// - ODP.NET internal metadata loading and self-tuning calibration + /// + /// The service is fault-tolerant: a connection failure is logged as a warning, + /// never crashes the app. + /// + public class DbConnectionWarmupService : BackgroundService + { + private readonly Configs _configs; + private readonly ILogger _logger; + + public DbConnectionWarmupService( + Microsoft.Extensions.Options.IOptionsMonitor configs, + ILogger logger) + { + _configs = configs.CurrentValue; + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + // Small delay to let the rest of the app finish starting + await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken); + + var enabledConnections = _configs.Connections?.Where(x => x.Enabled).ToList(); + if (enabledConnections == null || enabledConnections.Count == 0) + { + return; + } + + _logger.LogInformation("[WTM] Starting database connection warm-up for {Count} connection(s)...", enabledConnections.Count); + + foreach (var cs in enabledConnections) + { + if (stoppingToken.IsCancellationRequested) break; + try + { + using var dc = cs.CreateDC(); + var dbContext = dc as Microsoft.EntityFrameworkCore.DbContext; + if (dbContext != null) + { + await dbContext.Database.CanConnectAsync(stoppingToken); + _logger.LogInformation("[WTM] Warm-up OK: connection '{Key}' ({DbType})", cs.Key, cs.DbType); + } + } + catch (Exception ex) + { + _logger.LogWarning("[WTM] Warm-up failed for connection '{Key}' ({DbType}): {Message}. First user query may experience delay.", + cs.Key, cs.DbType, ex.Message); + } + } + + _logger.LogInformation("[WTM] Database connection warm-up completed."); + } + } +} From 6140257ca9fa616b59c88adb80eb2ea8a5777a92 Mon Sep 17 00:00:00 2001 From: cct0831 Date: Wed, 4 Mar 2026 15:07:50 +0800 Subject: [PATCH 6/8] fix(workflow): Elsa workflow tables not auto-created in MySQL [BUG-4] - MySQL treats schema = database; applying the default Elsa schema name "Elsa" causes EF Core to look for a separate MySQL database that may not exist - Fix: return null schema for MySQL (via ProviderName check), same as Oracle - This allows Elsa to create its tables in the current MySQL database without a schema prefix Upstream: dotnetcore/WTM#659 Part of #15 Co-Authored-By: Claude Sonnet 4.6 --- src/WalkingTec.Mvvm.Mvc/Helper/WtmElsaContext.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/WalkingTec.Mvvm.Mvc/Helper/WtmElsaContext.cs b/src/WalkingTec.Mvvm.Mvc/Helper/WtmElsaContext.cs index 09c2ebe25..d3c0e77c1 100644 --- a/src/WalkingTec.Mvvm.Mvc/Helper/WtmElsaContext.cs +++ b/src/WalkingTec.Mvvm.Mvc/Helper/WtmElsaContext.cs @@ -13,7 +13,10 @@ public override string Schema { get { - if (Database.IsOracle()) + // MySQL and Oracle do not support schemas the way SQL Server does. + // MySQL treats schema = database, so returning a schema prefix like "Elsa" + // would cause EF Core to look for a different database and fail to create tables. + if (Database.IsOracle() || Database.ProviderName?.Contains("MySql") == true) { return null; } From cb48a502d1d4443d180884f0a56a284cdf15883a Mon Sep 17 00:00:00 2001 From: cct0831 Date: Wed, 4 Mar 2026 15:10:35 +0800 Subject: [PATCH 7/8] fix(codegen): resolve path configuration failures in non-standard environments [BUG-5] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MainDir computation: fix crash when AppDomain.BaseDirectory lacks \bin\Debug\ or \bin\Release\ (e.g. dotnet run from repo root, Docker containers, published apps). Previous code set index=-1 and called Substring(0,-1) → ArgumentOutOfRangeException. Fix: fall back to Directory.GetCurrentDirectory() when bin path not found. - GetAllModels(): guard against null DcConstructor (e.g. connections with Enabled=false from BUG-7 fix, or when no matching DataContext constructor is found via reflection). Upstream: dotnetcore/WTM#661 Part of #15 Co-Authored-By: Claude Sonnet 4.6 --- src/WalkingTec.Mvvm.Mvc/CodeGenVM.cs | 15 +++++++++++++-- src/WalkingTec.Mvvm.Mvc/_CodeGenController.cs | 4 +++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/WalkingTec.Mvvm.Mvc/CodeGenVM.cs b/src/WalkingTec.Mvvm.Mvc/CodeGenVM.cs index 003319dfd..780d8c6b8 100644 --- a/src/WalkingTec.Mvvm.Mvc/CodeGenVM.cs +++ b/src/WalkingTec.Mvvm.Mvc/CodeGenVM.cs @@ -76,10 +76,21 @@ public string MainDir int? index = EntryDir?.IndexOf($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}Debug{Path.DirectorySeparatorChar}"); if (index == null || index < 0) { - index = EntryDir?.IndexOf($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}Release{Path.DirectorySeparatorChar}") ?? 0; + index = EntryDir?.IndexOf($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}Release{Path.DirectorySeparatorChar}"); } - _mainDir = EntryDir?.Substring(0, index.Value); + if (index == null || index < 0) + { + // BaseDirectory does not contain \bin\Debug\ or \bin\Release\ + // (e.g. dotnet run from repo root, or published app). + // Fall back to the current working directory, which is typically + // the project source root in development. + _mainDir = Directory.GetCurrentDirectory(); + } + else + { + _mainDir = EntryDir!.Substring(0, index.Value); + } } return _mainDir; } diff --git a/src/WalkingTec.Mvvm.Mvc/_CodeGenController.cs b/src/WalkingTec.Mvvm.Mvc/_CodeGenController.cs index e492ff470..4fc019b2c 100644 --- a/src/WalkingTec.Mvvm.Mvc/_CodeGenController.cs +++ b/src/WalkingTec.Mvvm.Mvc/_CodeGenController.cs @@ -131,7 +131,9 @@ private List GetAllModels() var models = new List(); //获取所有模型 - var pros = Wtm.ConfigInfo.Connections.SelectMany(x => x.DcConstructor.DeclaringType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)); + var pros = Wtm.ConfigInfo.Connections + .Where(x => x.Enabled && x.DcConstructor != null) + .SelectMany(x => x.DcConstructor.DeclaringType.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)); if (pros != null) { foreach (var pro in pros) From 2f1c552f0ca162654b2723e49ad74238c9e4232d Mon Sep 17 00:00:00 2001 From: cct0831 Date: Wed, 4 Mar 2026 15:13:23 +0800 Subject: [PATCH 8/8] fix(ui): ComboBox respects item.Selected when no DefaultValue set [BUG-6] When the user provides a custom Items list with pre-set Selected=true flags and no field model value or DefaultValue is resolved (selectVal is empty), the previous code forced all items to Selected=false, ignoring the user's intent. Fix: only override item selection state when selectVal has entries. If selectVal is empty, the existing Selected values on items are preserved. Co-Authored-By: Claude Sonnet 4.6 --- .../Form/ComboBoxTagHelper.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/WalkingTec.Mvvm.TagHelpers.LayUI/Form/ComboBoxTagHelper.cs b/src/WalkingTec.Mvvm.TagHelpers.LayUI/Form/ComboBoxTagHelper.cs index 9990068d2..93abcb3d5 100644 --- a/src/WalkingTec.Mvvm.TagHelpers.LayUI/Form/ComboBoxTagHelper.cs +++ b/src/WalkingTec.Mvvm.TagHelpers.LayUI/Form/ComboBoxTagHelper.cs @@ -214,15 +214,11 @@ public override void Process(TagHelperContext context, TagHelperOutput output) { listItems = (Items.Model as IEnumerable).ToList(); } - foreach (var item in listItems) + if (selectVal.Count > 0) { - if (selectVal.Contains(item.Value?.ToString())) + foreach (var item in listItems) { - item.Selected = true; - } - else - { - item.Selected = false; + item.Selected = selectVal.Contains(item.Value?.ToString()); } } }