sobota, 17 marca 2012

Entity Framework Code First - Data Adnotations

Tym razem coś a atrybutach - po co są i co nam dają.
Weźmiemy na warsztat klasę nad którą pracowaliśmy przez kilka ostatnich postów:


Code:
namespace EF_CodeFirst
{
    public class Person
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int BirthYear { get; set; }
        public string Street { get; set; }

        public bool SalesRepresentative { get; set; }
        public CompanyCar CompanyCar { get; set; }
    }
}

W bazie klasa ta jest zmapowana na następującą tabelę:


Zacznijmy od tego, iż kolumny przechowujące dane znakowe są typu NVARCHAR(MAX). Jak wiadomo na takich kolumnach nie da się nałożyć indeksów. W przyszłości aby poprawić szybkość wyszukiwania z pewnością będziemy chcieli je nałożyć. Z drugiej strony wiemy że 99% imion mieści się w około 40 znakach jak i nazwiska nie przekraczają tej granicy.
Dodatkowo chcielibyśmy, aby nazwisko i ulica były polami obowiązkowymi (NOT NULL). Obecnie do każdego pola oprócz klucza głównego można wprowadzić wartość NULL.
Kolejną zmianą jaką dokonamy będzie zmiana nazwy pola w klasie Id na IdPerson - w bazie jednak chcemy aby nadal pozostała nazwa Id.
Zobaczmy w jaki sposób za pomocą atrybutów można dokonać opisanych zmian:


Code:
using System.ComponentModel.DataAnnotations;

namespace EF_CodeFirst
{
    public class Person
    {
        [Key]
        [Column("Id")]
        public int IdPerson { get; set; }

        [StringLength(40)]
        public string FirstName { get; set; }

        [StringLength(40)]
        [Required]
        public string LastName { get; set; }
        public int BirthYear { get; set; }

        [StringLength(256)]
        [Required]
        public string Street { get; set; }

        public bool SalesRepresentative { get; set; }
        public CompanyCar CompanyCar { get; set; }

        public override string ToString()
        {
            return string.Format("{0} {1} {2} {3} {4}", IdPerson, FirstName, LastName, BirthYear, Street);
        }
    }
}

Następnie tworzymy migrację za pomocą polecenia które podałem już w poprzednim poście:

Add-Migration AddAtributesToPersonClass

Zostanie wygenerowana klasa zawierająca potrzebne dane do przeprowadzenia migracji. Jeżeli teraz uruchomilibyśmy proces uaktualniania struktury bazy danych, otrzymalibyśmy następujący błąd:


Błąd mówi wyraźnie, że ustawiona kolumna Street nie może zawierać pustych wartości - w bazie mamy jeden rekord, który w polu street ma właśnie wartość NULL.
Tak więc dopóki owego NULL-a się nie pozbędziemy, nie zaktualizujemy schematu bazy. Jak w takim razie poradzić sobie z taką sytuacją? Można skorzystać z własnych komend SQL, o których pisałem w poprzednim poście.
Uzupełniamy więc klasę definiującą przeprowadzane migracje o nasz SQL:


Code:
namespace EF_CodeFirst.Migrations
{
    using System.Data.Entity.Migrations;
    
    public partial class AddAtributesToPersonClass : DbMigration
    {
        public override void Up()
        {
            Sql("UPDATE People SET street='' WHERE STREET IS NULL");
            AlterColumn("People", "FirstName", c => c.String(maxLength: 40));
            AlterColumn("People", "LastName", c => c.String(nullable: false, maxLength: 40));
            AlterColumn("People", "Street", c => c.String(nullable: false, maxLength: 256));
        }
        
        public override void Down()
        {
            AlterColumn("People", "Street", c => c.String());
            AlterColumn("People", "LastName", c => c.String());
            AlterColumn("People", "FirstName", c => c.String());
        }
    }
}

Teraz po wywołaniu polecenia:
Update-Database

zmiany zostaną wprowadzone do bazy a sama baza będzie mieć następującą postać:


Zobaczmy pełną listę dostępnych atrybutów:
  • Key - określa klucz główny (primary key)
  • StringLength - określa długość dla pola znakowego
  • MaxLength - określa maksymalną długość dla pola tekstowego
  • MinLength - określa minimalną liczbę znaków zapisaną do pola znakowego
  • Required - określa że dana wartość jest wymagana
  • ConcurrencyCheck - sprawdza przed zapisem czy wartość nie została zmieniona (opiszę dokładnie o co chodzi w kolejnym poście)
  • Timestamp - pole wykorzystywane w przypadku sprawdzenia dostępu wielowątkowego
  • ComplexType - definiuje że typ posiada pola także w innej klasie
  • Column - definiuje dla danego pola w klasie nazwę kolumny w bazie, typ danych oraz kolejność w zakładanej tabeli
  • Table - nazwa tabeli oraz schemy
  • NotMapped - właściwość nie jest mapowana z tabeli
  • DatabaseGenerated - definiuje w jaki sposób jest generowana wartość dla danego pola (autoinkrementacja, wyliczalne)
  • ForeignKey, InverseProperty - definiują klucz obcy dla relacji
Obecnie jest to pełna dostępna lista atrybutów, z pewnością w przyszłości zostanie uaktualniona o kolejne.

Brak komentarzy:

Prześlij komentarz