Obecnie używam DbContext
podobnego do tego:
namespace Models
{
public class ContextDB: DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<UserRole> UserRoles { get; set; }
public ContextDB()
{
}
}
}
Następnie używam następującego wiersza u góry WSZYSTKICH moich kontrolerów, które potrzebują dostępu do bazy danych. Używam go również w mojej klasie UserRepository, która zawiera wszystkie metody odnoszące się do użytkownika (takie jak pobranie aktywnego użytkownika, sprawdzenie, jakie ma role itp.):
ContextDB _db = new ContextDB();
Myśląc o tym… zdarzają się sytuacje, w których jeden użytkownik może mieć aktywnych wiele DbContexts… tj. jeśli odwiedza kontroler, który korzysta z UserRepository ... to może nie być najlepszy pomysł i mam kilka pytań na ten temat
- Kiedy powinienem utworzyć nowy kontekst DbContext / czy powinienem mieć jeden kontekst globalny, który mam przekazywać?
- Czy mogę mieć jeden globalny kontekst, którego mogę używać ponownie we wszystkich miejscach?
- Czy to powoduje spadek wydajności?
- Jak wszyscy to robią?
DbContext
na żądanie. Stworzyłbym również warstwę usług. Sprawdź to pytanie i odpowiedź SOOdpowiedzi:
Używam kontrolera podstawowego, który ujawnia
DataBase
właściwość, do której mają dostęp kontrolery pochodne.public abstract class BaseController : Controller { public BaseController() { Database = new DatabaseContext(); } protected DatabaseContext Database { get; set; } protected override void Dispose(bool disposing) { Database.Dispose(); base.Dispose(disposing); } }
Wszystkie kontrolery w mojej aplikacji pochodzą
BaseController
i są używane w następujący sposób:public class UserController : BaseController { [HttpGet] public ActionResult Index() { return View(Database.Users.OrderBy(p => p.Name).ToList()); } }
A teraz odpowiedz na twoje pytania:
Kontekst powinien być tworzony na żądanie. Utwórz kontekst, zrób z nim to, co musisz, a następnie pozbądź się go. W przypadku rozwiązania klasy bazowej, z którego korzystam, musisz się tylko martwić o użycie kontekstu.
Nie próbuj mieć globalnego kontekstu (nie tak działają aplikacje internetowe).
Nie, jeśli zachowasz kontekst wokół, będzie on śledzić wszystkie aktualizacje, dodatki, usunięcia itp., A to spowolni Twoją aplikację, a nawet może spowodować pojawienie się w niej dość subtelnych błędów.
Prawdopodobnie powinieneś zdecydować się na ujawnienie swojego repozytorium lub kontekstu kontrolerowi, ale nie obu. Posiadanie dwóch kontekstów umożliwiających dostęp za pomocą tej samej metody doprowadzi do błędów, jeśli obaj mają różne wyobrażenia o bieżącym stanie aplikacji.
Osobiście wolę ujawniać
DbContext
bezpośrednio, ponieważ większość przykładów repozytoriów, które widziałem, i tak kończy się jako cienkie opakowaniaDbContext
.Pierwsze utworzenie pliku
DbContext
jest dość kosztowne, ale po jego wykonaniu wiele informacji jest zapisywanych w pamięci podręcznej, dzięki czemu kolejne instancje są znacznie szybsze. istnieje większe prawdopodobieństwo, że problemy z wydajnością wynikają z utrzymywania kontekstu, niż w przypadku tworzenia ich za każdym razem, gdy potrzebujesz dostępu do bazy danych.To zależy.
Niektórzy ludzie wolą używać struktury iniekcji zależności, aby przekazać konkretne wystąpienie ich kontekstu do kontrolera podczas jego tworzenia. Obie opcje są w porządku. Mój jest bardziej odpowiedni dla aplikacji na małą skalę, w których wiesz, że używana baza danych nie ulegnie zmianie.
niektórzy mogą twierdzić, że nie możesz tego wiedzieć i dlatego metoda wstrzykiwania zależności jest lepsza, ponieważ sprawia, że aplikacja jest bardziej odporna na zmiany. Moja opinia na ten temat jest taka, że prawdopodobnie się nie zmieni (SQL server i Entity Framework nie są niejasne) i że najlepiej spędzam czas na pisaniu kodu specyficznego dla mojej aplikacji.
źródło
Próbuję odpowiedzieć na podstawie własnego doświadczenia.
1. Kiedy powinienem utworzyć nowy kontekst DbContext / powinienem mieć jeden kontekst globalny, który będę przekazywać?
Kontekst powinien być wstrzykiwany przez iniekcję zależności i nie powinien być tworzony samodzielnie. Najlepszym rozwiązaniem jest utworzenie go jako usługi o określonym zakresie przez iniekcję zależności. (Zobacz moją odpowiedź na pytanie 4)
Należy również rozważyć użycie odpowiedniej warstwowej struktury aplikacji, takiej jak Kontroler> BusinessLogic> Repozytorium. W tym przypadku nie byłoby tak, że kontroler otrzymuje kontekst db, ale zamiast tego repozytorium. Wstrzyknięcie / utworzenie instancji kontekstu db w kontrolerze mówi mi, że architektura Twojej aplikacji łączy wiele obowiązków w jednym miejscu, czego - w żadnych okolicznościach - nie mogę polecić.
2. Czy mogę mieć jeden globalny kontekst, którego mogę używać ponownie we wszystkich miejscach?
Tak, możesz , ale pytanie powinno brzmieć: „ Czy powinienem ...” -> NIE. Kontekst ma być używany na żądanie, aby zmienić repozytorium, a następnie ponownie go usunąć.
3. Czy to powoduje spadek wydajności?
Tak, ponieważ DBContext po prostu nie jest stworzony do tego, aby był globalny. Przechowuje wszystkie dane, które zostały wprowadzone lub przeszukane, dopóki nie zostaną zniszczone. Oznacza to, że kontekst globalny będzie coraz większy i większy, operacje na nim będą coraz wolniejsze, aż pojawią się wyjątki braku pamięci lub umrzesz ze starości, ponieważ wszystko spowolniło do pełzania.
Otrzymasz również wyjątki i wiele błędów, gdy wiele wątków jednocześnie uzyskuje dostęp do tego samego kontekstu.
4. Jak wszyscy to robią?
DBContext wstrzyknięty przez iniekcję zależności przez fabrykę; zakres:
services.AddDbContext<UserDbContext>(o => o.UseSqlServer(this.settings.DatabaseOptions.UserDBConnectionString));
Mam nadzieję, że moje odpowiedzi będą pomocne.
źródło
W tej chwili próbuję tego podejścia, które pozwala uniknąć tworzenia wystąpienia kontekstu, gdy wywołujesz akcje, które go nie używają.
public abstract class BaseController : Controller { public BaseController() { } private DatabaseContext _database; protected DatabaseContext Database { get { if (_database == null) _database = new DatabaseContext(); return _database; } } protected override void Dispose(bool disposing) { if (_database != null) _database.Dispose(); base.Dispose(disposing); } }
źródło
GC.GetTotalMemory()
zwróciło (nie jest idealne, ale tak właśnie znalazłem) i różnica nigdy nie była większa niż 8 KB.Jest to oczywiście starsze pytanie, ale jeśli używasz DI, możesz zrobić coś takiego i określić zakres wszystkich obiektów na czas trwania żądania
public class UnitOfWorkAttribute : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>(); context.BeginTransaction(); } public override void OnActionExecuted(HttpActionExecutedContext actionContext) { var context = IoC.CurrentNestedContainer.GetInstance<DatabaseContext>(); context.CloseTransaction(actionContext.Exception); } }
źródło
Kontekst należy usunąć natychmiast po każdej operacji Save (). W przeciwnym razie każdy kolejny zapis będzie trwał dłużej. Miałem projekt, który tworzył i zapisywał złożone jednostki bazy danych w cyklu. Ku mojemu zdziwieniu, operacja stała się trzykrotnie szybsza po przeniesieniu "using (var ctx = new MyContext ()) {...}" wewnątrz cyklu.
źródło