2016-04-06 51 views
5

Pracuję nad aplikacją konsoli C# i używam struktury encji 5.0 jako warstwy dostępu do danych z serwerem sql. teraz chcę śledzić zmiany i zapisywać je w tabeli dziennika. tak robić, więc jestem inicjowanie 2 DbContext obiektów jeden dla danych biznesowych, podczas gdy drugi dla danych dziennika, jak następuje:Inicjowanie 2 obiektów DBContext jeden dla dzienników, a drugi dla danych biznesowych wewnątrz aplikacji konsoli C#

class Sync 
    { 
     static void Main(string[] args) 
     { 
      string syncResult = "Sync started"; 
      Entities entities = new Entities();//for business data 
      Entities entities2 = new Entities();//for logs 
      try 
      { 
       //code goes here 
       entities.SaveChanges(); 
      } 
      catch (Exception e) 
      { 
       syncResult = string.IsNullOrEmpty(e.Message) ? "Error" : e.Message; 

      } 
      entities.Dispose(); 
      entities2.LogHistories.Add(new LogHistory() { Description = syncResult }); 
      entities2.SaveChanges(); 
      entities2.Dispose(); 

teraz przewidzianego oddzielne obiekty DbContext dla moich dzienników, z następującego powodu/s: -

  1. jeśli mój pierwszy obiekt podmiot nie jest w stanie zapisać zmiany, z jakiegokolwiek powodu, takich jak nieobsługiwany błędu walidacji, lub próbuje włamać się do systemu, itd .. potem drugi entities2 nadal będzie w stanie uratować wpis do dziennika. weźmy ten przykład. powiedzmy, że integruję się z trzecią częścią API i oczekuję, że zwrócą JSON w określonym formacie. Teraz załóżmy, że zwracany obiekt Json miał brakujące dane, w tym przypadku, gdy próbuję dodać obiekt, który on podniesie i wyjdzie ... teraz, ponieważ mam oddzielny obiekt obiektu dla dzienników, wówczas zapis w dzienniku zostanie zapisany (nie będzie to miało wpływu na wyjątek danych biznesowych). ale gdybym miał jeden obiekt DBContext, to zapis w dzienniku nie może zapisać, ponieważ nie mogę zapisać danych biznesowych. Tak więc moje pytanie brzmi: jeśli zainicjuję 2 obiekty DBContext jeden dla dzienników, a drugi dla danych biznesowych prawidłowe podejście do podążasz za nią, czy też jest to zła decyzja?
+2

Wystarczy przenieść do innego sposobu rejestrowania (Log (opis string)), utworzyć tam nowy kontekst (nie wcześniej jak go stworzyć w swoim przykładzie - dlaczego?) i gotowe. Oczywiście rejestrowanie zwykle nie odbywa się w ten sposób, ponieważ jeśli zapisujesz dzienniki w bazie danych, która powinna być przynajmniej asynchroniczna i umieszczona w kolejce, aby nie spowalniały twoich głównych operacji, to już inna historia. – Evk

+0

@Evk, więc jeśli przeniesię logikę logowania do oddzielnej klasy/metody, to będzie to poprawne podejście, ale posiadanie 2 klas DBContext w głównej metodzie jest problemem !! –

+1

Cóż, to nie jest "problem". Posiadanie całego kodu wśród innych logiki jest po prostu kiepską praktyką, więc przynajmniej przeniesienie go do innej metody sprawi, że kod będzie bardziej czytelny i łatwiejszy w utrzymaniu. Ale będzie jeszcze lepiej, jeśli przeniesiesz przetwarzanie dziennika do innej klasy, która uruchamia oddzielny wątek i ma kolejkę z oczekującymi komunikatami. Następnie wstawia logi jeden po drugim do bazy danych, ponawiając próbę, jeśli połączenie jest z jakiegoś powodu utracone, i najważniejsze, że robi to synchronicznie, niezależnie od twojego innego kodu. Więc masz błąd, umieszczasz to w kolejce i nadal wykonujesz pożyteczną pracę. – Evk

Odpowiedz

1

Posiadanie wielu kontekstów dla pojedynczej bazy danych może być użyteczne, jeśli baza danych zawiera wiele schematów bazy danych i użytkownik chce obsługiwać każdy z nich jako osobny obszar zamknięty. Nie jest to jasne z Twojego kodu/wymagania, jeśli tak jest.

Jeśli tabele, z którymi pracujesz, są w tej samej bazie danych, ten sam schemat &, to nie widzę powodu, aby użyć dwóch kontekstów DbContext. Nawet jeśli masz błąd sprawdzania poprawności lub wyjątek, możesz nadal zapisywać w tabeli dziennika w tym samym kontekście.

Jeśli próbujesz zalogować błędy i/lub inne istotne informacje, dlaczego nie log4net używać do logowania?

EDIT

Moim zdaniem rejestracja powinna być niezależna od zwykłych transakcji, dzięki czemu nie trzeba myśleć o tym scenariuszu. Biorąc to pod uwagę, w transakcji można zapisać i zalogować, ale także zalogować, jeśli istnieje wyjątek. Dopóki czegoś nie brakuje, nadal nie widzę potrzeby 2 DBContextów.

Oto kilka wskazówek dotyczących transakcji.

https://msdn.microsoft.com/en-us/data/dn456843.aspx

+1

Nie zrozumiałem twojego punktu. teraz powiedzmy, że nie mogę zapisać mojej pierwszej jednostki z powodu zbyt długiego pola tekstowego (wysyłam 100 znaków do DB, podczas gdy pole w databse ma tylko 20 znaków), w tym przypadku nie będę mógł zapisać wpis do dziennika, ponieważ nie mogę zapisać danych biznesowych. Natomiast jeśli mam dwie jednostki, mogę zapisać wpis dziennika, nawet jeśli pierwsza jednostka zgłasza wyjątek.zrozumiałeś mój punkt widzenia? –

+1

Nie było jasne, czy rejestrowanie było związane z wyjątkami dotyczącymi rejestrowania lub po prostu informacjami. Czy chcesz się zalogować, jeśli nie możesz zapisać danych biznesowych? Czy rejestrowanie jest częścią tej samej transakcji? –

+1

teraz powiedzmy, że najpierw wdrażam moją aplikację i chcę wszystko rejestrować. teraz załóżmy, że mamy błąd w kodzie, gdzie znajduje się pole wewnątrz tabeli Emp o nazwie FNAME z varchar (20), ale nasza aplikacja jest w porządku z wysłaniem 25 znaków do DB (to jest błąd !!). wtedy, gdy użytkownik próbuje zapisać nowy rekord EMP i wpisuje 25 znaków w FNAME i kliknie przycisk save, wtedy zostanie zgłoszony wyjątek, –

5

Nie używaj 2 konteksty, należy użyć rejestratora (np log4net with AdoNetAppender). Jak zauważył Evk w komentarzu poniżej, EF automatycznie zawija wszystko do transakcji, gdy zadzwonisz pod numer SaveChanges(), więc jak tylko pojawi się błąd, nic nie zostanie zatwierdzone do bazy danych i możesz zarejestrować błąd. Na przykład:

static void Main(string[] args) 
{ 
    ILog log = LogManager.GetLogger("Entities"); 

    using(var ctx = new Entities()) 
    { 
     try 
     { 
      ... 
      ctx.SaveChanges(); 
     } 
     catch(Exception ex) 
     { 
      log.Error(ex.Message); 
     } 
    } 
} 

ten sposób aplikacja pozostaje czysta, można aktualizować tylko wtedy, gdy wszystko się powiedzie, gdy dzwonisz SaveChanges() na końcu bloku using, a gdy tylko się zalogować wyjątek dzieje. Również lepiej jest używać bloków, aby zawsze usuwać kontekst, nawet jeśli wystąpi nieoczekiwany wyjątek.Logger zajmie się zapisaniem błędów do bazy danych w innym wątku bez spowalniania programu. Być może możesz wypróbować: NLog, Słyszałem, że jest lepszy (= łatwiejszy do skonfigurowania/użycia) niż log4net, ale nadal muszę spróbować sam.

+1

Jeśli program OP używa dwóch kontekstów, ponieważ loguje się do bazy danych, a Ty sugerujesz użycie osobnego programu rejestrującego, po co używać zakresu transakcji? ctx.SaveChanges już działa w transakcji i nie musisz wywoływać go dwukrotnie, ponieważ nie logujesz się do bazy danych. – Evk

+0

@Ekk Hmm prawda, zapomniałem EF automatycznie korzysta z transakcji –

+1

@AlexanderDerck \t więc jak mój LogManager napisze do DB !! musi używać struktury Entity .. i aby móc logować wyjątek, to musi mieć oddzielny DBContext jest poprawny? –

0

Występują pewne problemy.

  1. Wciąż działamy, jeśli napotykamy wyjątek przy wprowadzaniu danych (może to być, jeśli nie ma to wpływu na żaden stan). Ale jeśli dostaniemy wyjątek przy wyrębie, nie poradzimy sobie?

  2. Co robi nasza logika biznesowa? Jeśli otrzymamy błędne dane json, nasza logika biznesowa powinna je sprawdzić i obsłużyć.

    OK Powiedzmy, że json jest ważny, przekazuje logikę biznesową. Mamy pole string na db z varchar (20), ale dane mają 25 znaków. Co robi nasza walidacja modelu?

    Powinieneś obsługiwać te rzeczy na wyższych poziomach.

  3. To naruszenie jednej odpowiedzialności. Ta metoda powinna mieć tylko jedną odpowiedzialność, która jest jednostką zapisującą do db. Nie powinien też być odpowiedzialny za rejestrację. Powinieneś zaimplementować go na innej klasie.

    Z tego powodu występuje problem. Próbujesz dać dwie zalety tej metody. Następnie próbujesz wybrać 1 dbcontext lub 2 dbcontexts, których potrzebuję. Jeśli podążasz za Pojedynczą Odpowiedzialnością, ten problem nie wystąpi.

Powinieneś zaimplementować usługę rejestratora. Twoje zajęcia nie powinny wiedzieć lub obchodzić, jak obsługuje je serwis logger. Możesz go zapisać w db, pliku lub chmurze, np. loggly. Dodatkowo twoja usługa rejestratora nie powinna współdzielić tego samego kontekstu z innymi klasami.

+0

, więc jak moja usługa logger napisze do DB !! trzeba użyć struktury Entity .. i być w stanie zarejestrować wyjątek, to musi mieć oddzielne DBContext jest to poprawne? –

+0

Tak, użyj innego obiektu kontekstu db, w przeciwnym razie twój program rejestrujący może zapisywać/edytować elementy do bazy danych, która nie jest powiązana z programem rejestrującym (jeśli usługa i rejestrator udostępniają/używają tego samego kontela). Drugi sposób jest nieco brudny, możesz użyć tego samego kontekstu, ale przed dodaniem logu do kontekstu powinieneś odłączyć wszystkie zmodyfikowane i dodane rzeczy, aby upewnić się, że twoja usługa rejestratora jest właśnie jednostką rejestrującą reklamy. Plus jeśli chcesz czysty kod, możesz użyć funkcji [Postsharp for Exception handling] (https://michaelllucas.wordpress.com/2014/11/05/exception-handling-using-postsharp-c/) –

-2

Entity Framework 5 pierwszych migracji kodu jest w stanie zarządzać tylko jednym DbContext na fizyczną instancję bazy danych. Entity Framework 6 first first migrations jest w stanie zarządzać wieloma DbContext na fizyczną instancję bazy danych.

Zapoznaj się całkiem ładny przykład w:

http://www.dotnet-tricks.com/Tutorial/entityframework/2VOa140214-Entity-Framework-6-Code-First-Migrations-with-Multiple-Data-Contexts.html

+0

to jest całkowicie błędne. używając EF-5 i mogę mieć 2 obiekty kontekstowe db odnoszące się do tego samego DB !!! –