LI En NQ nter to S rpris SQL se‐A für Appl likat tion n

Werbung
 LINQ to SSQL für nterrprisse‐A
Appllikattion
n En
Abstra
act: Anhand
d eines einfa
fachen Enterrprise­Anw
wendungssze
enarios wird eevaluiert, ob
b und inwie
eweit LINQ tto SQL sich ffür den Einsatz in solcheen Anwendu
ungstypen e
eignet. Hierf
rfür beleuch
hten wir zun
nächst die grund
dsätzlichen A
Anforderun
ngen solcheer Architektturen, stelle
en dann die Featurres von LIN
NQ to SQL vo
or und erörttern danach
h deren Anw
wendung. Die Errgebnisse zeeigen, dass das rigide O
Object­Traccking­Mode
ell von LINQ
Q to SQL
L bei prozesssübergreife
fendem Dateenaustauscch nicht flexxibel genug ist. Diees verursaccht insbeson
ndere bei zu
ustandsloseen Anwen
ndungsumg
gebungen (zz. B. ASP.NE
ET, WCF­Serrvices) Prob
bleme. Die in
n Visuall Studio geb
botene Toollunterstützu
ung ist noch
h nicht auf a
alle Design
nansätze an
nwendbar. In Bezug au
uf das reinee O/R­Mapp
ping leistet LINQ tto SQL allerrdings gute Dienste. TTill Rebenich Seenior eXpert SDX AG Bo
orsigallee 19 6038
88 Frankfurt www
w.sdx‐ag.de +49 (69) 24 75 18 ‐ 0 1 Ein
nleitung Dieser A
Artikel soll an
nhand eines eeinfachen An
nwendungsszenarios im Enterprise‐U
Umfeld die Vor‐ und Nachteile des Einsatzes von LINQ
Q to SQL aufzzeigen. Hierffür gehen wirr zunächst au
uf die grundleggenden Anfo
orderungen ssolcher Appliikationen ein
n, stellen dan
nn die Features von LINQ
Q to SQL im Einzeelnen vor und
d setzen diesse im genann
nten Kontextt ein. 1.1 Anwendun
A
ngsszenarrio Enterprise‐Applikatio
onen sind geewöhnlich schichtenorien
ntiert. Hierfü
ür wird jede FFunktionalitäät zunächstt kategorisieert und dann einer von no
ormalerweisse drei logischen Schichteen (Layer) zugeordnet: Presenttation‐Layer, Business‐Layer, und Datta‐Layer. In d
den meisten Fällen wird man hier Design anwen
nden, indem man zunäch
hst Contractss in Form von
n außerdeem ein Contract‐Driven D
Interfacees erstellt un
nd danach mittels Objectt‐Factory Insttanzen der SSchnittstellen
nimplementierung erzeugt. Diese Vorgeehensweise eermöglicht die parallele EEntwicklung jeder einzelnen Schicht, ohne Architektur isst auch notw
wendig, wenn
n dass Entwickler sich gegenseitig blockieren. EEine solche A
Anwendungen für diie Verteilungg auf mehrerre physikaliscche Schichten (Tiers) entw
wickelt werd
den. Bei der V
Verteilung m
müssen die veerwendeten Datenobjektte über Prozeessgrenzen h
hinweg zwiscchen den physikalischen Schichten ausgetaauscht werden, was dere
en (De‐)Seriaalisierung erfforderlich maacht. müssen vorab Software‐D
Designentsch
heidungen getroffen werrden (1): Zudem m
1. Business‐Entitiess werden als Data‐Trransfer‐Objeccts eingesetzt, d. h. ssie enthalte
en keine nd nur einfacche oder kein
ne Assoziatio
onen zu andeeren Entities. Businesslogik un
den so präp
pariert, dass sie über einen e
WCF‐SService veröffentlicht 2. Conttracts (Interrfaces) werd
werd
den können. 3. Die B
Businesslogik wird als Traansaction‐Skkript in die Scchnittstellenimplementieerung ausgellagert. Alternativ kann im Business‐Layeer auch ein D
Domain Model zum Einsaatz kommen (1). Dann mü
üssen Data‐Transfeer‐Objects beereitgestellt u
und die Dateen der Domaain‐Objects vor der allerdinggs separate D
Verwend
dung über W
WCF‐Servicescchnittstellen
n auf diese ge
emappt werd
den. 1.2 Objektrela
O
ationales M
Mapping Ein zentrraler Aspekt bei der Konzzeption einer Anwendun
ng ist das Perrsistieren von
n objektorien
ntierten Datenstrrukturen, gew
wöhnlich in ein relationaales Datenbaankmanagem
mentsystem. Verfolgt man die oben genannte Strattegie, Dateneentitäten mö
öglichst einfaach zu gestallten, hat man
n damit gewöhnlich del, dessen SStruktur sich nicht ohne w
weiteres weniger Probleme als bei einem komplexen Domain Mod
uf Tabellen aabbilden lässt. Unabhänggig von der ge
ewählten Strrategie findeen fast imme
er direkt au
entsprecchende Transsformationen statt. Prob
bleme treten dann meisteens in folgen
nden Situatio
onen auf: •
•
•
Kom
mplexe Vererbungshierarcchien Valu
ue‐Objects, d
d. h. Objektee, die nur im
m Kontext der sie referen
nzierenden O
Objekte Sinn
n machen und normalerweeise keinen P
Primärschlüsssel enthalten
n über Collections N:m‐Beziehungeen, meistens abgebildet ü
Till Rebeenich Seite 2 von 17 2 LIN
NQ to SQL
L Mit der n
neuesten Veersion (3.5) d
des .NET‐Fram
meworks wu
urde erstmalss LINQ (Langguage Integraated Query) eeingeführt. LINQ selbst isst nichts andeeres als eine
e Abfragespraache, die direkt in die verwend
dete Program
mmiersprache (C#, VB…) integriert istt. Extension Methods, An
nonyme Type
en, Lambda Expressions und Expresssion Trees bilden die Gru
undlage dieseer Erweiterung. Mithilfe von d der Interfaces IQuery
yable und I
IQueryable
e<T> lassen sich außerdem Expression Trees und
beliebige Dattenformate d
definieren. LINQ to Objeccts wendet d
dieses Prinzip
p auf Query‐Provider für b
erable‐Objeekte an, LINQ
Q to XML auff XML‐Daten und LINQ to
o SQL auf relationale IEnume
Datenbaanken. Der Vorteil dieser Strategie istt, dass der Zu
ugriff auf Info
ormationen über eine einheitliche Abfragessprache erfo
olgt, die unab
bhängig vom
m Datenformaat ist. Im Fall vvon LINQ to SSQL erzeugt d
der Query‐Provider im Hintergrund eein entsprech
hendes SELECT‐
Statemeent, das dann
n auf der Dattenbank ausggeführt wird. Die Ergebnisse der Abfrrage werden
n als IEnume
erable‐Objeekt zurückgeegeben. LINQ
Q to SQL gehtt noch einen Schritt weitter, denn neb
ben der genannten Möglichkkeit, LINQ alss Abfragespraache zu verw
wenden, werden folgende Features bereitgeestellt (1): •
•
•
•
•
Objeektrelationales Mappingg: Entityklassengenerieru
ung zur Lau
ufzeit als an
nonyme Typ
pen oder explizites Mapping von Entityklassen übeer O/R‐Desiggner oading (verzö
ögertes Nach
hladen von Instanzen asssoziierter Entities) Defeerred/Lazy Lo
Chan
nge Trackingg Conccurrency Cheecks Zenttrales Persisttenzmanagem
ment 2.1 Datenkont
D
text und o
objektrela
ationales Mapping Die zentrale Kompon
nente von LIN
NQ to SQL isst der Data C
Context. Dieser fungiert aals Zugriffspu
unkt auf he Funktionen, die LINQ tto SQL bereittstellt. Listing 1 verdeutlicht diesen A
Aspekt. sämtlich
DataCon
ntext ctx = new Data
aContext(“
“Server=(lo
ocal);Init
tial Catalo
og= “ +
“
“Adventure
eWorks;Inte
egrated Se
ecurity=Tr
rue”);
var que
ery = (fro
om x in ctx
x.GetTable
e<Customer>
>() where
x.Sa
alesTerrit
tory.Countr
ryRegionCo
ode == “US”
” select x
x);
h (var cus
st in query
y)
foreach
{
C
Console.Wr
riteLine(“I
ID = {0}, CountryCode = {1}”,
, cust.Cus
stomerID,
cust
t.SalesTer
rritory.Cou
untryRegio
onCode);
}
Listing 1
1: Zugriff auf relationale Daten mit LINQ Man beaachte, dass d
die SELECT‐Anfrage erst b
bei der Iterattion in der fo
oreach‐Sch
hleife auf derr Datenbaank ausgefüh
hrt wird, nich
ht etwa schon bei der Definition des LLINQ‐Query.. Liegt dem K
Kontext kein kon
nkretes Typ‐M
Mapping vorr, werden ano
onyme Typen für die anggeforderten Entities und deren Abhängigkeiten erzeeugt. Da dies erst zur Laufzeit geschie
eht, muss maan hier mit dem var‐
Schlüsseelwort arbeiten. Till Rebeenich Seite 3 von 17 Besser isst es, einen D
Datenkontexxt mit starkem
m Typkonzep
pt zu erzeugeen. Dies ist aallerdings nur möglich,, wenn die Datenbankentitäten, überr die Abfrage
en erfolgen, im Vorfeld ggegen entsprechende Entityklaassen gemap
ppt werden u
und eine Spezialisierung vvon DataCo
ontext für d
die angespro
ochene Datenbaank definiert wird. LINQ to SQL unterstü
ützt dabei grrundsätzlich SSingle Table Inheritance.. Ist eine Verrerbungshierrarchie über meehrere Tabelllen verteilt, m
muss eine Sicht eingesetzt werden.
2.1.1 Attribut­ba
A
asiertes Ma
apping Eine Möglichkeit ist, das Typ‐Mapping direkt im Quellcod
de vorzunehm
men, indem die Klassenm
member mit entsprechenden Attributen vversehen weerden. Listingg 2 liefert hieerfür ein Beisspiel. [Databa
ase(Name = "Adventur
reWorks")]
public class Adv
ventureWork
ksContext : DataCont
text
{
p
private
st
tatic Mappi
ingSource _mappingSource = ne
ew
Attr
ributeMapp
pingSource(
();
p
public
Adv
ventureWork
ksContext(
(string connection)
: ba
ase(connec
ction, _map
ppingSourc
ce)
{
}
public Tab
p
ble<Custome
er> Custom
mers
{
get { this.Ge
etTable<Cus
stomer>(); }
}
public Tab
p
ble<Custome
erAddress>
> Addresses
{
get { this.Ge
etTable<Cus
stomerAddr
ress>(); }
}
}
[Table(
(Name = "S
Sales.Custo
omer")]
public class Cus
stomer
{
p
private
in
nt _custome
erID;
p
private
st
tring _acco
ountNumber
r;
p
private
En
ntitySet<Cu
ustomerAdd
dress> _ad
ddresses;
[Column(Na
[
ame = "Cust
tomerID", Storage = "_custome
erID", DbT
Type = "Int
t",
CanB
BeNull = false,
f
IsPr
rimaryKey = true)]
p
public
int
t CustomerI
ID
{
get { return _customerI
ID; }
set { _custom
merID = val
lue; }
}
[Column(Na
[
ame = "Acco
ountNumber
r", Storag
ge = "_acco
ountNumber
r",
DbTy
ype = "Var
rChar(50)")
)]
p
public
str
ring Accoun
ntNumber
{
get { return _accountNu
umber; }
set { _accoun
ntNumber = value; }
}
Till Rebeenich Seite 4 von 17 [Associati
[
ion(Name = "Customer
r_Addresses", Storag
ge = "_add
dresses",
Othe
erKey = "_
_customerID
D")]
p
public
Ent
titySet<Cus
stomerAddr
ress> Addr
resses
{
get { return _addresses
s; }
set { _addres
sses.Assign
n(value); }
}
p
public
Cus
stomer() { }
}
(Name = "S
Sales.Custo
omerAddres
ss")]
[Table(
public class Cus
stomerAddre
ess
{
[
[Column(Na
ame = "Cust
tomerID", Storage = "_custome
erID",
DbTy
ype = "Int
t")]
p
private
in
nt _custome
erID;
p
private
in
nt _address
sID;
p
private
En
ntityRef<Cu
ustomer> _customer;
_
[Column(Na
[
ame = "Addr
ressID", Storage
S
= "_addressI
ID", DbTyp
pe = "Int",
,
IsPr
rimaryKey = true, Ca
anBeNull = false)]
p
public
int
t AddressID
D
{
get { return _addressID
D; }
ssID = valu
ue; }
set { _addres
}
[Associati
[
ion(Name = "Customer
r_Addresses", Storag
ge = "_cus
stomer",
This
sKey = "_c
customerID"
", IsForei
ignKey = tr
rue)]
p
public
Cus
stomer Cust
tomer
{
get { return _customer.
.Entity; }
set { _custom
mer.Entity = value; }
}
p
public
Cus
stomerAddre
ess() { }
}
Listing 2
2: Mapping m
mithilfe von A
Attributen im
m Quelltext
Der Nach
hteil dieses A
Ansatzes ist, dass Änderu
ungen in der Datenbank eine Neukom
mpilierung der gesamteen Solution n
notwendig m
machen, denn
n die Mappin
ng‐Informatio
onen sind en
ng mit dem C
Code verwobeen. Bei exten
nsiven Änderrungen komm
mt es dann schnell zu sch
hwer nachvo
ollziehbaren Laufzeitffehlern. Till Rebeenich Seite 5 von 17 2.1.2 XML­basier
X
rtes Mappin
ng Alternativ können die für das Maapping benöttigten Metad
daten vom C
Code getrenn
nt gehalten w
werden, m Einsatz kommt. Der Daata‐Context eerhält diese Information dann in indem ein XML‐Mapping‐File zum
nes XmlMapp
pingSourc
ce‐Parameteers. Für das in
n Listing 2 geezeigte Beisp
piel sähe ein solches Form ein
Mappingg‐File folgend
dermaßen au
us: <?xml version="1
v
1.0" encodi
ing="utf-8
8"?>
<Databa
ase Name="AdventureW
Works"
x
xmlns="htt
tp://schema
as.microso
oft.com/linqtosql/ma
apping/200
07">
<
<Table
Nam
me="Sales.C
Customer" Member="Customers">
>
<Typ
pe Name="C
Customer">
<Column
n Name="Cus
stomerID" Member="CustomerID"
"
S
Storage="_
_customerID
D" DbType=
="INT NOT N
NULL"
I
IsPrimaryK
Key="true" CanBeNull
l="false" /
/>
<Column
n Name="Acc
countNumbe
er" Member="AccountN
Number"
S
Storage="_
_accountNum
mber" DbTy
ype="VarCha
ar(50)" />
>
<Column
n Name="Typ
pe" Member
r="Type" Storage="_T
Type"
D
DbType="Un
niqueIdenti
ifier" />
<Associ
iation Name
e="Custome
er_Address" Member="
"Addresses
s"
S
Storage="_
_addresses"
" OtherKey
y="_custome
erID"/>
</Ty
ype>
<
</Table>
<
<Table
Nam
me="Sales.C
CustomerAd
ddress" Member="Addr
resses">
<Typ
pe Name="C
CustomerAdd
dress">
<Column
n Name="Cus
stomerID" Member="_
_customerID
D"
S
Storage="_
_customerID
D" Dbtype=
="INT NOT N
NULL"
C
CanBeNull=
="false" />
>
<Column
n Name="Add
dressID" Member="Ad
M
ddressID"
S
Storage="_
_addressID"
" DbType="
"Int" />
<Associ
iation Name
e="Custome
er_Address" Member="
"Customer"
"
S
Storage="_
_customer" ThisKey="
"_customerI
ID"
I
IsForeignK
Key="true" />
</Ty
ype>
<
</Table>
</Datab
base>
Listing 3
3: Mapping m
mithilfe von X
XML Die sepaarate Haltungg von Datenb
bindungsmettadaten maccht den Codee zwar lesbarrer, allerdinggs müssen Schemaändeerungen häu
ufig an mehreeren Stellen nachgetrageen werden, w
was ebenfallss eine erhöhte Fehleranfälligkeit mit sicch bringt. Das hier vorgestelltee manuelle M
Mapping von Entityklasse
en macht vorr allem dann Sinn, wenn diese hträglich an eeine Datenbaank gebundeen werden so
ollen. Im bereits vvorhanden siind und nach
umgekeh
hrten Fall kö
önnen jedoch
h sowohl die Entityklasse
en als auch das Mapping aus einem vorhandenen Datenb
bankschemaa automatisch generiert w
werden. Hierrfür stehen zzwei Method
den zur ng: Zum eineen bietet sich
h die Verwen
ndung des O/R‐Designerss von Visual Studio 2008 an Verfügun
(siehe Abbildung 1). Über den Seerver‐Exploreer zieht man
n zunächst die gewünschtten Tabellen
n auf die Anhängigkeitten im Daten
nbankschema gepflegt sind, werden Oberfläcche. Wenn reeferentielle A
Assoziationen zwisch
hen den korrrespondieren
nden Entitykklassen autom
matisch ersteellt. Im Hinte
ergrund nklusive der Mapping‐Atttribute. Alteernativ kann man hierfür auch erzeugt der Designerr den Code in
mmandozeilenprogramm „sqlmetal“ vverwenden. Der Aufruf von das Kom
sqlmeta
al.exe /co
ode:Entitie
es.cs /map
p:Mapping.x
xml DataCo
ontext.dbml
ml
Till Rebeenich Seite 6 von 17 auf der V
Visual‐Studio
o‐Kommondo
ozeile erzeuggt den Entityyklassencodee und den Daatenkontext in einer Datei namens „Entities.cs“ und d
das XML‐basiierte Mappin
ng in „Mappiing.xml“. Alss Grundlage d
dient das ml“ definiertee Schema. in „DataContext.dbm
Abbild
dung 1: O/R‐‐Designer in Visual Studiio 2008 2.2 Verzögerte
V
es Nachladen Das „Defferred Loading“, oft auch
h „Lazy Loading“ genannt, ist ein scho
on von andeeren objektreelationalen M
Mappern bekkannter Mech
hanismus zur Performancesteigerungg. Bei 1:n‐
Beziehun
ngen werden
n die Elemen
nte der n‐Meenge erst dan
nn tatsächlich aus der Daatenbank gelladen, wenn siee benötigt werden, d. h. wenn der Geetter des korrrespondiereenden Properties im Container aufgeruffen wird. Diees findet norm
malerweise iintern und vom Aufruferr vollkommen unbemerktt statt. LINQ to SQL implemeentiert diesee Funktion üb
ber die schon
n erwähnte C
Collection‐Klasse ySet<T>, üb
ber die mehrwertige Frem
mdschlüsselb
beziehungen
n abgebildet werden. Dies ist Entity
gleicherm
maßen Vor‐ und Nachteiil, denn einerseits ist man so an die V
Verwendung dieses Colle
ection‐
Typs geb
bunden, d. h. man kann kkeine eigeneen Collection‐Klassen einssetzen, andeererseits musss man sich um die bereits in
ntegrierte „LLazy Loading“‐Funktionallität als Entw
wickler nicht selbst kümm
mern. et<T> und Zum ersttgenannten Punkt muss noch ergänzzt werden, daass es sich beei EntitySe
Entity
yRef<T> um
m finale Klasssen handelt, von denen aalso nicht abggeleitet werd
den kann. In Bezugg auf das in LListing 2 gezeeigte Beispiel würde also ein Aufruf vvon Address
ses der Klassse Custom
mer ein Nach
hladen der m
mit dem Kund
den assoziierrten Adressen in die Colleection vom TTyp Entity
ySet<Custo
omerAddre
ess> auslöseen. Intern wird dabei ein SQL‐Kommaando erstellt und Till Rebeenich Seite 7 von 17 ausgefüh
hrt, welches die abhängigen Zeilen der Tabelle „C
CustomerAddress“ anhan
nd des Spaltenn
namens des iin OtherKey genannten
n Members sselektiert. Alternativ lässt sich d
das verzögerrte Nachladeen auch explizit unterbind
den und man
nuell steuern
n: Adventu
ureWorksDa
ataContext ctx = new
w Adventure
eWorksData
aContext(
“
“Server=(l
local);Init
tial Catal
log= “ +
“
“Adventure
eWorks;Inte
egrated Se
ecurity=Tr
rue”);
ctx.Def
ferredLoad
dingEnabled
d = false;
DataLoa
adOptions options = new DataL
LoadOptions
s();
options
s.LoadWith
h<Customer>
>(c => c.A
Addresses);
;
ctx.Loa
adOptions = options;
;
Listing 4
4: Manuelle SSteuerung des verzögertten Nachlade
ens Listing 4
4 zeigt, wie über Lambda‐‐Ausdrücke u
und in Verbindung mit eiiner DataLo
oadOptions
s‐
Instanz eexplizit festgelegt werden kann, welcche assoziierten Entitäten
n bei der Erzzeugung des Containeerobjektes direkt geladen
n werden sollen. In obige
em Beispiel w
würden also alle mit dem
m Kunden verbundenen Adressobjeekte dann in
n die Collectio
on geladen, wenn auch d
das Kundeno
objekt selbst errzeugt wird. 2.3 Zentrales P
Persisten
nzmanagement Bisher haben wir gessehen, wie D
Daten aus der Datenbankk geladen und
d in objektorrientierte Strrukturen ungen des Ob
bjektes verfo
olgen und die
ese in übersetzzt werden. LIINQ to SQL kkann jedoch aauch Änderu
die Dateenbank zurücckschreiben o
oder neue Datensätze in Tabellen ein
nfügen. Hierbei fungiert der Datenko
ontext wieder als Black Bo
ox, d. h. alle Persistenzoperationen w
werden zentral über eine
e Instanz von Dat
taContext angestoßen. 2.3.1 Change Tra
C
acking Um Änderungen dess Objektzustaandes zu erkennen, werd
den alle überr den Datenkkontext gelad
denen n verwalteten Table<T>
>‐Instanzen zwischengesspeichert. Dies hat folgen
nde Entities iin den intern
Vorteile:: 1. Der Datenkonteext fungiert als Cache für Entity‐In
nstanzen. Errneute Aufru
ufe desselbe
en LINQ‐
n nicht an die Datenbank weite
ergeleitet, sondern die schon im Kontext Queries werden
gesp
peicherten Instanzen werden w
zurrückgegeben
n. Dies ist deshalb möglich, weil w
jede Objeektinstanz eine Identität hat, die über die gemappten Primärsschlüsselattrribute realisiert ist. 2. Ändeerungen derr zwischengeespeicherten
n Objektzustände werdeen zentral deetektiert und
d selektiv zurü
ückgeschrieben, d. h. es werden nur die Daten derjenigen Objekte zurückgeschrie
eben, die auch
h tatsächlich geändert wurden. Hierzu ein kleines Beeispiel, welch
hes ein Cust
tomer‐Objekkt aus der Daatenbank läd
dt, es modifizziert und danach w
wieder speicchert. Die darauf folgenden Zeilen initiieren die Erstellung ein
nes neuen un
nd die Löschung eines vorhandenen Kunden. Custome
er custome
er = (from x in ctx.Customers where x.C
CustomerID = 5
s
select
x).
.FirstOrDef
fault();
if (cus
stomer != null)
{
c
customer.A
AccountNumb
ber = “081
15”;
}
Till Rebeenich Seite 8 von 17 Custome
er newCust
tomer = new
w Customer
r();
newCust
tomer.Acco
ountNumber = “4711”;
ctx.Cus
stomers.In
nsertOnSubm
mit(newCus
stomer);
Custome
er obsolet
teCustomer = (from x in ctx.Cu
ustomers where
w
x.Cus
stomerID = 6
s
select
x).
.FirstOrDef
fault();
if (obs
soleteCust
tomer != nu
ull)
{
c
ctx.Custom
mers.Delete
eOnSubmit(
(obsoleteCustomer);
}
ctx.Sub
bmitChange
es();
Listing 5
5: Zurückschrreiben von Ä
Änderungen in die Daten
nbank Die Ändeerungsverfollgung bedien
nt sich hierbeei zweier untterschiedlich
her Mechanissmen. Implemeentieren die Entityklassen die INoti
ifyPropert
tyChange‐SSchnittstelle, so erwartett der Kontext,, dass nach d
der Zuweisun
ng eines neueen Wertes an eine der Prroperties dass Proper
rtyChanged
d‐Ereignis au
usgelöst wird
d. Andernfalls werden inttern Kopien d
der zwischen
ngespeichertten Entity‐Zu
ustände angeefertigt, die d
dann beim A
Aufruf von Su
ubmitChan
nges() mit dem
m neuen Zustaand verglichen werden.
Über diee von GetCh
hangeSet()
) zurückgegeebene Chang
geSet‐Instaanz kann man feststellen
n, welche Entities ggeändert, geelöscht oder neu eingefügt wurden. V
Vorteilhaft isst, dass alle zzunächst unaabhängig von der Datenbank d
durchgeführtten Änderun
ngen über ein
ne einzige M
Methode perssistiert werde
en anges(). M
Möchte man neue Objekttinstanzen peersistieren, sso erstellt maan können: SubmitCha
nz der Entitykklasse, weist deren Eigen
nschaften diee gewünschten Werte zu und zunächstt eine Instan
übergibtt die Instanz danach an I
InsertOnSu
ubmit() de
er entsprechenden Tabl
le<T>‐Instan
nz. Ähnlich ffunktioniert auch das Löschen vorhandener Entitties, allerdinggs heißt die entsprechen
nde Methodee dann Dele
eteOnSubm
mit(). 2.3.2 Optimistisc
O
ches Sperre
en Die Zwisschenspeicheerung von En
ntityinstanzen ermöglichtt es dem Dattenkontext aauch, ein optimisttisches Sperren durchzuführen. Für diesen Zweckk kann im Maapping ein so
ogenanntes „„Version‐
Propertyy“ definiert w
werden. Gew
wöhnlich würrde man hierr wohl einen DateTime‐‐Wert wähle
en, der den Zeitpunkt der letzten Änderu
ung anzeigt ((Zeitstempell); die Verweendung andeerer Datentyp
pen ist ebenfallss möglich. Dieses Attribu
ut muss gegeen eine entsp
prechende Taabellenspalte gemappt w
werden, die bei jeedem UPDATTE mit der akktuellen Zeit überschrieb
ben wird. Wird ein
ne zuvor aus der Datenbaank geladenee Entityklasse
eninstanz vo
on zwei Benu
utzern gleichzeitig modifizieert, so änderrt sich der W
Wert der Verssionsspalte der korrespon
ndierenden ZZeile in der Datenbaanktabelle, so
obald Benutzzer 1 Submi
itChanges(
() aufruft. K
Kurz darauf w
wird Benutze
er 2 wahrsch
heinlich das ggleiche tun, aallerdings wird diesmal e
eine Exceptio
on geworfen.. Intern hat d
der Datenko
ontext nämlicch einen Verrgleich des Veersionswerte
es durchgefü
ührt und festtgestellt, dasss die Instanz zzwischenzeittlich von Ben
nutzer 1 geän
ndert wurde.. Die Meth
hode Submi
itChanges(
() liegt in zw
wei Überladu
ungen vor. W
Wird sie parameterlos auffgerufen, so wird b
beim Auftretten eines Versionskonflikktes eine Aussnahme erzeeugt. Alternaativ kann man
n aber auch ein
nen Wert derr Conflict
tMode‐Enum
meration als P
Parameter üb
bergeben. Damit gibt maan dem Kontext eine Lösungsstrategie vo
or, die dann im Falle von Versionskon
nflikten ausggeführt wird. Till Rebeenich Seite 9 von 17 2.3.3 Mapping vo
M
on Stored P
Procedures und Functiions Neben TTabellen und Views können auch Storred Procedurres und Funcctions im Dattenkontext ggemappt werden, und zwar als Methoden der spezialisierten Data
aContext‐K
Klasse selbstt. Auch hier b
bedient man sich
h am einfach
hsten des O/R
R‐Designers und zieht die
e Datenbankkobjekte per Drag & Drop
p direkt auf die O
Oberfläche. B
Beim Aufruf dieser Methoden wird dann intern eeine paramettrisierte SQL‐‐Anfrage erstellt u
und auf der D
Datenbank ausgeführt. R
Rückgabewerrte, seien es nun Objektinstanzen von Entityklaassen oder skkalare Wertee, werden eb
benfalls unte
erstützt. Listing 6 zeigt eine Erweiteru
ung des in Listingg 1 gezeigten
n Beispiels. D
Die Methodee SaveCustomer() maappt die gleicchnamige Sto
ored Procedure in der Dattenbank. public class Adv
ventureWork
ksContext : DataCont
text
{
p
private
st
tatic Mappi
ingSource _mappingSource = ne
ew
Attr
ributeMapp
pingSource(
();
p
public
Adv
ventureWork
ksContext(
(string connection)
: ba
ase(connec
ction, _map
ppingSourc
ce)
{
}
/
//... [Function(
[
(Name = "Sa
aveCustome
er", IsCom
mposable = false)]
[
[Parameter
r(Name = "c
customerID
D", DbType = "Int")]
[
[Parameter
r(Name = "a
accountNum
mber", DbType = "Var
rChar(50)"
")]
p
public
int
t SaveCusto
omer(ref int?
i
customerID, str
ring accou
untNumber)
{
IExe
ecuteResul
lt result = ExecuteM
MethodCall(
(this,
(Method
dInfo)Metho
odInfo.Get
tCurrentMethod(),
custome
erID, accou
untNumber)
);
retu
urn ((int)result.Ret
turnValue);
}
}
Listing 6
6: Mapping e
einer Stored Procedure im
m Datenkon
ntext 2.3.4 Steuerung S
von schreib
benden Dattenbankzug
griffen Bisher haben wir unss mit Kontexxten beschäfttigt, in denen
n alle Zugrifffe direkt auf die gemapptten n oder Views in der Daten
nbank erfolggten. Diese V
Vorgehensweeise ist insbessondere bei Tabellen
komplexxeren Anwen
ndungen nich
ht empfehlen
nswert. Statttdessen solltten Datenban
nkzugriffe ge
enerell über Sto
ored Procedu
ures oder Fun
nctions durchgeführt we
erden. LINQ tto SQL ermögglicht die De
efinition benutzerdefinierter Stored Proceedures für scchreibende, nicht jedoch für lesende Datenbankzzugriffe. nn also grund
dsätzlich dass Verhalten d
des Datenkon
ntextes beim
m Aufruf von Man kan
Submit
tChanges()
) beeinflusseen. Soll z. B. die Method
de SaveCust
tomer() in obigem Beisspiel (Listingg 6) generell für alle neue
en und geänderten Objektin
nstanzen aufggerufen werrden, so erste
ellt man zweei Methoden im Datenkontext, die als Parameter ein
ne Instanz deer zu persistiierenden Enttityklasse erw
warten. Inneerhalb dieserr Methodeen ruft man dann die gem
mappte Storred Procedurre auf: Till Rebeenich Seite 1
10 von 17 public void Inse
ertCustomer
r(Customer
r instance)
)
{
i
int?
custo
omerID = ne
ew int?();
;
t
this.SaveC
Customer(re
ef custome
erID, instance.Accou
untNumber);
i
instance.C
CustomerID = custome
erID.GetVa
alueOrDefau
ult();
}
public void Upda
ateCustomer
r(Customer
r instance)
)
{
i
int?
custo
omerID = ne
ew int?(in
nstance.Cu
ustomerID);
;
t
this.SaveC
Customer(re
ef custome
erID, instance.Accou
untNumber);
}
public void Dele
eteCustomer
r(Customer
r instance)
)
{
/
//...
}
Löschende Zugriffe kkönnen eben
nfalls auf diesse Weise kon
nfiguriert weerden. Till Rebeenich Seite 1
11 von 17 3 Praxistest Das in Abschnitt 1.1 erwähnte Anwendungssszenario lässst sich mithilffe der folgen
nden Diagram
mme (Abbildu
ung 2 und Ab
bbildung 3) b
beschreiben.. Man beachtte hierbei diee Besonderh
heiten beim Deploym
ment. Auf dem
m Presentation Tier verw
wenden wir e
eine ASP.NETT Webapplikkation, die im
m IIS betriebeen wird. Diese greift auf eeinen ebenfaalls im IIS lau
ufenden WCFF‐Service zu, der als Busin
ness Tier fungiert.. Der Data Laayer wird wiee dargestellt im gleichen Prozess geh
hostet und grreift über die
e von Microsofft SQL Server bereitgesteellten Protokkolle (z. B. TC
CP) auf die Datenbank zu. Nun stellt sich die Frrage: Kann LINQ to SQL in
n diesem Anw
wendungsko
ontext sinnvo
oll eingesetztt werden?? Presentation
IBusinesssServic e
IBusinessServic e
Common
n
B
BusinessLogi
c
«lib rary»
«library»
Entitie s
Util s
ITablePro
ovider
ITablePro
ovider
DataAccess
«flow»
«file»
Databas e
ng 2: Typisch
he Komponen
ntenarchitekktur einer En
nterprise‐An
nwendung Abbildun
Till Rebeenich Seite 1
12 von 17 Web Serv
S
er
«execution environment»
IIS
Prese
entation
H
HttpServerPort
HttpPort
WSPort
WSClientPorrt
WSSerrverPort
Window s Serv er
DattabaseServ er
«execution environment»
IIS
DB
BServerPort
WSPort
DBPort
Logi c
DBClientPort
DataAcce
ess
IBusin
nessServic e
Abbildun
ng 3: Deployyment des Anwendungssszenarios In unserem Anwendungsszenario
o haben wir bewusst Pro
ozessgrenzen
n vorgesehen
n, da diese in
n den meisten Enterprise‐A
Anwendungeen vorkommen. In der Re
egel müssen mehrere Ap
pplikationen in untersch
hiedlichen Au
usführungsko
ontexten auff gleiche Services zugreiffen können. Werden hierrbei Daten prrozessübergrreifend ausggetauscht, zum Beispiel zw
wischen Presentation‐ und Business Tier, dann weerden die übeertragenen EEntities beim
m Verlassen d
des Client‐Ko
ontextes serialisiert und aauf Serviceseite wieder d
deserialisiertt oder umgekehrt. Dies isst übrigens u
unabhängig vvom verwendeten Protokolll der Fall. Bei den in Abschnitt 2 verwendeeten Beispielen sind wir sstets davon aausgegangen
n, dass alle drei Layer m einzigen Prozess laufen
n. Es stellen ssich deshalb folgende Deetailfragen: in einem
•
•
Wie können Object O
Chang
ge Tracking
g und Deferrred Loadin
ng über Pro
ozessgrenzen
n hinaus wendet werd
den? verw
Wie verhält sich LINQ to SQLL bei mehreren DataCon
ntext‐Instanzen? 3.1 Änderungs
Ä
sverfolgung über P
Prozessgrrenzen Beim Stu
udium der M
MSDN‐Dokum
mentation erffährt man, dass Entities aautomatisch vom Datenkkontext „abgehängt“ (detach
hed) werden, sobald diesse serialisiertt werden (1). Konkret bedeutet dies, dass walteten Refe
erenzen ungü
ültig macht. LINQ to SQLL geht in eine Serialisierung die im Datenkkontext verw
us, dass für jeeden einzeln
nen Vorgang eine neue D
DataContex
xt‐Instanz diesem FFall davon au
Till Rebeenich Seite 1
13 von 17 verwend
det wird. Anggenommen d
die Daten ein
nes Kunden N
Nr. 5 sollen aauf einer Weebseite darge
estellt und danach verändeert werden, d
dann wird folgender Ablaauf vorausgeesetzt (2): 1. Aufrruf einer Metthode auf deem Business‐Layer, die w
wiederum deen Data‐Layeer kontaktiert, um die Dateen des Kundeen Nr. 5 zu laaden. 2. Der Data‐Layer erzeugt zun
nächst eine DataConte
ext‐Instanz, selektiert K
Kunde Nr. 5 5 aus der Table<Custom
mer>‐Collecttion und gibtt diese Instan
nz an den Bu
usiness‐Layerr zurück. 3. Der Business‐Laayer gibt die erzeugte Customer‐In
nstanz an den d
Aufrufer zurück, wobei w
sie aufggrund der Pro
ozessgrenze z. B. als Teil einer SOAP‐‐Message serrialisiert wird
d. 4. Auf dem Presenttation‐Layerr werden Datta Objects ve
erwendet, um die Instan
nz direkt an A
ASP.NET‐
m die Änderu
ungen über e
ein Webform
mular abgescchickt wurden, erfolgt Conttrols zu binden. Nachdem
der A
Aufruf einer entsprechen
nden Business‐Methode,, welche die Instanz entggegennimmt. 5. Der Business‐Layer kontaktiert erneut den Data‐Laayer, der eine neue Da
ataContext
t‐Instanz T
tomer>‐Collection bind
det. Dies ersteellt und diee deserialisiierte Entitätt an die Table<Cust
gescchieht über d
die Methode Attach(). 6. Danaach wird Su
ubmitChang
ges() auf dem Datenkkontext aufggerufen, um
m die Änderu
ungen zu perssistieren. Attach
h() ermögliccht das Bindeen einer außerhalb des P
Prozesses verränderten En
ntity an den Datenko
ontext. Die M
Methode hat drei Überlad
dungen: Attach
h(Customer
r entity) nimmt an, d
dass die als P
Parameter üb
bergebene In
nstanz außerrhalb des Kontextees nicht veräändert wurdee. Möchte m
man, dass derr Kontext die Instanz als ggeändert maarkiert, so muss man stattdeessen Attac
ch(Custome
er entity
y, bool as
sModified
d) oder h(Customer
r entity, Customer
r original) verwenden. Erstere V
Version nimm
mt die Attach
Instanz u
und einen W
Wert entgegen
n, der angibtt, ob die Entiity als modifiziert anzuseehen ist. Sie sschreibt allerdinggs zwingend vor, dass das Mapping des Typs Cus
stomer ein V
Version‐Prop
perty enthaltten muss. Diie letztgenan
nnte Version erwartet, daass man neb
ben der geänderten Instanz auch das Original mitlieferrt. LINQ to SQ
QL kann durcch einen Verrgleich der Objektzuständ
de dann entsscheiden, ob
b eine Änderun
ng vorliegt. D
Diese Einschrränkungen m
macht LINQ to
o SQL nicht o
ohne Grund: beim nachtrräglichen Anhängeen deserialisierter Entitätten muss nämlich die Än
nderungsverffolgung für d
diese und alle
e von ihr abhängiggen Instanzeen wieder akttiviert werdeen. Leider bietet damit kkeine der oben genannteen Überladun
ngen die für Enterprisean
nwendungen
n man nicht imm
mer Einfluss darauf, ob eein Versionsaattribut benötigtte Flexibilitätt (5; 6). Zum einen hat m
in der Daatenbank existiert, zum aanderen wird
d man normalerweise deen Originalzu
ustand einer geladeneen Entität au
uf Seite des A
Aufrufers unggern zwische
enspeichern.. Davon abgeesehen werd
den die zwischen
n den Servicee‐Endpunkteen ausgetausschten Nachrrichten dadu
urch unnötig vergrößert. Man würde siich hier ein intuitiveres V
Verhalten des Datenkonttextes wünscchen. Umgeh
hen kann man diesen Missstan
nd derzeit nu
ur, indem maan das Objecct Tracking im
m Datenkonttext komplettt deaktiviertt, was diesen jeedoch zum R
Read‐Only‐Ko
ontext degradiert. Die Su
ubmitChang
ges()‐Methode steht d
dann nicht meehr zur Verfü
ügung und alle Persistenzzmechanismen müssen d
durch explizitten Aufruf gemapptter Stored Prrocedures od
der Function
ns angestoße
en werden. Till Rebeenich Seite 1
14 von 17 3.2 Verzögerte
V
es Laden Beim Ein
nsatz von LIN
NQ to SQL in N‐Tier‐Appliikationsszenaarien ist „Deeferred Loadiing“ nicht sin
nnvoll. Der Grun
nd hierfür istt, dass diese Funktionalittät von der ständigen Üb
berwachung der Entity
ySet<T>‐Insstanzen jedes Entitytyps abhängt. Verlässt eine Entityinstanz den Einflusssbereich des Dateenkontextes (nach Serialiisierung), wird das verzögerte Laden nicht mehr ffunktioniere
en. Vielmehr hängt es vo
on den Data
aMember‐Atttributen in d
der jeweiligen Entityklassse ab, ob die Instanzeen assoziierteer Entityklasssen zuvor geeladen werde
en oder nichtt, denn der D
Data Contracct Serializer ruft während der Serialisierung jedes der so gekennzeichneeten Membeer explizit auff und löst dabei deen verzögerteen Ladevorgang aus. Hat man
n das Object Tracking auffgrund der in
n Abschnitt 3
3.1 genannteen Schwierigkeiten deakttiviert, so steht damit auch dass verzögerte Laden nicht mehr zur Ve
erfügung. 3.3 Automatis
A
ierung Visual Sttudio 2008 bietet einige ssehr nützlich
he Automatissierungsfeatures an, die sowohl die Erstellun
ng von Entityyklassen als aauch das Mapping auf Daatenbankschemata unterrstützen. Wie
e intensiv man von dieesen Werkzeugen Gebrau
uch machen kann, hängtt allerdings vvom Vorgehe
en beim Anwendungsdesign aab. Beim Bottom‐Up‐Ansatz geht maan von der D
Datenbanksch
hicht aufwärrts, d. h. man
n erstellt das Datenbaankdesign un
nd verwendeet den O/R‐Designer, um die Datenbaankobjekte auf ein objektorrientiertes En
ntitymodell zzu mappen. Hierbei kann
n man den Designer so ko
onfigurieren, dass Kontext und Entities in zwei unteerschiedlicheen Namespacces erzeugt w
werden. Leid
der werden b
beide Namespaces dann ab
ber in einer eeinzigen Dattei zusammengefasst. Mö
öchte man die erzeugten
n Klassen auf zwei Dateien verrteilen, musss man dies m
manuell mitte
els Copy & Paaste tun – un
nd dies nach jeder n für alle Meember der En
ntityklassen bereits Data
aMember‐Atttribute Schemaäänderung. Feerner können
generierrt werden, allerdings nich
ht selektiv nu
ur für bestim
mmte Membeer. Beim Top‐Down‐Anssatz geht man dagegen in
n umgekehrtter Richtung vor, indem zzunächst Anwendungsfälle, daanach ein Klaassenmodell und dann eiin Datenbankmodell ersttellt wird. In diesem man bereits Entityklassen, bevor man sich überhaupt um derren relationaale Abbildungg Fall hat m
Gedankeen gemacht h
hat. Der O/R
R‐Designer biietet leider kkeine Möglich
hkeit an, diese Klassen nachträgglich auf ein relationales Schema zu m
mappen, d. h
h. dies muss manuell und
d ohne jede Automattisierung bew
werkstelligt w
werden. Till Rebeenich Seite 1
15 von 17 4 Fazit Die Verw
wendung von
n LINQ to SQ
QL für verteiltte Enterprise
eanwendunggen macht deerzeit nur eingesch
hränkt Sinn. Der von Microsoft in Visu
ual Studio 20
008 unterstü
ützte Bottom
m‐Up‐Ansatz ist in solchen Projekten hääufig nicht reealisierbar. In
n diesem Falll muss man dann auf jeggliche erung von Entityklassen u
und Datenko
ontext in Toolunteerstützung verzichten. Die automatissche Generie
untersch
hiedlichen Naamespaces, Dateien oder gar Assemb
blies ist nicht möglich un
nd erschwertt deren Einsatz in verteilten Anwendungen. LINQ to SQL schreibtt die Verwen
ndung spezieller Collectio
on‐Klassen und Hilfsattrib
bute (z. B. Fremdscchlüsselattrib
bute) in den Entityklassen vor. Dies isst in vielen Fäällen eine en
norme Einschräänkung, wenn
n beispielsweise benutzeerdefinierte C
Collections eeingesetzt weerden sollen
n. Hier würde m
man sich eineen schnittstellenbasierten Ansatz wünschen, um die für das D
Deferred Loading und Objeect Tracking benötigte Fu
unktionalitätt in eigenen Collection‐Klassen impleementieren zzu können, sofern dies gewünscht ist. Beim Austausch von Entities übeer Prozessgreenzen hinweg stellt das rigide Object Tracking ein
n nicht unerheb
bliches Hindeernis dar. Diee vom Datenkontext gebo
otene Funktiionalität ist ffür solche Szenarien derzeit n
nicht flexibel genug. Ein D
Deaktivieren
n dieses Features versetztt den Kontexxt in den schreibggeschützten M
Modus und aalle Insert‐, U
Update‐ und Delete‐Operationen mü
üssen manue
ell aufgeruffen werden. Schreibeende und löschende Dateenbankzugrifffe des Konte
extes können
n über Stored Procedure
es konfigurriert werden,, nicht jedoch lesende Zu
ugriffe. Der d
direkte Zugrifff auf Tabelleen und Sichten ist aber in d
der Regel niccht gewünsch
ht oder überr Datenbank‐‐Zugriffsrech
hte unterbunden. Das bettrifft auch dass verzögerte Laden, denn
n hier operieert LINQ to SQ
QL ebenfalls direkt auf Taabelleneben
ne. Positiv isst die reine O
O/R‐Mappingg‐Funktionalität von LINQ
Q to SQL hervorzuheben. Die Umwan
ndlung von SQL‐‐ auf CLR‐Datentypen un
nd umgekehrrt verläuft klaaglos und oh
hne Fehler. A
Auch die gute
e Performance beim D
Datenzugriff ffällt positiv aauf. Es bleibtt also abzuwaarten, was die nächste V
Version von LLINQ to SQL b
bringt. Till Rebeenich Seite 1
16 von 17 Quelle
enverzeichnis 1. Fowle
er, Martin. Pa
atterns of En
nterprise App
plication Arch
hitecture. s.l. : Addison‐W
Wesley Profe
essional, 2002. 2. Micro
osoft Corporaation. Linq to
o SQL: Langu
uage‐Integrated Query fo
or Relational Data. msdn.. [Online] 2008. http:///msdn.microsoft.com/een‐us/library//bb425822.aaspx. 3. —. N‐TTier and Rem
mote Applicaations with LINQ to SQL. msdn. [Onlin
ne] Microsofft Corporatio
on, 2008. http://m
msdn.microso
oft.com/en‐u
us/library/bb
b882661.aspx. 4. Kulkarni, Dinesh. Attach() if yo
ou have som
mething detacched. MSDN
N Blogs. [Online] Microsoft Corporattion, 2008. h
http://blogs.msdn.com/d
dinesh.kulkarrni/archive/2
2007/10/08//attach‐if‐you‐have‐
something‐detached
d.aspx. 5. Strahll, Rick. Comp
plex Detacheed Entities in LINQ to SQLL ‐ More Nigh
htmares. Ricck Strahl's W
Web Log. [Online] West Wind Technologiees, 01. October 2007. http
p://www.weest‐
m/weblog/posts/162336
6.aspx. wind.com
6. Sieme
er, Andrew. LINQ to SQL ‐ Implementting the Repo
ository Patteern. Andrew Siemer's Blo
og. [Online] 05. Februaryy 2008. http://geekswith
hblogs.net/A
AndrewSiemeer/archive/2008/02/05/linq‐to‐
sql‐‐‐imp
plementing‐tthe‐repository‐pattern.aspx. Till Rebeenich 17 von 17 Seite 1
Herunterladen