Gegevens ophalen en CUD-bewerkingen in n-tier-toepassingen (LINQ naar SQL)
Wanneer u entiteitsobjecten, zoals Klanten of Orders, serialiseert naar een client via een netwerk, worden deze entiteiten losgekoppeld van hun gegevenscontext. De gegevenscontext houdt de wijzigingen of koppelingen met andere objecten niet meer bij. Dit is geen probleem zolang de clients alleen de gegevens lezen. Het is ook relatief eenvoudig om clients in staat te stellen nieuwe rijen aan een database toe te voegen. Als uw toepassing echter vereist dat clients gegevens kunnen bijwerken of verwijderen, moet u de entiteiten koppelen aan een nieuwe gegevenscontext voordat u aanroept DataContext.SubmitChanges. Als u bovendien een optimistische gelijktijdigheidscontrole met oorspronkelijke waarden gebruikt, hebt u ook een manier nodig om de database zowel de oorspronkelijke entiteit als de entiteit als de gewijzigde entiteit op te geven. De Attach
methoden zijn beschikbaar om u in staat te stellen entiteiten in een nieuwe gegevenscontext te plaatsen nadat ze zijn losgekoppeld.
Zelfs als u proxyobjecten serialiseert in plaats van de LINQ naar SQL-entiteiten, moet u nog steeds een entiteit maken op de gegevenstoegangslaag (DAL) en deze koppelen aan een nieuwe System.Data.Linq.DataContext, om de gegevens naar de database te verzenden.
LINQ naar SQL is volledig ongedifferentieerd over hoe entiteiten worden geserialiseerd. Zie How to: Entities Serializable (Entities Serializable) voor meer informatie over het gebruik van de hulpprogramma's Object Relational Designer en SQLMetal om klassen te genereren die serializeerbaar zijn met behulp van Windows Communication Foundation (WCF).
Notitie
Roep alleen de Attach
methoden aan voor nieuwe of gedeserialiseerde entiteiten. De enige manier om een entiteit los te koppelen van de oorspronkelijke gegevenscontext, is dat deze moet worden geserialiseerd. Als u een niet-gekoppelde entiteit probeert te koppelen aan een nieuwe gegevenscontext en die entiteit nog steeds uitgestelde loaders uit de vorige gegevenscontext heeft, genereert LINQ naar SQL een uitzondering. Een entiteit met uitgestelde laadfuncties uit twee verschillende gegevenscontexten kan ongewenste resultaten veroorzaken wanneer u invoeg-, update- en verwijderbewerkingen op die entiteit uitvoert. Zie Deferred versus Direct laden voor meer informatie over uitgestelde laadfuncties.
Gegevens ophalen
Clientmethode-aanroep
In de volgende voorbeelden ziet u een voorbeeldmethode-aanroep naar de DAL vanuit een Windows Forms-client. In dit voorbeeld wordt de DAL geïmplementeerd als een Windows-servicebibliotheek:
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.
}
Implementatie van middelste laag
In het volgende voorbeeld ziet u een implementatie van de interfacemethode op de middelste laag. Hier volgen de twee belangrijkste punten die u moet noteren:
- De DataContext wordt gedeclareerd op methodebereik.
- De methode retourneert een IEnumerable verzameling van de werkelijke resultaten. De serializer voert de query uit om de resultaten terug te sturen naar de client-/presentatielaag. Als u de queryresultaten lokaal wilt openen op de middelste laag, kunt u de uitvoering afdwingen door de queryvariabele aan te roepen
ToList
ofToArray
op te geven. Vervolgens kunt u die lijst of matrix retourneren als eenIEnumerable
.
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();
}
Een exemplaar van een gegevenscontext moet een levensduur hebben van één 'werkeenheid'. In een losjes gekoppelde omgeving is een werkeenheid meestal klein, misschien één optimistische transactie, inclusief één aanroep naar SubmitChanges
. Daarom wordt de gegevenscontext gemaakt en verwijderd op het methodebereik. Als de werkeenheid aanroepen naar bedrijfsregelslogica bevat, wilt u over het algemeen het DataContext
exemplaar voor die hele bewerking behouden. In ieder geval DataContext
zijn exemplaren niet bedoeld om gedurende lange tijd in leven te worden gehouden voor willekeurige aantallen transacties.
Met deze methode worden productobjecten geretourneerd, maar niet de verzameling Order_Detail objecten die aan elk product zijn gekoppeld. Gebruik het DataLoadOptions object om dit standaardgedrag te wijzigen. Zie Procedure: Bepalen hoeveel gerelateerde gegevens worden opgehaald voor meer informatie.
Gegevens invoegen
Als u een nieuw object wilt invoegen, roept de presentatielaag alleen de relevante methode aan op de interface van de middelste laag en geeft het nieuwe object door dat moet worden ingevoegd. In sommige gevallen kan het efficiënter zijn voor de client om slechts enkele waarden door te geven en de middelste laag het volledige object te maken.
Implementatie van middelste laag
Op de middelste laag wordt een nieuw DataContext object gemaakt, het object wordt aan de DataContext methode InsertOnSubmit gekoppeld en het object wordt ingevoegd wanneer SubmitChanges het wordt aangeroepen. Uitzonderingen, callbacks en foutvoorwaarden kunnen net als in elk ander webservicescenario worden verwerkt.
' 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();
}
Gegevens verwijderen
Als u een bestaand object uit de database wilt verwijderen, roept de presentatielaag de relevante methode aan op de interface van de middelste laag en geeft u de kopie door die de oorspronkelijke waarden van het object bevat die moeten worden verwijderd.
Verwijderingsbewerkingen omvatten optimistische gelijktijdigheidscontroles en het object dat moet worden verwijderd, moet eerst worden gekoppeld aan de nieuwe gegevenscontext. In dit voorbeeld is de Boolean
parameter ingesteld om aan te false
geven dat het object geen tijdstempel (RowVersion) heeft. Als uw databasetabel tijdstempels genereert voor elke record, zijn gelijktijdigheidscontroles veel eenvoudiger, met name voor de client. Geef het oorspronkelijke of gewijzigde object door en stel de Boolean
parameter in op true
. In ieder geval is het op de middelste laag meestal nodig om de ChangeConflictException. Zie Optimistische gelijktijdigheid: Overzicht voor meer informatie over het afhandelen van optimistische gelijktijdigheidsconflicten.
Wanneer u entiteiten met beperkingen voor refererende sleutels voor gekoppelde tabellen verwijdert, moet u eerst alle objecten in EntitySet<TEntity> de verzamelingen verwijderen.
' 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).
}
}
Gegevens bijwerken
LINQ naar SQL ondersteunt updates in deze scenario's met optimistische gelijktijdigheid:
- Optimistische gelijktijdigheid op basis van tijdstempels of RowVersion-getallen.
- Optimistische gelijktijdigheid op basis van oorspronkelijke waarden van een subset van entiteitseigenschappen.
- Optimistische gelijktijdigheid op basis van de volledige oorspronkelijke en gewijzigde entiteiten.
U kunt ook updates of verwijderingen uitvoeren op een entiteit samen met de bijbehorende relaties, bijvoorbeeld een klant en een verzameling bijbehorende orderobjecten. Wanneer u wijzigingen aanbrengt op de client in een grafiek met entiteitsobjecten en hun onderliggende (EntitySet
) verzamelingen, en de optimistische gelijktijdigheidscontroles oorspronkelijke waarden vereisen, moet de client die oorspronkelijke waarden opgeven voor elke entiteit en EntitySet<TEntity> elk object. Als u clients in staat wilt stellen een set gerelateerde updates, verwijderingen en invoegingen te maken in één methode-aanroep, moet u de client een manier bieden om aan te geven welk type bewerking moet worden uitgevoerd op elke entiteit. Op de middelste laag moet u vervolgens de juiste Attach methode aanroepen en vervolgens InsertOnSubmit, DeleteAllOnSubmitof InsertOnSubmit (zonder Attach
, voor invoegingen) voor elke entiteit voordat u aanroept SubmitChanges. Haal geen gegevens op uit de database als een manier om oorspronkelijke waarden te verkrijgen voordat u updates probeert uit te voeren.
Zie Optimistische gelijktijdigheid voor meer informatie over optimistische gelijktijdigheid : Overzicht. Zie How to: Manage Change Conflicts (Wijzigingenconflicten beheren) voor gedetailleerde informatie over het oplossen van optimistische gelijktijdigheidswijzigingsconflicten.
In de volgende voorbeelden ziet u elk scenario:
Optimistische gelijktijdigheid met tijdstempels
' 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).
}
Met subset van oorspronkelijke waarden
In deze benadering retourneert de client het volledige geserialiseerde object, samen met de waarden die moeten worden gewijzigd.
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).
}
}
}
Met volledige entiteiten
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)/
}
}
}
Als u een verzameling wilt bijwerken, roept AttachAll u in plaats van Attach
.
Verwachte entiteitsleden
Zoals eerder vermeld, moeten alleen bepaalde leden van het entiteitsobject worden ingesteld voordat u de Attach
methoden aanroept. Entiteitsleden die moeten worden ingesteld, moeten voldoen aan de volgende criteria:
- Maak deel uit van de identiteit van de entiteit.
- Naar verwachting moet worden gewijzigd.
- Wees een tijdstempel of laat het UpdateCheck bijbehorende kenmerk naast iets ingesteld
Never
.
Als een tabel een tijdstempel of versienummer gebruikt voor een optimistische gelijktijdigheidscontrole, moet u deze leden instellen voordat u aanroept Attach. Een lid is toegewezen voor optimistische gelijktijdigheidscontrole wanneer de IsVersion eigenschap is ingesteld op waar op dat kolomkenmerk. Aangevraagde updates worden alleen verzonden als het versienummer of de tijdstempelwaarden hetzelfde zijn in de database.
Een lid wordt ook gebruikt in de optimistische gelijktijdigheidscontrole zolang het lid niet is UpdateCheck ingesteld op Never
. De standaardwaarde is Always
als er geen andere waarde is opgegeven.
Als een van deze vereiste leden ontbreekt, wordt er een ChangeConflictException gegenereerd tijdens SubmitChanges ('Rij niet gevonden of gewijzigd').
Provincie
Nadat een entiteitsobject is gekoppeld aan het DataContext exemplaar, wordt het object beschouwd als de PossiblyModified
status. Er zijn drie manieren om af te dwingen dat een bijgevoegd object wordt beschouwd Modified
.
Voeg deze als ongewijzigd toe en wijzig de velden vervolgens rechtstreeks.
Koppel deze aan de Attach overbelasting die huidige en oorspronkelijke objectexemplaren gebruikt. Hiermee wordt de wijzigingstracker geleverd met oude en nieuwe waarden, zodat deze automatisch weet welke velden zijn gewijzigd.
Koppel deze aan de Attach overbelasting die een tweede Booleaanse parameter gebruikt (ingesteld op true). Dit vertelt de wijzigingstracker dat het object moet worden gewijzigd zonder dat er oorspronkelijke waarden hoeven te worden opgegeven. In deze benadering moet het object een versie-/tijdstempelveld hebben.
Zie Objectstatussen en Wijzigingen bijhouden voor meer informatie.
Als een entiteitsobject al voorkomt in de id-cache met dezelfde identiteit als het object dat wordt gekoppeld, wordt er een DuplicateKeyException gegenereerd.
Wanneer u bijvoegt met een IEnumerable
set objecten, wordt er een DuplicateKeyException gegenereerd wanneer er een al bestaande sleutel aanwezig is. Resterende objecten zijn niet gekoppeld.