последние дни я боролся с внедрением DbContext в МОЙ фоновый рабочий. С одной стороны, я хочу внедрить dbContext в моего backgorund работника, но с другой стороны, я также хочу использовать его в своем API.
Впрыскивание в моем API, кажется, работает нормально, но так как мойрабочий - это одноэлементный файл, я не могу следовать стандартному сроку действия области действия для своего dbcontext, и я должен добавить его как переходный процесс.
Я уже пытался создать единицу работы, в которой я могу обновитьсосредоточить себя на моем работнике, эффективно создавая какую-то ограниченную службу. Я бы обновлял контекст каждый раз, когда работник снова проходил свой цикл. Это сработало, и приложение работало так, как я хотел, но я больше не мог правильно тестировать, так как сам создавал новый код DbContext в коде. Я чувствую, что должен быть лучший способ справиться с этим.
Моя структура проекта выглядит следующим образом:
API => содержит контроллеры + модели, которые я использую для почтовых запросов. Проект API должен использовать мою базу данных, чтобы получать и публиковать данные. Он использует репозитории для этого
Core (библиотека классов) => содержит некоторые базовые модели
Domain (библиотека классов) => Содержит мои доменные модели + репозитории. Вся работа с базой данных проходит здесь
Worker => Содержит некоторую логику. Работник должен использовать мою базу данных, чтобы получать и публиковать данные. Для этого
Services (библиотека классов) используются репозитории => Некоторые сервисы, которые содержат некоторую логику. Работник использует мои репозитории для доступа к базе данных
Tests => Тесты для всего кода. Я хочу иметь возможность проводить интеграционные тесты и здесь.
В настоящее время я внедряю все репозитории и сервисы как в моем API, так и в работнике:
Worker configureservices:
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddDbContext<CatAPIDbContext>(ServiceLifetime.Transient);
services.AddTransient(typeof(IFeedingProfileRepository), typeof(FeedingProfileRepository));
services.AddTransient(typeof(IFeedingTimesRepository), typeof(FeedingTimesRepository));
services.AddTransient(typeof(IFeedHistoryRepository), typeof(FeedHistoryRepository));
services.AddTransient(typeof(IMotorController), typeof(MotorController));
services.AddTransient(typeof(IFoodDispenser), typeof(FoodDispenser));
services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>));
services.AddTransient(typeof(IFeedingTimeChecker), typeof(FeedingTimeChecker));
services.AddHostedService<Worker>();
});
(EDIT) Рабочий код:
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public IFeedingTimeChecker _feedingTimeChecker { get; }
public Worker(ILogger<Worker> logger, IFeedingTimeChecker feedingTimeChecker)
{
_logger = logger;
_feedingTimeChecker = feedingTimeChecker;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
_feedingTimeChecker.ResetFeedingTimesGivenIfNeeded();
_feedingTimeChecker.FeedIfNeeded();
}
catch(Exception ex)
{
_logger.LogError(ex.ToString());
}
await Task.Delay(10000, stoppingToken);
}
}
}
(EDIT) FeedingTimeChecker (вызывается работником)
private FeedingProfile _currentProfile { get; set; }
public DateTime lastResetDataFeedingTimes;
public DateTime lastProfileRefresh;
private readonly ILogger<FeedingTimeChecker> _logger;
private IFeedingProfileRepository _feedingProfileRepository { get; set; }
private IFeedingTimesRepository _feedingTimesRepository { get; set; }
private IFoodDispenser _foodDispenser { get; }
public FeedingTimeChecker(IFeedingProfileRepository feedingProfileRepository, IFeedingTimesRepository feedingTimesRepository,IFoodDispenser foodDispenser, ILogger<FeedingTimeChecker> logger)
{
lastResetDataFeedingTimes = DateTime.MinValue.Date;
lastProfileRefresh = DateTime.MinValue.Date;
_foodDispenser = foodDispenser;
_logger = logger;
_feedingTimesRepository = feedingTimesRepository;
_feedingProfileRepository = feedingProfileRepository;
}
public void UpdateCurrentProfile()
{
if(Time.GetDateTimeNow - TimeSpan.FromSeconds(5) > lastProfileRefresh)
{
_logger.LogInformation("Refreshing current profile");
_currentProfile = _feedingProfileRepository.GetCurrentFeedingProfile();
lastProfileRefresh = Time.GetDateTimeNow;
}
}
API configureServices:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddDbContext<CatAPIDbContext>();
services.AddTransient(typeof(IFeedingProfileRepository), typeof(FeedingProfileRepository));
services.AddTransient(typeof(IFeedingTimesRepository), typeof(FeedingTimesRepository));
services.AddTransient(typeof(IFeedHistoryRepository), typeof(FeedHistoryRepository));
services.AddTransient(typeof(IMotorController), typeof(MotorController));
services.AddTransient(typeof(IFoodDispenser), typeof(FoodDispenser));
services.AddTransient(typeof(IGenericRepository<>), typeof(GenericRepository<>));
}
в моих репозиториях, которые я используюdbContext, подобный следующему:
public class GenericRepository<T> : IGenericRepository<T> where T : class
{
public CatAPIDbContext _dbContext { get; set; }
public GenericRepository(CatAPIDbContext dbContext)
{
_dbContext = dbContext;
}
public T GetById(object id)
{
return _dbContext.Set<T>().Find(id);
}
}
В результате я ожидаю, что мой работник и API будут работать правильно, всегда получая самые последние данные и утилизируя dbContext при каждом отдельном запросе, так как я используювременное время жизни для моего dbContext.
Однако в моем работнике я всегда получаю следующую ошибку: Экземпляр типа сущности 'FeedingTime' не может быть отслежен, потому что другой экземпляр другого экземпляра этого типа с тем же ключом ужеотслеживается.
Эта ошибка возникает, когда я пытаюсь установить столбец в таблице FeedingTime. У feedProfile имеется 0-много feedTimes, а feedProfile постоянно извлекается.
Любое решение, в котором я могу поддерживать проверяемую чистую кодовую базу, но пока не сталкиваюсь с этой проблемой, будет приветствоваться.
Спасибозаранее