Entity Framework
Codice First DataAnnotations
Ricerca…
Osservazioni
Entity Framework Code-First fornisce un set di attributi DataAnnotation, che è possibile applicare alle classi e proprietà del dominio. Gli attributi DataAnnotation sostituiscono le convenzioni Code-First predefinite.
- System.ComponentModel.DataAnnotations include gli attributi che influiscono sul nullability o sulla dimensione della colonna.
- Il namespace System.ComponentModel.DataAnnotations.Schema include gli attributi che influiscono sullo schema del database.
Nota: DataAnnotations fornisce solo un sottoinsieme di opzioni di configurazione. L'API Fluent fornisce un set completo di opzioni di configurazione disponibili in Code-First.
[Key] attributo
Key è un campo in una tabella che identifica in modo univoco ogni riga / record in una tabella di database.
Utilizzare questo attributo per sovrascrivere la convenzione Code-First predefinita . Se applicato a una proprietà, verrà utilizzato come colonna chiave primaria per questa classe.
using System.ComponentModel.DataAnnotations;
public class Person
{
[Key]
public int PersonKey { get; set; } // <- will be used as primary key
public string PersonName { get; set; }
}
Se è richiesta una chiave primaria composta, l'attributo [Chiave] può anche essere aggiunto a più proprietà. L'ordine delle colonne all'interno della chiave composta deve essere fornito nel modulo [ Chiave, Colonna (Ordine = x)] .
using System.ComponentModel.DataAnnotations;
public class Person
{
[Key, Column(Order = 0)]
public int PersonKey1 { get; set; } // <- will be used as part of the primary key
[Key, Column(Order = 1)]
public int PersonKey2 { get; set; } // <- will be used as part of the primary key
public string PersonName { get; set; }
}
Senza l'attributo [Chiave] , EntityFramework tornerà alla convenzione predefinita che deve utilizzare la proprietà della classe come chiave primaria denominata "Id" o "ID ClassName}".
public class Person
{
public int PersonID { get; set; } // <- will be used as primary key
public string PersonName { get; set; }
}
[Richiesto] attributo
Se applicato a una proprietà di una classe di dominio, il database creerà una colonna NOT NULL.
using System.ComponentModel.DataAnnotations;
public class Person
{
public int PersonID { get; set; }
[Required]
public string PersonName { get; set; }
}
La colonna risultante con il vincolo NOT NULL:
Nota: può essere utilizzato anche con asp.net-mvc come attributo di convalida.
[MaxLength] e [MinLength] attributi
L'attributo [MaxLength (int)] può essere applicato a una stringa o proprietà di tipo array di una classe di dominio. Entity Framework imposterà la dimensione di una colonna sul valore specificato.
using System.ComponentModel.DataAnnotations;
public class Person
{
public int PersonID { get; set; }
[MinLength(3), MaxLength(100)]
public string PersonName { get; set; }
}
La colonna risultante con la lunghezza della colonna specificata:
L'attributo [MinLength (int)] è un attributo di convalida, non influenza la struttura del database. Se proviamo a inserire / aggiornare una persona con PersonName con lunghezza inferiore a 3 caratteri, questo commit fallirà. Riceveremo una DbUpdateConcurrencyException
che avremo bisogno di gestire.
using (var db = new ApplicationDbContext())
{
db.Staff.Add(new Person() { PersonName = "ng" });
try
{
db.SaveChanges();
}
catch (DbEntityValidationException ex)
{
//ErrorMessage = "The field PersonName must be a string or array type with a minimum length of '3'."
}
}
Entrambi gli attributi [MaxLength] e [MinLength] possono anche essere utilizzati con asp.net-mvc come attributo di convalida.
Attributo [intervallo (min, max)]
Specifica un intervallo minimo e massimo numerico per una proprietà
using System.ComponentModel.DataAnnotations;
public partial class Enrollment
{
public int EnrollmentID { get; set; }
[Range(0, 4)]
public Nullable<decimal> Grade { get; set; }
}
Se proviamo a inserire / aggiornare un Grado con valore fuori intervallo, questo commit fallirà. Riceveremo una DbUpdateConcurrencyException
che avremo bisogno di gestire.
using (var db = new ApplicationDbContext())
{
db.Enrollments.Add(new Enrollment() { Grade = 1000 });
try
{
db.SaveChanges();
}
catch (DbEntityValidationException ex)
{
// Validation failed for one or more entities
}
}
Può anche essere utilizzato con asp.net-mvc come attributo di convalida.
Risultato:
Attributo [DatabaseGenerated]
Specifica come il database genera valori per la proprietà. Ci sono tre valori possibili:
-
None
specifica che i valori non sono generati dal database. -
Identity
specifica che la colonna è una colonna identity , che viene in genere utilizzata per le chiavi primarie intere. -
Computed
specifica che il database genera il valore per la colonna.
Se il valore è diverso da None
, Entity Framework non impegna le modifiche apportate alla proprietà sul database.
Per impostazione predefinita (basata su StoreGeneratedIdentityKeyConvention
), una proprietà chiave intera verrà trattata come una colonna Identity. Per sovrascrivere questa convenzione e imporla a essere considerata come una colonna non identificata, è possibile utilizzare l'attributo DatabaseGenerated
con il valore None
.
using System.ComponentModel.DataAnnotations.Schema;
public class Foo
{
[Key]
public int Id { get; set; } // identity (auto-increment) column
}
public class Bar
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; } // non-identity column
}
Il seguente SQL crea una tabella con una colonna calcolata:
CREATE TABLE [Person] (
Name varchar(100) PRIMARY KEY,
DateOfBirth Date NOT NULL,
Age AS DATEDIFF(year, DateOfBirth, GETDATE())
)
GO
Per creare un'entità per rappresentare i record nella tabella precedente, è necessario utilizzare l'attributo DatabaseGenerated
con un valore Computed
.
[Table("Person")]
public class Person
{
[Key, StringLength(100)]
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public int Age { get; set; }
}
[NotMapped] attributo
Con la convenzione Code-First, Entity Framework crea una colonna per ogni proprietà pubblica che è di un tipo di dati supportato e ha sia un getter che un setter. L' annotazione [NotMapped] deve essere applicata a qualsiasi proprietà per cui NON vogliamo una colonna in una tabella di database.
Un esempio di una proprietà che potremmo non voler memorizzare nel database è il nome completo di uno studente in base al loro nome e cognome. Questo può essere calcolato al volo e non è necessario memorizzarlo nel database.
public string FullName => string.Format("{0} {1}", FirstName, LastName);
La proprietà "FullName" ha solo un getter e nessun setter, quindi per impostazione predefinita, Entity Framework NON creerà una colonna per esso.
Un altro esempio di una proprietà che potremmo non voler memorizzare nel database è "AverageGrade" di uno studente. Non vogliamo ottenere AverageGrade su richiesta; invece potremmo avere una routine altrove che la calcola.
[NotMapped]
public float AverageGrade { set; get; }
Il valore "AverageGrade" deve essere contrassegnato con [NotMapped] , altrimenti Entity Framework creerà una colonna per esso.
using System.ComponentModel.DataAnnotations.Schema;
public class Student
{
public int Id { set; get; }
public string FirstName { set; get; }
public string LastName { set; get; }
public string FullName => string.Format("{0} {1}", FirstName, LastName);
[NotMapped]
public float AverageGrade { set; get; }
}
Per l'entità sopra vedremo all'interno di DbMigration.cs
CreateTable(
"dbo.Students",
c => new
{
Id = c.Int(nullable: false, identity: true),
FirstName = c.String(),
LastName = c.String(),
})
.PrimaryKey(t => t.Id);
e in SQL Server Management Studio
[Tabella] attributo
[Table("People")]
public class Person
{
public int PersonID { get; set; }
public string PersonName { get; set; }
}
Indica a Entity Framework di utilizzare un nome tabella specifico invece di generarne uno (ad es. Person
o Persons
)
Possiamo anche specificare uno schema per la tabella usando l'attributo [Table]
[Table("People", Schema = "domain")]
[Colonna] attributo
public class Person
{
public int PersonID { get; set; }
[Column("NameOfPerson")]
public string PersonName { get; set; }
}
Indica a Entity Framework di utilizzare un nome di colonna specifico utilizzando invece il nome della proprietà. È inoltre possibile specificare il tipo di dati del database e l'ordine della colonna nella tabella:
[Column("NameOfPerson", TypeName = "varchar", Order = 1)]
public string PersonName { get; set; }
[Indice] attributo
public class Person
{
public int PersonID { get; set; }
public string PersonName { get; set; }
[Index]
public int Age { get; set; }
}
Crea un indice di database per una colonna o un insieme di colonne.
[Index("IX_Person_Age")]
public int Age { get; set; }
Questo crea un indice con un nome specifico.
[Index(IsUnique = true)]
public int Age { get; set; }
Questo crea un indice univoco.
[Index("IX_Person_NameAndAge", 1)]
public int Age { get; set; }
[Index("IX_Person_NameAndAge", 2)]
public string PersonName { get; set; }
Questo crea un indice composito usando 2 colonne. Per fare ciò è necessario specificare lo stesso nome indice e fornire un ordine di colonne.
Nota : l'attributo Index è stato introdotto in Entity Framework 6.1. Se si utilizza una versione precedente, le informazioni in questa sezione non si applicano.
Attributo [ForeignKey (stringa)]
Specifica il nome della chiave esterna personalizzata se si desidera una chiave esterna non conforme alla convenzione di EF.
public class Person
{
public int IdAddress { get; set; }
[ForeignKey(nameof(IdAddress))]
public virtual Address HomeAddress { get; set; }
}
Questo può essere utilizzato anche quando si hanno più relazioni con lo stesso tipo di entità.
using System.ComponentModel.DataAnnotations.Schema;
public class Customer
{
...
public int MailingAddressID { get; set; }
public int BillingAddressID { get; set; }
[ForeignKey("MailingAddressID")]
public virtual Address MailingAddress { get; set; }
[ForeignKey("BillingAddressID")]
public virtual Address BillingAddress { get; set; }
}
Senza gli attributi ForeignKey
, EF potrebbe farli BillingAddressID
e usare il valore di BillingAddressID
quando si BillingAddressID
il MailingAddress
, oppure potrebbe venire con un nome diverso per la colonna in base alle proprie convenzioni di denominazione (come Address_MailingAddress_Id
) e provare ad usarlo invece (che comporterebbe un errore se stai usando questo con un database esistente).
[StringLength (int)] attributo
using System.ComponentModel.DataAnnotations;
public class Post
{
public int Id { get; set; }
[StringLength(100)]
public string Title { get; set;}
[StringLength(300)]
public string Abstract { get; set; }
public string Description { get; set; }
}
Definisce una lunghezza massima per un campo stringa.
Nota : può essere utilizzato anche con asp.net-mvc come attributo di convalida.
[Timestamp] attributo
L' attributo [TimeStamp] può essere applicato a una sola proprietà di matrice di byte in una determinata classe di entità. Entity Framework creerà una colonna timestamp non annullabile nella tabella del database per quella proprietà. Entity Framework utilizzerà automaticamente questa colonna TimeStamp nel controllo della concorrenza.
using System.ComponentModel.DataAnnotations.Schema;
public class Student
{
public int Id { set; get; }
public string FirstName { set; get; }
public string LastName { set; get; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
[ConcurrencyCheck] Attributo
Questo attributo è applicato alla proprietà della classe. È possibile utilizzare l'attributo ConcurrencyCheck quando si desidera utilizzare le colonne esistenti per il controllo della concorrenza e non una colonna timestamp separata per la concorrenza.
using System.ComponentModel.DataAnnotations;
public class Author
{
public int AuthorId { get; set; }
[ConcurrencyCheck]
public string AuthorName { get; set; }
}
Dall'esempio precedente, l'attributo ConcurrencyCheck viene applicato alla proprietà AuthorName della classe Author. Quindi, Code-First includerà la colonna AuthorName nel comando update (clausola where) per verificare la concorrenza ottimistica.
[InverseProperty (string)] attributo
using System.ComponentModel.DataAnnotations.Schema;
public class Department
{
...
public virtual ICollection<Employee> PrimaryEmployees { get; set; }
public virtual ICollection<Employee> SecondaryEmployees { get; set; }
}
public class Employee
{
...
[InverseProperty("PrimaryEmployees")]
public virtual Department PrimaryDepartment { get; set; }
[InverseProperty("SecondaryEmployees")]
public virtual Department SecondaryDepartment { get; set; }
}
InverseProperty può essere utilizzato per identificare i due rapporti modo quando esistono più due relazioni strada tra due entità.
Indica a Entity Framework quali proprietà di navigazione devono corrispondere alle proprietà sull'altro lato.
Entity Framework non sa quale mappa di proprietà di navigazione con quali proprietà sull'altro lato quando esistono più relazioni bidirezionali tra due entità.
Ha bisogno del nome della proprietà di navigazione corrispondente nella classe correlata come parametro.
Questo può essere usato anche per entità che hanno una relazione con altre entità dello stesso tipo, formando una relazione ricorsiva.
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
public class TreeNode
{
[Key]
public int ID { get; set; }
public int ParentID { get; set; }
...
[ForeignKey("ParentID")]
public TreeNode ParentNode { get; set; }
[InverseProperty("ParentNode")]
public virtual ICollection<TreeNode> ChildNodes { get; set; }
}
Nota anche l'uso dell'attributo ForeignKey
per specificare la colonna utilizzata per la chiave esterna sulla tabella. Nel primo esempio, le due proprietà sulla classe Employee
potevano avere l'attributo ForeignKey
applicato per definire i nomi delle colonne.
[ComplexType] attributo
using System.ComponentModel.DataAnnotations.Schema;
[ComplexType]
public class BlogDetails
{
public DateTime? DateCreated { get; set; }
[MaxLength(250)]
public string Description { get; set; }
}
public class Blog
{
...
public BlogDetails BlogDetail { get; set; }
}
Segna la classe come tipo complesso in Entity Framework.
I tipi complessi (o oggetti valore nella progettazione guidata da domini) non possono essere tracciati da soli ma sono tracciati come parte di un'entità. Questo è il motivo per cui BlogDetails nell'esempio non ha una proprietà chiave.
Possono essere utili quando si descrivono entità di dominio su più classi e si sovrappongono tali classi in un'entità completa.