2009-08-29 13 views
7
public class TestBL 
{ 
    public static void AddFolder(string folderName) 
    { 
     using (var ts = new TransactionScope()) 
     { 
      using (var dc = new TestDataContext()) 
      { 
       var folder = new Folder { FolderName = folderName }; 

       dc.Folders.InsertOnSubmit(folder); 
       dc.SubmitChanges(); 

       AddFile("test1.xyz", folder.Id); 
       AddFile("test2.xyz", folder.Id); 
       AddFile("test3.xyz", folder.Id); 

       dc.SubmitChanges(); 
      } 

      ts.Complete(); 
     } 
    } 

    public static void AddFile(string filename, int folderId) 
    { 
     using (var dc = new TestDataContext()) 
     { 
      dc.Files.InsertOnSubmit(
       new File { Filename = filename, FolderId = folderId }); 

      dc.SubmitChanges(); 
     } 
    } 
} 

To jest przykład zagnieżdżonego DataContext (untested). Problem zaczyna się po dodaniu TransactionScope do naszego małego eksperymentu (jak pokazano powyżej). Pierwszy AddFile w funkcji AddFolder eskaluje transakcję do DTC (który jest źle na wszelkie sposoby), ponieważ AddFile inicjuje nowy DataContext, otwierając w ten sposób drugie połączenie z DB.Jak obsłużyć zagnieżdżony datacontext w BL?

  1. Jak mogę użyć zagnieżdżonego DataContext, który nie spowoduje użycia DTC?
  2. Czy to wszystko jest po prostu błędne? Czy powinienem używać inaczej DataContext?
+0

Niestety, czułem, że to pytanie nie przyciągnęło uwagi, na które zasłużyło i nie otrzymałem w pełni odpowiedzi :( –

Odpowiedz

3

W miarę możliwości należy unikać eskalacji do DTC. Kiedy po raz pierwszy przeczytałem twoje pytanie, mój gut powiedział, że twoja transakcja nie zwiększy się do DTC, ponieważ używasz tego samego ciągu połączenia w obu kontekstach danych. Jednak według this article, myliłem się.

Nie jesteś sam w zakresie najlepszych praktyk z kontekstami danych w confusion. Jeśli przeszukujesz sieć, odpowiedzi są dostępne na całej mapie. W twoim przykładzie możesz przekazać kontekst danych do metody AddFile. Można też przekształcić ten dostęp do danych w klasę, która zachowuje czas życia kontekstu danych, dopóki folder i pliki nie zostaną zapisane. Rick Strahl opublikował an article w kilku technikach.

Wciąż żadna z odpowiedzi, które widziałem wokół LINQ na SQL wydaje się bardzo satysfakcjonująca. Czy rozważałeś unikanie zarządzania warstwą danych za pomocą ORM? Użyłem NetTiers z wielkim sukcesem, ale słyszę dobre rzeczy o PLINQO. Oba wymagają CodeSmith, ale są many alternatives.

+0

To jest jeden ze sposobów na obejrzenie tego. Niestety, wkrótce nie zmienię mojego ORM, więc " Będę musiał znaleźć odpowiednie rozwiązanie problemu –

+0

Wygląda na to, że zmiana ORM byłaby mądrym rozwiązaniem, ponieważ Microsoft dostarcza bardzo podstawowy, który nie będzie pasował do potrzeb przedsiębiorstwa, więc przełączyłem się na NHibernate :) –

0

Wymyśliłem sposób radzenia sobie z takimi sytuacjami.

Przykładowa klasa bazowa dla podmiotu BL (jednostka będzie dziedziczyć tej klasy)

abstract public class TestControllerBase : IDisposable 
{ 
    public TestDataContext CurrentDataContext { get; private set; } 

    protected TestControllerBase() 
    { 
     CurrentDataContext = new TestDataContext(); 
    } 

    protected TestControllerBase(TestDataContext dataContext) 
    { 
     CurrentDataContext = dataContext; 
    } 

    protected void ClearDataContext() 
    { 
     CurrentDataContext.Dispose(); 
     CurrentDataContext = new TestDataContext(); 
    } 

    public void Dispose() 
    { 
     CurrentDataContext.Dispose(); 
    } 
} 

Zaimplementowane kontroler

public sealed class BLTestController : TestControllerBase 
{ 
    public BLTestController() { } 

    public BLTestController(TestDataContext dataContext) 
     : base(dataContext) { } 

    // The entity functions will be implemented here using CurrentDataContext 
} 

Proste zastosowanie wdrożonego kontrolera

var testController = new BLTestControllerA(); 

testController.DeleteById(1); 

Bardziej skomplikowane korzystanie z wdrożonego kontrolera (2 kontrolery w tym samym DataContext)

var testControllerA = new BLTestControllerA(); 
var testControllerB = new BLTestControllerB(testControllerA.CurrentDataContext); 

testControllerA.DeleteById(1); 
testControllerB.DeleteById(1); 

Chciałbym zobaczyć więcej pomysłów na rozwiązanie tego zagadkę i komentarze o powyższym kodzie.

0

Nie musisz robić 2 lub więcej transakcji w obie strony dla tej transakcji. Wierzę, że LINQ-DataContext jest inteligentny, aby rozpoznać, że te pliki należą do obiektu folderu i wstawi najpierw wiersz z folderami, a potem pliki (wszystko w kontekście transakcji, np. BEGIN TRAN/COMMIT). Jednak musisz wykonać:

dc.Files.InsertOnSubmit(
       new File { Filename = filename, Folder = folder }); 

zamiast FolderId.Coś takiego:

public class TestBL 
{ 
    public static void AddFolder(string folderName) 
    { 
     using (var ts = new TransactionScope()) 
     { 
      using (var dc = new TestDataContext()) 
      { 
       var folder = new Folder { FolderName = folderName }; 

       AddFile(dc, "test1.xyz", folder); 
       AddFile(dc, "test2.xyz", folder); 
       AddFile(dc, "test3.xyz", folder); 

       dc.SubmitChanges(); 
      } 

      ts.Complete(); 
     } 
    } 

    private static void AddFile(DataContext dc, string filename, Folder folder) 
    { 
      dc.Files.InsertOnSubmit(
       new File { Filename = filename, Folder = folder }); 
    } 

    public static void AddFile(string filename, int folderId) 
    { 
     using (var dc = new TestDataContext()) 
     { 
      var folder = new Folder { FolderId = folderId }; 
      dc.Attach(folder, false); 
      AddFile(dc, filename, folder); 

      dc.SubmitChanges(); 
     } 
    } 
} 

do Twojego pytania dotyczące DTC: Nie sądzę, że to możliwe, aby uniknąć DTC tutaj ze względu na 2 otwarte połączenia. Wydaje mi się, że ostatnio dokonali pewnych zmian w tym obszarze, zob. here, ale opisywany tam scenariusz jest nieco inny (2 połączenia otwierane i zamykane jeden po drugim, w przeciwieństwie do 2 połączeń otwartych jednocześnie).

+0

Link do ciebie Podany tutaj wyjaśnił doskonale relacje zakres transakcji/DTC, choć jest nieco inny od tego, o co pytałem. –

1

Oprócz przekazania DataContext do AddFiles jako parametru, można również przekazać jedną wartość połączenia DataContext do innego DataContext. Gwarantuje to, że inne DataContext ma takie samo połączenie.

Każdy obiekt DataContext ma również właściwość Transaction, którą prawdopodobnie można ustawić i przekazać, zamiast używać obiektu TransactionScope.

+0

Nie wydaje mi się, że można przekazać właściwość Connection, ale może masz na myśli coś innego - czy możesz dodać przykład kodu? –

+0

@Eran: Obiekt DataContext ma przeciążony konstruktor, który akceptuje dowolny obiekt IDbConnection. Więc możesz zrobić nowy DataContext (OldDataContext.Connection) – rossisdead

Powiązane problemy