Udostępnij za pośrednictwem


Pobieranie danych i operacje CUD w aplikacjach N-warstwowych (LINQ to SQL)

Podczas serializacji obiektów jednostek, takich jak Klienci lub Zamówienia do klienta za pośrednictwem sieci, te jednostki są odłączone od kontekstu danych. Kontekst danych nie śledzi już zmian ani skojarzeń z innymi obiektami. Nie jest to problem, o ile klienci odczytują tylko dane. Stosunkowo proste jest również umożliwienie klientom dodawania nowych wierszy do bazy danych. Jeśli jednak aplikacja wymaga, aby klienci mogli aktualizować lub usuwać dane, przed wywołaniem metody DataContext.SubmitChangesnależy dołączyć jednostki do nowego kontekstu danych. Ponadto jeśli używasz optymistycznego sprawdzania współbieżności z oryginalnymi wartościami, konieczne będzie również zapewnienie bazy danych zarówno oryginalnej jednostki, jak i jednostki zgodnie z modyfikacją. Dostępne Attach są metody umożliwiające umieszczenie jednostek w nowym kontekście danych po ich odłączeniu.

Nawet jeśli serializujesz obiekty serwera proxy zamiast jednostek LINQ to SQL, nadal musisz utworzyć jednostkę w warstwie dostępu do danych (DAL) i dołączyć ją do nowego System.Data.Linq.DataContextelementu , aby przesłać dane do bazy danych.

LINQ to SQL jest całkowicie obojętny na sposób serializacji jednostek. Aby uzyskać więcej informacji na temat używania narzędzi object Relational Projektant i SQLMetal do generowania klas, które można serializować przy użyciu programu Windows Communication Foundation (WCF), zobacz How to: Make Entities Serializable (How to: Make Entities Serializable).

Uwaga

Wywołaj Attach tylko metody dla nowych lub deserializowanych jednostek. Jedynym sposobem odłączenia jednostki od oryginalnego kontekstu danych jest serializacja. Jeśli spróbujesz dołączyć nieufaną jednostkę do nowego kontekstu danych, a ta jednostka nadal ma odroczone moduły ładujące z poprzedniego kontekstu danych, linQ to SQL zgłosi wyjątek. Jednostka z odroczonymi modułami ładujących z dwóch różnych kontekstów danych może spowodować niepożądane wyniki podczas wykonywania operacji wstawiania, aktualizowania i usuwania dla tej jednostki. Aby uzyskać więcej informacji na temat odroczonych modułów ładujących, zobacz Odroczone i Bezpośrednie ładowanie.

Pobieranie danych

Wywołanie metody klienta

W poniższych przykładach pokazano przykładowe wywołanie metody do dal z klienta windows Forms. W tym przykładzie dal jest implementowane jako biblioteka usług systemu Windows:

Private Function GetProdsByCat_Click(ByVal sender As Object, ByVal e _  
    As EventArgs)  
  
    ' Create the WCF client proxy.  
    Dim proxy As New NorthwindServiceReference.Service1Client  
  
    ' Call the method on the service.  
    Dim products As NorthwindServiceReference.Product() = _  
        proxy.GetProductsByCategory(1)  
  
    ' If the database uses original values for concurrency checks,  
    ' the client needs to store them and pass them back to the  
    ' middle tier along with the new values when updating data.  
  
    For Each v As NorthwindClient1.NorthwindServiceReference.Product _  
        In products  
        ' Persist to a List(Of Product) declared at class scope.  
        ' Additional change-tracking logic is the responsibility  
        ' of the presentation tier and/or middle tier.  
        originalProducts.Add(v)  
    Next  
  
    ' (Not shown) Bind the products list to a control  
    ' and/or perform whatever processing is necessary.  
End Function  
private void GetProdsByCat_Click(object sender, EventArgs e)  
{  
    // Create the WCF client proxy.  
    NorthwindServiceReference.Service1Client proxy =
    new NorthwindClient.NorthwindServiceReference.Service1Client();  
  
    // Call the method on the service.  
    NorthwindServiceReference.Product[] products =
    proxy.GetProductsByCategory(1);  
  
    // If the database uses original values for concurrency checks,
    // the client needs to store them and pass them back to the
    // middle tier along with the new values when updating data.  
    foreach (var v in products)  
    {  
        // Persist to a list<Product> declared at class scope.  
        // Additional change-tracking logic is the responsibility  
        // of the presentation tier and/or middle tier.  
        originalProducts.Add(v);  
    }  
  
    // (Not shown) Bind the products list to a control  
    // and/or perform whatever processing is necessary.  
    }  

Implementacja warstwy środkowej

Poniższy przykład przedstawia implementację metody interfejsu w warstwie środkowej. Poniżej przedstawiono dwa główne kwestie, które należy zwrócić uwagę:

  • Element DataContext jest zadeklarowany w zakresie metody.

  • Metoda zwraca IEnumerable kolekcję rzeczywistych wyników. Serializator wykona zapytanie, aby wysłać wyniki z powrotem do warstwy klienta/prezentacji. Aby uzyskać dostęp do wyników zapytania lokalnie w warstwie środkowej, możesz wymusić wykonywanie przez wywołanie ToList zmiennej zapytania lub ToArray na niej. Następnie możesz zwrócić listę lub tablicę IEnumerablejako .

Public Function GetProductsByCategory(ByVal categoryID As Integer) _  
    As IEnumerable(Of Product)  
  
    Dim db As New NorthwindClasses1DataContext(connectionString)  
    Dim productQuery = _  
    From prod In db.Products _  
    Where prod.CategoryID = categoryID _  
    Select prod  
  
    Return productQuery.AsEnumerable()  
  
End Function  
public IEnumerable<Product> GetProductsByCategory(int categoryID)  
{  
    NorthwindClasses1DataContext db =
    new NorthwindClasses1DataContext(connectionString);  
  
    IEnumerable<Product> productQuery =  
    from prod in db.Products  
    where prod.CategoryID == categoryID  
    select prod;  
  
    return productQuery.AsEnumerable();
}  

Wystąpienie kontekstu danych powinno mieć okres istnienia jednej "jednostki pracy". W luźno powiązanym środowisku jednostka pracy jest zwykle mała, być może jedna optymistyczna transakcja, w tym jedno wywołanie metody SubmitChanges. W związku z tym kontekst danych jest tworzony i usuwany w zakresie metody. Jeśli jednostka pracy zawiera wywołania logiki reguł biznesowych, zazwyczaj chcesz zachować DataContext wystąpienie dla tej całej operacji. W każdym razie DataContext wystąpienia nie są przeznaczone do przechowywania aktywności przez długi czas w dowolnej liczbie transakcji.

Ta metoda zwróci obiekty Product, ale nie kolekcję obiektów Order_Detail skojarzonych z każdym produktem. Użyj obiektu , DataLoadOptions aby zmienić to domyślne zachowanie. Aby uzyskać więcej informacji, zobacz Jak kontrolować, ile powiązanych danych jest pobieranych.

Wstawianie danych

Aby wstawić nowy obiekt, warstwa prezentacji wywołuje tylko odpowiednią metodę w interfejsie warstwy środkowej i przekazuje nowy obiekt do wstawienia. W niektórych przypadkach może to być bardziej wydajne, aby klient mógł przekazać tylko niektóre wartości i utworzyć pełny obiekt w warstwie środkowej.

Implementacja warstwy środkowej

W warstwie środkowej tworzony jest nowy DataContext obiekt jest dołączany do DataContext obiektu przy użyciu InsertOnSubmit metody , a obiekt jest wstawiany po SubmitChanges wywołaniu. Wyjątki, wywołania zwrotne i warunki błędów mogą być obsługiwane tak samo jak w każdym innym scenariuszu usługi sieci Web.

' No call to Attach is necessary for inserts.  
Public Sub InsertOrder(ByVal o As Order)  
  
    Dim db As New NorthwindClasses1DataContext(connectionString)  
    db.Orders.InsertOnSubmit(o)  
  
    ' Exception handling not shown.  
    db.SubmitChanges()  
  
End Sub  
// No call to Attach is necessary for inserts.  
    public void InsertOrder(Order o)  
    {  
        NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString);  
        db.Orders.InsertOnSubmit(o);  
  
        // Exception handling not shown.  
        db.SubmitChanges();  
    }  

Usuwanie danych

Aby usunąć istniejący obiekt z bazy danych, warstwa prezentacji wywołuje odpowiednią metodę w interfejsie warstwy środkowej i przekazuje jego kopię zawierającą oryginalne wartości obiektu do usunięcia.

Operacje usuwania obejmują optymistyczne kontrole współbieżności, a obiekt, który ma zostać usunięty, musi najpierw zostać dołączony do nowego kontekstu danych. W tym przykładzie Boolean parametr jest ustawiony na wartość , aby wskazać false , że obiekt nie ma znacznika czasu (RowVersion). Jeśli tabela bazy danych generuje znaczniki czasu dla każdego rekordu, kontrole współbieżności są znacznie prostsze, zwłaszcza dla klienta. Wystarczy przekazać oryginalny lub zmodyfikowany obiekt i ustawić Boolean parametr na true. W każdym razie w warstwie środkowej zazwyczaj konieczne jest przechwycenie elementu ChangeConflictException. Aby uzyskać więcej informacji na temat obsługi optymistycznych konfliktów współbieżności, zobacz Optymistyczna współbieżność: omówienie.

Podczas usuwania jednostek, które mają ograniczenia klucza obcego w skojarzonych tabelach, należy najpierw usunąć wszystkie obiekty w jego EntitySet<TEntity> kolekcjach.

' Attach is necessary for deletes.  
Public Sub DeleteOrder(ByVal order As Order)  
    Dim db As New NorthwindClasses1DataContext(connectionString)  
  
    db.Orders.Attach(order, False)  
    ' This will throw an exception if the order has order details.  
    db.Orders.DeleteOnSubmit(order)  
  
    Try  
        ' ConflictMode is an optional parameter.  
        db.SubmitChanges(ConflictMode.ContinueOnConflict)  
  
    Catch ex As ChangeConflictException  
        ' Get conflict information, and take actions  
        ' that are appropriate for your application.  
        ' See MSDN Article "How to: Manage Change  
        ' Conflicts (LINQ to SQL).  
  
    End Try  
End Sub  
// Attach is necessary for deletes.  
public void DeleteOrder(Order order)  
{  
    NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString);  
  
    db.Orders.Attach(order, false);  
    // This will throw an exception if the order has order details.  
    db.Orders.DeleteOnSubmit(order);  
    try  
    {  
        // ConflictMode is an optional parameter.  
        db.SubmitChanges(ConflictMode.ContinueOnConflict);  
    }  
    catch (ChangeConflictException e)  
    {  
       // Get conflict information, and take actions  
       // that are appropriate for your application.  
       // See MSDN Article How to: Manage Change Conflicts (LINQ to SQL).  
    }  
}  

Aktualizowanie danych

LinQ to SQL obsługuje aktualizacje w tych scenariuszach obejmujących optymistyczną współbieżność:

  • Optymistyczna współbieżność na podstawie sygnatur czasowych lub liczb RowVersion.

  • Optymistyczna współbieżność oparta na oryginalnych wartościach podzestawu właściwości jednostki.

  • Optymistyczna współbieżność oparta na kompletnych oryginalnych i zmodyfikowanych jednostkach.

Można również wykonywać aktualizacje lub usuwać na jednostce wraz z jej relacjami, na przykład Klient i kolekcję skojarzonych obiektów Zamówienia. Podczas wprowadzania modyfikacji na kliencie do grafu obiektów jednostki i ich kolekcji podrzędnych (EntitySet), a optymistyczne kontrole współbieżności wymagają oryginalnych wartości, klient musi podać te oryginalne wartości dla każdej jednostki i EntitySet<TEntity> obiektu. Jeśli chcesz umożliwić klientom tworzenie zestawu powiązanych aktualizacji, usuwania i wstawiania w ramach pojedynczego wywołania metody, należy podać klientowi sposób wskazania typu operacji do wykonania w każdej jednostce. W warstwie środkowej należy wywołać odpowiednią Attach metodę, a następnie InsertOnSubmitmetodę , DeleteAllOnSubmitlub InsertOnSubmit (bez Attach, dla wstawiania) dla każdej jednostki przed wywołaniem metody SubmitChanges. Nie pobieraj danych z bazy danych jako sposobu uzyskiwania oryginalnych wartości przed wypróbowaniem aktualizacji.

Aby uzyskać więcej informacji na temat optymistycznej współbieżności, zobacz Optymistyczna współbieżność: omówienie. Aby uzyskać szczegółowe informacje na temat rozwiązywania optymistycznych konfliktów zmian współbieżności, zobacz How to: Manage Change Conflicts (Jak zarządzać konfliktami zmian).

W poniższych przykładach przedstawiono każdy scenariusz:

Optymistyczna współbieżność ze znacznikami czasu

' Assume that "customer" has been sent by client.  
' Attach with "true" to say this is a modified entity  
' and it can be checked for optimistic concurrency  
' because it has a column that is marked with the  
' "RowVersion" attribute.  
  
db.Customers.Attach(customer, True)  
  
Try  
    ' Optional: Specify a ConflictMode value  
    ' in call to SubmitChanges.  
    db.SubmitChanges()  
Catch ex As ChangeConflictException  
    ' Handle conflict based on options provided.  
    ' See MSDN article "How to: Manage Change  
    ' Conflicts (LINQ to SQL)".  
End Try  
// Assume that "customer" has been sent by client.  
// Attach with "true" to say this is a modified entity  
// and it can be checked for optimistic concurrency because  
//  it has a column that is marked with "RowVersion" attribute  
db.Customers.Attach(customer, true)  
try  
{  
    // Optional: Specify a ConflictMode value  
    // in call to SubmitChanges.  
    db.SubmitChanges();  
}  
catch(ChangeConflictException e)  
{  
    // Handle conflict based on options provided  
    // See MSDN article How to: Manage Change Conflicts (LINQ to SQL).  
}  

Z podzbiorem oryginalnych wartości

W tym podejściu klient zwraca kompletny serializowany obiekt wraz z wartościami, które mają zostać zmodyfikowane.

Public Sub UpdateProductInventory(ByVal p As Product, ByVal _  
    unitsInStock As Short?, ByVal unitsOnOrder As Short?)  
  
    Using db As New NorthwindClasses1DataContext(connectionString)  
        ' p is the original unmodified product  
        ' that was obtained from the database.  
        ' The client kept a copy and returns it now.  
        db.Products.Attach(p, False)  
  
        ' Now that the original values are in the data context,  
        ' apply the changes.  
        p.UnitsInStock = unitsInStock  
        p.UnitsOnOrder = unitsOnOrder  
  
        Try  
            ' Optional: Specify a ConflictMode value  
            ' in call to SubmitChanges.  
            db.SubmitChanges()  
  
        Catch ex As Exception  
            ' Handle conflict based on options provided.  
            ' See MSDN article "How to: Manage Change Conflicts  
            ' (LINQ to SQL)".  
        End Try  
    End Using  
End Sub  
public void UpdateProductInventory(Product p, short? unitsInStock, short? unitsOnOrder)  
{  
    using (NorthwindClasses1DataContext db = new NorthwindClasses1DataContext(connectionString))  
    {  
        // p is the original unmodified product  
        // that was obtained from the database.  
        // The client kept a copy and returns it now.  
        db.Products.Attach(p, false);  
  
        // Now that the original values are in the data context, apply the changes.  
        p.UnitsInStock = unitsInStock;  
        p.UnitsOnOrder = unitsOnOrder;  
        try  
        {  
             // Optional: Specify a ConflictMode value  
             // in call to SubmitChanges.  
             db.SubmitChanges();  
        }  
        catch (ChangeConflictException e)  
        {  
            // Handle conflict based on provided options.  
            // See MSDN article How to: Manage Change Conflicts  
            // (LINQ to SQL).  
        }  
    }  
}  

Z kompletnymi jednostkami

Public Sub UpdateProductInfo(ByVal newProd As Product, ByVal _  
    originalProd As Product)  
  
    Using db As New NorthwindClasses1DataContext(connectionString)  
        db.Products.Attach(newProd, originalProd)  
  
        Try  
            ' Optional: Specify a ConflictMode value  
            ' in call to SubmitChanges.  
            db.SubmitChanges()  
  
        Catch ex As Exception  
            ' Handle potential change conflict in whatever way  
            ' is appropriate for your application.  
            ' For more information, see the MSDN article  
            ' "How to: Manage Change Conflicts (LINQ to  
            ' SQL)".  
        End Try  
  
    End Using  
End Sub  
public void UpdateProductInfo(Product newProd, Product originalProd)  
{  
     using (NorthwindClasses1DataContext db = new  
        NorthwindClasses1DataContext(connectionString))  
     {  
         db.Products.Attach(newProd, originalProd);  
         try  
         {  
               // Optional: Specify a ConflictMode value  
               // in call to SubmitChanges.  
               db.SubmitChanges();  
         }  
        catch (ChangeConflictException e)  
        {  
            // Handle potential change conflict in whatever way  
            // is appropriate for your application.  
            // For more information, see the MSDN article  
            // How to: Manage Change Conflicts (LINQ to SQL)/  
        }
    }  
}  

Aby zaktualizować kolekcję, wywołaj metodę AttachAll zamiast Attach.

Oczekiwane elementy członkowskie jednostki

Jak wspomniano wcześniej, przed wywołaniem Attach metod wymagane są tylko niektóre elementy członkowskie obiektu jednostki. Elementy członkowskie jednostki wymagane do ustawienia muszą spełniać następujące kryteria:

  • Należy do tożsamości jednostki.

  • Należy się spodziewać modyfikacji.

  • Być znacznikiem czasu lub mieć atrybut UpdateCheck ustawiony na coś poza Neverelementem .

Jeśli w tabeli użyto sygnatury czasowej lub numeru wersji w celu sprawdzenia optymistycznej współbieżności, należy ustawić te elementy członkowskie przed wywołaniem metody Attach. Element członkowski jest przeznaczony do optymistycznego sprawdzania współbieżności, gdy IsVersion właściwość jest ustawiona na wartość true dla tego atrybutu Kolumna. Wszystkie żądane aktualizacje zostaną przesłane tylko wtedy, gdy numer wersji lub wartości znacznika czasu są takie same w bazie danych.

Element członkowski jest również używany w optymistowym sprawdzaniu współbieżności, o ile element członkowski nie ma UpdateCheck ustawionej wartości Never. Wartość domyślna to Always , jeśli nie określono żadnej innego wartości.

Jeśli brakuje jednego z tych wymaganych elementów członkowskich, ChangeConflictException zostanie zgłoszony podczas SubmitChanges ("Nie znaleziono lub zmieniono wiersza").

Stan

Po dołączeniu DataContext obiektu jednostki do wystąpienia obiekt jest uznawany za w PossiblyModified stanie . Istnieją trzy sposoby wymuszania, aby dołączony obiekt był traktowany jako Modified.

  1. Dołącz je jako niezmodyfikowane, a następnie bezpośrednio zmodyfikuj pola.

  2. Dołącz go z Attach przeciążeniem, które pobiera bieżące i oryginalne wystąpienia obiektów. Zapewnia to monitor zmian ze starymi i nowymi wartościami, dzięki czemu automatycznie będzie wiedział, które pola uległy zmianie.

  3. Dołącz go z Attach przeciążeniem, które przyjmuje drugi parametr logiczny (ustawiony na wartość true). Spowoduje to, że monitor zmian rozważy modyfikację obiektu bez konieczności podawania żadnych oryginalnych wartości. W tym podejściu obiekt musi mieć pole sygnatury czasowej/wersji.

Aby uzyskać więcej informacji, zobacz Stany obiektów i Śledzenie zmian.

Jeśli obiekt jednostki już występuje w pamięci podręcznej identyfikatorów z taką samą tożsamością jak dołączony obiekt, DuplicateKeyException zgłaszany jest obiekt .

Po dołączeniu zestawu IEnumerable obiektów element jest zgłaszany, DuplicateKeyException gdy istnieje już istniejący klucz. Pozostałe obiekty nie są dołączone.

Zobacz też