Всем привет.
Архитектура приложения построена таким образом:
Запросы от EF не будет удовлетворять требуемой производительности, да и каждый раз будет излишняя нагрузка на БД.. Пока далеко не зашел, нужно исключить пробел в построении архитектуры кэширования, чем и хочу обернуть уровень доменных сервисов. На данный момент запросы идут в соответствии с доменной логикой, со всей требуемой вложенностью. И только придя к этапу кэширования понимаю, что это может быть не правильным, по крайней мере я не могу найти решение моей проблемы. Т.е. на репозиторий будет оверрайд CachedRepository, и взаимодействие в нем с DistributedCache/MemCache(тут уже не важно).
Интерфейс репозитория:
public interface IRepository<T> where T : class, IEntity
{
IQueryable<T> GetMany();
IQueryable<T> GetMany(Expression<Func<T, bool>> predicate);
T GetSingle(Expression<Func<T, bool>> expression);
T GetSingle(long id);
Task<T> GetSingleAsync(Expression<Func<T, bool>> expression);
bool Any(Expression<Func<T, bool>> expression);
void Add(T entity);
Task AddAsync(T entity);
void AddRange(IEnumerable<T> entitys);
void Update(T entity);
void UpdateRange(IEnumerable<T> entities);
void Delete(T entity);
void DeleteRange(IEnumerable<T> entities);
void AddOrUpdateRange(IEnumerable<T> entities);
}
Вот пример метода доменного сервиса:
public class QuestionDomainService : IQuestionDomainService
{
private readonly IDbContext _dbContext;
private readonly IRepository<Question> _questionRepository;
private readonly FileSettings _fileSettings;
public QuestionDomainService(
IDbContext dbContext,
IRepository<Question> questionRepository,
IOptions<FileSettings> fileSettings)
{
_dbContext = dbContext;
_questionRepository = questionRepository;
_fileSettings = fileSettings.Value;
}
public async Task<QuestionReadDto> ReadAsync(ISpecification<Question> spec)
{
return new QuestionReadQuery(_questionRepository, _fileSettings).Query(spec);
}
/*...*/
}
Сам Query:
public class QuestionReadQuery
{
private readonly IRepository<Question> _questionRepository;
private readonly FileSettings _fileSettings;
public QuestionReadQuery(IRepository<Question> questionRepository, FileSettings fileSettings)
{
_questionRepository = questionRepository;
_fileSettings = fileSettings;
}
public async Task<QuestionReadDto> Query(ISpecification<Question> spec)
{
return await _questionRepository.GetMany()
.Include(o => o.SubscriptionToQuestions)
.Include(o => o.Author)
.ThenInclude(o => o.Avatar)
.ThenInclude(o => o.FileInfoToImageSizes)
.ThenInclude(o => o.ImageSize)
.Include(o => o.Category)
.ThenInclude(o => o.FileInfo)
.ThenInclude(o => o.FileInfoToImageSizes)
.ThenInclude(o => o.ImageSize)
.Include(o => o.Category)
.ThenInclude(o => o.Parent)
.ThenInclude(o => o.FileInfo)
.ThenInclude(o => o.FileInfoToImageSizes)
.ThenInclude(o => o.ImageSize)
.Include(o => o.CommentToQuestions)
.ThenInclude(o => o.Comment)
.ThenInclude(o => o.Author)
.ThenInclude(o => o.Avatar)
.ThenInclude(o => o.FileInfoToImageSizes)
.ThenInclude(o => o.ImageSize)
.Include(o => o.CommentToQuestions)
.ThenInclude(o => o.Comment)
.ThenInclude(o => o.Reciever)
.Include(o => o.Answers)
.ThenInclude(o => o.Author)
.ThenInclude(o => o.Avatar)
.ThenInclude(o => o.FileInfoToImageSizes)
.ThenInclude(o => o.ImageSize)
.Include(o => o.Answers)
.ThenInclude(o => o.LikeToAnswers)
.Include(o => o.Answers)
.ThenInclude(o => o.CommentToAnswers)
.ThenInclude(o => o.Comment)
.ThenInclude(o => o.Author)
.ThenInclude(o => o.Avatar)
.ThenInclude(o => o.FileInfoToImageSizes)
.ThenInclude(o => o.ImageSize)
.Include(o => o.Answers)
.ThenInclude(o => o.CommentToAnswers)
.ThenInclude(o => o.Comment)
.ThenInclude(o => o.Reciever)
.Where(spec.Value)
.Select(SelectorExp())
.FirstOrDefaultAsync();
}
/*...*/
}
Основная проблема в том, что кэширование большой вложенности влечет за собою опасность не корректности данных на вложенных уровнях, т.е. поменялась аватарка пользователя и актуальность данных рухнула.. Либо смотреть обьекты с типом(User) у которого изменился аватар, и во всех кэшах менять ссылку, но оболочка EF Core не позволяет реализовать такого великолепного решения(велосипеда).
Везде описаны архитектуры кэширования с простыми запросами одной вложенности, таким образом я предполагаю, что реализация слоя кэширования возможна при обращении к репозиторию одной вложенности. А при надобности вложенных сущностей, обращаться к репозиториям вложенных сущностей?
В таком случае на первый запрос по широкой доменному требованию(когда много зависимостей), и при этом ничего не закэшировано(после релиза, например). Хорошо, смотрим в сторону лоадеров. Но при огромном количестве записей в базу этот релиз может затянутся на долго, да и база не выдержит.. Как бороться с этим?
Еще неизвестно как быть со спецификациями, т.к. неизвестно условие запроса, и нельзя обратиться к MemCache например.. Их нужно убирать?
Да и в целом, поделитесь мыслями, а еще лучше конкретной реализацией :)
Виртуальный выделенный сервер (VDS) становится отличным выбором
Был создан полигон, состоящий из трёх точек и одна независимая точка:
Этот код выполняется в нескольких потоках и обычно все нормальноНо иногда SelectSingleNode возвращает null
Как заблокировать только горизонтальную автопрокрутку и оставить автопрокрутку вертикальную?
Помогите разобратьсяЯ только начал изучать Sharp и LINQ по этому каждый шаг дается с трудом, вот и сейчас столкнулся с проблемой, решение которой...