Cum se configurează smartphone-uri și PC-uri. Portal informativ
  • Acasă
  • Fier
  • Indicarea explicită a tipului de coloană. Schimbarea codificării șirurilor din Unicode în ASCII

Indicarea explicită a tipului de coloană. Schimbarea codificării șirurilor din Unicode în ASCII

Indică faptul că noua coloană este o coloană de identitate. Când sunt adăugate la o masă linie nouă motorul bazei de date generează o valoare secvenţială unică pentru această coloană. Coloanele de identitate sunt utilizate de obicei cu o constrângere PRIMARY KEY pentru a menține identificatorii unici de rând într-un tabel. Proprietatea IDENTITATE poate fi atribuită coloanelor tinyint, smallint, int, bigint, zecimal (p, 0) sau numerice (p, 0). Numai o coloană de identitate poate fi creată pentru fiecare tabel. Valorile implicite nu pot fi utilizate în coloana de identitate. Trebuie specificat ca valoarea initiala, și incrementați sau nu specificați nimic. Dacă niciuna dintre valori nu este specificată, valoarea implicită este (1,1).

Sintaxă:

[IDENTITATE [(sămânță, creștere)]

seed - valoarea folosită pentru primul rând încărcat în tabel.

increment - s valoarea de increment de adăugat la valoarea de identificare a rândului încărcată anterior.

[ Orarul cameră] IDENTITATE INT (1, 1) CHEIE PRIMARĂ NU NULĂ

    Câmpuri calculate.

Sintaxă:

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

O expresie care specifică valoarea unei coloane calculate. O coloană calculată este o coloană virtuală care nu este stocată fizic în tabel decât dacă este setat indicatorul PERSISTED pentru aceasta. Valoarea coloanei este calculată pe baza unei expresii care utilizează alte coloane din același tabel.

De exemplu, o definiție de coloană calculată ar putea fi după cum urmează:

Costul bunurilor LA FEL DE Pret pentru unul * Cantitate.

O expresie poate fi un nume de coloană necalculat, o constantă, o funcție, o variabilă sau orice combinație a acestora, conectată prin unul sau mai mulți operatori. Expresia nu poate fi o subinterogare sau nu poate conține aliasuri de tip de date.

Coloanele calculate pot fi folosite în listele de selectare, clauzele WHERE, clauzele ORDER BY și oriunde altundeva unde pot fi folosite expresii regulate, cu excepția cazurilor următoare.

O coloană calculată nu poate fi utilizată ca definiție a unei constrângeri DEFAULT sau FOREIGN KEY sau împreună cu o definiție a constrângerii NOT NULL. Cu toate acestea, o coloană calculată poate fi utilizată ca coloană cheie index sau ca parte a unei constrângeri PRIMARY KEY sau UNIQUE dacă valoarea coloanei calculate este determinată de o expresie deterministă (previzibilă) și tipul de date al rezultatului este permis în coloanele index. .

De exemplu, dacă tabelul conține coloane întregi Ași b, coloană calculată a + b pot fi incluse în index și coloana calculată a + DATEPART (dd, GETDATE ())- nu poate, deoarece valoarea sa se poate modifica la apelurile ulterioare.

O coloană calculată nu poate fi coloana țintă a unei instrucțiuni INSERT sau UPDATE.

SGBD determină automat anulabilitatea pentru coloanele calculate pe baza expresiilor utilizate. Se presupune că rezultatul majorității expresiilor este null, chiar dacă sunt folosite numai coloane care nu sunt null, deoarece o posibilă depășire sau pierdere de precizie poate avea ca rezultat o valoare nulă. Pentru a afla dacă o coloană calculată a unui tabel poate fi nulă, utilizați funcția COLUMNPROPERTY cu proprietatea PermiteNull... Asigurați-vă că expresia nu permite valori nule, puteți specificând ISNULL cu constanta expresie_verificare unde constantă este o valoare diferită de zero, înlocuind orice valoare NULL. Coloanele bazate pe expresii calculate care conțin UDT-uri CLR necesită permisiunea REFERENȚE pentru tip.

Indică faptul că componenta SQL Server va stoca fizic valorile calculate în tabel și le va actualiza ori de câte ori orice coloană de care coloana calculată depinde de modificări. Specificarea PERSISTED pe o coloană calculată vă permite să creați un index pe coloana calculată care este determinist, dar imprecis.

La crearea unui tabelÎn plus față de tehnicile discutate, puteți specifica cuvântul cheie opțional CONSTRAINT pentru a da constrângerii un nume unic în baza de date.

CREA T CAPABIL Comenzi(

ID_ComandăINT NU NULL ,

Farfurie INT NU NULL,

Cantitate_ portii INT NOT NULL CHECK ( Cantitate_ portii >0 ) ,

Data DATA NU NUL ,

CHEIA PRINCIPALA ( ID_Comandă, Farfurie, Data),

CONSTRÂNGERE CHEIE EXTERNĂ

CHEIE EXTERNĂ ( Farfurie, Data) REFERINȚE Meniul( Farfurie, Data)) ;

Apoi puteți lucra cu constrângerea numită ca și cu un obiect de bază de date. Dacă numele constrângerii nu este specificat, motorul DBMS îl va crea automat, alegând numele conform regulilor definite în sistem.

Un exemplu de script construit de sistem pentru a crea tabelul Dish_view_view

CREATE TABLE. [Dish_type_reference] (

IDENTITATE (1,1) NU NUL,

[Vizualizare] (20) NOT NULL,

CONSTRING CHEIE PRIMARĂ CLUSTER

) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ACTIVAT

Ați văzut unele dintre convențiile de configurare pe care le utilizează această abordare. În special, ați văzut cum să personalizați modelul de entitate folosind adnotări de date exprimate prin atribute C # și folosind API-ul Fluent. În acest articol și în articolele ulterioare, vom arunca o privire mai atentă asupra acestor modalități de personalizare a modelului de date.

Personalizarea tipurilor de coloane

Mai devreme, când ați analizat cazul de utilizare Code-First, ați văzut deja utilizarea unor atribute de metadate care vă permiteau să personalizați tipul de date al unei coloane într-un tabel de bază de date și să îi aplicați constrângeri (de exemplu, specificând dacă acceptă valori nule). Ne vom uita la aceste atribute mai detaliat mai jos.

Limitare de lungime

Tabelul de mai jos arată convențiile pentru limitarea lungimii coloanelor, cum sunt adnotate și implementate în API-ul Fluent:

Constrângerea de lungime poate fi impusă șirurilor de caractere sau matricelor de octeți. Prin convenție, Code-First utilizează numai constrângerea de lungime maximă, ceea ce înseamnă că SQL Server setează tipul de date pentru șiruri și matrice de octeți la NVARCHAR (n) și VARBINARY (n), unde n este lungimea specificată în constrângere. În mod implicit, dacă nu a fost aplicată nicio constrângere de lungime proprietăților modelului, Code-First va seta lungimile maxime posibile de coloană la NVARCHAR (max) și VARBINARY (max).

După cum se arată în tabel, folosind adnotări de date, puteți seta lungimea minimă pentru o proprietate de model folosind atributul MinLength - această restricție nu va afecta în niciun fel tabelul bazei de date. (Așa cum s-a descris mai devreme, atributele metadatelor pot fi utilizate nu numai în Entity Framework, ci, de exemplu, și în validarea modelului ASP.NET.) De aceea, metoda HasMinLength () lipsește din API-ul Fluent, deoarece acest API face parte din Entity Framework și este responsabil pentru configurarea convențiilor Code-First-only.

Este de remarcat faptul că puteți specifica lungimea maximă și minimă a câmpului într-un atribut StringLength folosind parametrii numiți ai acestui atribut. V următorul exemplu arată utilizarea constrângerilor de lungime folosind adnotări (aici folosim modelul exemplu pe care l-am creat în articolul „Using Code-First” mai devreme):

Utilizarea sistemului; folosind System.Collections.Generic; folosind System.ComponentModel.DataAnnotations; namespace CodeFirst (clasa publică Client (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 Fotografie (get; set;) // Link către comenzi Lista virtuală publică Orders (get; set;)) public class Order (public int OrderId (get; set;) public string ProductName (get; set;) public string Descriere (get; set;) public int Cantitate (get; set;) public DateTime PurchaseDate (get; set;) // Link către publicul cumpărător Client Client (get; set;)))

Și în următorul cod setare similară se face folosind API-ul Fluent (rețineți că, pentru a utiliza acest API, trebuie să înlocuiți metoda de configurare OnModelCreating ()în clasa context, care în exemplul nostru este clasa SampleContext):

() .Proprietate (c => c.FirstName) .HasMaxLength (30); modelBuilder.Entity () .Proprietate (c => c.Email) .HasMaxLength (100); modelBuilder.Entity () .Property (o => o.ProductName) .HasMaxLength (500); )

Specificarea explicită a tipului unei coloane

După cum s-a descris mai devreme, Entity Framework mapează automat tipurile de date model la tipuri de date conforme cu SQL. Code-First vă permite să manipulați acest proces pentru a specifica în mod explicit tipul de date pentru o coloană, așa cum se arată în exemplul de mai jos:

Clasa publică Client (public int CustomerId (get; set;) // ... public byte Photo (get; set;) // ...) // la fel cu Fluent API protected override void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder. Entitate () .Property (c => c.CustomerId) .HasColumnType ("smallint"); modelBuilder.Entity () .Property (c => c.Photo) .HasColumnType ("imagine"); )

Suport pentru coloană NULL

Convenția Entity Framework Null Support pe o coloană de tabel afirmă că toate tipurile .NET care acceptă valori nule (obiecte) sunt mapate la tipurile SQL care nu acceptă în mod explicit și invers, tipurile .NET care nu acceptă (structuri) nule sunt mapate la Tipuri SQL cu o instrucțiune explicită NOT NULL.

Pentru a indica în mod explicit că un tip de date nu ar trebui să accepte valori nule, trebuie să utilizați atributul Required pe modelul de date sau să utilizați metoda IsRequired () a obiectului de configurare din API-ul Fluent. Pentru a indica în mod explicit, dimpotrivă, că tipul de date ar trebui să accepte valori NULL, trebuie să utilizați colecția Nullable sau utilizați sintaxa C #, care pentru tipurile de valori care acceptă null include un semn de întrebare după acel tip (de exemplu, int?).

Exemplul de mai jos arată cum să utilizați aceste setări pentru a configura informații despre tipurile nullabile sau nenullabile într-un tabel de bază de date:

Clasa publică Client (public int CustomerId (get; set;) public string FirstName (get; set;) public string LastName (get; set;) // Acest câmp poate fi NULL, // deoarece am specificat în mod explicit tipul int? public int? Age (get; set;) // Similar cu proprietatea anterioară public Nullable Age1 (get; set;) // ...) // la fel cu Fluent API protected override void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.Entity () .Property (c => c.FirstName) .IsRequired (); modelBuilder.Entity () .Property (c => c.LastName) .IsRequired (); )

Rețineți că numai suportul NOT NULL pentru tipurile de date de referință poate fi configurat în API-ul Fluent, iar suportul NULL nu poate fi configurat pentru tipurile de valori, deoarece Suportul NULL pentru ele este specificat în mod explicit atunci când se declară tipul proprietății în clasa modelului.

Setarea cheilor primare

Entity Framework necesită ca fiecare clasă de model de entitate să aibă o cheie unică (deoarece pentru fiecare tabel din bază relațională date, trebuie utilizată cheia primară). Această cheie este utilizată în obiectul context pentru a urmări modificările în obiectele model. Code-First face câteva presupuneri atunci când caută o cheie într-un tabel. De exemplu, când am generat mai devreme baza de date pentru clasele de entități Customer și Order, când analizăm abordarea Code-First, Entity Framework a marcat câmpurile CustomerId și OrderId din tabele ca chei primare și le-a setat să accepte tipuri nenullabile. :

De asemenea, EF a adăugat automat suport pentru incrementarea automată la aceste câmpuri (rețineți că în T-SQL acest lucru se face folosind instrucțiunea IDENTITY). Cel mai adesea, cheile primare dintr-o bază de date sunt de tip INT sau GUID, deși există tip primitiv poate fi folosită ca cheie primară. O cheie primară dintr-o bază de date poate fi compusă din mai multe coloane de tabel, în mod similar, o cheie de model de entitate EF poate fi compusă din mai multe proprietăți de model. Veți vedea mai târziu cum să configurați cheile compuse.

Setarea explicită a cheilor primare

În cazul celor două clase ale noastre, Client și Comandă, nu este nevoie să vă faceți griji cu privire la specificarea explicită a cheii primare, deoarece folosim proprietățile CustomerId și OrderId, care urmează convenția de denumire a tastei „+ Id”. Să ne uităm la un exemplu în care trebuie să specificați în mod explicit o cheie primară. Adăugați următoarea clasă simplă la fișierul dvs. model:

Proiect de clasă publică (identificator de ghid public (get; set;) public DateTime StartDate (get; set;) public DateTime EndDate (get; set;) public zecimal Cost (get; set;))

Scopul nostru este de a indica faptul că proprietatea Identifier din această clasă este cheia primară a tabelului. După cum puteți vedea, numele acestei proprietăți nu urmează convenția de denumire a cheii primare a Entity Framework.

Adăugați o descriere a noului tabel la clasa de context SampleContext:

Clasa publică SampleContext: DbContext (// ... public DbSet Proiecte (get; set;) // ...)

Acum, dacă rulați aplicația noastră și introduceți datele unui client nou, se va arunca o excepție în clasa DbModelBuilder din cauza faptului că nu poate construi corect modelul de date al entității. (Pentru a vă reaminti, aplicația noastră este un site simplu ASP.NET cu posibilitatea de a introduce un client nou.)

Această excepție este cauzată de faptul că Entity Framework nu găsește o proprietate numită Id sau ProjectId în tabelul Project și Code-First nu poate afla ce câmp să folosească ca cheie primară a tabelului. Această problemă poate fi corectată folosind adnotări de date sau API-ul Fluent, așa cum se arată în exemplul de mai jos:

Proiect de clasă publică (identificator de ghid public (get; set;) public DateTime StartDate (get; set;) public DateTime EndDate (get; set;) public zecimal Cost (get; set;)) // la fel cu Fluent API protected override void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.Entity () .HasKey (p => p.Identifier); )

Vă rugăm să rețineți că atunci când utilizați Fluent Metoda API HasKey () este specificat după apelarea metodei Entity () și nu după apelarea Entității () .Proprietate (), așa cum s-a făcut în exemplele de mai sus, întrucât cheia primară este setată la nivel de tabel, nu la nivel de proprietate.

Configurarea autoincrementului pentru cheile primare

După cum puteți vedea din tabel, urmând convențiile, Entity Framework specifică incrementarea automată pentru proprietățile int. În tabelul Proiect creat anterior, tipul Guid este specificat pentru cheia primară, ca urmare, EF nu folosește un contor pentru acest câmp atunci când creează un tabel în baza de date. Acest lucru este prezentat în figură:

Să adăugăm un nou formular web la proiectul nostru, pe care îl vom numi DatabaseGenerated.aspx. În gestionarea Page_Load, adăugați următorul cod în care adăugăm date noi în tabelul Project. V în acest caz aceste date vor fi adăugate ori de câte ori deschidem pagina noastră de formulare web într-un browser.

Utilizarea sistemului; folosind System.Data.Entity; folosind CodeFirst; namespace ProfessorWeb.EntityFramework (clasa parțială publică DatabaseGenerated: System.Web.UI.Page (protejat void Page_Load (expedător obiect, EventArgs e)) (// Această setare este necesară pentru ca baza de date să fie automat // ștearsă și recreată când structura este modificată modelele // (pentru a facilita testarea exemplelor) Database.SetInitializer (nou DropCreateDatabaseIfModelChanges ()); SampleContext context = nou SampleContext (); Proiect proiect = proiect nou (StartDate = DateTime.Now, EndDate = DateTime.Now.AddMonths (1), Cost = 8000M); context.Proiecte.Add (proiect); context.SaveChanges (); )))

Rulați proiectul și deschideți formularul web DatabaseGenerated.aspx. Ca rezultat, o nouă înregistrare va fi adăugată la tabelul Proiect:

Nici baza de date, nici Entity Framework nu știe că am dori să creăm un nou Guid pentru fiecare înregistrare nouă, așa că va fi generat automat un ID care conține toate zerourile. Dacă reîmprospătați pagina în browser (de fapt, în acest caz, codul va încerca să se introducă intrare nouăîn tabel), atunci Entity Framework va returna o SqlException, care este aruncată deoarece încercăm să inserăm o înregistrare cu un identificator care există deja în tabel, de exemplu. în acest caz, constrângerea cheii primare este declanșată - trebuie să fie unică pentru fiecare înregistrare nouă.

Drept urmare, să decidă această problemă, ar trebui să generăm un Guid unic în cod. Tabelele care utilizează incrementarea automată pentru cheile primare nu au această muncă suplimentară. pentru fiecare înregistrare nouă introdusă, contorul creează o nouă valoare pentru cheia primară, regăsind valoarea cheii primare pentru ultima înregistrare și adăugând 1 la aceasta (dacă s-a folosit IDENTITATE (1,1)).

Pentru a rezolva această problemă pentru chei de alt tip decât int, trebuie să utilizați atributul metadate DatabaseGenerated, în constructorul căruia îl specificați Enumerarea DatabaseGeneratedOption având trei sensuri posibile:

Nici unul

Baza de date nu generează nicio valoare unică pentru cheia primară. De fapt, folosind această opțiune, puteți dezactiva adăugarea automată a autoincrementului la cheile primare de tip int.

Identitate

Când se inserează valori într-un tabel, se va crea baza de date valoare unică pentru cheia primară.

Calculat

Similar cu Identity, cu singura excepție că cheia primară va fi generată nu numai atunci când înregistrările sunt introduse în tabel, ci și când sunt actualizate.

Modificați clasa modelului pentru a instrui baza de date să creeze o cheie primară unică:

Proiect de clasă publică (identificator de ghid public (get; set;) public DateTime StartDate (get; set;) public DateTime EndDate (get; set;) public zecimal Cost (get; set;))

Rulați proiectul exemplu și reîmprospătați pagina de mai multe ori pentru a insera mai multe înregistrări în tabel și pentru a vă asigura că excepția nu mai este aruncată. Figura de mai jos prezintă datele adăugate în tabel:

Acordați atenție ID-urilor generate automat pentru proiecte. Același efect poate fi obținut folosind metoda HasDatabaseGeneratedOption () din API-ul Fluent:

Suprascriere protejată void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.Entity () .Proprietate (p => p.Identifier) ​​​​.HasDatabaseGeneratedOption (DatabaseGeneratedOption.Identity); )

Lucrul cu tipuri de date complexe

Entity Framework a susținut capacitatea de a utiliza tipuri complexe încă de la prima versiune. De fapt, un tip complex în .NET este o clasă care poate fi referită într-o clasă model. Un tip complex nu are cheie și poate fi utilizat pe mai multe obiecte dintr-un model. Să luăm un exemplu cu următorul model:

Clasa publică Utilizator (public int UserId (get; set;) public int SocialNumber (get; set;) public șir FirstName (get; set;) public șir LastName (get; set;) public șir StreetAddress (get; set;) public șir Oraș (get; set;) public șir ZipCode (get; set;))

În această clasă, adresa de reședință a utilizatorului poate fi distinsă în clasa separatași pentru a face referire la el:

Clasa publică Utilizator (public int UserId (get; set;) public int SocialNumber (get; set;) public string FirstName (get; set;) public string LastName (get; set;) public Address Adresa (get; set;)) public class Address (public int AddressId (get; set;) public string StreetAddress (get; set;) public string Oraș (get; set;) public string ZipCode (get; set;))

Prin convenție, Entity Framework va analiza acest model ca două tabele separate. Dar scopul nostru este să creăm un tip complex din clasa Address. Modul tradițional pentru a crea un tip complex din clasa Address introduce eliminarea AddressId:

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

Pe lângă regula conform căreia un tip complex nu trebuie să aibă o cheie, Code-First impune alte două reguli care trebuie urmate pentru a detecta un tip complex. În primul rând, un tip complex ar trebui să conțină numai proprietăți simple. În al doilea rând, o clasă care utilizează acest tip nu are voie să specifice un tip de colecție pentru o proprietate de tip complex. Cu alte cuvinte, dacă doriți să utilizați tipul complex Adresă în clasa Utilizator, atunci o proprietate de acest tip nu ar trebui să fie marcată ca Listă

sau folosiți o altă colecție.

După cum se arată în imaginea de mai jos, după rularea aplicației, Code-First recunoaște tipul complex și creează câmpuri personalizate în tabelul User (nu uitați să adăugați declarația User în clasa de context):

Observați cum sunt denumite câmpurile care descriu adresa utilizatorului: ComplexTypeName_PropertyName. Aceasta este convenția de denumire a Entity Framework pentru tipurile complexe.

Personalizarea tipurilor complexe, ocolind convențiile Code-First

Ce se întâmplă dacă clasa dvs. de tip complex nu respectă convențiile Entity Framework, de exemplu, doriți să utilizați câmpul AddressId din clasa Address? Dacă acum adăugăm acest câmp la clasa Address și rulăm proiectul, atunci în loc de un tabel User și un tip complex de Adresă, Entity Framework va crea două tabele legate între ele printr-o cheie străină. Pentru a remedia această problemă, puteți specifica în mod explicit Atribut ComplexTypeîn clasa de model sau utilizare Metoda ComplexType (). clasa DbModelBuilder din API-ul Fluent:

Public class Address (public int AddressId (get; set;) public string StreetAddress (get; set;) public string Oraș (get; set;) public string ZipCode (get; set;)) // la fel cu Fluent API protected override void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.ComplexType

(); }

S-a spus mai sus că o clasă care descrie un tip complex ar trebui să aibă numai proprietăți simple (adică să nu se refere la alte obiecte). Acest acord poate fi depășit folosind aceleași mijloace. Mai jos este un exemplu în care a fost adăugat un nou tip complex UserInfo care face referire la un alt tip FullName:

Clasa publică User (public int UserId (get; set;) public UserInfo UserInfo (get; set;) public Address Address (get; set;)) public class UserInfo (public int SocialNumber (get; set;) // Nu este o proprietate simplă public FullName FullName (get; set;)) public class FullName (șir public FirstName (get; set;) public string LastName (get; set;)) public class Address (public int AddressId (get; set;) public string StreetAddress ( get; set;) public șir Oraș (get; set;) public șir ZipCode (get; set;))

Instruind Code-First că UserInfo este un tip complex folosind atributul ComplexType, am depășit limitarea tipurilor complexe folosind convenția implicită.

Merită remarcat faptul că Code-First vă permite să personalizați tipuri complexe la fel ca tabelele obișnuite, folosind API-ul Fluent sau adnotări. Mai jos este un exemplu pentru configurarea unei adrese de tip complex:

Public class Address (public int AddressId (get; set;) public string StreetAddress (get; set;) public string Oraș (get; set;) public string ZipCode (get; set;)) // la fel cu Fluent API protected override void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.ComplexType

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

Figura de mai jos arată structura tabelului User. Aici puteți vedea cum EF denumește proprietățile unor tipuri complexe cu referințe în interior și cum EF impune o constrângere în câmpul StreetAddress:

Descrierea altor setări

În această secțiune, vom analiza pe scurt toate setările rămase ale coloanei tabelului, care sunt rareori utilizate datorită caracteristicilor lor specifice.

Coloane de marcaj temporal

Tipul de date TIMESTAMP în T-SQL specifică o coloană care este definită ca VARBINARY (8) sau BINARY (8), în funcție de proprietatea coloanei, să fie NULL. Pentru fiecare bază de date, sistemul conține un contor care crește ori de câte ori este inserat sau actualizat orice rând care conține o celulă TIMESTAMP și se atribuie acelei celule. valoare dată... Astfel, folosind celule de tip TIMESTAMP, puteți determina timpul relativ ultima schimbare rândurile corespunzătoare ale tabelului. (ROWVERSION este sinonim cu TIMESTAMP.)

În sine, valoarea stocată într-o coloană TIMESTAMP nu este importantă. Această coloană este folosită în mod obișnuit pentru a determina dacă un anumit rând dintr-un tabel s-a modificat de la ultima accesare. Acest lucru permite accesul concurent la un tabel al bazei de date, permițând blocării altor fire de execuție dacă firul de execuție curent și-a modificat valorile la rând.

În Code-First, pentru a indica faptul că o coloană ar trebui să fie de tip TIMESTAMP, trebuie folosit același nume Atribut marca temporalăîn adnotări sau Metoda IsRowVersion ().în API-ul Fluent, așa cum se arată în exemplul de mai jos:

Octet public RowVersion (get; set;) // la fel cu Fluent API protected override void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.Entity () .Proprietate (p => p.RowVersion) .IsRowVersion (); )

O modalitate mai puțin obișnuită de a fi în siguranță atunci când lucrați cu fire paralele este de a specifica o verificare a concurenței pentru fiecare coloană. Această metodă poate fi folosită în continuare în SGBD-uri care nu acceptă tipurile de timestamp/Rowversion. Când lucrează în acest fel, firul de execuție nu verifică dacă înregistrarea din tabel s-a schimbat, ci pur și simplu blochează accesul la ea pentru alte fire până când încheie procesul de scriere. Pentru a specifica coloanele pentru a trece verificarea concurenței, utilizați Atributul ConcurrencyCheckîn adnotări, sau Metoda IsConcurrencyToken ().în API-ul Fluent.

Schimbarea codificării șirurilor din Unicode în ASCII

În mod implicit, Entity Framework va converti totul tipuri de șiruri date model, cum ar fi tipuri de date șir sau char la șir SQL care utilizează codificarea Unicode pe doi octeți NVARCHAR sau NCHAR. Puteți modifica acest comportament pentru a-i spune în mod explicit lui EF să folosească codificarea ASCII pe un singur octet - VARCHAR și CHAR vor fi utilizate în consecință. Pentru a face acest lucru, trebuie să utilizați Metoda IsUnicode (). cu un parametru boolean transmis în API-ul Fluent. Adnotările nu oferă posibilitatea de a personaliza codificarea șirurilor.

Specificarea preciziei pentru un tip zecimal

Poți să folosești Metoda HasPrecision (). cu doi parametri trecuți acestuia, care este utilizat în API-ul Fluent. Adnotările de date din Code-First nu oferă o alternativă la această metodă. În mod implicit, Entity Framework setează precizia la 18 și scara la 2 pentru tipurile Decimal.

Exemplul de mai jos demonstrează utilizarea acestei metode pentru proprietatea Cost a tabelului Proiect pe care l-am creat mai devreme când ne uităm la cheile primare:

Suprascriere protejată void OnModelCreating (DbModelBuilder modelBuilder) (modelBuilder.Entity () .Proprietate (p => p.Cost) .HasPrecision (6, 3); )

Ce este IDENTITATEA

IDENTITY nu este un tip de date. Aceasta este o proprietate suplimentară, o limitare impusă tipurilor de date întregi în MS SQL Server. Acestea. această proprietate poate fi aplicată câmpurilor de următoarele tipuri: tinyint, smallint, int, bigint, zecimală (p, 0), sau numeric (p, 0)

Analogul FoxPro al acestei proprietăți este tipul de date Integer-AutoIncrement. Doar nu presupuneți că Integer-AutoIncrement este câmpul cu proprietatea Identity. Deloc. Acesta este exact analogul. Ele sunt în mare parte asemănătoare, dar au o serie de diferențe. Acest articol se va concentra pe proprietatea IDENTITY din serverul MS SQL.

Câmpurile cu proprietatea IDENTITY au următoarele caracteristici:

  • Un singur câmp cu proprietatea IDENTITY poate exista într-un singur tabel.
  • Un câmp cu proprietatea IDENTITY nu poate fi editat. Sunt doar pentru citire.
  • Valoarea câmpului cu proprietatea IDENTITATE este atribuită automat atunci când este creată o nouă înregistrare.

Mai sunt câteva caracteristici, dar sunt deja o consecință a caracteristicilor enumerate.

Noua valoare este ultima valoare utilizată plus o valoare fixă. Rețineți că noua valoare nu se bazează pe valoarea maximă în înregistrările existente, și după ultima valoare utilizată. Aceasta înseamnă că înregistrările cu ultima valoare utilizată pot să nu existe fizic, însă această valoare va fi utilizată.

Cu alte cuvinte, găurile din secvența valorilor câmpului cu proprietatea IDENTITATE sunt perfect acceptabile. Lista de semnificații nu este deloc continuă

De obicei, 1 este specificat ca increment, dar poate fi orice număr întreg. Inclusiv negativ.

Datorită acestei naturi a câmpurilor cu proprietatea IDENTITY, astfel de câmpuri sunt adesea folosite ca chei primare. Cu alte cuvinte, ca câmpuri, după valoarea cărora puteți identifica întotdeauna în mod unic o înregistrare de tabel.

Rețineți că proprietatea IDENTITY nu are control asupra unicității datelor. De exemplu, dacă câmpul a fost inițial de tip INTEGER și au fost introduse un număr de valori în el. Și apoi structura tabelului a fost schimbată și proprietatea IDENTITY a fost impusă acestui câmp, atunci înregistrările noi ar putea avea aceleași date care au fost deja introduse mai devreme în acest tabel. Prin urmare, dacă un câmp cu proprietatea IDENTITY este folosit ca cheie primară, atunci ar trebui impusă o constrângere suplimentară de unicitate acestui câmp.

Dezavantajul utilizării câmpurilor cu proprietatea IDENTITY ca cheie primară

Cu toate acestea, în ciuda avantajelor clare ale utilizării câmpurilor cu proprietatea IDENTITATE ca cheie primară, acestea au și un dezavantaj serios.

Valoarea câmpurilor cu proprietatea IDENTITY nu poate fi cunoscută până când înregistrarea este creată fizic.

Și ce dacă? Care sunt problemele? Să creăm o înregistrare și să aflăm noua sa valoare.

Problema este că pentru a afla valoarea unui câmp al oricărei înregistrări, trebuie mai întâi găsită această înregistrare. Și căutarea unei înregistrări este efectuată doar de valoarea cheii primare. Cel al cărui sens trebuie determinat. Un cerc vicios: pentru a citi o valoare, trebuie să cunoști această valoare!

Structura de stocare a datelor în serverul MS SQL este fundamental diferită de structura de stocare a datelor din fișierele DBF. Nu conține concepte precum „ număr fizicînregistrează "," înregistrarea următoare "," ultima înregistrare ", etc. Adică este imposibil să mergi la "ultima înregistrare" pentru a citi valoarea cheii sale primare.

Mai mult, deși noua valoare a câmpului cu proprietatea IDENTITY este întotdeauna mai mare decât oricare dintre valorile existente(dacă creșterea este număr pozitiv), dar este imposibil să se determine această nouă valoare prin simpla calculare a maximului valorilor existente. Nu, valoarea maximă în sine, desigur, va fi obținută. Pur și simplu nu există nicio garanție că valoarea rezultată este valoarea înregistrării exacte care a fost creată.

Ideea este că, de regulă, serverul MS SQL este utilizat în aplicații multiutilizator. Aceasta înseamnă că mai mulți utilizatori simultan pot crea înregistrări noi în același timp. Se pare că un utilizator a creat o nouă înregistrare, apoi a început să calculeze valoarea maximă, iar în acel moment un alt utilizator a creat și o nouă înregistrare. Ca urmare, primul utilizator ca valoare maximă va primi valoarea intrării create de al doilea utilizator.

Deci, ar trebui să încetăm să mai folosim câmpuri cu proprietatea IDENTITY ca cheie primară? Deloc. Totuși, există modalități de a determina valoarea unui câmp cu proprietatea IDENTITY a unei noi înregistrări.

Cum se determină valoarea unui câmp cu proprietatea IDENTITATE într-o înregistrare nouă

De fapt, există trei strategii fundamentale pentru a determina valoarea unui câmp cu proprietatea IDENTITY într-o înregistrare nouă, tocmai creată.

Acum să aruncăm o privire mai atentă la avantajele și dezavantajele fiecărei strategii.

Valoarea returnată de variabila de sistem @@ IDENTITY

Există o serie de variabile de sistem în serverul MS SQL, a căror valoare se modifică automat atunci când apar anumite evenimente. În special, variabila de sistem @@ IDENTITY este setată automat la valoarea câmpului IDENTITATE al ultimei înregistrări create în conexiunea curentă. Acestea. crearea de noi înregistrări într-o altă conexiune (de către un alt utilizator) nu va afecta în niciun fel valoarea acesteia în această legătură.

Ei bine, asta este, soluția. Pur și simplu după crearea unei noi înregistrări, citim valoarea variabilei de sistem @@ IDENTITY și avem valoarea dorită.

În general, este adevărat. Singura problemă este că variabila de sistem @@ IDENTITY își schimbă valoarea atunci când este creată o înregistrare orice masa.

În practică, aceasta înseamnă că dacă pe un tabel este setat un declanșator de inserare, în corpul căruia este dată o comandă INSERT pentru a crea o înregistrare într-un alt tabel, care, la rândul său, are și un câmp cu proprietatea IDENTITY, atunci Variabila de sistem @@ IDENTITY va primi valoarea câmpului din acest al doilea tabel.

Cu alte cuvinte, vă puteți baza pe valoarea variabilei de sistem @@ IDENTITY, dar amintiți-vă că această variabilă nu este legată de valoarea unui câmp dintr-un tabel.

Valoarea returnată de funcția SCOPE_IDENTITY ().

A fost introdusă versiunea MS SQL 2000 functia sistemului SCOPE_IDENTITY (). Această funcție returnează și valoarea câmpului cu proprietatea IDENTITATE a ultimei înregistrări create, dar creată în SCOPE curent.

Este destul de dificil să traduci în mod adecvat termenul SCOPE în rusă. Dar, aproximativ, puteți spune acest lucru: SCOPE este o procedură sau o funcție. Cu alte cuvinte, SCOPE_IDENTITY () va returna valoarea câmpului cu proprietatea IDENTITY a ultimei înregistrări create în cadrul procedurii în care a fost apelată această funcție.

Un declanșator este un SCOPE diferit (o funcție diferită), deci nu va afecta în niciun fel valoarea returnată a SCOPE_IDENTITY ().

Chiar dacă doi utilizatori au apelat aceeași procedură în același timp, dar fiecare a numit procedura în SCOPE. Acestea. iarasi nu exista conflict.

Dezavantajele acestei funcții este că ar trebui să fie apelată în SCOPE, unde a fost creată noua înregistrare a tabelului care ne interesează. Și acest lucru nu este întotdeauna posibil.

Cu alte cuvinte, pentru a utiliza SCOPE_IDENTITY () corect, trebuie să monitorizați întotdeauna domeniul de aplicare al SCOPE. Adesea prin crearea unor proceduri speciale.

Găsirea unei noi înregistrări după valoarea altor câmpuri

Dacă vă amintiți, principala problemă cu definirea valorii unui câmp cu proprietatea IDENTITY este că acest câmp este folosit ca cheie primară. Acestea. prin valoarea sa se găsește înregistrarea necesară.

Cu toate acestea, adesea tabelele au un câmp sau un set de câmpuri prin care o înregistrare poate fi, de asemenea, identificată în mod unic. De exemplu, dacă este vorba despre director, atunci, desigur, directorul are un câmp „Nume”. De asemenea, este evident că acest câmp trebuie să fie unic în director. În caz contrar, sensul folosirii cărții de referință în sine se pierde pur și simplu. De ce să introduceți înregistrări cu aceeași valoare?

De ce să nu folosiți acest „Nume” ca cheie primară? De ce aveți nevoie de un câmp cu proprietatea IDENTITATE? Acesta este un subiect pentru o altă conversație. Pe scurt, „Titlul” este pentru utilizator (date externe), iar IDENTITATE este pentru asigurarea integrității referențiale a bazei de date (date interne).

Valoarea câmpului cu proprietatea IDENTITY din noua înregistrare este necunoscută. Dar semnificația câmpului „Titlu” din această nouă înregistrare este binecunoscută. Utilizatorul a introdus-o singur! Aceasta înseamnă că după crearea unei noi înregistrări, puteți găsi această nouă înregistrare după valoarea câmpului „Nume” și puteți citi valoarea câmpului cu proprietatea IDENTITATE.

Singura problemă este că un astfel de câmp sau set de câmpuri nu există întotdeauna pentru identificarea unică a unei înregistrări. Apropo, acesta este unul dintre motivele pentru introducerea așa-numitelor chei surogat. Aceleași câmpuri cu proprietatea IDENTITY.

Dacă, totuși, decideți să utilizați această strategie pentru a găsi o înregistrare nouă, asigurați-vă că impuneți o constrângere unică câmpului „Nume” (sau setului de câmpuri pe care l-ați ales). Adică, astfel încât să nu existe două înregistrări cu aceeași valoare în acest câmp din întâmplare.

Cum să lucrați cu câmpuri cu proprietatea IDENTITY în FoxPro

Cu partea teoretică terminată, acum „să încercăm să decolăm cu tot binele ăsta”. Acestea. haideți să decidem cum să folosim toate aceste cunoștințe în FoxPro. Să lămurim încă o dată problema care trebuie rezolvată.

O înregistrare este adăugată la tabelul serverului MS SQL cu un câmp cu proprietatea IDENTITY. Este necesar să obțineți valoarea câmpului cu proprietatea IDENTITY pe partea FoxPro imediat după crearea unei noi înregistrări.

FoxPro are trei opțiuni fundamentale pentru organizarea muncii cu serverul MS SQL

  • Utilizarea Vizualizării de la distanță
  • Folosind adaptorul cursorului

Aici ar trebui să vă opriți asupra evenimentului care creează de fapt o înregistrare pe serverul MS SQL. Ei bine, cu Pass-Trough, totul este clar. Aceasta este de fapt o comandă directă către server pentru a crea o nouă înregistrare. Dar cu Remote View și Cursor Adapter este puțin diferit.

Rezultatul muncii atât a Vizualizării de la distanță, cât și a adaptorului de cursor este un cursor. Acestea. o masă temporară situată fizic pe mașina clientului. În mod implicit, acest cursor se deschide automat în buffering-ul de rând optimist (3) și poate fi comutat doar la tamponul optimist de tabel (5). Este imposibil să comutați la modul de stocare pesimistă sau să dezactivați deloc tamponarea pentru acest cursor.

În consecință, o nouă înregistrare va fi mai întâi creată fizic pe computerul client chiar în acest cursor. Mai exact, în buffer-ul acestui cursor. Crearea fizică a unei înregistrări pe serverul MS SQL va avea loc numai după ce buffer-ul este golit.

Pentru tamponarea șirurilor, tamponul poate fi golit automat, efectuând una dintre următoarele:

  • Săriți (sau încercați să sari) la o altă înregistrare
  • Închiderea cursorului
  • Trecerea la modul tampon de masă
  • Prin comanda TableUpdate ()

Pentru tamponarea tabelului, tamponul poate fi golit numai de comanda TableUpdate () și nu altfel.

Nicio altă acțiune și operațiune cu Remote View sau cu adaptorul cursor nu va crea o înregistrare nouă pe serverul MS SQL. Dacă în timpul executării oricărei operațiuni s-a dovedit că a fost creată o nouă înregistrare pe serverul MS SQL, aceasta înseamnă că cursorul se afla în modul de buffering de linie și a avut loc unul dintre evenimentele care au cauzat ștergerea automată a bufferului.

De exemplu, acest lucru se poate întâmpla cu comanda Requery () pentru vizualizarea de la distanță. Dar asta nu înseamnă că comanda Requery () șterge tamponul. Deloc. Doar una dintre condițiile pentru executarea comenzii Requery () este închiderea cursorului preexistent. Dar acest eveniment va cauza doar spălarea automată a buffer-ului dacă cursorul este în modul de buffering de linie.

Pentru a evita astfel de neînțelegeri, comutați cursorul în modul tampon tabel (5). În acest caz, puteți controla oricând procesul de spălare a tamponului.

Cu toate acestea, trebuie înțeles că, chiar dacă setați modul de stocare a tabelului, modificați mai multe înregistrări în Remote View sau în adaptorul cursorului și apoi lansați comanda TableUpdate (), buffer-ul va fi în continuare golit câte o înregistrare la un moment dat. Acestea. nu va fi trimisă o singură comandă către server, de exemplu, pentru modificare, ci un set de comenzi pentru modificarea fiecărei înregistrări separat.

În ceea ce privește operațiunile de creare a unei noi înregistrări, rezultă din aceasta că în toate cazurile adaptorului de cursor mereu este introdusă o singură înregistrare odată.

Utilizarea directă a tehnologiei Pass-Through prin funcția SQLEXEC ().

Cu această metodă de lucru, programatorul lucrează direct cu serverul MS SQL. El însuși formează toate comenzile trimise către server, primește rezultatul și îl procesează el însuși. În acest caz, nu este dificil să trimiteți o cerere suplimentară către server pentru valoarea funcției SCOPE_IDENTITY.

LOCAL lcNewValue, lnResut lcNewValue = "(! LANG: valoare nouă" 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 acest exemplu m.lnConnectHandle este un număr, numărul conexiunii la serverul MS SQL, care este configurat mai devreme. MyTab este un tabel care are un câmp cu o proprietate IDENTITY.

După executarea celei de-a doua interogări în cursorul rezultat NewIdent în câmpul NewIdent din prima înregistrare și obțineți valoarea dorită. V sintaxa dată atât comanda de inserare, cât și apelul funcției SCOPE_IDENTITY () apar în același SCOPE. Prin urmare, obținem valoarea dorită.

Utilizarea Vizualizării de la distanță

Remote View este un fel de „supliment” peste tehnologia Pass-Through. Practic, aceeași comandă INSERT INTO este executată la crearea unei noi înregistrări. Cu toate acestea, problema este că, chiar dacă citiți numărul conexiunii în care rulează Remote View și apoi executați o interogare pentru a determina valoarea returnată de SCOPE_IDENTITY (), vom obține NULL, deoarece în acest caz comanda de inserare și SCOPE_IDENTITY () sunt executate în diferite SCOPE. Prin urmare, există doar două moduri de a determina valoarea unui câmp cu proprietatea IDENTITY.

* Determinarea valorii variabilei de sistem @@ IDENTITY LOCAL lnConnectHandle lnConnectHandle = CursorGetProp ("ConnectHandle", "MyRemoteView") SQLExec (m.lnConnectHandle, "SELECT [email protected]@IDENTITY "," NewIdent ")? NewIdent.NewIdent * Determinați după valoarea unui alt câmp LOCAL lnConnectHandle, lcNickName lnConnectHandle = CursorGetProp (" ConnectHandle "," MyRemoteView ") lcNickName = MyRemoteView.NickeNeNeckNameCt. "," NewIdent ")? NewIdent.TabId

În ambele exemple, MyRemoteView este numele Remote View. NickName este numele câmpului din Remote View după valoarea căreia se caută o nouă înregistrare, iar TabID este același câmp cu proprietatea IDENTITY a cărei valoare trebuie determinată

Se presupune că crearea unei noi înregistrări a avut loc deja. Fie comanda TableUpdate () a fost dată în mod explicit, fie vizualizarea de la distanță este în modul tampon șir și a fost făcută o încercare de a naviga la o altă înregistrare.

Și de ce în al doilea caz, s-ar părea, mai mult solutie evidenta căutați după Requery ()? Ceva asemănător cu

REQUERY ("MyRemoteView") SELECT MyRemoteView LOCATE FOR NickName = "Valoare nouă"? MyRemoteView.TabID

Există mai multe motive pentru aceasta.

În primul rând, Requery () presupune că interogarea este executată din nou. Acestea. se presupune că buffer-ul tuturor înregistrărilor Remote View a fost golit. Dar s-ar putea să nu fie așa. De exemplu, spălați memoria tampon o înregistrare la un moment dat într-o buclă.

În al doilea rând, de obicei, Remote View conține conditii suplimentare selectarea înregistrărilor, iar înregistrarea nou creată poate să nu fie deloc inclusă în condițiile de selecție. Acestea. după Requery (), înregistrarea, deși va fi creată fizic pe server, nu va fi afișată în Remote View în sine.

Folosind adaptorul cursorului

Obiectul Cursor Adapter a fost introdus în FoxPro începând cu Visual FoxPro 8. Este conceput pentru a facilita lucrul cu date de la distanță atunci când se efectuează unele operațiuni standard... În special, operațiunile de creare a unei noi înregistrări. Prin intermediul adaptorului cursor, puteți implementa toate cele trei opțiuni pentru obținerea valorii unui câmp cu proprietatea IDENTITY într-o înregistrare nouă.

Valoarea returnată de variabila de sistem @@ IDENTITY

După ce tamponul este golit și o înregistrare nouă este creată fizic pe serverul MS SQL, evenimentul AfterInsert va fi declanșat în obiectul Cursor Adapter. Aici trebuie doar să faceți o cerere suplimentară către server și să citiți valoarea variabilei de sistem @@ IDENTITY

PROCEDURA AfterInsert LPARAMETERS cFldState, lForce, cInsertCmd, lResult IF lResult = .T. && inserarea a avut succes. Trebuie să obțineți valoarea ID LOCAL currentArea currentArea = SELECT () TRY IF 1 = SQLEXEC (this.DataSource, "SELECT [email protected]@IDENTITY "," NewIdent ") REPLACE TabId WITH NewIdent.NewIdent IN (This.Alias) USE IN IDRes ELSE LOCAL laError (1) = AERROR (laError) * Analizați matricea laError pentru a clarifica cauza erorii ENDIF FINALLY SELECT ( currentArea) ENDTRY ENDIF ENDPROC

Cred că codul este suficient de clar și nu are nevoie de nicio explicație. După crearea cu succes a unei noi înregistrări pe server folosind SQLExec (), valoarea @@ IDENTITY a fost definită pentru aceeași conexiune și scrisă în înregistrarea curentă a cursorului în câmpul cheie.

În Visual FoxPro 9, o proprietate a fost adăugată obiectului Adaptor cursor InsertCmdRefreshCmd... Aceasta este o comandă care este trimisă la server numai după introducerea cu succes a unei noi înregistrări. Acestea. nu este nevoie să te convingi de însuși faptul de a crea un nou disc.

Împreună cu o altă proprietate nouă InsertCmdRefreshFieldList puteți face upgrade de la variabila de sistem @@ IDENTITY mai ușor. Pentru a face acest lucru, trebuie doar să faceți următoarele setări Obiect CursorAdapter

CursorAdapter.InsertCmdRefreshCmd = „SELECTARE @@ IDENTITY” CursorAdapter.InsertCmdRefreshFieldList = „TabId”

Aici TabId nu este numele câmpului cu proprietatea IDENTITY din tabelul de pe serverul MS SQL în sine, ci numele câmpului cursorului primit pe partea client în care este afișat conținutul câmpului cu proprietatea IDENTITY. De regulă, aceste nume sunt aceleași, dar, în general, pot fi diferite. Aflați mai multe despre proprietate InsertCmdRefreshFieldList cititi mai jos. În secțiunea privind găsirea unei noi înregistrări după valoarea altor câmpuri.

Valoarea returnată de funcția SCOPE_IDENTITY ().

Aici totul este ceva mai complicat. Ideea este că nu puteți acționa în același mod ca și în cazul definirii variabilei de sistem @@ IDENTITY. SQLExec () și adaptorul cursor funcționează în diferite SCOPE. Prin urmare, cu această abordare, SCOPE_IDENTITY () va returna întotdeauna NULL. Pentru a depăși această contradicție, se folosesc proceduri speciale.

În primul rând, trebuie să creați o astfel de procedură stocată pe serverul MS SQL însuși

CREATE PROCEDURE Get_ValueInt @ValueIn Int, @ValueOut Int OUTPUT AS SET NOCOUNT ON SELECT @ [email protected]

După cum puteți vedea, scopul acestei proceduri este pur și simplu de a atribui valoarea unui parametru de intrare unui parametru de ieșire. Acum puteți utiliza această procedură pentru a determina valoarea SCOPE_IDENTITY () în adaptorul de cursor. Pentru a face acest lucru, în eveniment Înainte de a introduce se face o astfel de înlocuire

PROCEDURĂ Înainte de inserare LPARAMETRI cFldState, lForce, cInsertCmd cInsertCmd = cInsertCmd +; "; DECLARE @id int" +; "; SELECT @ id = SCOPE_IDENTITY ()" +; "; EXEC Get_ValueInt @id, [email protected]„ENDPROC

Rețineți că în această înlocuire MyTab nu mai este numele tabelului de pe serverul MS SQL, ci numele cursorului creat de client. Mai precis, numele care este înregistrat în proprietatea Alias ​​a adaptorului cursor în sine. În consecință, „TabId” este numele câmpului cursorului creat de client și care conține valoarea câmpului cu proprietatea IDENTITY

În acest caz, în esență, se formează o procedură dinamică stocată. Acestea. nu doar o comandă INSERT, ci o secvență de comenzi. Comenzile sunt separate unele de altele prin punct și virgulă, deși acest lucru nu este necesar. Este suficient să separați comenzile unele de altele cu un spațiu simplu.

Găsirea unei noi înregistrări după valoarea altor câmpuri

Dacă aveți Visual FoxPro 8, atunci tot ce trebuie să faceți este să utilizați o metodă similară cu metoda folosită pentru a găsi valoarea variabilei de sistem @@ IDENTITY. Acestea. în metoda AfterInsert a obiectului Cursor Adapter, executați o solicitare suplimentară prin SQLExec ()

Începând cu Visual FoxPro 9, obiectul Adaptor cursor are proprietăți suplimentare pentru a automatiza această procedură fără a scrie cod suplimentar.

InsertCmdRefreshFieldList- o listă de câmpuri, a căror valoare va fi actualizată după Inserare
InsertCmdRefreshKeyFieldList- un câmp sau un set de câmpuri cheie NOT care identifică, de asemenea, în mod unic înregistrarea, care va fi folosit pentru a căuta o nouă înregistrare

Trebuie remarcat faptul că aceste proprietăți nu specifică numele câmpurilor din tabelul serverului MS SQL în sine, ci numele corespunzătoare ale câmpurilor cursorului primite pe partea clientului. Pentru același exemplu, ar fi cam așa:

InsertCmdRefreshFieldList = „TabID” InsertCmdRefreshKeyFieldList = „NickName”

Cu alte cuvinte, pe baza valorii câmpului NickName, se va găsi o înregistrare în tabelul de pe serverul MS SQL și se va citi valoarea câmpului TabID, după care această valoare va fi scrisă într-o nouă înregistrare pe partea clientului.


Mai detaliat, atât o discuție pe acest subiect, cât și exemple de utilizare, puteți vedea aici



Vladimir Maximov
Ultima actualizare: 01.05.06

Top articole similare