sobota, 17 marca 2012

Entity Framework Code First - atrybut ConcurrencyCheck

W ostatnim poście opisałem listę atrybutów z których możemy skorzystać w EF. Tutaj chciałbym dokończyć ten artykuł opisując bardziej szczegółowo jeden z ciekawszych moim zdaniem atrybutów - ConcurrencyCheck.
Zadaniem tego atrybutu jest sprawdzenie, czy podczas obecnego zapisu nie została dokonana w międzyczasie zmiana bezpośrednio w bazie danych na danym polu.

Zobaczmy na przykład.
W bazie w tabeli Person mamy następujący rekord:


Teraz napiszemy kod pozwalający pobrać wartość rekordu za pomocą EF:


Kod:

Code:
using System;
using System.Configuration;
using System.Linq;

namespace EF_CodeFirst
{
    class Program
    {
        static void Main()
        {
            var connectionString = ConfigurationManager.ConnectionStrings["MyDatabaseConnectionString"].ConnectionString;

            using (var context = new DatabaseContext(connectionString))
            {
                var userInDb = context.Persons.First();
                Console.WriteLine(userInDb);
            }
        }
    }
}

Dodajmy jeszcze teraz następujące dwie linijki kodu:


Code:
using System;
using System.Configuration;
using System.Linq;

namespace EF_CodeFirst
{
    class Program
    {
        static void Main()
        {
            var connectionString = ConfigurationManager.ConnectionStrings["MyDatabaseConnectionString"].ConnectionString;

            using (var context = new DatabaseContext(connectionString))
            {
                var userInDb = context.Persons.First();
                Console.WriteLine(userInDb);
                userInDb.FirstName = "Maciek";
                context.SaveChanges();
            }
        }
    }
}

Tak więc to co dodaliśmy powoduje zmianę imienia na Maciek oraz zapisanie zmian do bazy.

Przeprowadzimy teraz następują próbę:
1. ustawimy break point na linijkę z zapisem zmian do bazy danych.
2. Uruchomimy aplikację
3. Za pomocą np. Management Studio zmienimy wartość imienia na np. aaa
4. Wznowimy działanie programu.

Jaki będzie wynik tych operacji? Oczywiście wszystko się powiedzie. Mimo iż zmieniliśmy imię, nie do końca znając wartość w bazie danych, EF nie zaprotestował i pozwolił nam na to.
Niesie to za sobą niebezpieczeństwo tego iż, jeżeli będziemy zmieniać komuś np. NIP a w tym czasie ktoś inny na innym stanowisku zrobi to samo, nieumyślnie nadpiszemy jego zmiany.

Dlatego powstał atrybut  ConcurrencyCheck, który w momencie zapisu, uda się jeszcze raz do bazy i sprawdzi czy na danej krotce nie dokonano zmian.
Nałóżmy więc ten atrybut na nasze testowe pole:


Code:
using System.ComponentModel.DataAnnotations;

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

        [StringLength(40)]
        [ConcurrencyCheck]
        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);
        }
    }
}

W naszym scenariuszu po próbie zapisania zmian zostanie wyrzucony następujący wyjątek:


Tak więc mamy świetne rozwiązanie bardzo częstego problemu, które w dodatku sprowadza się do nałożenia jednego atrybutu.

Brak komentarzy:

Prześlij komentarz