#c# #entity-framework #dependency-injection #appsettings
Вопрос:
У меня есть класс, содержащий некоторую информацию, которую я храню в базе данных.
class SomeThing
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Info => "extra info";
}
Естественно, последнее свойство опущено EF by entity.Ignore(a => a.Info);
. Теперь я хотел бы, чтобы текстовая дополнительная информация была настроена из appsettings.json
, поэтому я представил поставщика опций и ввел ее с помощью конструктора (после его регистрации Startup.cs
). DI требует, чтобы я использовал параметризованный конструктор, в то время как EF требует конструктора без параметров, поэтому мой класс становится немного забитым.
class SomeThing
{
private IOptions<Config> Config { get; }
public SomeThing() { }
public SomeThing(IOptions<Config> config)
{
Config = config;
}
public Guid Id { get; set; }
public string Name { get; set; }
public string Info => Config.Value.Info;
}
Теперь неуклюжая сложность заставляет меня подозревать, что мой дизайн несовершенен. Кроме того, я не вижу, как EF должен вводить значение параметров, поскольку он полагается на конструктор без параметров. Я рассматривал возможность реализации пустого конструктора и каким-то образом извлекать службы, вручную извлекая значение, но, на мой взгляд, это огромный красный флаг.
Как я должен подойти к дизайну такого класса?
Комментарии:
1. Как правило, вы не смешиваете проблемы сущностей с опциями или DI. Зачем вам нужна
Info
ваша сущность?2. Значит, что-то используется и как сущность, и как что-то другое? EF не будет запускать ваш DI, и ваш DI не будет загружать свои свойства из базы данных, так почему же? Пожалуйста, объясните свою проблему более подробно, в том числе, когда и как вы используете этот класс.
3. @TheGeneral Мне нужна строка для настройки URL-адресов. Это часть возвращаемого URL-адреса, который хранится в моем потоке авторизации для внешнего IDP. В зависимости от того, в какой среде мы выполняем, мне нужно поместить разные части, чтобы создать фактический URL-адрес для перенаправления.
4. @CodeCaster Я использую сущность в качестве «держателя» для входящих подключений (которые затем я передаю внешнему IDP), и когда пользователь авторизуется, я получаю ответный вызов на свой сервер. Мне нужно где-то сохранить сконструированный URL-адрес возврата, так как это не тот же сеанс, который открывается при возврате запроса от внешнего IDP. И поскольку я не могу сохранить его как статическое поле в памяти, мне нужно его сохранить. Часть сохраненного объекта было бы неплохо настроить с помощью файла настроек.
5. Я понятия не имею, что вы под этим подразумеваете или зачем вам нужна сущность для хранения этого URL-адреса.
Ответ №1:
Независимо от того, что это сомнительное дизайнерское решение, вы должны делегировать эту работу классу репозитория.
ThingRepo.GetSomeThing()
извлекает сущность и Info
при необходимости заполняет свойство. Затем он может внедриться IOptions<T>
в конструктор.
class ThingRepo
{
private Config _config;
private DbContext _db;
public ThingRepo(IOptions<Config> config, DbContext db)
{
_db = db;
_config = config.Value;
}
public async Task<Thing> GetSomeThing()
{
var thing = await _db.Set<Thing>().FirstAsync();
thing.Info = _config.Info;
return thing;
}
}
Это позволяет обойти ограничение необходимости требовать IOptions<T>
в конструкторе сущностей.
Комментарии:
1. Я тоже склоняюсь к этому. Возможно, создание фабричного фасада, обертывающего контекст БД и обогащающего объект перед хранением. И я согласен с вами, что это создает плохую атмосферу плохого дизайна. Однако я не уверен, как подойти к этому иначе. Мне нужно, чтобы мои данные сохранялись после запроса на авторизацию и до того, как поступит ответ на аутентификацию от внешнего IDP. Эта часть работает отлично (в значительной степени благодаря вам). Однако теперь я хотел бы, чтобы части краткосрочного хранилища можно было настраивать. И это-ухаб на дороге…
2. Если у вас есть мудрое предложение, не стесняйтесь стрелять. В противном случае дайте мне знать, что вы не видите ничего очевидного на основе предоставленной скудной информации, и я приму один из ответов в качестве ответа.
3. IdentityServer сохраняет запросы на авторизацию в
PersistedGrantDbContext
и в других случаях , когда ему необходимо временно хранить данные. Вы можете расширить его и присвоить идентификаторам, а также использовать для хранения всего, что связано с аутентификацией, вместе. Ознакомьтесь сDeviceFlowStore
примером: github.com/DuendeSoftware/IdentityServer/blob/…4. Это на самом деле очень интересное предложение. Я даже могу использовать его в качестве распределенного держателя для информации, так как сейчас мы запускаем несколько модулей. Мило! Что касается техники, я исправляюсь. Что касается ваших правок на мой вопрос: бум ! Я ничего не мог поделать. Извините. 🙂
5. Ничто не помешает пользователю вставить указанный DbContext в другой класс, в другом месте, где требуется эта сущность, и у нее не будет этого хранилища, ни логики, и
Info
свойство сохранит свое значение по умолчанию, т. е.null
. Кроме того, Entity Framework уже реализует класс репозитория и так далее.
Ответ №2:
Поскольку SomeThing
это определение сущности для вашей базы данных, я бы не стал пытаться использовать DI для ввода параметров. Я все равно не думаю, что это поддерживаемый сценарий в EF Core.
Если вам действительно нужен доступ к Info
объекту, вы можете сослаться на другую службу, совместимую с DI, но опять же, не было бы проще получить доступ к ней напрямую из кода Info
, из которого вы когда-либо вызывали доступ SomeThing
Обычно ядро EF предназначено для хранения данных в базе данных.