Конфигурирование DBContext в конструкторе моего базового класса репозитория - PullRequest
0 голосов
/ 27 апреля 2019

У меня есть ситуация, когда мне нужно создать экземпляр моего DBContext после запуска моего решения.Я задал этот вопрос , в котором указано, что я могу сделать это с помощью аргумента конструктора .

. В качестве примера было предложено реализовать следующее:

var connection = @"Server=(localdb)\mssqllocaldb;Database=JobsLedgerDB;Trusted_Connection=True;ConnectRetryCount=0";
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlServer(connection);

using (var context = new BloggingContext(optionsBuilder.Options))
{
   // do stuff
}

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

В настоящее время у меня есть это:

    public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntityBase, new()
{

    public JobsLedgerAPIContext _context;

    #region Properties
    public EntityBaseRepository(JobsLedgerAPIContext context)
    {
        _context = context;
    }
    #endregion
    public virtual IQueryable<T> GetAll()
    {
        return _context.Set<T>().AsQueryable();
    }

    public virtual int Count()
    {
        return _context.Set<T>().Count();
    }
     ......

Как я могу реализовать это изменение и создать экземпляр DBContext в конструкторе (там, обходя необходимость добавить контекств качестве службы при запуске), а затем с переносом каждого из виртуальных методов в «использование» и т. д.

РЕДАКТИРОВАТЬ. Камило указал, что я не указал, когда у меня есть имя базы данных.

Основная ситуация заключается в том, что система запускается (это проект Aurelia SPA, который не имеет отношения к этой проблеме), отправляет пакет вбраузер, который показывает экран входа в систему.Пользователь входит в систему. Пользователь проверяется с помощью контроллера JWT. После проверки в контроллере (с использованием базы данных каталога, содержащей одну таблицу с 3 полями - имя пользователя, пароль, имя базы данных), я использую имя базы данных для создания строки подключения изатем создайте экземпляр моего DBContext в этот момент ... через конструктор.

Ответы ниже необходимо изменить, так как ответ с фабричным ответом (многообещающим) содержит ошибки, обнаруженные этим вопросом .. Нкоси ответил отличным ответом на ошибку.

РЕДАКТИРОВАТЬ 2 .. Это ответ на отредактированный вопрос ниже:

Вот мой оригинальный клиентский репозиторийwith: base (context) на конструкторе.

using JobsLedger.DATA.Abstract;
using JobsLedger.MODEL.Entities;

namespace JobsLedger.DATA.Repositories
{
    public class ClientRepository : EntityBaseRepository<Client>, IClientRepository
    {
        private new JobsLedgerAPIContext _context;

        public ClientRepository(JobsLedgerAPIContext context) : base(context)
        {
            _context = context;
        }

        public void RelatedSuburbEntities(Suburb _suburb)
        {
            _context.Entry(_suburb).Reference<State>(a => a.State).Load();
        }
    }
}

Имеет ссылку на базовый класс "context".Я не уверен, как изменить это, учитывая, что мне все еще нужно, чтобы ": base (context)" в конце.Кроме того, у меня есть метод, который также обращается к _context, который является частью конструктора ...

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

Кроме того, учитывая, что теперь я добавил синглтон при запуске, мне нужно удалить исходную запись?:

        services.AddDbContext<JobsLedgerAPIContext>(options => options.
          UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly("JobsLedger.API")));

эффективно заменив его моей единственной ссылкой, как показано ниже:

services.AddSingleton (typeof (IContextFactory <>), typeof (ContextFactory <>));

Ответы [ 2 ]

2 голосов
/ 27 апреля 2019

Отредактировано

Ответ был отредактирован для исправления обнаруженной ошибки и исправлено от Nkosi.Спасибо, @ Nkosi.

Реализация фабричного шаблона.Вы можете создать фабрику, назовите ее ContextFactory, как показано ниже:

Сначала определите интерфейс. В дальнейшем изменено, удален параметр connectionString

public interface IContextFactory<T> where T : DbContext
{
    T CreateDbContext();
}

Создать фабричный класс, реализующий этот интерфейс (отредактировано в соответствии с Nkosi answer ). Далее модифицируется для внедрения IHttpContextAccessor

public class ContextFactory<T> : IContextFactory<T> where T : DbContext
{
    private readonly HttpContext _httpContext;

    public ContextFactory(IHttpContextAccessor contextAccessor)
    {
        _httpContext = contextAccessor.HttpContext;
    }

    public T CreateDbContext()
    {
        // retreive the connectionString from the _httpContext.Items
        // this is saved in the controller action method
        var connectionString = (string)_httpContext.Items["connection-string"];
        var optionsBuilder = new DbContextOptionsBuilder<T>();
        optionsBuilder.UseSqlServer(connectionString);
        return (T)Activator.CreateInstance(typeof(T), optionsBuilder.Options);
    }
}

Затем измените ваш базовый репозиторий и сделайте JobsLedgerAPIContext защищенным.Этот контекст будет установлен производным классом. Доработано для удаления конструктора .Он будет использовать конструктор без параметров.

public class EntityBaseRepository<T> : IEntityBaseRepository<T> where T : class, IEntityBase, new()
{
    protected JobsLedgerApiContext Context { get; set; }

    public virtual IQueryable<T> GetAll()
    {
        return Context.Set<T>().AsQueryable();
    }

    public virtual int Count()
    {
        return Context.Set<T>().Count();
    }
}

Измените ваш производный класс, чтобы использовать IContextFactory. Далее модифицируется для использования метода _contextFactory.CreateDbContext() без параметров

Для IClientRepository должен быть определен метод SetContext.

public class ClientRepository : EntityBaseRepository<Client>, IClientRepository
{
    private readonly IContextFactory<JobsLedgerApiContext> _contextFactory;

    public ClientRepository(IContextFactory<JobsLedgerApiContext> factory)
    {
        _contextFactory = factory;
    }

    // this method will set the protected Context property using the context
    // created by the factory
    public void SetContext()
    {
        Context = _contextFactory.CreateDbContext();
    }

    public void RelatedSuburbEntities(Suburb suburb)
    {
        Context.Entry(suburb).Reference<State>(a => a.State).Load();
    }
}

В контроллере, который получаетIClientRepository экземпляр, вы можете установить соединение в HttpContext.Items, которое будет действительным для запроса.Это значение будет затем извлечено ContextFactory с использованием IHttpContextAccessor.Затем вы просто вызываете метод _repository.SetContext(); в хранилище.

public class HomeController : Controller
{
    private readonly IClientRepository _repository;

    public HomeController(IClientRepository repository)
    {
        _repository = repository;
    }

    public IActionResult Index()
    {
       // save the connectionString in the HttpContext.Items
       HttpContext.Items["connection-string"] = "test-connection";

       // set the context 
       _repository.SetContext();

       return View();
    }
}

Убедитесь, что вы зарегистрировали IContextFactory в ConfigureServices как открытые обобщения и Singleton, как показано ниже, а также зарегистрируйте HttpContextAccessor и IClientRepository

services.AddHttpContextAccessor();
services.AddSingleton(typeof(IContextFactory<>), typeof(ContextFactory<>));
services.AddTransient<IClientRepository, ClientRepository>();
1 голос
/ 27 апреля 2019

Вы можете определить свой JobsLedgerAPIContext следующим образом:

public class JobsLedgerAPIContext : DbContext
{
    // public DbSet<Job> Jobs { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer("Data Source=localhost;Integrated Security=SSPI;Initial Catalog=dotnetcore;");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // may need to reflect entity classes and register them here.

        base.OnModelCreating(modelBuilder);
    }
}
...