2014-06-19 24 views
5

mam 2 różne jednostki pracy: jeden oparty na ADO.NET, wywołanie procedury przechowywane głównie (uowADO) i jeszcze jeden przy użyciu Entity Framework 6 (uowEF), ostatnio dodane w celu wspierania Oracle db, więc nie muszę przepisywać wszystkich SP (moja wiedza jest tam ograniczona).Jak zaprojektować jednostkę pracy, aby obsługiwać operacje zbiorcze i zapewniać większą wydajność?

Więc warstwy biznesowej jest ładowanie tylko jeden z nich (w zależności od konfiguracji) podczas wykonywania operacji na bazie danych (ale nie mogę z nich korzystać równolegle, ponieważ uowADO nie obsługuje Oracle)

Po dodaniu nowego uowEF Zauważyłem duże problemy z wydajnością, oczywiście głównie w operacjach masowych.

Zasadniczo mam tylko Commit i Rollback metody na obecnym IUnitOfWork ... teraz bardzo blisko tego, co ten article zaleca.

Więc, mam zamiar przerobić tę jednostkę pracy. Na przykład, czytałem o wyłączaniu dbContext.Configuration.AutoDetectChangesEnabled czasami, gdy są zaangażowane operacje zbiorcze, i inne wskazówki dotyczące optymalizacji dotyczące EF, które mogą pomóc.

Niestety nie jestem pewien, w jaki sposób zaprojektować taką jednostkę pracy, aby to generic tak, że mogę go używać we wszystkich przypadkach z BL i dla obu warstw danych dostępowych: ADO.NET i EF.

Wszelkie przemyślenia, rekomendacje, dobre linki na ten temat?

+0

Wzór zdarzeń domeny, ewentualna spójność. Ale nie jest to łatwe, jeśli nie znasz DDD i architektur sterowanych zdarzeniami. – MikeSW

Odpowiedz

1

Nie ma jednoznacznej odpowiedzi.Zawsze jest to kompromis między elastycznością a wydajnością. Wszystkie te wzorce (repozytorium, jednostka pracy) są dobre dla elastyczności, ale nie dla wydajności, ponieważ konkretne implementacje zwykle wymagają pewnych poprawek, aby zapewnić maksymalną wydajność, a te ulepszenia mogą nie być (w rzeczywistości nie będą) zgodne z ogólnym interfejsem. Możesz dostosować interfejs, ale może nie działać z innymi implementacjami. Wszystkie te ORMy są bardzo różne i bardzo trudno (prawie niemożliwe) zaimplementować ogólny interfejs repozytorium/UOF do obsługi wszystkich z nich, szczególnie jeśli masz ADO (ORM niskiego poziomu, w rzeczywistości trudno nazwać go ORM) i EF (ORM wysokiego poziomu). Ustawienie AutoDetectChangesEnabled na false to tylko niewielka część tego, co możesz zrobić z EF. Aby uzyskać więcej wydajności z tego, musisz również wdrożyć obiekty w określony sposób (dodaj niektóre właściwości, niektóre atrybuty). Jeśli spojrzymy na linq2sql (inny ORM), wymaga to skompilowane zapytania, więc zapomnij o metodach, takich jak ten:

T Single(Expression<Func<T, bool>> predicate); 

w repozytoriach. Mówię tylko o repozytoriach relacyjnych baz danych. A co z repozytoriami baz danych NoSql? A co z repozytoriami opartymi na zupełnie innych źródłach danych? Tak, możliwe jest zapewnienie ogólnego interfejsu z ogólną logiką, ale w przypadku niektórych źródeł danych byłoby to bardzo powolne. Jeśli naprawdę chcesz, aby wdrożyć rozwiązanie ogólne i uzyskać maksimum osiągów, swoje repozytoria dla UOF powinien mieć bardzo specyficzny interfejs jak:

IEnumerable<T> GetStudentsByName(srting name) 

void InsertStudents<T>(IEnumerable<T> students) 

gdzie T - podmioty poziomie biznesowym, które nie powinny zależeć od konkretnej implementacji repozytorium. Repozytorium powinno być odpowiedzialne za przekształcenie T w podmioty akceptujące ORM, do których realizuje dostęp. Należy jednak pamiętać, że rozwiązanie będzie zbyt złożone i trudne do obsłużenia.

Gdybym był tobą, wybrałbym jeden główny ORM, który najlepiej pasowałby do moich wymagań i zaprojektowałbym repozytoria wokół ORM, aby uzyskać od niego maksimum perfuzji. Ale utrzymam interfejs wystarczająco elastyczny, aby mieć możliwość wdrożenia co najmniej powolnego (ale działającego) dostępu do innych źródeł danych lub ORMów.

+0

Wreszcie dane wejściowe, które potwierdzają moje myśli ... dziękuję! Myślałem, że biorę to wszystko w niewłaściwy sposób, ponieważ naprawdę trudno jest znaleźć dobre rozwiązanie. Masz całkowitą rację, faktycznie zaimplementowałem wszystko tak, jak ty również zasugerowałeś. Na razie mam tylko rozwiązanie, które działa zarówno dla ADO, jak i EF, ale z powolnym EF, a także mapper z dodatkową pamięcią podręczną między moją domeną a encja EF. Przynajmniej teraz wiem, że nie jestem daleko. – Learner

1

Niestety nie jestem pewien, w jaki sposób zaprojektować taką jednostkę pracy, aby generic tak, że można go używać we wszystkich przypadkach z BL i dla obu warstw danych dostępu: ADO.NET i EF.

Zacznij od stwierdzenia, że ​​EF ma sposób dostępu do bazy danych. Opiera się na tym ADO.NET. Na koniec używa poleceń, datareaders itp., Aby wykonać pracę na niskim poziomie.

To jest trywialne, aby ustawić rzeczy w taki sposób, że można wyodrębnić połączenie w kontekście EF, a następnie bezpośrednio użyć ADO.NET.

Jako że EF w żaden sposób nie obsługuje wydajnych operacji masowych o niewielkich rozmiarach - i tak można i trzeba polegać na ADO.NET. Sam EF jest doskonałym anty-przykładem z punktu widzenia SQL'a, jak obsługiwać bazę danych dla większych woluminów. Jest to jedna aktualizacja/wstawienie w wierszu - chociaż (nawet ignorując SqlBulkCopy na przykład) może dla większości baz danych wydać jedną dla wielu wierszy, ponieważ składnia doskonale to umożliwia.

Zgodnie z treścią: przejście do podstawowego połączenia jest naprawdę banalne, gdy lubisz czytać dokumentację próbek. To jest kod, którego używam do wstawiania zbiorczego. Klasa ObjectBulkCopy jest trochę bardziej skomplikowane, ale tutaj można zobaczyć, jak uzyskać połączenie z bazą danych:

public static void BulkInsert<T>(this Repository repository, IEnumerable<T> objects) where T : class 
     { 
      var bulkCopy = new ObjectBulkCopy<T>(); 
      var connection = (SqlConnection) repository.Database.Connection; 
      bulkCopy.Insert (objects, connection); 
     } 

Poważnie banalne - jest tam w każdym repozytorium jako właściwość, gotowy do odlewu.

+0

Mówisz, że "bezpośrednio korzystasz z ADO.NET" ... nie jestem pewien, czy widzę jasny sposób, w jaki sposób ... możesz pokazać mi pseudo- kod? Może powinienem mieć również pseudo-kod w moim pytaniu, aby pokazać, co mam do tej pory. – Learner

+0

Nie jest wymagane pseudokodowanie. Wszystko czego potrzebujesz to połączenie .... i jest łatwo dostępne. Dodawanie kodu. – TomTom

+0

Wiem, jak uzyskać połączenie, ale nie rozumiem, jak chciałeś to mieć. Teraz widzę, że używasz metody "BulkInsert" do repozytoriów. Właściwie w repozytorium mam tylko jedną funkcję "Wstaw". Jeśli chcę wykonać wiele wstawek, otwieram jedną transakcję sql i używam pętli z BL. – Learner