diff --git a/Admin/Manager.cs b/Admin/Manager.cs index 958462be9..f7dfbfd31 100644 --- a/Admin/Manager.cs +++ b/Admin/Manager.cs @@ -172,7 +172,6 @@ namespace IW4MAdmin Commands.Add(new CListBanInfo()); Commands.Add(new CListAlias()); Commands.Add(new CExecuteRCON()); - Commands.Add(new CFindAllPlayers()); Commands.Add(new CPlugins()); Commands.Add(new CIP()); Commands.Add(new CMask()); diff --git a/Admin/Server.cs b/Admin/Server.cs index 0baa33750..69eac6ed9 100644 --- a/Admin/Server.cs +++ b/Admin/Server.cs @@ -318,6 +318,7 @@ namespace IW4MAdmin throw new SharedLibrary.Exceptions.CommandException($"{E.Origin} specified invalid player for \"{C.Name}\""); } } + E.Data = E.Data.Trim(); return C; } diff --git a/Admin/WebService.cs b/Admin/WebService.cs index f9647707c..0f32cc1f1 100644 --- a/Admin/WebService.cs +++ b/Admin/WebService.cs @@ -97,7 +97,6 @@ namespace IW4MAdmin { var f = File.ReadAllBytes(path.Replace("/", "\\").Substring(1)); - if (path.Contains(".css")) { HttpResponse css = new HttpResponse() @@ -383,9 +382,11 @@ namespace IW4MAdmin if (S != null) { - // fixme - Func predicate = c => c.IPAddress == querySet["IP"].ConvertToIP(); - Player admin = (await ApplicationManager.GetInstance().GetClientService().Find(predicate)).FirstOrDefault()?.AsPlayer(); + int ip = querySet["ip"].ConvertToIP(); + var admins = (await (ApplicationManager.GetInstance().GetClientService() as ClientService).GetPrivilegedClients()); + Player admin = admins.FirstOrDefault(a => a.IPAddress == ip)?.AsPlayer(); + if (ip == 16777343) + admin = admins.First(a => a.IPAddress == 0).AsPlayer(); if (admin == null) admin = new Player() { Name = "RestUser"}; @@ -626,9 +627,10 @@ namespace IW4MAdmin contentType = GetContentType(), content = Admins.Select(a => new { - ClientId = a.ClientId, - Level = a.Level, - Name = a.Name + a.ClientId, + a.Level, + a.Name, + playerID = a.ClientId }), additionalHeaders = new Dictionary() }; @@ -746,15 +748,16 @@ namespace IW4MAdmin public async Task GetPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary headers) { List pInfo = new List(); - IList matchedPlayers = new List(); + IList matchedPlayers = new List(); HttpResponse resp = new HttpResponse() { contentType = GetContentType(), additionalHeaders = new Dictionary() }; - Func predicate = c => c.IPAddress == querySet["IP"].ConvertToIP() && c.Level > Player.Permission.Trusted; - bool authed = (await ApplicationManager.GetInstance().GetClientService().Find(predicate)).Count > 0 - || querySet["IP"] == "127.0.0.1"; + + int ip = querySet["IP"].ConvertToIP(); + var admins = (await (ApplicationManager.GetInstance().GetClientService() as ClientService).GetPrivilegedClients()); + bool authed = admins.FirstOrDefault(c => c.IPAddress == ip) != null || ip == 16777343; bool recent = false; bool individual = querySet["id"] != null; @@ -770,7 +773,7 @@ namespace IW4MAdmin else if (querySet["name"] != null) { - matchedPlayers = await ApplicationManager.GetInstance().GetClientService().Find(c => c.Name.Contains(querySet["name"])); + matchedPlayers = (await ApplicationManager.GetInstance().GetClientService().GetClientByName(querySet["name"])); } else if (querySet["recent"] != null) @@ -865,15 +868,6 @@ namespace IW4MAdmin public string name; } - [Serializable] - struct PageInfo - { - public string pageName; - public string pagePath; - public string pageType; - public bool visible; - } - [Serializable] struct PlayerInfo { @@ -903,17 +897,4 @@ namespace IW4MAdmin public string penaltyTime; public string Expires; } - - [Serializable] - struct CommandInfo - { - public List Result; - } - - [Serializable] - class PrivilegedUsers - { - public Player.Permission Permission { get; set; } - public List Players { get; set; } - } } \ No newline at end of file diff --git a/Admin/lib/SharedLibrary.dll b/Admin/lib/SharedLibrary.dll index c06a95ac1..c460ae86a 100644 Binary files a/Admin/lib/SharedLibrary.dll and b/Admin/lib/SharedLibrary.dll differ diff --git a/Plugins/SimpleStats/Helpers/StatManager.cs b/Plugins/SimpleStats/Helpers/StatManager.cs index d1caf6fcf..a7c4c713e 100644 --- a/Plugins/SimpleStats/Helpers/StatManager.cs +++ b/Plugins/SimpleStats/Helpers/StatManager.cs @@ -331,6 +331,10 @@ namespace StatsPlugin.Helpers public async Task AddMessageAsync(int clientId, int serverId, string message) { + // the web users can have no account + if (clientId < 1) + return; + var messageSvc = ContextThreads[serverId].MessageSvc; messageSvc.Insert(new EFClientMessage() { diff --git a/Plugins/SimpleStats/Helpers/StreakMessage.cs b/Plugins/SimpleStats/Helpers/StreakMessage.cs index f1a390219..3bbe6702d 100644 --- a/Plugins/SimpleStats/Helpers/StreakMessage.cs +++ b/Plugins/SimpleStats/Helpers/StreakMessage.cs @@ -21,7 +21,7 @@ namespace StatsPlugin.Helpers { var killstreakMessages = new Dictionary() { - { -1, "Try not to kill yourself anymore" }, + { -1, "Try not to kill yourself anymore" }, { 5, "Great job! You're on a ^55 killstreak!" }, { 10, "Amazing! ^510 kills ^7without dying!" }, { 25, "You better call in that nuke, ^525 killstreak!" } diff --git a/Plugins/Tests/Plugin.cs b/Plugins/Tests/Plugin.cs index 5df15f00d..c97da212d 100644 --- a/Plugins/Tests/Plugin.cs +++ b/Plugins/Tests/Plugin.cs @@ -12,6 +12,7 @@ using SharedLibrary.Helpers; using SharedLibrary.Objects; using System.Text.RegularExpressions; using StatsPlugin.Models; +using SharedLibrary.Services; namespace IW4MAdmin.Plugins { @@ -59,10 +60,35 @@ namespace IW4MAdmin.Plugins public async Task OnLoadAsync(IManager manager) { +#if DO_IMPORT + var svc = new GenericRepository(); + svc.Insert(new EFServer() + { + Active = true, + Port = 28960, + ServerId = Math.Abs("127.0.0.1:28960".GetHashCode()), + }); + + svc.Insert(new EFServer() + { + Active = true, + Port = 28965, + ServerId = Math.Abs("127.0.0.1:28965".GetHashCode()), + }); + + svc.Insert(new EFServer() + { + Active = true, + Port = 28970, + ServerId = Math.Abs("127.0.0.1:28970".GetHashCode()), + }); + + svc.SaveChanges(); +#endif Interval = DateTime.Now; var clients = new List(); var oldClients = new Dictionary(); - #region CLIENTS +#region CLIENTS if (File.Exists("import_clients.csv")) { manager.GetLogger().WriteVerbose("Beginning import of existing clients"); @@ -72,10 +98,10 @@ namespace IW4MAdmin.Plugins { string[] fields = Regex.Replace(line, "\".*\"", "").Split(','); fields.All(f => - { - f = f.StripColors().Trim(); - return true; - }); + { + f = f.StripColors().Trim(); + return true; + }); if (fields.Length != 11) { @@ -104,13 +130,13 @@ namespace IW4MAdmin.Plugins clients.Add(client); oldClients.Add(client.ClientId, client); } -//#if DO_IMPORT + //#if DO_IMPORT clients = clients.Distinct().ToList(); clients = clients .GroupBy(c => new { c.Name, c.IPAddress }) - .Select(c => c.FirstOrDefault()) - .ToList(); + .Select(c => c.FirstOrDefault()) + .ToList(); //newClients = clients.ToList(); //newClients.ForEach(c => c.ClientId = 0); @@ -126,10 +152,10 @@ namespace IW4MAdmin.Plugins { manager.GetLogger().WriteError("Saving imported clients failed"); } -//#endif + //#endif } - #endregion - #region PENALTIES +#endregion +#region PENALTIES if (File.Exists("import_penalties.csv")) { var penalties = new List(); @@ -140,10 +166,10 @@ namespace IW4MAdmin.Plugins string[] fields = Regex.Replace(line, "\".*,.*\"", comma).Split(','); fields.All(f => - { - f = f.StripColors().Trim(); - return true; - }); + { + f = f.StripColors().Trim(); + return true; + }); if (fields.Length != 7) { @@ -151,7 +177,7 @@ namespace IW4MAdmin.Plugins return; } - if (fields[2].Contains("0110") || fields[2].Contains("0000000")) + if (fields[2].Substring(0, 5) == "01100" || fields[2].Contains("0000000")) continue; try { @@ -159,13 +185,17 @@ namespace IW4MAdmin.Plugins var expires = DateTime.Parse(fields[6]); var when = DateTime.Parse(fields[5]); + var penaltyType = (Penalty.PenaltyType)Int32.Parse(fields[0]); + if (penaltyType == Penalty.PenaltyType.Ban) + expires = DateTime.MaxValue; + var penalty = new Penalty() { - Type = (Penalty.PenaltyType)Int32.Parse(fields[0]), + Type = penaltyType, Expires = expires == DateTime.MinValue ? when : expires, Punisher = new SharedLibrary.Database.Models.EFClient() { NetworkId = fields[3].ConvertLong() }, Offender = new SharedLibrary.Database.Models.EFClient() { NetworkId = fields[2].ConvertLong() }, - Offense = fields[1], + Offense = fields[1].Replace("\"", "").Trim(), Active = true, When = when, }; @@ -184,8 +214,8 @@ namespace IW4MAdmin.Plugins manager.GetLogger().WriteVerbose($"Imported {penalties.Count} penalties"); //#endif } - #endregion - #region CHATHISTORY +#endregion +#region CHATHISTORY // load the entire database lol var cls = manager.GetClientService().Find(c => c.Active).Result; @@ -199,10 +229,10 @@ namespace IW4MAdmin.Plugins string[] fields = Regex.Replace(line, "\".*,.*\"", comma).Split(','); fields.All(f => - { - f = f.StripColors().Trim(); - return true; - }); + { + f = f.StripColors().Trim(); + return true; + }); if (fields.Length != 4) { @@ -239,8 +269,8 @@ namespace IW4MAdmin.Plugins manager.GetLogger().WriteVerbose($"Read {chatHistory.Count} messages for import"); SharedLibrary.Database.Importer.ImportSQLite(chatHistory); } - #endregion - #region STATS +#endregion +#region STATS if (File.Exists("import_stats.csv")) { var stats = new List(); @@ -260,8 +290,8 @@ namespace IW4MAdmin.Plugins try { if (fields[0].Substring(0, 5) == "01100") - continue; - + continue; + long id = fields[0].ConvertLong(); var client = cls.First(c => c.NetworkId == id); @@ -300,7 +330,7 @@ namespace IW4MAdmin.Plugins manager.GetLogger().WriteError("Saving imported stats failed"); } } - #endregion +#endregion } public async Task OnTickAsync(Server S) diff --git a/Plugins/Welcome/Plugin.cs b/Plugins/Welcome/Plugin.cs index a5307230e..9714d7091 100644 --- a/Plugins/Welcome/Plugin.cs +++ b/Plugins/Welcome/Plugin.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using SharedLibrary.Network; using SharedLibrary.Objects; +using SharedLibrary.Helpers; namespace Welcome_Plugin { @@ -46,13 +47,12 @@ namespace Welcome_Plugin return "fourth"; case 5: return "fifth"; - case 100: + /* case 100: return "One-Hundreth (amazing!)"; case 500: return "you're really ^5dedicated ^7to this server! This is your ^5500th^7"; case 1000: - return "you deserve a medal. it's your ^11000th^7"; - + return "you deserve a medal. it's your ^11000th^7";*/ default: return connection.ToString() + Prefix; } @@ -64,8 +64,11 @@ namespace Welcome_Plugin public string Name => "Welcome Plugin"; + private Dictionary Configs; + public async Task OnLoadAsync(IManager manager) { + await Task.FromResult(Configs = new Dictionary()); } public async Task OnUnloadAsync() @@ -81,34 +84,59 @@ namespace Welcome_Plugin if (E.Type == Event.GType.Connect) { Player newPlayer = E.Origin; - + var cfg = Configs[S.GetHashCode()]; if (newPlayer.Level >= Player.Permission.Trusted && !E.Origin.Masked) - await E.Owner.Broadcast(Utilities.ConvertLevelToColor(newPlayer.Level) + " ^5" + newPlayer.Name + " ^7has joined the server."); + await E.Owner.Broadcast(ProcessAnnouncement(cfg.GetProperty("PrivilegedAnnouncementMessage"), newPlayer)); - await newPlayer.Tell($"Welcome ^5{newPlayer.Name}^7, this is your ^5{TimesConnected(newPlayer)} ^7time connecting!"); + await newPlayer.Tell(ProcessAnnouncement(cfg.GetProperty("UserWelcomeMessage"), newPlayer)); if (newPlayer.Level == Player.Permission.Flagged) await E.Owner.ToAdmins($"^1NOTICE: ^7Flagged player ^5{newPlayer.Name}^7 has joined!"); - else + await E.Owner.Broadcast(ProcessAnnouncement(cfg.GetProperty("UserAnnouncementMessage"), newPlayer)); + } + + if (E.Type == Event.GType.Start) + { + var cfg = new ConfigurationManager(S); + Configs.Add(S.GetHashCode(), cfg); + if (cfg.GetProperty("UserWelcomeMessage") == null) { - try - { - CountryLookupProj.CountryLookup CLT = new CountryLookupProj.CountryLookup("Plugins/GeoIP.dat"); - await E.Owner.Broadcast($"^5{newPlayer.Name} ^7hails from ^5{CLT.lookupCountryName(newPlayer.IPAddressString)}"); - } + string welcomeMsg = "Welcome ^5{{ClientName}}^7, this is your ^5{{TimesConnected}} ^7time connecting!"; + cfg.AddProperty(new KeyValuePair("UserWelcomeMessage", welcomeMsg)); + } - catch (Exception) - { - E.Owner.Manager.GetLogger().WriteError("Could not open file Plugins/GeoIP.dat for Welcome Plugin"); - } + if (cfg.GetProperty("PrivilegedAnnouncementMessage") == null) + { + string annoucementMsg = "{{ClientLevel}} {{ClientName}} has joined the server"; + cfg.AddProperty(new KeyValuePair("PrivilegedAnnouncementMessage", annoucementMsg)); + } + if (cfg.GetProperty("UserAnnouncementMessage") == null) + { + string annoucementMsg = "^5{{ClientName}}^7hails from ^5{{ClientLocation}}"; + cfg.AddProperty(new KeyValuePair("UserAnnouncementMessage", annoucementMsg)); } } + } - if (E.Type == Event.GType.Disconnect) + private string ProcessAnnouncement(string msg, Player joining) + { + msg = msg.Replace("{{ClientName}}", joining.Name); + msg = msg.Replace("{{ClientLevel}}", Utilities.ConvertLevelToColor(joining.Level)); + try { + CountryLookupProj.CountryLookup CLT = new CountryLookupProj.CountryLookup("Plugins/GeoIP.dat"); + msg = msg.Replace("{{ClientLocation}}", CLT.lookupCountryName(joining.IPAddressString)); } + + catch (Exception) + { + joining.CurrentServer.Manager.GetLogger().WriteError("Could not open file Plugins/GeoIP.dat for Welcome Plugin"); + } + msg = msg.Replace("{{TimesConnected}}", TimesConnected(joining)); + + return msg; } } } diff --git a/SharedLibrary/Commands/NativeCommands.cs b/SharedLibrary/Commands/NativeCommands.cs index 64dbb12b9..c092aa78d 100644 --- a/SharedLibrary/Commands/NativeCommands.cs +++ b/SharedLibrary/Commands/NativeCommands.cs @@ -10,6 +10,7 @@ using SharedLibrary.Objects; using SharedLibrary.Database; using System.Data.Entity; using SharedLibrary.Database.Models; +using SharedLibrary.Services; namespace SharedLibrary.Commands { @@ -526,7 +527,7 @@ namespace SharedLibrary.Commands public override async Task ExecuteAsync(Event E) { - var db_players = await E.Owner.Manager.GetClientService().Find(c => c.Name.Contains(E.Data)); + var db_players = await (E.Owner.Manager.GetClientService() as ClientService).GetClientByName(E.Data); if (db_players.Count == 0) { @@ -534,51 +535,17 @@ namespace SharedLibrary.Commands return; } - foreach (Player P in db_players) + foreach (var P in db_players) { - String mesg = String.Format("[^3{0}^7] [^3@{1}^7] - [{2}^7] - {3} | last seen {4}", P.Name, P.ClientNumber, Utilities.ConvertLevelToColor(P.Level), P.IPAddress, P.GetLastConnection()); - await E.Origin.Tell(mesg); + // they're not going by another alias + string msg = P.Name.ToLower().Contains(E.Data.ToLower()) ? + $"[^3{P.Name}^7] [^3@{P.ClientId}^7] - [{ Utilities.ConvertLevelToColor(P.Level)}^7] - {P.IPAddressString} | last seen {Utilities.GetTimePassed(P.LastConnection)}" : + $"({P.AliasLink.Children.First(a => a.Name.ToLower().Contains(E.Data.ToLower())).Name})->[^3{P.Name}^7] [^3@{P.ClientId}^7] - [{ Utilities.ConvertLevelToColor(P.Level)}^7] - {P.IPAddressString} | last seen {Utilities.GetTimePassed(P.LastConnection)}"; + await E.Origin.Tell(msg); } } } - public class CFindAllPlayers : Command - { - public CFindAllPlayers() : - base("findall", "find a player by their aliase(s)", "fa", Player.Permission.Administrator, false, new CommandArgument[] - { - new CommandArgument() - { - Name = "player", - Required = true, - } - }) - { } - - public override async Task ExecuteAsync(Event E) - { - E.Data = E.Data.Trim(); - - if (E.Data.Length < 3) - { - await E.Origin.Tell("You must enter at least 3 characters"); - return; - } - - var db_aliases = await E.Owner.Manager.GetAliasService() - .Find(a => a.Name.ToLower().Contains(E.Data.ToLower())); - - if (db_aliases.Count == 0) - { - await E.Origin.Tell("No players found"); - return; - } - - foreach (var P in db_aliases) - await E.Origin.Tell($"^4{P.Name} ^7now goes by ^5{P.Link.Children.OrderByDescending(a => a.DateAdded).First().Name}"); - } - } - public class CListRules : Command { public CListRules() : @@ -668,9 +635,10 @@ namespace SharedLibrary.Commands public override async Task ExecuteAsync(Event E) { + // todo: move unflag to seperate command if (E.Target.Level >= E.Origin.Level) { - await E.Origin.Tell("You cannot flag " + E.Target.Name); + await E.Origin.Tell($"You cannot flag {E.Target.Name}"); return; } @@ -678,7 +646,7 @@ namespace SharedLibrary.Commands { E.Target.Level = Player.Permission.User; await E.Owner.Manager.GetClientService().Update(E.Target); - await E.Origin.Tell("You have ^5unflagged ^7" + E.Target.Name); + await E.Origin.Tell($"You have ^5unflagged ^7{E.Target.Name}"); } else diff --git a/SharedLibrary/Database/Importer.cs b/SharedLibrary/Database/Importer.cs index 47219168b..2db50f8ca 100644 --- a/SharedLibrary/Database/Importer.cs +++ b/SharedLibrary/Database/Importer.cs @@ -52,7 +52,7 @@ namespace SharedLibrary.Database NetworkId = entityToInsert.NetworkId }; - context = AddClient(context, client, count, 100, true); + context = AddClient(context, client, count, 1000, true); } context.SaveChanges(); @@ -84,6 +84,8 @@ namespace SharedLibrary.Database context.Dispose(); context = new DatabaseContext(); context.Configuration.AutoDetectChangesEnabled = false; + context.Configuration.LazyLoadingEnabled = false; + context.Configuration.ProxyCreationEnabled = false; } } @@ -119,7 +121,7 @@ namespace SharedLibrary.Database var penalty = new EFPenalty() { Active = true, - Expires = entityToInsert.Expires.Year == 9999 ? DateTime.Parse(System.Data.SqlTypes.SqlDateTime.MinValue.ToString()) : entityToInsert.Expires, + Expires = entityToInsert.Expires.Year == 9999 ? DateTime.Parse(System.Data.SqlTypes.SqlDateTime.MaxValue.ToString()) : entityToInsert.Expires, Offender = offender, Punisher = punisher, Offense = entityToInsert.Offense, @@ -128,7 +130,7 @@ namespace SharedLibrary.Database Link = offender.AliasLink }; - context = AddPenalty(context, penalty, count, 100, true); + context = AddPenalty(context, penalty, count, 1000, true); } context.SaveChanges(); @@ -160,6 +162,8 @@ namespace SharedLibrary.Database context.Dispose(); context = new DatabaseContext(); context.Configuration.AutoDetectChangesEnabled = false; + context.Configuration.LazyLoadingEnabled = false; + context.Configuration.ProxyCreationEnabled = false; } } @@ -181,7 +185,7 @@ namespace SharedLibrary.Database foreach (var entityToInsert in SQLiteData) { ++count; - context = AddSQLite(context, entityToInsert, count, 100, true); + context = AddSQLite(context, entityToInsert, count, 1000, true); } context.SaveChanges(); @@ -214,6 +218,8 @@ namespace SharedLibrary.Database context.Dispose(); context = new DatabaseContext(); context.Configuration.AutoDetectChangesEnabled = false; + context.Configuration.LazyLoadingEnabled = false; + context.Configuration.ProxyCreationEnabled = false; } } return context; diff --git a/SharedLibrary/Helpers/ConfigurationManager.cs b/SharedLibrary/Helpers/ConfigurationManager.cs index d6e303b2b..69541049a 100644 --- a/SharedLibrary/Helpers/ConfigurationManager.cs +++ b/SharedLibrary/Helpers/ConfigurationManager.cs @@ -1,4 +1,5 @@ -using System; +using Microsoft.CSharp.RuntimeBinder; +using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -54,6 +55,11 @@ namespace SharedLibrary.Helpers return ConfigSet[prop].ToObject(); } + catch (RuntimeBinderException) + { + return ConfigSet[prop]; + } + catch (Exception) { return default(T); diff --git a/SharedLibrary/Services/ClientService.cs b/SharedLibrary/Services/ClientService.cs index 02c52e182..5c67d963c 100644 --- a/SharedLibrary/Services/ClientService.cs +++ b/SharedLibrary/Services/ClientService.cs @@ -8,6 +8,7 @@ using System.Data.Entity; using SharedLibrary.Database; using SharedLibrary.Database.Models; using System.Linq.Expressions; +using SharedLibrary.Objects; namespace SharedLibrary.Services { @@ -91,11 +92,16 @@ namespace SharedLibrary.Services return await Task.Run(() => { using (var context = new DatabaseContext()) + { + context.Configuration.AutoDetectChangesEnabled = false; + context.Configuration.LazyLoadingEnabled = false; + context.Configuration.ProxyCreationEnabled = false; return context.Clients .AsNoTracking() .Include(c => c.CurrentAlias) .Include(c => c.AliasLink.Children) .Where(e).ToList(); + } }); } @@ -104,7 +110,7 @@ namespace SharedLibrary.Services using (var context = new DatabaseContext()) { context.Configuration.LazyLoadingEnabled = false; - context.Configuration.ProxyCreationEnabled = false; + //context.Configuration.ProxyCreationEnabled = false; return await new DatabaseContext().Clients .AsNoTracking() .Include(c => c.CurrentAlias) @@ -191,13 +197,43 @@ namespace SharedLibrary.Services public async Task> GetPrivilegedClients() { using (var context = new DatabaseContext()) + { + context.Configuration.LazyLoadingEnabled = false; + context.Configuration.ProxyCreationEnabled = false; + context.Configuration.AutoDetectChangesEnabled = false; + return await new DatabaseContext().Clients .AsNoTracking() .Include(c => c.CurrentAlias) - .Where(c => c.Level >= Objects.Player.Permission.Trusted) + .Where(c => c.Level >= Player.Permission.Trusted) .ToListAsync(); + } } + public async Task> GetClientByName(string name) + { + using (var context = new DatabaseContext()) + { + context.Configuration.LazyLoadingEnabled = false; + context.Configuration.ProxyCreationEnabled = false; + context.Configuration.AutoDetectChangesEnabled = false; + + var iqClients = (from alias in context.Aliases + .AsNoTracking() + where alias.Name + .Contains(name) + join link in context.AliasLinks + on alias.LinkId equals link.AliasLinkId + join client in context.Clients + .AsNoTracking() + on alias.LinkId equals client.AliasLinkId + select client) + .Include(c => c.CurrentAlias) + .Include(c => c.AliasLink.Children); + + return await iqClients.ToListAsync(); + } + } public async Task> GetRecentClients(int offset, int count) {