Werken met complexe datatypes. De waarde die wordt geretourneerd door de systeemvariabele @@ IDENTITY

Wat is IDENTITEIT

IDENTITEIT is geen gegevenstype. Dit is een extra eigenschap, een beperking opgelegd aan integer typen gegevens in MS SQL-server... Die. deze eigenschap kan worden toegepast op velden van de volgende typen: tinyint, kleintje, int, bigint, decimaal (p, 0), of numeriek (p, 0)

De FoxPro-analoog van deze eigenschap is het gegevenstype Integer-AutoIncrement. Ga er niet vanuit dat Integer-AutoIncrement het veld is met de eigenschap Identity. Helemaal niet. Dit is precies analoog. Ze lijken grotendeels op elkaar, maar hebben een aantal verschillen. Dit artikel gaat over de eigenschap IDENTITY in MS SQL-server.

Velden met de eigenschap IDENTITY hebben de volgende kenmerken:

  • Er kan slechts één veld met de eigenschap IDENTITY in één tabel voorkomen.
  • Een veld met de eigenschap IDENTITY kan niet worden bewerkt. Ze zijn alleen-lezen.
  • De waarde voor het veld met de eigenschap IDENTITY wordt automatisch toegewezen wanneer een nieuw record wordt gemaakt.

Er zijn nog enkele functies, maar deze zijn al een gevolg van de genoemde functies.

De nieuwe waarde is de laatst gebruikte waarde plus een vaste waarde. Merk op dat de nieuwe waarde niet gebaseerd is op de maximale waarde in bestaande records en door de laatst gebruikte waarde. Dit betekent dat de records met de laatst gebruikte waarde mogelijk niet fysiek bestaan, maar deze waarde zal worden gebruikt.

Met andere woorden, gaten in de reeks veldwaarden met de eigenschap IDENTITY zijn perfect acceptabel. De lijst met betekenissen is helemaal niet continu

Gewoonlijk wordt 1 opgegeven als de toename, maar het kan elk geheel getal zijn. Inclusief negatief.

Vanwege deze aard van velden met de eigenschap IDENTITY, worden dergelijke velden vaak gebruikt als primaire sleutels. Met andere woorden, als velden, aan de hand waarvan u een tabelrecord altijd uniek kunt identificeren.

Houd er rekening mee dat de eigenschap IDENTITY geen controle heeft over de uniciteit van de gegevens. Als het veld bijvoorbeeld aanvankelijk had INTEGER-type, en er zijn een aantal waarden in ingevoerd. En toen de structuur van de tabel werd gewijzigd en de eigenschap IDENTITY werd opgelegd aan dit veld, zouden nieuwe records mogelijk dezelfde gegevens bevatten die al eerder in deze tabel waren ingevoerd. Daarom, als een veld met de IDENTITY-eigenschap wordt gebruikt als primaire sleutel, dan moet dit veld over elkaar heen worden geplaatst extra beperking door uniciteit.

Nadeel van het gebruik van velden met IDENTITY-eigenschap als primaire sleutel

Ondanks de duidelijke voordelen van het gebruik van velden met de eigenschap IDENTITY als primaire sleutel, hebben ze echter ook een ernstig nadeel.

De waarde van velden met de eigenschap IDENTITY is pas bekend als de record fysiek is gemaakt.

En dan? Wat zijn de problemen? Laten we een record maken en de nieuwe waarde ervan ontdekken.

Het probleem is dat om de waarde van een veld van een record te achterhalen, dit record eerst moet worden gevonden. En het zoeken naar een record wordt gewoon uitgevoerd door de waarde van de primaire sleutel. Degene wiens betekenis moet worden bepaald. Een vicieuze cirkel: om een ​​waarde te lezen, moet je deze waarde weten!

De gegevensopslagstructuur in MS SQL-server verschilt fundamenteel van de gegevensopslagstructuur in DBF-bestanden. Het bevat geen concepten als " fysiek nummer vermeldingen "," volgende invoer "," laatste invoer ", enz. Dat wil zeggen, u kunt niet naar" laatste invoer"om de waarde van zijn primaire sleutel te lezen.

Bovendien, hoewel de nieuwe waarde van het veld met de IDENTITY-eigenschap altijd groter is dan een van bestaande waarden(als de toename een positief getal is), maar het is ook onmogelijk om deze nieuwe waarde te bepalen door simpelweg het maximum van de bestaande waarden te berekenen. Nee, de maximale waarde zelf wordt natuurlijk verkregen. Er is gewoon geen garantie dat de resulterende waarde de waarde is van de exacte record die is gemaakt.

Het punt is dat MS SQL-server in de regel wordt gebruikt in toepassingen voor meerdere gebruikers. Dit betekent dat meerdere gebruikers tegelijk nieuwe records kunnen aanmaken. Het blijkt dat de ene gebruiker een nieuw record heeft aangemaakt, vervolgens de maximale waarde is gaan berekenen en op dat moment heeft een andere gebruiker ook een nieuw record aangemaakt. Het resultaat is dat de eerste gebruiker als maximale waarde krijgt de waarde van het item dat door de tweede gebruiker is gemaakt.

Moeten we dus stoppen met het gebruik van velden met de eigenschap IDENTITY als primaire sleutel? Helemaal niet. Toch zijn er manieren om de waarde van een veld met de eigenschap IDENTITY van een nieuw record te bepalen.

Hoe de waarde van een veld met de eigenschap IDENTITY in een nieuw record te bepalen

Eigenlijk zijn er drie fundamentele strategieën voor het bepalen van de waarde van een veld met de eigenschap IDENTITY in een nieuw, zojuist gemaakt record

Laten we nu de voor- en nadelen van elke strategie eens nader bekijken.

De waarde die wordt geretourneerd door de systeemvariabele @@ IDENTITY

Er zijn een aantal systeemvariabelen in MS SQL-server, waarvan de waarde automatisch verandert wanneer bepaalde gebeurtenissen plaatsvinden. In het bijzonder wordt de systeemvariabele @@ IDENTITY automatisch ingesteld gelijk aan velden met de eigenschap IDENTITY van de laatst gemaakte record in de huidige verbinding. Die. het aanmaken van nieuwe records in een andere verbinding (door een andere gebruiker) heeft geen enkele invloed op de waarde ervan in deze verbinding.

Nou, dit is het dan, de oplossing. Gewoon na het aanmaken van een nieuw record, lezen we de waarde van de @@ IDENTITY systeemvariabele en hebben de gewenste waarde.

Over het geheel genomen is het waar. Het enige probleem is dat de @@ IDENTITY systeemvariabele zijn waarde verandert wanneer een record wordt aangemaakt in ieder tafel.

In de praktijk betekent dit dat als een insert-trigger op een tabel wordt gezet, in de body waarvan een INSERT-commando wordt gegeven om een ​​record aan te maken in een andere tabel, die op zijn beurt ook een veld met de eigenschap IDENTITY heeft, de @@ IDENTITY systeemvariabele krijgt de waarde van het veld uit deze tweede tabel.

Met andere woorden, u kunt vertrouwen op de waarde van de @@ IDENTITY systeemvariabele, maar onthoud dat deze variabele niet gebonden is aan de waarde van een veld in één tabel.

De waarde die wordt geretourneerd door de functie SCOPE_IDENTITY ()

De MS SQL 2000-versie geïntroduceerd systeem functie: SCOPE_IDENTITY (). Deze functie retourneert ook de waarde van het veld met de eigenschap IDENTITY van de laatst gemaakte record, maar gemaakt binnen de huidige SCOPE.

Het is nogal moeilijk om de term SCOPE adequaat in het Russisch te vertalen. Maar grofweg kun je dit zeggen: SCOPE is één procedure of functie. Met andere woorden, SCOPE_IDENTITY () retourneert de waarde van het veld met de eigenschap IDENTITY van de laatste record die is gemaakt in de procedure waarin deze functie werd aangeroepen.

Een trigger is een andere SCOPE (een andere functie), dus het heeft op geen enkele manier invloed op de geretourneerde waarde van SCOPE_IDENTITY ().

Zelfs als twee gebruikers dezelfde procedure tegelijkertijd aanroepen, maar elk de procedure in zijn SCOPE hebben aangeroepen. Die. weer is er geen conflict.

Het nadeel van deze functie is dat deze moet worden aangeroepen binnen de SCOPE, waar het nieuwe record van de tabel die voor ons van belang is, is gemaakt. En dit is niet altijd mogelijk.

Met andere woorden, om SCOPE_IDENTITY () correct te gebruiken, moet u altijd het bereik van SCOPE controleren. Vaak door het creëren van speciale procedures.

Een nieuw record zoeken op basis van de waarde van andere velden

Als u het zich herinnert, is het grootste probleem bij het definiëren van de waarde van een veld met de eigenschap IDENTITY dat dit veld als de primaire sleutel wordt gebruikt. Die. het is door zijn waarde dat het vereiste record wordt gevonden.

Vaak hebben tabellen echter een veld of een reeks velden waarmee een record ook uniek kan worden geïdentificeerd. Bijvoorbeeld, als het komt over de directory, dan heeft de directory natuurlijk een veld "Naam". Het ligt ook voor de hand dat dit veld uniek moet zijn binnen de directory. Anders gaat de betekenis van het gebruik van het naslagwerk zelf gewoon verloren. Waarom records invoeren met dezelfde waarde?

Waarom gebruikt u deze "Naam" niet als de primaire sleutel? Waarom heb je überhaupt een veld met de eigenschap IDENTITY nodig? Dit is een onderwerp voor een ander gesprek. Kortom, "Titel" is voor de gebruiker (externe gegevens) en IDENTITY is voor het verstrekken van referentiële integriteit databases (interne gegevens).

De waarde van het veld met de eigenschap IDENTITY in het nieuwe record is onbekend. Maar de betekenis van het veld 'Titel' in dit nieuwe record is bekend. De gebruiker heeft het zelf ingevuld! Dit betekent dat u na het aanmaken van een nieuw record dit nieuwe record kunt vinden aan de waarde van het veld "Naam" en de waarde van het veld met de eigenschap IDENTITY kunt lezen.

Het enige probleem is dat zo'n veld of set van velden niet altijd bestaat om een ​​record uniek te identificeren. Dit is trouwens een van de redenen om de zogenaamde surrogaatsleutels... Diezelfde velden met de eigenschap IDENTITY.

Als u desondanks besluit deze strategie te gebruiken om een ​​nieuw record te vinden, zorg er dan voor dat u een unieke beperking oplegt aan het veld "Naam" (of de reeks velden die u hebt gekozen). Dat wil zeggen, zodat er niet per ongeluk twee records met dezelfde waarde in dit veld staan.

Hoe te werken met velden met de IDENTITY-eigenschap in FoxPro

Nu het theoretische gedeelte klaar is, "laten we proberen om met al dit goede van start te gaan." Die. laten we beslissen hoe we al deze kennis in FoxPro gaan gebruiken. Laten we het probleem dat moet worden opgelost nog een keer verduidelijken.

Aan de MS SQL-servertabel wordt een record toegevoegd met een veld met de eigenschap IDENTITY. Het is noodzakelijk om de waarde van het veld met de IDENTITY-eigenschap aan de FoxPro-kant onmiddellijk na het maken van een nieuw record op te halen.

FoxPro heeft drie fundamentele opties voor het organiseren van werk met MS SQL-server

  • Externe weergave gebruiken
  • Cursoradapter gebruiken

Hier moet u stilstaan ​​bij welke gebeurtenis daadwerkelijk een record op de MS SQL-server creëert. Met Pass-Trough is alles duidelijk. Dit is eigenlijk een direct commando aan de server om een ​​nieuw record aan te maken. Maar met Remote View en Cursor Adapter is het een beetje anders.

Het resultaat van het werk van zowel Remote View als Cursor Adapter is een cursor. Die. een tijdelijke tabel die zich fysiek op de computer van de klant bevindt. Standaard wordt deze cursor automatisch geopend in optimistische rijbuffering (3) en kan alleen worden overgeschakeld naar optimistische tabelbuffering (5). Het is onmogelijk om voor deze cursor naar de pessimistische buffermodus over te schakelen of de buffering helemaal uit te schakelen.

Het nieuwe record wordt dus eerst fysiek precies op klant machine in deze cursor. Om precies te zijn, in de buffer van deze cursor. De fysieke aanmaak van een record op de MS SQL-server vindt pas plaats nadat de buffer is leeggemaakt.

Voor stringbuffering kan de buffer automatisch worden leeggemaakt door een van de volgende handelingen uit te voeren:

  • Springen (of proberen te springen) naar een ander record
  • De cursor sluiten
  • Overschakelen naar tabelbuffermodus
  • Op commando TableUpdate ()

Voor tabelbuffering kan de buffer alleen worden leeggemaakt met de opdracht TableUpdate () en niet anders.

Geen enkele andere actie en bewerking met de Remote View of de Cursor Adapter zal een nieuw record op de MS SQL-server creëren. Als tijdens het uitvoeren van een bewerking bleek dat er een nieuw record was gemaakt op de MS SQL-server, betekent dit dat de cursor in de regelbuffermodus stond en dat een van de gebeurtenissen plaatsvond die ervoor zorgden dat de buffer automatisch werd leeggemaakt.

Dit kan bijvoorbeeld gebeuren met de opdracht Requery () voor de Remote View. Maar dit betekent niet dat de opdracht Requery () de buffer leegt. Helemaal niet. Slechts een van de voorwaarden voor het uitvoeren van de opdracht Requery () is het sluiten van de reeds bestaande cursor. Maar deze gebeurtenis zorgt er alleen voor dat de buffer automatisch wordt leeggemaakt als de cursor zich in de regelbuffermodus bevindt.

Vermijden soortgelijke misverstanden, zet u de cursor in de tabelbuffermodus (5). In dit geval kunt u altijd het proces van het doorspoelen van de buffer regelen.

Het moet echter duidelijk zijn dat zelfs als u de tabelbuffermodus instelt, verschillende records in de Remote View of Cursor Adapter wijzigt en vervolgens de opdracht TableUpdate () geeft, de buffer nog steeds met één record tegelijk wordt leeggemaakt. Die. er wordt niet één commando naar de server gestuurd om bijvoorbeeld te wijzigen, maar een set commando's om elk record afzonderlijk te wijzigen.

Met betrekking tot de bewerkingen van het maken van een nieuw record, volgt hieruit dat in alle gevallen van de Cursor Adapter altijd er wordt slechts één record tegelijk ingevoegd.

Direct gebruik van Pass-Through-technologie via de functie SQLEXEC ()

Bij deze werkwijze werkt de programmeur direct met de MS SQL server. Hij vormt zelf alle commando's die naar de server worden gestuurd, ontvangt het resultaat en verwerkt het zelf. In dit geval is het niet moeilijk om te verzenden aanvullende aanvraag server door de waarde van de SCOPE_IDENTITY functie

LOKAAL lcNewValue, lnResut lcNewValue = "(! LANG: Nieuwe waarde" lnResut = SQLExec(m.lnConnectHandle,"INSERT INTO MyTab (Field1) VALUES (?m.lcNewValue)") IF m.lnResut>0 SQLExec(m.lnConnectHandle,"SELECT NewIdent=SCOPE_IDENTITY()","NewIdent") ?NewIdent.NewIdent ELSE LOCAL laError(1) =AERROR(laError) * Анализ массива laError для уточнения причины ошибки ENDIF !}

V dit voorbeeld m.lnConnectHandle is een getal, het nummer van de verbinding met de MS SQL-server, die eerder is geconfigureerd. MyTab is een tabel met een veld met een IDENTITY-eigenschap.

Na het uitvoeren van de tweede query in de resulterende cursor NewIdent in het NewIdent-veld van het eerste record en de gewenste waarde ophalen. V gegeven syntaxis zowel de insert-opdracht als de functieaanroep SCOPE_IDENTITY () komen voor in dezelfde SCOPE. Daarom krijgen we de gewenste waarde.

Externe weergave gebruiken

Remote View is een soort "add-on" over de Pass-Through-technologie. In principe wordt hetzelfde INSERT INTO-commando uitgevoerd bij het maken van een nieuw record. Het probleem is echter dat zelfs als u het nummer van de verbinding leest waarin Remote View wordt uitgevoerd en vervolgens een query uitvoert om de waarde te bepalen die wordt geretourneerd door SCOPE_IDENTITY (), we NULL krijgen, omdat in dit geval de opdracht insert en SCOPE_IDENTITY () worden uitgevoerd in verschillende SCOPE's. Daarom zijn er maar twee manieren om de waarde van een veld met de eigenschap IDENTITY te bepalen.

* Bepalen van de waarde van de @@ IDENTITY LOCAL systeemvariabele lnConnectHandle lnConnectHandle = CursorGetProp ("ConnectHandle", "MyRemoteView") SQLExec (m.lnConnectHandle, "SELECT [e-mail beveiligd]@IDENTITY "," NewIdent ")? NewIdent.NewIdent * Bepaal door de waarde van een ander veld LOCAL lnConnectHandle, lcNickName lnConnectHandle = CursorGetProp (" ConnectHandle "," MyRemoteView ") lcNickName = MyRemoteView.NickeNeCOMMeCtNeCtLeCtView =NRemoteCtName. "," NewIdent ")? NewIdent.TabId

In beide voorbeelden is MyRemoteView de naam van uw Remote View. NickName is de naam van het veld in de Remote View op basis waarvan een nieuw record wordt gezocht, en TabID is hetzelfde veld met de IDENTITY-eigenschap waarvan de waarde moet worden bepaald

Er wordt aangenomen dat het aanmaken van een nieuw record al heeft plaatsgevonden. De opdracht TableUpdate () is expliciet gegeven, of de Remote View bevindt zich in de tekenreeksbuffermodus en er is geprobeerd naar een ander record te navigeren.

Waarom werd de schijnbaar meer voor de hand liggende zoekoplossing na Requery () in het tweede geval niet gebruikt? Zoiets als

VERZOEK ("MyRemoteView") SELECTEER MyRemoteView LOCATE FOR NickName = "Nieuwe waarde"? MyRemoteView.TabID

Hier zijn verschillende redenen voor.

Ten eerste neemt Requery () aan dat de query opnieuw wordt uitgevoerd. Die. er wordt van uitgegaan dat de buffer van alle Remote View-records is leeggemaakt. Maar dit is misschien niet zo. Spoel de buffer bijvoorbeeld één voor één in een lus.

Ten tweede bevat Remote View meestal: aanvullende voorwaarden selectie van records, en het nieuw gemaakte record wordt mogelijk helemaal niet opgenomen in de selectievoorwaarden. Die. na Requery (), wordt het record, hoewel het fysiek op de server wordt gemaakt, niet weergegeven in de Remote View zelf.

Cursoradapter gebruiken

Het object Cursor Adapter is geïntroduceerd in FoxPro, beginnend met Visual FoxPro 8. Het is ontworpen om het gemakkelijker te maken om met externe gegevens te werken bij het uitvoeren van bepaalde standaard bewerkingen... In het bijzonder de bewerkingen van het maken van een nieuw record. Via de cursoradapter kunt u alle drie de opties implementeren om de waarde van een veld met de eigenschap IDENTITY in een nieuw record te krijgen.

De waarde die wordt geretourneerd door de systeemvariabele @@ IDENTITY

Nadat de buffer is leeggemaakt en fysiek een nieuw record is gemaakt op de MS SQL-server, wordt de gebeurtenis AfterInsert geactiveerd in het Cursor Adapter-object. Hierin hoef je alleen maar een extra verzoek aan de server te doen en de waarde van de @@ IDENTITY-systeemvariabele te lezen

PROCEDURE AfterInsert LPARAMETERS cFldState, lForce, cInsertCmd, lResult IF lResult = .T. && invoeging is gelukt. U moet de ID-waarde LOCAL currentArea currentArea = SELECT () TRY IF 1 = SQLEXEC (this.DataSource, "SELECT [e-mail beveiligd]@IDENTITY "," NewIdent ") VERVANG TabId MET NewIdent.NewIdent IN (This.Alias) GEBRUIK IN IDRes ELSE LOKAAL laError (1) = AERROR (laError) * Analyseer de laError-array om de oorzaak van de fout te verduidelijken ENDIF SELECTEER EINDELIJK ( currentArea) ENDTRY ENDIF ENDPROC

Ik denk dat de code duidelijk genoeg is en geen uitleg nodig heeft. Na succesvolle vestiging een nieuw record op de server, met SQLExec () voor dezelfde verbinding, definieer de @@ IDENTITY-waarde en schrijf deze waarde naar huidig ​​record cursor naar het sleutelveld.

In Visual FoxPro 9 is een eigenschap toegevoegd aan het object Cursor Adapter InsertCmdRefreshCmd... Dit is een commando dat pas naar de server wordt gestuurd na het succesvol invoegen van een nieuw record. Die. het is niet nodig om overtuigd te zijn van het feit dat je een nieuw record moet maken.

In combinatie met een ander nieuw pand InsertCmdRefreshFieldList u kunt gemakkelijker upgraden van de @@ IDENTITY-systeemvariabele. Om dit te doen, hoeft u alleen de volgende instellingen van het CursorAdapter-object te maken:

CursorAdapter.InsertCmdRefreshCmd = "SELECT @@ IDENTITY" CursorAdapter.InsertCmdRefreshFieldList = "TabId"

TabId is hier niet de naam van het veld met de eigenschap IDENTITY in de tabel op de MS SQL-server zelf, maar de naam van het aan de clientzijde ontvangen cursorveld waarin de inhoud van het veld met de eigenschap IDENTITY wordt weergegeven. In de regel zijn deze namen hetzelfde, maar over het algemeen kunnen ze verschillend zijn. Meer informatie over het pand InsertCmdRefreshFieldList Lees hieronder. In de sectie over het vinden van een nieuw record op de waarde van andere velden.

De waarde die wordt geretourneerd door de functie SCOPE_IDENTITY ()

Hier is alles wat gecompliceerder. Het punt is dat u niet op dezelfde manier kunt handelen als bij het definiëren van de systeemvariabele @@ IDENTITY. SQLExec () en Cursor Adapter werken in verschillende SCOPE. Daarom zal SCOPE_IDENTITY () bij deze benadering altijd NULL retourneren. Om deze tegenstrijdigheid te overwinnen, worden speciale procedures gebruikt.

Eerst moet u zo'n opgeslagen procedure op de MS SQL-server zelf maken

PROCEDURE MAKEN Get_ValueInt @ValueIn Int, @ValueOut Int UITVOER ALS SET NOCOUNT ON SELECT @ [e-mail beveiligd]

Zoals u kunt zien, is het doel van deze procedure eenvoudigweg om een ​​waarde toe te kennen invoerparameter uitvoerparameter. U kunt nu deze procedure gebruiken om de SCOPE_IDENTITY () waarde in de cursoradapter te bepalen. Om dit te doen, in het geval VoorInvoegen een dergelijke vervanging is gemaakt

PROCEDURE Voordat LPARAMETERS invoegen cFldState, lForce, cInsertCmd cInsertCmd = cInsertCmd +; "; VERKLAREN @id int" +; "; SELECT @ id = SCOPE_IDENTITY ()" +; "; EXEC Get_ValueInt @id, [e-mail beveiligd]"ENDPROC

Houd er rekening mee dat bij deze vervanging MyTab niet langer de naam is van de tabel op de MS SQL-server, maar de naam van de cursor die door de client is gemaakt. Om precies te zijn, de naam die is vastgelegd in de Alias-eigenschap van de Cursor Adapter zelf. Dienovereenkomstig is "TabId" de naam van het cursorveld dat door de client is gemaakt en dat de waarde van het veld met de eigenschap IDENTITY bevat

V in dit geval, in wezen wordt een dynamisch opgeslagen procedure gevormd. Die. niet slechts één INSERT-opdracht, maar een reeks opdrachten. Commando's worden van elkaar gescheiden door puntkomma's, hoewel dit niet vereist is. Het is voldoende om commando's van elkaar te scheiden met een eenvoudige spatie.

Een nieuw record zoeken op basis van de waarde van andere velden

Als u Visual FoxPro 8 hebt, hoeft u alleen maar een methode te gebruiken die vergelijkbaar is met de methode die wordt gebruikt om de waarde van de @@ IDENTITY-systeemvariabele te vinden. Die. voer in de methode AfterInsert van het object Cursor Adapter een extra verzoek uit via SQLExec ()

Vanaf Visual FoxPro 9 heeft het object Cursor Adapter: extra eigenschappen zodat u deze procedure kunt automatiseren zonder extra code te schrijven

InsertCmdRefreshFieldList- een lijst met velden waarvan de waarde wordt bijgewerkt na Invoegen
InsertCmdRefreshKeyFieldList- een veld, of een set NOT-sleutelvelden die het record ook uniek identificeren, dat zal worden gebruikt om naar een nieuw record te zoeken

Opgemerkt moet worden dat deze eigenschappen niet de namen van de velden van de tabel van de MS SQL-server zelf specificeren, maar de overeenkomstige namen van de velden van de cursor die aan de clientzijde is ontvangen. Voor hetzelfde voorbeeld zou het ongeveer als volgt zijn:

InsertCmdRefreshFieldList = "TabID" InsertCmdRefreshKeyFieldList = "Bijnaam"

Met andere woorden, op basis van de waarde van het NickName-veld wordt een record gevonden in de tabel op de MS SQL-server en wordt de waarde van het TabID-veld ingelezen, waarna deze waarde wordt weggeschreven naar een nieuw record op de kant van de cliënt.


Meer in detail, zowel een bespreking van dit onderwerp als voorbeelden van gebruik kunt u hier zien



Vladimir Maximov
Laatste update: 01.05.06

Geeft aan dat de nieuwe kolom een ​​identiteitskolom is. Wanneer toegevoegd aan een tabel nieuwe lijn de database-engine genereert een unieke sequentiële waarde voor deze kolom. Identiteitskolommen worden doorgaans gebruikt met een PRIMARY KEY-beperking om unieke rij-ID's in een tabel te behouden. De eigenschap IDENTITY kan worden toegewezen aan tinyint, smallint, int, bigint, decimale (p, 0) of numerieke (p, 0) kolommen. Er kan voor elke tabel slechts één identiteitskolom worden gemaakt. Standaardwaarden kunnen niet worden gebruikt in de identiteitskolom. U moet zowel de beginwaarde als de verhoging opgeven, of niets. Als geen van de waarden is opgegeven, is de standaard (1,1).

Syntaxis:

[IDENTITEIT [(zaad, toename)]

seed - de waarde die wordt gebruikt voor de allereerste rij die in de tabel is geladen.

lncrement - s de incrementwaarde die moet worden toegevoegd aan de eerder geladen rij-ID-waarde.

[ Rooster Kamer] INT IDENTITEIT (1, 1) NIET NULL PRIMAIRE SLEUTEL

    Berekende velden.

Syntaxis:

<имя_столбца> ALS <выражение> ]

Een expressie die de waarde van een berekende kolom specificeert. Een berekende kolom is een virtuele kolom die niet fysiek in de tabel wordt opgeslagen, tenzij de vlag PERSISTED ervoor is ingesteld. De kolomwaarde wordt berekend op basis van een expressie die andere kolommen in dezelfde tabel gebruikt.

Een berekende kolomdefinitie kan er bijvoorbeeld als volgt uitzien:

Prijs van goederen ALS Prijs voor één * Hoeveelheid.

Een expressie kan een niet-berekende kolomnaam, constante, functie, variabele of een combinatie hiervan zijn, verbonden door een of meer operators. De expressie mag geen subquery zijn of aliassen van het gegevenstype bevatten.

Berekende kolommen kunnen worden gebruikt in geselecteerde lijsten, WHERE-clausules, ORDER BY en overal waar normale uitdrukkingen kunnen worden gebruikt, behalve als volgt.

Een berekende kolom kan niet worden gebruikt als definitie van een DEFAULT- of FOREIGN KEY-beperking, of in combinatie met een NOT NULL-beperkingsdefinitie. Een berekende kolom kan echter worden gebruikt als een indexsleutelkolom of als onderdeel van een PRIMARY KEY of UNIQUE-beperking als de waarde van de berekende kolom wordt bepaald door een deterministische (voorspelbare) expressie en het gegevenstype van het resultaat is toegestaan ​​in de indexkolommen .

Als de tabel bijvoorbeeld kolommen met gehele getallen bevat een en B, berekende kolom a + b kan worden opgenomen in de index en de berekende kolom a + DATUMPART (dd, GETDATUM ())- kan niet, omdat de waarde ervan bij volgende oproepen kan veranderen.

Een berekende kolom kan niet de doelkolom zijn van een INSERT- of UPDATE-instructie.

Het DBMS bepaalt automatisch de nullabiliteit voor berekende kolommen op basis van de gebruikte expressies. Er wordt aangenomen dat het resultaat van de meeste expressies nullable is, zelfs als alleen kolommen worden gebruikt die niet nullable zijn, aangezien een mogelijke overloop of verlies van precisie kan resulteren in een null-waarde. Als u wilt weten of een berekende kolom van een tabel null kan zijn, gebruikt u de functie COLUMNPROPERTY met de eigenschap Staat Null toe... Zorg ervoor dat de uitdrukking dit niet toestaat null-waarden, kunt u door ISNULL op te geven met de constante check_expression waarbij constante een waarde is die niet nul is en elke NULL-waarde vervangt. Op berekende expressies gebaseerde kolommen die CLR UDT's bevatten, vereisen REFERENCES-machtigingen voor het type.

Specificeert dat de SQL Server-component berekende waarden fysiek in een tabel opslaat en deze bijwerkt wanneer een kolom waarvan de berekende kolom afhankelijk is, verandert. Door PERSISTED op te geven voor een berekende kolom, kunt u een index maken voor de berekende kolom die deterministisch maar onnauwkeurig is.

Bij een tabel maken Naast de besproken technieken kunt u het optionele trefwoord CONSTRAINT opgeven om de beperking een naam te geven die uniek is binnen de database.

CREËREN t BEKWAAM Bestellingen(

ID_OrderINT NIET NULL ,

Gerecht INT NIET NUL,

Hoeveelheid_ porties INT NIET NULL CONTROLE ( Hoeveelheid_ porties >0 ) ,

datum DATUM NIET NUL ,

HOOFDSLEUTEL ( ID_Order, Gerecht, datum),

DWANG VREEMDE SLEUTEL

VREEMDE SLEUTEL ( Gerecht, datum) REFERENTIES Menu( Gerecht, datum)) ;

Dan kunt u met de genoemde beperking werken zoals met een databaseobject. Als de naam van de beperking niet is opgegeven, maakt de DBMS-engine deze automatisch aan, waarbij de naam wordt gekozen volgens de regels die in het systeem zijn gedefinieerd.

Een voorbeeld van een script dat door het systeem is gebouwd om de tabel Dish_view_view te maken

MAAK TABEL. [Dish_type_reference] (

IDENTITEIT (1,1) NIET NUL,

[Bekijken] (20) NIET NUL,

BEPERKING PRIMAIRE SLEUTEL GECLUSTERD

) MET (PAD_INDEX = UIT, STATISTICS_NORECOMPUTE = UIT, IGNORE_DUP_KEY = UIT, ALLOW_ROW_LOCKS = AAN, ALLOW_PAGE_LOCKS = AAN) AAN

U hebt enkele van de configuratieconventies gezien die deze benadering gebruikt. U hebt met name gezien hoe u het entiteitsmodel kunt aanpassen met behulp van gegevensannotaties die worden uitgedrukt via C # -attributen en met behulp van de Fluent API. In deze en volgende artikelen gaan we dieper in op deze manieren om het datamodel aan te passen.

Kolomtypen aanpassen

Eerder, toen u naar het gebruik van Code-First keek, zag u al het gebruik van enkele metadata-attributen waarmee u het gegevenstype van een kolom in een databasetabel kon aanpassen en er beperkingen op kon toepassen (bijvoorbeeld door op te geven of het ondersteunt null-waarden). We zullen deze kenmerken hieronder in meer detail bekijken.

Lengtebeperking

De onderstaande tabel toont de conventies voor het beperken van kolomlengtes, hoe deze worden geannoteerd en geïmplementeerd in de Fluent API:

De lengtebeperking kan worden opgelegd aan strings of bytearrays. Volgens conventies gebruikt Code-First alleen de beperking maximale lengte, betekent dit dat SQL Server het gegevenstype voor tekenreeksen en bytearrays instelt op NVARCHAR (n) en VARBINARY (n), waarbij n de lengte is die is opgegeven in de beperking. Als er geen lengtebeperking is toegepast op modeleigenschappen, stelt Code-First standaard de maximaal mogelijke kolomlengten in op NVARCHAR (max) en VARBINARY (max).

Zoals in de tabel wordt getoond, kunt u met behulp van gegevensannotaties de minimumlengte voor een modeleigenschap instellen met behulp van het kenmerk MinLength - deze beperking heeft op geen enkele manier invloed op de databasetabel. (Zoals eerder beschreven kunnen metadata-attributen niet alleen in Entity Framework worden gebruikt, maar bijvoorbeeld ook in ASP.NET-modelvalidatie.) Daarom ontbreekt de methode HasMinLength () in de Fluent API, aangezien deze API maakt deel uit van het Entity Framework en is verantwoordelijk voor het opzetten van Code-First-only-conventies.

Het is vermeldenswaard dat u de maximale en minimale veldlengte in één StringLength-attribuut kunt specificeren met behulp van de benoemde parameters van dit attribuut. Het volgende voorbeeld demonstreert het gebruik van lengtebeperkingen met behulp van annotaties (hier gebruiken we het voorbeeldmodel dat we in het artikel hebben gemaakt "Code eerst gebruiken" eerder):

Systeem gebruiken; met behulp van System.Collections.Generic; met behulp van System.ComponentModel.DataAnnotations; naamruimte CodeFirst (public class Customer (public int CustomerId (get; set;) public string FirstName (get; set;) public string LastName (get; set;) public string Email (get; set;) public int Age (get; set ;) public byte Photo (get; set;) // Link naar openbare virtuele lijst van bestellingen Orders (get; set;)) public class Order (public int OrderId (get; set;) public string ProductName (get; set;) public string Beschrijving (get; set;) public int Aantal (get; set;) public DateTime Aankoopdatum (get; set;) // Link naar de openbare koper Klant Klant (get; set;)))

En in de volgende code wordt een vergelijkbare configuratie uitgevoerd met behulp van de Fluent API (onthoud dat om deze API te gebruiken, u de configuratiemethode moet overschrijven OpModelCreëren () in de klasse context, die in ons voorbeeld de klasse SampleContext is):

() .Eigenschap (c => c.Voornaam) .HasMaxLength (30); modelBuilder.Entity () .Eigenschap (c => c.E-mail) .HasMaxLength (100); modelBuilder.Entity () .Eigenschap (o => o.ProductName) .HasMaxLength (500); )

Expliciet het type van een kolom specificeren

Zoals eerder beschreven, wijst het Entity Framework automatisch modelgegevenstypen toe aan SQL-compatibele gegevenstypen. Met Code-First kunt u dit proces manipuleren om het gegevenstype voor een kolom expliciet op te geven, zoals in het onderstaande voorbeeld:

Public class Customer (public int CustomerId (get; set;) // ... public byte Photo (get; set;) // ...) // hetzelfde met Fluent API-beveiligde overschrijving void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder. Entiteit () .Property (c => c.CustomerId) .HasColumnType ("smallint"); modelBuilder.Entity () .Eigenschap (c => c.Foto) .HasColumnType ("afbeelding"); )

Ondersteuning voor kolom NULL

De Entity Framework Null Support Convention on a Table Column stelt dat alle .NET-typen die null-waarden (objecten) ondersteunen, worden toegewezen aan SQL-typen met een expliciete NULL-instructie, en vice versa, .NET-typen die geen null- (structuren) ondersteunen worden toegewezen aan SQL-typen met een expliciete NOT NULL-instructie.

Om expliciet aan te geven dat een gegevenstype geen null-waarden mag ondersteunen, gebruikt u het vereiste attribuut in het gegevensmodel, of gebruik de methode IsRequired () van het config-object in de Fluent API. Om expliciet aan te geven dat het gegevenstype NULL-waarden moet ondersteunen, moet u de Nullable-verzameling gebruiken of gebruik de C#-syntaxis, die voor waardetypen die null ondersteunen, een vraagteken achter dat type bevat (bijvoorbeeld int?).

Het onderstaande voorbeeld laat zien hoe u deze instellingen kunt gebruiken om informatie over nullable of non-nullable typen in een databasetabel te configureren:

Public class Customer (public int CustomerId (get; set;) public string FirstName (get; set;) public string LastName (get; set;) // Dit veld kan NULL zijn, // omdat we expliciet hebben gespecificeerd int-type? publiek int? Leeftijd (get; set;) // Vergelijkbaar met de vorige eigenschap public Nullable Age1 (get; set;) // ...) // hetzelfde met Fluent API protected override void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.Entity () .Eigenschap (c => c.FirstName) .IsRequired (); modelBuilder.Entity () .Eigenschap (c => c.Achternaam) .IsRequired (); )

Merk op dat alleen NOT NULL-ondersteuning voor referentiegegevenstypen kan worden geconfigureerd in de Fluent API en dat NULL-ondersteuning niet kan worden geconfigureerd voor waardetypen, aangezien NULL-ondersteuning voor hen wordt expliciet gespecificeerd bij het declareren van het type van de eigenschap in de modelklasse.

Primaire sleutels instellen

Entity Framework vereist dat elke entiteitsmodelklasse een unieke sleutel heeft (omdat voor elke tabel in relationele basis gegevens, moet de primaire sleutel worden gebruikt). Deze sleutel wordt in het contextobject gebruikt om wijzigingen in modelobjecten bij te houden. Code-First maakt een aantal aannames bij het opzoeken van een sleutel in een tabel. Toen we bijvoorbeeld eerder de database voor de entiteitsklassen Customer en Order hebben gegenereerd, heeft het Entity Framework de velden CustomerId en OrderId in de tabellen gemarkeerd als primaire sleutels en ingesteld om niet-nullable typen te ondersteunen, toen we naar de Code-First-benadering keken. :

EF heeft ook automatisch ondersteuning voor auto-increment toegevoegd aan deze velden (onthoud dat dit in T-SQL wordt gedaan met behulp van de IDENTITY-instructie). Meestal zijn primaire sleutels in een database van het type INT of GUID, hoewel elke primitief type kan als primaire sleutel worden gebruikt. Een primaire sleutel in een database kan zijn samengesteld uit meerdere tabelkolommen, op dezelfde manier kan een EF-entiteitsmodelsleutel zijn samengesteld uit meerdere modeleigenschappen. U zult later zien hoe u samengestelde sleutels instelt.

Expliciete instelling van primaire sleutels

In het geval van onze twee klassen, Klant en Bestelling, hoeft u zich geen zorgen te maken over het expliciet specificeren van de primaire sleutel, aangezien we gebruiken de eigenschappen CustomerId en OrderId, die de naamgevingsconventie "+ Id" volgen. Laten we eens kijken naar een voorbeeld waarin u expliciet een primaire sleutel moet specificeren. Voeg de volgende eenvoudige klasse toe aan uw modelbestand:

Public class Project (public Guid Identifier (get; set;) public DateTime StartDate (get; set;) public DateTime EndDate (get; set;) public decimaal Kosten (get; set;))

Ons doel is om aan te geven dat de eigenschap Identifier in deze klasse de primaire sleutel van de tabel is. Zoals u kunt zien, volgt de naam van deze eigenschap niet de naamgevingsconventie voor primaire sleutels van Entity Framework.

Voeg een beschrijving van de nieuwe tabel toe aan de contextklasse SampleContext:

Publieke klasse SampleContext: DbContext (// ... public DbSet Projecten (krijgen; instellen;) // ...)

Als u nu onze toepassing uitvoert en de gegevens van een nieuwe klant invoert, wordt er een uitzondering gegenereerd in de klasse DbModelBuilder, omdat het het entiteitsgegevensmodel niet correct kan bouwen. (Ter herinnering: onze applicatie is een eenvoudige ASP.NET-site met de mogelijkheid om een ​​nieuwe klant in te voegen.)

Deze uitzondering wordt veroorzaakt doordat het Entity Framework geen eigenschap met de naam Id of ProjectId vindt in de tabel Project, en Code-First kan niet achterhalen welk veld moet worden gebruikt als de primaire sleutel van de tabel. Dit probleem kan worden gecorrigeerd met behulp van gegevensannotaties of de Fluent API, zoals weergegeven in het onderstaande voorbeeld:

Public class Project (public Guid Identifier (get; set;) public DateTime StartDate (get; set;) public DateTime EndDate (get; set;) public decimaal Kosten (get; set;)) // hetzelfde met Fluent API protected override void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.Entity () .HasKey (p => p.Identifier); )

Merk op dat bij gebruik van de Fluent API de HasKey ()-methode wordt gespecificeerd na de aanroep van de Entity-methode () en niet na het aanroepen van Entity () .Eigenschap (), zoals in de bovenstaande voorbeelden is gedaan, aangezien de primaire sleutel wordt ingesteld op tabelniveau, niet op propertyniveau.

Automatische verhoging instellen voor primaire sleutels

Zoals u in de tabel kunt zien, specificeert Entity Framework volgens conventies auto-incrementing voor int-eigenschappen. In de eerder aangemaakte Project tabel is het Guid type gespecificeerd voor de primaire sleutel, hierdoor gebruikt EF geen teller voor dit veld bij het aanmaken van een tabel in de database. Dit is weergegeven in de figuur:

Laten we een nieuw webformulier aan ons project toevoegen, dat we DatabaseGenerated.aspx zullen noemen. Voeg in de handler Page_Load de volgende code toe waar we nieuwe gegevens aan de tabel Project toevoegen. In dit geval worden deze gegevens toegevoegd wanneer we een pagina van ons webformulier in een browser openen.

Systeem gebruiken; met behulp van System.Data.Entity; met behulp van CodeFirst; namespace ProfessorWeb.EntityFramework (openbare gedeeltelijke klasse DatabaseGenerated: System.Web.UI.Page (protected void Page_Load (objectafzender, EventArgs e)) (// Deze instelling is nodig zodat de database automatisch // wordt verwijderd en opnieuw wordt aangemaakt wanneer de structuur is gewijzigd modellen // (om het gemakkelijker te maken om voorbeelden te testen) Database.SetInitializer (nieuwe DropCreateDatabaseIfModelChanges ()); SampleContext-context = nieuwe SampleContext (); Projectproject= nieuw project (StartDate = DateTime.Now, EndDate = DateTime.Now.AddMonths (1), kosten = 8000M); context.Projecten.Toevoegen (project); context.SaveChanges (); )))

Voer het project uit en open het webformulier DatabaseGenerated.aspx. Als gevolg hiervan wordt een nieuw record toegevoegd aan de tabel Project:

Noch de database, noch het Entity Framework weet wat we zouden willen maken nieuwe identificatie Gids voor elk nieuw record, dus er wordt automatisch een id gegenereerd met allemaal nullen. Als u de pagina in de browser vernieuwt (in dit geval zal de code zelfs proberen een nieuw record in de tabel in te voegen), dan zal Entity Framework een SqlException genereren, die wordt gegenereerd omdat we proberen een record in te voegen dat heeft een identifier die al in de tabel bestaat, bijv. e. in dit geval wordt de primaire sleutelbeperking geactiveerd - deze moet uniek zijn voor elke nieuwe record.

Als gevolg hiervan zouden we, om dit probleem op te lossen, een unieke Guid in de code moeten genereren. Tabellen die automatisch ophogen voor primaire sleutels gebruiken, zijn hiervan verstoken extra werk sinds voor elk nieuw record dat wordt ingevoegd, creëert de teller een nieuwe waarde voor de primaire sleutel door de primaire sleutelwaarde voor het laatste record op te halen en er 1 aan toe te voegen (als IDENTITEIT (1,1) werd gebruikt).

Om dit probleem op te lossen voor andere sleutels dan int, moet u het DatabaseGenerated metadata-attribuut gebruiken, in de constructor waarvan u specificeert DatabaseGeneratedOption opsomming die drie mogelijke waarden heeft:

Geen

De database genereert geen unieke waarde voor de primaire sleutel. Met deze optie kunt u zelfs uitschakelen automatisch toevoegen autoincrement naar primaire sleutels van het type int.

Identiteit

Bij het invoegen van waarden in een tabel, zal de database creëren unieke waarde voor de primaire sleutel.

berekend

Net als bij Identiteit, met als enige uitzondering dat de primaire sleutel niet alleen wordt gegenereerd wanneer records in de tabel worden ingevoegd, maar ook wanneer ze worden bijgewerkt.

Wijzig de modelklasse om de database opdracht te geven een unieke primaire sleutel te maken:

Public class Project (public Guid Identifier (get; set;) public DateTime StartDate (get; set;) public DateTime EndDate (get; set;) public decimaal Kosten (get; set;))

Voer het voorbeeldproject uit en vernieuw de pagina meerdere keren om meerdere records in de tabel in te voegen en ervoor te zorgen dat de uitzondering niet langer wordt gegenereerd. De onderstaande afbeelding toont de gegevens die aan de tabel zijn toegevoegd:

Let op de automatisch gegenereerde ID's voor projecten. Hetzelfde effect kan worden bereikt met de methode HasDatabaseGeneratedOption () in de Fluent API:

Beveiligde overschrijving ongeldig OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.Entity () .Property (p => p.Identifier) ​​​​HasDatabaseGeneratedOption (DatabaseGeneratedOption.Identity); )

Werken met complexe gegevenstypen

Entity Framework ondersteunt de mogelijkheid om complexe typen te gebruiken sinds de eerste versie. In feite is een complex type in .NET een klasse waarnaar kan worden verwezen in een modelklasse. Een complex type heeft geen sleutel en kan voor meerdere objecten in een model worden gebruikt. Laten we een voorbeeld nemen van het volgende model:

Public class Gebruiker (public int UserId (get; set;) public int SocialNumber (get; set;) public string FirstName (get; set;) public string LastName (get; set;) public string StreetAddress (get; set;) public string Stad (get; set;) openbare string ZipCode (get; set;))

In deze klasse kan het woonadres van de gebruiker worden opgedeeld in een aparte klasse en ernaar verwijzen:

Public class Gebruiker (public int UserId (get; set;) public int SocialNumber (get; set;) public string FirstName (get; set;) public string LastName (get; set;) public Address Address (get; set;)) public class Address (public int AddressId (get; set;) public string StreetAddress (get; set;) public string City (get; set;) public string ZipCode (get; set;))

Volgens afspraak zal het Entity Framework dit model ontleden als twee afzonderlijke tabellen. Maar ons doel is om een ​​complex type te maken van de klasse Address. De traditionele manier om een ​​complex type van de klasse Address te maken, is door de AddressId te verwijderen:

Public class Address (// public int AddressId (get; set;) public string StreetAddress (get; set;) public string City (get; set;) public string ZipCode (get; set;))

Naast de regel dat een complex type geen sleutel mag hebben, legt Code-First nog twee andere regels op die moeten worden gevolgd om een ​​complex type te detecteren. Ten eerste mag een complex type alleen eenvoudige eigenschappen bevatten. Ten tweede mag een klasse die dit type gebruikt geen verzamelingstype specificeren voor een complexe typeeigenschap. Met andere woorden, als u het complexe type Address in de klasse User wilt gebruiken, moet een eigenschap van dit type niet worden gemarkeerd als List

of gebruik een andere collectie.

Zoals te zien is in de onderstaande afbeelding, herkent Code-First na het uitvoeren van de toepassing het complexe type en maakt aangepaste velden in de tabel User (vergeet niet om de User-declaratie in de contextklasse toe te voegen):

Merk op hoe de velden die het adres van de gebruiker beschrijven heten: ComplexTypeName_PropertyName. Dit is de naamgevingsconventie van het Entity Framework voor complexe typen.

Complexe typen aanpassen en code-first-conventies omzeilen

Wat als uw complexe typeklasse niet voldoet aan de conventies van het Entity Framework, bijvoorbeeld als u het veld AddressId in de Address-klasse wilt gebruiken? Als we dit veld nu toevoegen aan de Address-klasse en het project uitvoeren, dan zal Entity Framework in plaats van één User-tabel en een complex Address-type twee tabellen creëren die aan elkaar zijn gerelateerd door een externe sleutel. Om dit probleem op te lossen, kunt u expliciet specificeren: ComplexType-kenmerk in de modelklasse of gebruik ComplexType () methode de klasse DbModelBuilder in de Fluent API:

Public class Address (public int AddressId (get; set;) public string StreetAddress (get; set;) public string City (get; set;) public string ZipCode (get; set;)) // same with Fluent API protected override void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.ComplexType

(); }

Er werd hierboven gezegd dat een klasse die een complex type beschrijft alleen eenvoudige eigenschappen zou moeten hebben (d.w.z. niet verwijzend naar andere objecten). Deze overeenkomst kan met dezelfde middelen worden overwonnen. Hieronder ziet u een voorbeeld waarin een nieuw complex type UserInfo is toegevoegd dat verwijst naar een ander FullName-type:

Public class User (public int UserId (get; set;) public UserInfo UserInfo (get; set;) public Address Address (get; set;)) public class UserInfo (public int SocialNumber (get; set;) // Not simple property public FullName FullName (get; set;)) public class FullName (public string FirstName (get; set;) public string LastName (get; set;)) public class Address (public int AddressId (get; set;) public string StreetAddress ( get; set;) public string Stad (get; set;) public string Postcode (get; set;))

Door Code-First te instrueren dat UserInfo een complex type is met het kenmerk ComplexType, hebben we de beperking van complexe typen overwonnen door de standaardconventie te gebruiken.

Het is vermeldenswaard dat u met Code-First complexe typen kunt aanpassen, net als gewone tabellen met behulp van de Fluent API of annotaties. Hieronder ziet u een voorbeeld voor het instellen van een complex type Adres:

Public class Address (public int AddressId (get; set;) public string StreetAddress (get; set;) public string City (get; set;) public string ZipCode (get; set;)) // same with Fluent API protected override void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.ComplexType

() .Eigenschap (a => a.StreetAddress) .HasMaxLength (100); )

De onderstaande afbeelding toont de structuur van de gebruikerstabel. Hier kun je zien hoe EF eigenschappen benoemt van complexe typen met verwijzingen erin en hoe EF een beperking oplegt aan het veld StreetAddress:

Beschrijving van andere instellingen

In deze sectie zullen we kort alle overige tabelkolominstellingen bekijken, die vanwege hun specifieke kenmerken zelden worden gebruikt.

Tijdstempelkolommen

Het gegevenstype TIMESTAMP in T-SQL geeft een kolom op die is gedefinieerd als VARBINARY (8) of BINARY (8), afhankelijk van de kolomeigenschap, als NULL. Voor elke database houdt het systeem een ​​teller bij die oploopt wanneer een rij met een TIMESTAMP-cel wordt ingevoegd of bijgewerkt en die cel aan die cel toewijst. Met behulp van cellen van het type TIMESTAMP kunt u dus de relatieve tijd bepalen laatste wijziging overeenkomstige rijen van de tabel. (ROWVERSION is synoniem met TIMESTAMP.)

Op zichzelf is de waarde die is opgeslagen in een TIMESTAMP-kolom niet belangrijk. Deze kolom wordt vaak gebruikt om te bepalen of een specifieke rij in een tabel is gewijzigd sinds de laatste keer dat deze is geopend. Hierdoor kan gelijktijdige toegang tot een databasetabel worden aangepakt door andere threads te laten blokkeren als de huidige thread waarden op een rij heeft gewijzigd.

In Code-First voor het specificeren dat een kolom moet hebben TIMESTAMP-type dezelfde naam moet worden gebruikt Tijdstempelkenmerk in annotaties of IsRowVersion () methode in de Fluent API zoals weergegeven in het onderstaande voorbeeld:

Public byte RowVersion (get; set;) // hetzelfde met Fluent API-beveiligde overschrijving void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.Entity () .Eigenschap (p => p.RowVersion) .IsRowVersion (); )

Een minder gebruikelijke manier om veilig te werken met parallelle threads is om voor elke kolom een ​​gelijktijdigheidscontrole op te geven. Deze methode kan nog steeds worden gebruikt in DBMS'en die geen Timestamp / Rowversion-typen ondersteunen. Als u op deze manier werkt, controleert de thread niet of het record in de tabel is gewijzigd, maar blokkeert hij eenvoudig de toegang ertoe voor andere threads totdat het schrijfproces is voltooid. Om de kolommen te specificeren die door de gelijktijdigheidscontrole moeten komen, gebruik ConcurrencyCheck attribuut in annotaties, of IsConcurrencyToken () methode in de Fluent API.

Stringcodering wijzigen van Unicode naar ASCII

Standaard converteert het Entity Framework alle gegevenstypen van modeltekenreeksen, zoals tekenreeks of char, naar SQL-tekenreeksgegevenstypen die de dubbelbyte Unicode-codering NVARCHAR of NCHAR gebruiken. Je kunt dit gedrag veranderen en EF expliciet vertellen om een ​​enkele byte te gebruiken ASCII-codering- respectievelijk de typen VARCHAR en CHAR zullen worden gebruikt. Om dit te doen, moet je IsUnicode () methode met een booleaanse parameter die eraan is doorgegeven in de Fluent API. Annotaties bieden niet de mogelijkheid om de codering van tekenreeksen aan te passen.

Precisie specificeren voor een decimaal type

Om de nauwkeurigheid aan te geven voor: Decimaal type(aantal cijfers in een getal) en schaal (aantal cijfers rechts van de komma in een getal) kunnen worden gebruikt HasPrecision () methode met twee parameters die eraan worden doorgegeven, die worden gebruikt in de Fluent API. Gegevensannotaties in Code-First bieden geen alternatief voor deze methode. Standaard stelt het Entity Framework de precisie in op 18 en de schaal op 2 voor decimale typen.

Het onderstaande voorbeeld demonstreert het gebruik van deze methode voor de eigenschap Kosten van de tabel Project die we eerder hebben gemaakt bij het bekijken van primaire sleutels:

Beveiligde overschrijving ongeldig OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.Entity () .Eigendom (p => p.Kosten) .HasPrecision (6, 3); )