Dela via


Datahämtnings- och CUD-åtgärder i n-nivåprogram (LINQ till SQL)

När du serialiserar entitetsobjekt som Kunder eller Beställningar till en klient via ett nätverk kopplas dessa entiteter från deras datakontext. Datakontexten spårar inte längre deras ändringar eller deras associationer med andra objekt. Det här är inte ett problem så länge klienterna bara läser data. Det är också relativt enkelt att göra det möjligt för klienter att lägga till nya rader i en databas. Men om ditt program kräver att klienterna ska kunna uppdatera eller ta bort data måste du koppla entiteterna till en ny datakontext innan du anropar DataContext.SubmitChanges. Om du använder en optimistisk samtidighetskontroll med ursprungliga värden behöver du också ett sätt att ange databasen både den ursprungliga entiteten och entiteten som ändrad. Metoderna Attach tillhandahålls så att du kan placera entiteter i en ny datakontext när de har kopplats från.

Även om du serialiserar proxyobjekt i stället för LINQ till SQL-entiteter måste du fortfarande konstruera en entitet på dataåtkomstlagret (DAL) och koppla den till en ny System.Data.Linq.DataContextför att kunna skicka data till databasen.

LINQ till SQL är helt likgiltigt för hur entiteter serialiseras. Mer information om hur du använder verktygen Object Relational Designer och SQLMetal för att generera klasser som kan serialiseras med hjälp av Windows Communication Foundation (WCF) finns i How to: Make Entities Serializable (Gör entiteter serialiserbara).

Kommentar

Anropa endast metoderna för Attach nya eller deserialiserade entiteter. Det enda sättet för en entitet att kopplas från sin ursprungliga datakontext är att den serialiseras. Om du försöker koppla en oåtkomlig entitet till en ny datakontext, och den entiteten fortfarande har uppskjutna inläsare från sin tidigare datakontext, utlöser LINQ till SQL ett undantag. En entitet med uppskjutna inläsare från två olika datakontexter kan orsaka oönskade resultat när du utför åtgärder för att infoga, uppdatera och ta bort på den entiteten. Mer information om uppskjutna inläsare finns i Uppskjuten kontra Omedelbar inläsning.

Hämta data

Anrop till klientmetod

I följande exempel visas ett exempelmetodanrop till DAL från en Windows Forms-klient. I det här exemplet implementeras DAL som ett Windows-tjänstbibliotek:

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.
    }

Implementering på mellannivå

I följande exempel visas en implementering av gränssnittsmetoden på mellannivån. Följande är de två viktigaste punkterna att notera:

  • DataContext Deklareras i metodomfånget.
  • Metoden returnerar en IEnumerable samling av de faktiska resultaten. Serialiseraren kör frågan för att skicka tillbaka resultatet till klient-/presentationsnivån. Om du vill komma åt frågeresultatet lokalt på mellannivån kan du framtvinga körning genom att anropa ToList eller ToArray på frågevariabeln. Du kan sedan returnera listan eller matrisen som en IEnumerable.
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();
}

En instans av en datakontext bör ha en livslängd på en "arbetsenhet". I en löst kopplad miljö är en arbetsenhet vanligtvis liten, kanske en optimistisk transaktion, inklusive ett enda anrop till SubmitChanges. Därför skapas och tas datakontexten bort i metodomfånget. Om arbetsenheten innehåller anrop till affärsreglers logik vill du vanligtvis behålla instansen DataContext för hela åtgärden. I vilket fall DataContext som helst är instanser inte avsedda att hållas vid liv under långa tidsperioder över godtyckliga antal transaktioner.

Den här metoden returnerar Produktobjekt men inte samlingen med Order_Detail objekt som är associerade med varje produkt. Använd - DataLoadOptions objektet för att ändra det här standardbeteendet. Mer information finns i Så här: Kontrollera hur mycket relaterade data som hämtas.

Infoga data

Om du vill infoga ett nytt objekt anropar presentationsnivån bara den relevanta metoden i mellannivågränssnittet och skickar det nya objektet som ska infogas. I vissa fall kan det vara mer effektivt för klienten att bara skicka in vissa värden och låta mellannivån konstruera det fullständiga objektet.

Implementering på mellannivå

På mellannivån skapas en ny DataContext , objektet kopplas till DataContext med hjälp InsertOnSubmit av metoden och objektet infogas när SubmitChanges anropas. Undantag, återanrop och felvillkor kan hanteras precis som i andra webbtjänstscenarion.

' 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();
    }

Ta bort data

Om du vill ta bort ett befintligt objekt från databasen anropar presentationsnivån den relevanta metoden i mellannivågränssnittet och skickar i sin kopia som innehåller ursprungliga värden för objektet som ska tas bort.

Borttagningsåtgärder omfattar optimistiska samtidighetskontroller, och det objekt som ska tas bort måste först kopplas till den nya datakontexten. I det här exemplet är parametern Boolean inställd false på för att indikera att objektet inte har någon tidsstämpel (RowVersion). Om databastabellen genererar tidsstämplar för varje post är samtidighetskontroller mycket enklare, särskilt för klienten. Skicka bara in antingen det ursprungliga eller ändrade objektet och ange parametern Boolean till true. I vilket fall som helst är det normalt nödvändigt att fånga ChangeConflictException. Mer information om hur du hanterar optimistiska samtidighetskonflikter finns i Optimistisk samtidighet: Översikt.

När du tar bort entiteter som har sekundärnyckelbegränsningar för associerade tabeller måste du först ta bort alla objekt i dess EntitySet<TEntity> samlingar.

' 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).
    }
}

Uppdatera data

LINQ till SQL stöder uppdateringar i dessa scenarier med optimistisk samtidighet:

  • Optimistisk samtidighet baserat på tidsstämplar eller RowVersion-tal.
  • Optimistisk samtidighet baserat på ursprungliga värden för en delmängd av entitetsegenskaper.
  • Optimistisk samtidighet baserat på de fullständiga ursprungliga och ändrade entiteterna.

Du kan också utföra uppdateringar eller borttagningar på en entitet tillsammans med dess relationer, till exempel en kund och en samling av dess associerade Order-objekt. När du gör ändringar på klienten i ett diagram över entitetsobjekt och deras underordnade (EntitySet) samlingar, och de optimistiska samtidighetskontrollerna kräver ursprungliga värden, måste klienten ange de ursprungliga värdena för varje entitet och EntitySet<TEntity> objekt. Om du vill göra det möjligt för klienter att göra en uppsättning relaterade uppdateringar, borttagningar och infogningar i ett enda metodanrop måste du ge klienten ett sätt att ange vilken typ av åtgärd som ska utföras på varje entitet. På mellannivån måste du anropa lämplig Attach metod och sedan InsertOnSubmit, , DeleteAllOnSubmiteller InsertOnSubmit (utan Attach, för infogningar) för varje entitet innan du anropar SubmitChanges. Hämta inte data från databasen som ett sätt att hämta ursprungliga värden innan du provar uppdateringar.

Mer information om optimistisk samtidighet finns i Optimistisk samtidighet: Översikt. Detaljerad information om hur du löser optimistiska samtidighetsändringskonflikter finns i Så här hanterar du ändringskonflikter.

Följande exempel visar varje scenario:

Optimistisk samtidighet med tidsstämplar

' 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).
}

Med delmängd av ursprungliga värden

I den här metoden returnerar klienten det fullständiga serialiserade objektet tillsammans med de värden som ska ändras.

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).
        }
    }
}

Med fullständiga entiteter

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)/
        }
    }
}

Om du vill uppdatera en samling anropar AttachAll du i stället Attachför .

Förväntade entitetsmedlemmar

Som tidigare nämnts måste endast vissa medlemmar i entitetsobjektet anges innan du anropar Attach metoderna. Entitetsmedlemmar som måste anges måste uppfylla följande kriterier:

  • Vara en del av entitetens identitet.
  • Förväntas ändras.
  • Vara en tidsstämpel eller ha dess UpdateCheck attribut inställt på något annat Neverän .

Om en tabell använder en tidsstämpel eller ett versionsnummer för en optimistisk samtidighetskontroll måste du ange dessa medlemmar innan du anropar Attach. En medlem är dedikerad för optimistisk samtidighetskontroll när IsVersion egenskapen är inställd på true för det kolumnattributet. Alla begärda uppdateringar skickas endast om versionsnumret eller tidsstämpelvärdena är desamma i databasen.

En medlem används också i den optimistiska samtidighetskontrollen så länge medlemmen inte har UpdateCheck angett till Never. Standardvärdet är Always om inget annat värde har angetts.

Om någon av dessa obligatoriska medlemmar saknas utlöses en ChangeConflictException under SubmitChanges ("Rad hittades inte eller ändrades").

Tillstånd

När ett entitetsobjekt har kopplats till instansen DataContext anses objektet vara i PossiblyModified tillståndet . Det finns tre sätt att tvinga ett bifogat objekt att betraktas som Modified.

  1. Bifoga den som oförändrad och ändra sedan fälten direkt.

  2. Bifoga den med den Attach överlagring som tar aktuella och ursprungliga objektinstanser. Detta ger ändringsspåraren gamla och nya värden så att den automatiskt vet vilka fält som har ändrats.

  3. Bifoga den med överlagringen Attach som tar en andra boolesk parameter (inställd på true). Detta talar om för ändringsspåraren att överväga objektet som ändrats utan att behöva ange några ursprungliga värden. I den här metoden måste objektet ha ett fält för version/tidsstämpel.

Mer information finns i Objekttillstånd och Ändringsspårning.

Om ett entitetsobjekt redan inträffar i ID-cachen med samma identitet som objektet som bifogas genereras en DuplicateKeyException .

När du ansluter med en IEnumerable uppsättning objekt genereras en DuplicateKeyException när en redan befintlig nyckel finns. Återstående objekt är inte anslutna.

Se även