예제 #1
0
        public SqlCacheService(IDistributedCache distributedCache, IOptions <SqlServerCacheOptions> option)
        {
            GuardClausesParameter.Null(option, nameof(option));

            _distributedCache      = distributedCache;
            _sqlServerCacheOptions = option.Value;
        }
예제 #2
0
        /// <summary>
        /// Get queue email for send email wellcome user
        /// </summary>
        /// <param name="emailTemplate">Template email</param>
        /// <param name="userEmail">Email of user</param>
        /// <param name="userPassowrd">Password of user optional</param>
        /// <param name="userDisplayName">full name of user</param>
        /// <param name="linkCallback">link if have</param>
        /// <returns></returns>
        public virtual QueuedEmail GetQueuedEmailForNewUser(MessageTemplate emailTemplate, string userEmail, string userPassowrd, string userDisplayName, string linkCallback = "")
        {
            GuardClausesParameter.Null(emailTemplate, nameof(emailTemplate));
            GuardClausesParameter.NullOrEmpty(userEmail, nameof(userEmail));
            Dictionary <string, string> replaceValue = new Dictionary <string, string>
            {
                { "[useremail]", userEmail },
                { "[userpassword]", userPassowrd },
                { "[userfullname]", userDisplayName },
                { "[callbacklink]", linkCallback }
            };
            string title   = CoreUtility.ReplaceContentHelper(emailTemplate.Title, replaceValue);
            string content = CoreUtility.ReplaceContentHelper(emailTemplate.Body, replaceValue) + "<br />" + _systemSettings.Common.EmailSignature;

            return(new QueuedEmail()
            {
                To = userEmail,
                Title = title,
                EmailBody = content,
                Cc = null,
                Bcc = null,
                ToName = userDisplayName,
                EmailAccountId = null,
                SendTime = null,
                Priority = 5,
                TrySend = 0
            });
        }
예제 #3
0
        /// <summary>
        /// Get queue email for send email to revovery user passowrd
        /// </summary>
        /// <param name="emailTemplate">Template email</param>
        /// <param name="userEmail">active email user</param>
        /// <param name="webSiteName">system name</param>
        /// <param name="userDisplayName">User full name</param>
        /// <param name="recoveryLink">recovery link</param>
        public virtual QueuedEmail UserPasswordRecovery(MessageTemplate emailTemplate, string userEmail, string webSiteName, string userDisplayName, string recoveryLink)
        {
            GuardClausesParameter.Null(emailTemplate, nameof(emailTemplate));
            GuardClausesParameter.NullOrEmpty(userEmail, nameof(userEmail));
            GuardClausesParameter.NullOrEmpty(recoveryLink, nameof(recoveryLink));
            Dictionary <string, string> replaceValue = new Dictionary <string, string>
            {
                { "[systemname]", webSiteName },
                { "[userfullname]", userDisplayName },
                { "[recoverylink]", recoveryLink }
            };
            string title   = CoreUtility.ReplaceContentHelper(emailTemplate.Title, replaceValue);
            string content = CoreUtility.ReplaceContentHelper(emailTemplate.Body, replaceValue) + "<br />" + _systemSettings.Common.EmailSignature;

            return(new QueuedEmail()
            {
                To = userEmail,
                Title = title,
                EmailBody = content,
                Cc = null,
                Bcc = null,
                ToName = userDisplayName,
                EmailAccountId = null,
                SendTime = null,
                Priority = 5,
                TrySend = 0
            });
        }
예제 #4
0
        public virtual async Task <bool> InsertAsync(T entity)
        {
            GuardClausesParameter.Null(entity, nameof(entity));
            await DbContext.Set <T>().AddAsync(entity);

            return(await DbContext.SaveChangesAsync() > 0);
        }
예제 #5
0
        /// <summary>
        /// Lấy danh sách ngôn ngữ và .net hỗ trợ
        /// </summary>
        /// <param name="helper">Html helper</param>
        /// <returns>List SelectListItem</returns>
        public static List <SelectListItem> GetSpecificCulturesSelectItem(this IHtmlHelper helper)
        {
            GuardClausesParameter.Null(helper, nameof(helper));

            return(CultureInfo.GetCultures(CultureTypes.SpecificCultures).OrderBy(x => x.EnglishName).Select(x => new SelectListItem()
            {
                Value = x.IetfLanguageTag,
                Text = string.Format("{0}. {1}", x.EnglishName, x.IetfLanguageTag)
            }).ToList());
        }
예제 #6
0
        public virtual async Task SendEmailAync(EmailAccount emailAccount, QueuedEmail queuedEmail)
        {
            GuardClausesParameter.Null(emailAccount, nameof(emailAccount));
            GuardClausesParameter.Null(queuedEmail, nameof(queuedEmail));
            try
            {
                List <string> toList = null;
                if (!string.IsNullOrWhiteSpace(queuedEmail.To))
                {
                    toList = queuedEmail.To.Split(new char[] { ',', ';' }).ToList();
                }

                List <string> bccList = null;
                if (!string.IsNullOrWhiteSpace(queuedEmail.Bcc))
                {
                    bccList = queuedEmail.Bcc.Split(new char[] { ',', ';' }).ToList();
                }

                List <string> ccList = null;
                if (!string.IsNullOrWhiteSpace(queuedEmail.Cc))
                {
                    ccList = queuedEmail.Cc.Split(new char[] { ',', ';' }).ToList();
                }

                await CoreUtility.SendEmail(emailAccount.UserName,
                                            emailAccount.Password,
                                            queuedEmail.Title,
                                            queuedEmail.EmailBody,
                                            emailAccount.Email,
                                            emailAccount.Name, toList,
                                            emailAccount.Host,
                                            emailAccount.Port,
                                            emailAccount.EnableSsl,
                                            emailAccount.UseDefaultCredentials,
                                            bccList,
                                            ccList);

                emailAccount.SendCountByDay++;
                if (emailAccount.SendCountByDay >= emailAccount.DaySendLimit)
                {
                    emailAccount.DaySendLimited = DateTime.Now.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture);
                    emailAccount.SendCountByDay = 0;
                }
                queuedEmail.EmailAccountId = emailAccount.Id;
                queuedEmail.SendTime       = DateTime.Now;
            }
            catch (Exception ex)
            {
                _logger.LogError(new EventId(100, "SendEmail"), ex, ex.Message);
            }
            finally
            {
                queuedEmail.TrySend++;
            }
        }
예제 #7
0
        public async Task <T> GetLocalizedStringAsync <T>(T entity, string languageCulture, ObjectTypeEnum objectType) where T : IBaseEntity
        {
            GuardClausesParameter.Null(entity, nameof(entity));
            var listLocalizedString = await FindAllNoTrackingAsync(q => q.LanguageCulture == languageCulture && q.ObjectType == objectType && q.ObjectId == entity.Id);

            foreach (ObjectLocalized item in listLocalizedString)
            {
                CoreUtility.SetProperty(entity, item.PropertyName, item.LocalizedValue);
            }
            return(entity);
        }
예제 #8
0
        /// <summary>
        /// Lấy đối tượng từ khóa cache nếu chưa có cache sẽ gọi hàm lấy đối tượng, hàm bất đồng bộ
        /// </summary>
        /// <typeparam name="TItem">Loại đối tượng</typeparam>
        /// <param name="key">Khóa cache</param>
        /// <param name="factory">Hàm lấy đối tượng nếu cache null</param>
        /// <param name="time">Thời gian tồn tại cache tính bắng giây</param>
        /// <returns>TItem object</returns>
        public virtual async Task <TItem> GetOrCreateAsync <TItem>(string key, Func <Task <TItem> > factory, int time)
        {
            GuardClausesParameter.Null(factory, nameof(factory));

            if (!_memoryCache.TryGetValue(key, out object result))
            {
                result = await factory();

                _memoryCache.Set(AddKey(key), result, GetMemoryCacheEntryOptions(time));
            }
            return((TItem)result);
        }
예제 #9
0
        /// <summary>
        /// Hàm tạo icon hướng dẫn
        /// </summary>
        /// <param name="helper">HTML helper</param>
        /// <param name="value">Nội dung hướng dẫn</param>
        /// <returns>IHtmlContent</returns>
        public static IHtmlContent Hint(this IHtmlHelper helper, string value)
        {
            GuardClausesParameter.Null(helper, nameof(helper));

            //Hàm này đồng bộ với file QiLabelTagHelper
            TagBuilder builder = new TagBuilder("i");

            builder.MergeAttribute("title", value);
            builder.MergeAttribute("class", "fa fa-question-circle info hellp-hit");
            builder.MergeAttribute("data-toggle", "tooltip");
            builder.MergeAttribute("data-placement", "top");
            return(new HtmlString(builder.ToHtmlString()));
        }
예제 #10
0
        public virtual async Task <bool> UpdateRateAsync(string currencyCode, decimal rate)
        {
            GuardClausesParameter.Null(currencyCode, nameof(currencyCode));

            Currency updateEntity = await FindAsync(q => q.CurrencyCode == currencyCode);

            if (updateEntity != null)
            {
                updateEntity.Rate = rate;
                return(await UpdateAsync(updateEntity));
            }
            return(false);
        }
예제 #11
0
        public async Task <bool> ImportFromJsonAsync(string culture, string json)
        {
            GuardClausesParameter.Null(culture, nameof(culture));
            GuardClausesParameter.Null(json, nameof(json));

            List <LanguageResource> listInsert = JsonSerializer.Deserialize <List <LanguageResource> >(json);

            listInsert = listInsert.Where(q => !string.IsNullOrEmpty(q.Key) &&
                                          !string.IsNullOrEmpty(q.Culture) &&
                                          !string.IsNullOrEmpty(q.Type) &&
                                          !AnyAsync(c => c.Key == q.Key && c.Culture == q.Culture && c.Type == q.Type).Result).ToList();
            return(await InsertAsync(listInsert));
        }
예제 #12
0
        public async Task <bool> SetLocalizedStringAsync <T>(T entity, string objectId, string languageCulture, ObjectTypeEnum objectType)
        {
            GuardClausesParameter.Null(entity, nameof(entity));

            foreach (PropertyInfo item in CoreUtility.GetPropertyList(entity))
            {
                if (item.Name != "LanguageId")
                {
                    string value = CoreUtility.GetProperty <string>(entity, item.Name);
                    await SaveLocalizedAsync(languageCulture, objectId, objectType, item.Name, value);
                }
            }
            return(true);
        }
예제 #13
0
 public async Task <string> ExportToJsonAsync(string culture)
 {
     GuardClausesParameter.Null(culture, nameof(culture));
     return(JsonSerializer.Serialize((await FindAllNoTrackingAsync(q => q.Culture == culture)).Select(q => new
     {
         q.Culture,
         q.Key,
         q.Type,
         q.Value
     }), new JsonSerializerOptions()
     {
         WriteIndented = true
     }));
 }
예제 #14
0
        /// <summary>
        /// Set một giá trị theo key và mô tả
        /// </summary>
        /// <param name="key">Khóa</param>
        /// <param name="value">Giá trị</param>
        /// <returns>Nếu đã tồn tại thì sẽ update</returns>
        public async Task <bool> SetStringByKeyAsync(string key, string value)
        {
            GuardClausesParameter.Null(key, nameof(key));
            GuardClausesParameter.Null(value, nameof(value));

            SystemValue systemKeyValue = await FindAsync(q => q.Key == key);

            if (systemKeyValue != null)
            {
                systemKeyValue.Value = value;
                return(await UpdateAsync(systemKeyValue));
            }
            systemKeyValue = new SystemValue()
            {
                Key = key, Value = value
            };
            return(await InsertAsync(systemKeyValue));
        }
예제 #15
0
        /// <summary>
        /// Lấy người dùng hiện tại hoặc lấy tất cả
        /// Hàm này chỉ gọi được khi đã có người login
        /// </summary>
        /// <param name="claimsPrincipal">User claims</param>
        /// <returns>List SelectListItem</returns>
        public List <SelectListItem> GetAllUserOrOnlyMeSelectedItems(ClaimsPrincipal claimsPrincipal)
        {
            GuardClausesParameter.Null(claimsPrincipal, nameof(claimsPrincipal));

            string userId = claimsPrincipal.FindFirstValue(ClaimTypes.NameIdentifier);

            return(new List <SelectListItem>()
            {
                new SelectListItem()
                {
                    Text = _localizer["Tất cả mọi người"], Value = ""
                },
                new SelectListItem()
                {
                    Text = _localizer["Chỉ mình tôi"], Value = userId, Selected = true
                },
            });
        }
예제 #16
0
        /// <summary>
        /// get IDataSourceResult from iquery
        /// </summary>
        /// <typeparam name="T">Type of entity</typeparam>
        /// <param name="inputQuery">IQueryable of T</param>
        /// <param name="specification">a specification</param>
        /// <returns>Task IDataSourceResult T</returns>
        public static async Task <IDataSourceResult <T> > ToDataSourceResultAsync <T>(this IQueryable <T> inputQuery, ISpecification <T> specification) where T : class
        {
            GuardClausesParameter.Null(specification, nameof(specification));
            DataSourceResult <T> dataSourceResult = new DataSourceResult <T>();
            IQueryable <T>       query            = inputQuery;

            if (specification.Criteria != null)
            {
                query = query.Where(specification.Criteria);
            }

            query = specification.Includes.Aggregate(query, (current, include) => current.Include(include));

            query = specification.IncludeStrings.Aggregate(query, (current, include) => current.Include(include));

            if (specification.OrderBy != null)
            {
                query = query.OrderBy(specification.OrderBy);
            }
            else if (specification.OrderByDescending != null)
            {
                query = query.OrderByDescending(specification.OrderByDescending);
            }

            if (specification.Selector != null)
            {
                query = query.Select(specification.Selector);
            }

            if (specification.GroupBy != null)
            {
                query = query.GroupBy(specification.GroupBy).SelectMany(x => x);
            }

            if (specification.IsPagingEnabled)
            {
                dataSourceResult.Total = query.Count();
                query = query.Skip(specification.Skip).Take(specification.Take);
            }

            dataSourceResult.Data = await query.ToListAsync();

            return(dataSourceResult);
        }
예제 #17
0
        public virtual string CurrencyToString(decimal amount, Currency targetCurrency)
        {
            GuardClausesParameter.Null(targetCurrency, nameof(targetCurrency));

            if (!string.IsNullOrEmpty(targetCurrency.CustomFormatting))
            {
                return(amount.ToString(targetCurrency.CustomFormatting, CultureInfo.InvariantCulture));
            }
            else
            {
                if (!string.IsNullOrEmpty(targetCurrency.Culture))
                {
                    CultureInfo format = new CultureInfo(targetCurrency.Culture);
                    return(amount.ToString("C", format));
                }
                else
                {
                    return($"{amount.ToString("N", CultureInfo.InvariantCulture)} ({targetCurrency.CurrencyCode})");
                }
            }
        }
예제 #18
0
        /// <summary>
        /// Ap dụng điều kiện lọc vào cấu query
        /// </summary>
        /// <param name="inputQuery">Dữ liệu đầu vào</param>
        /// <param name="specification">Láy chi tiết dữ liệu</param>
        /// <returns>IQueryable T</returns>
        public static IQueryable <T> GetSpecificationQuery <T>(this IQueryable <T> inputQuery, ISpecification <T> specification) where T : class
        {
            GuardClausesParameter.Null(specification, nameof(specification));
            IQueryable <T> query = inputQuery;

            if (specification.Criteria != null)
            {
                query = query.Where(specification.Criteria);
            }

            query = specification.Includes.Aggregate(query, (current, include) => current.Include(include));

            query = specification.IncludeStrings.Aggregate(query, (current, include) => current.Include(include));

            if (specification.OrderBy != null)
            {
                query = query.OrderBy(specification.OrderBy);
            }
            else if (specification.OrderByDescending != null)
            {
                query = query.OrderByDescending(specification.OrderByDescending);
            }

            if (specification.Selector != null)
            {
                query = query.Select(specification.Selector);
            }

            if (specification.GroupBy != null)
            {
                query = query.GroupBy(specification.GroupBy).SelectMany(x => x);
            }

            if (specification.IsPagingEnabled)
            {
                query = query.Skip(specification.Skip).Take(specification.Take);
            }

            return(query);
        }
예제 #19
0
        /// <summary>
        /// Lấy đối tượng từ khóa cache nếu chưa có cache sẽ gọi hàm lấy đối tuộng
        /// </summary>
        /// <typeparam name="TItem">Loại đối tượng</typeparam>
        /// <param name="key">Khóa cache</param>
        /// <param name="paging"></param>
        /// <param name="factory">Hàm lấy đối tượng nếu cache null</param>
        /// <param name="time">Thời gian cache tính bằng s</param>
        /// <returns>TItem object</returns>
        public virtual async Task <TItem> GetPagingOrCreateAsync <TItem>(string key, Paging paging, Func <Paging, Task <TItem> > factory, int time)
        {
            GuardClausesParameter.Null(factory, nameof(factory));
            GuardClausesParameter.Null(paging, nameof(paging));

            var checkKey = key + paging.ToCacheKey();

            if (!_memoryCache.TryGetValue(checkKey, out object result))
            {
                TItem data = await factory(paging);

                _memoryCache.Set(AddKey(checkKey), new PagingCache <TItem>()
                {
                    Data = data, RowsCount = paging.RowsCount
                }, GetMemoryCacheEntryOptions(time));
                return(data);
            }
            PagingCache <TItem> pagingCache = (PagingCache <TItem>)result;

            paging.RowsCount = pagingCache.RowsCount;
            return(pagingCache.Data);
        }
예제 #20
0
        /// <summary>
        /// add sql localized
        /// </summary>
        /// <param name="services">Service container</param>
        /// <param name="dbConnection">connection string to db localized</param>
        public static void AddSqlLocalizationProvider(this IServiceCollection services, string dbConnection)
        {
            services.Configure <WebEncoderOptions>(webEncoderOptions => webEncoderOptions.TextEncoderSettings = new TextEncoderSettings(UnicodeRanges.All));

            using var systemDbContext = new SystemDbContext(new DbContextOptionsBuilder <SystemDbContext>().UseSqlServer(dbConnection).Options);
            SqlStringLocalizer.SetResourceLocalizations(systemDbContext.LanguageResources.ToList());
            services.Configure <SqlLocalizationOptions>(option => option.UseSettings(dbConnection, true));
            services.AddSingleton <IStringLocalizerFactory, SqlStringLocalizerFactory>();
            services.Configure <RequestLocalizationOptions>(options =>
            {
                var systemLanguages = systemDbContext.Languages.Where(q => q.Published || q.DisplayDefault).OrderByDescending(q => q.DisplayOrder);
                var defaultLanguage = systemLanguages.FirstOrDefault(q => q.DisplayDefault);

                GuardClausesParameter.NullOrEmpty(systemLanguages, nameof(systemLanguages));
                GuardClausesParameter.Null(defaultLanguage, nameof(defaultLanguage));

                var supportedCultures         = systemLanguages.Select(q => new CultureInfo(q.Culture)).ToList();
                options.DefaultRequestCulture = new RequestCulture(new CultureInfo(defaultLanguage.Culture));
                options.SupportedCultures     = supportedCultures;
                options.SupportedUICultures   = supportedCultures;
                options.RequestCultureProviders.Insert(0, new CustomRequestCultureProvider(context =>
                {
                    string cultureKey = context.Request.Query["culture"];
                    if (string.IsNullOrWhiteSpace(cultureKey))
                    {
                        cultureKey = context.Request?.Cookies[IdentityConstants.LanguageSessionCookieKey];
                    }
                    if (string.IsNullOrWhiteSpace(cultureKey))
                    {
                        cultureKey = defaultLanguage.Culture;
                    }
                    return(Task.FromResult(new ProviderCultureResult(cultureKey)));
                }));
            });

            //need startup website call following code
            //services.AddControllersWithViews().AddMvcLocalization().AddDataAnnotationsLocalization();
        }
예제 #21
0
        /// <summary>
        /// Hàm tạo log khi xử lý một hành động nào đó trong hệ thống
        /// </summary>
        /// <param name="message">Nội dung</param>
        /// <param name="userId">Id người dùng</param>
        /// <param name="complete">Trạng thái thành công</param>
        /// <param name="activityLogType">Id hành động</param>
        /// <param name="objectType">Id loại đối tượng</param>
        /// <param name="objectJson">Chuỗi Json đối tượng</param>
        /// <param name="isShowNotifi">Có hiển thị notification hay không</param>
        /// <param name="objectId">Id đối tượng nếu có, mặc định bằng 0</param>
        /// <returns>string</returns>
        protected virtual async Task <string> SaveActivityLogAsync(string message, string userId = null, bool complete = true, ActivityTypeEnum activityLogType = ActivityTypeEnum.System, ObjectTypeEnum objectType = ObjectTypeEnum.System, object objectJson = null, bool isShowNotifi = true, string objectId = null)
        {
            GuardClausesParameter.Null(activityLogType, nameof(activityLogType));
            GuardClausesParameter.Null(objectType, nameof(objectType));

            string notifiString = $"{_localizer[CoreConstants.ActivityTypes[activityLogType]]} {_localizer[CoreConstants.ObjectTypes[objectType]]} \"{message}\" {_localizer[complete ? "thành công" : "không thành công"]}.";

            if (isShowNotifi)
            {
                if (complete)
                {
                    SuccessNotification(notifiString);
                }
                else
                {
                    ErrorNotification(notifiString);
                }
            }
            var activityLog = new ActivityLog()
            {
                Complete   = complete,
                IpAddress  = HttpContext.GetCurrentIpAddress(),
                Message    = message,
                UserId     = userId ?? CurrentUserId,
                ObjectId   = objectId,
                ObjectJson = JsonSerializer.Serialize(objectJson, new JsonSerializerOptions()
                {
                    WriteIndented = true
                }),
                ObjectType = objectType,
                PageUrl    = HttpContext.GetThisPageUrl(),
                Type       = activityLogType
            };
            await _activityLogData.InsertAsync(activityLog);

            return(notifiString);
        }
예제 #22
0
        /// <summary>
        /// Delete FileResource file
        /// </summary>
        /// <param name="fileResource">FileResource to delete</param>
        public async Task DeleteFileAsync(FileResource fileResource)
        {
            GuardClausesParameter.Null(fileResource, nameof(fileResource));

            if (fileResource.Type == ResourceType.Image)
            {
                await DeleteImageFileAsync(fileResource.Path);
            }

            if (fileResource.Type == ResourceType.Movie)
            {
                await DeleteFileAsync(fileResource.Path);
            }

            if (fileResource.Type == ResourceType.Audio)
            {
                await DeleteFileAsync(fileResource.Path);
            }

            if (fileResource.Type == ResourceType.None)
            {
                await DeleteFileAsync(fileResource.Path);
            }
        }
예제 #23
0
 /// <summary>
 /// Add cache service to service container
 /// </summary>
 /// <param name="services">service container</param>
 /// <param name="setupAction">sql cache options</param>
 public static void AddCacheService(this IServiceCollection services, Action <SqlServerCacheOptions> setupAction)
 {
     GuardClausesParameter.Null(setupAction, nameof(setupAction));
     services.AddDistributedSqlServerCache(setupAction);
     services.AddTransient <IAsyncCacheService, SqlCacheService>();
 }
예제 #24
0
 public virtual async Task <int> CountAsync(ISpecification <T> specification)
 {
     GuardClausesParameter.Null(specification, nameof(specification));
     return(await DbContext.Set <T>().GetSpecificationQuery(specification).CountAsync());
 }
예제 #25
0
 public virtual async Task <int> CountAsync(Expression <Func <T, bool> > predicate)
 {
     GuardClausesParameter.Null(predicate, nameof(predicate));
     return(await DbContext.Set <T>().CountAsync(predicate));
 }
예제 #26
0
 public virtual Task <T> FindNoTrackingAsync(Expression <Func <T, bool> > predicate)
 {
     GuardClausesParameter.Null(predicate, nameof(predicate));
     return(DbContext.Set <T>().AsNoTracking().FirstOrDefaultAsync(predicate));
 }
예제 #27
0
 public virtual Task <bool> AnyAsync(Expression <Func <T, bool> > predicate)
 {
     GuardClausesParameter.Null(predicate, nameof(predicate));
     return(DbContext.Set <T>().AnyAsync(predicate));
 }
예제 #28
0
 public virtual async Task <bool> DeleteAsync(T entity)
 {
     GuardClausesParameter.Null(entity, nameof(entity));
     DbContext.Set <T>().Remove(entity);
     return(await DbContext.SaveChangesAsync() > 0);
 }
예제 #29
0
 public virtual async Task <bool> UpdateAsync(T entity)
 {
     GuardClausesParameter.Null(entity, nameof(entity));
     DbContext.Entry(entity).State = EntityState.Modified;
     return(await DbContext.SaveChangesAsync() > 0);
 }
예제 #30
0
 public virtual async Task <IReadOnlyList <T> > FindAllNoTrackingAsync(ISpecification <T> specification)
 {
     GuardClausesParameter.Null(specification, nameof(specification));
     return(await DbContext.Set <T>().AsNoTracking().GetSpecificationQuery(specification).ToListAsync());
 }