как обрабатывать время жизни DbContext в API / backgroundworker - PullRequest
1 голос
/ 29 октября 2019

последние дни я боролся с внедрением 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 постоянно извлекается.

Любое решение, в котором я могу поддерживать проверяемую чистую кодовую базу, но пока не сталкиваюсь с этой проблемой, будет приветствоваться.

Спасибозаранее

...