Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка ПИ Программирование на С# _Хотов.docx
Скачиваний:
11
Добавлен:
01.07.2025
Размер:
2 Мб
Скачать
☆
      1. Инициализация базы данных

Если нам необходимо, чтобы при первом обращении база данных уже была заполнена некоторыми начальными значениями, то мы можем произвести ее инициализацию.

Инициализация происходит при первом обращении к контексту данных.

Для инициализации мы можем использовать один из классов инициализаторов, которые имеются в библиотеке .NET:

  • CreateDatabaseIfNotExists: инициализатор, используемый по умолчанию. Он не удаляет автоматчески базу данных и данные, а в случае изменения структуры моделей и контекста данных выбрасывает исключение.

  • DropCreateDatabaseWhenModelChanges: данный инициализатор проверяет на соответствие моделям определения таблиц в базе данных. И если модели не соответствуют определению таблиц, то база данные пересоздается

  • DropCreateDatabaseAlways: этот инициализатор будет всегда пересоздавать базу данных.

Используем один из инициализаторов. Для этого нам надо переопределить метод Seed:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

class MyContextInitializer : DropCreateDatabaseAlways<MobileContext>

{

    protected override void Seed(MobileContext db)

    {

        Phone p1 = new Phone {Name = "Samsung Galaxy S5", Price = 14000 };

        Phone p2 = new Phone {Name = "Nokia Lumia 630", Price = 8000 };

             

        db.Phones.Add(p1);

        db.Phones.Add(p2);

        db.SaveChanges();

    }

}

public class Phone

{

    public int Id { get; set; }

    public string Name { get; set; }

    public int Price { get; set; }

}

 

class MobileContext : DbContext

{

    static MobileContext()

    {

        Database.SetInitializer<MobileContext>(new MyContextInitializer());

    }

 

    public MobileContext() : base("DefaultConnection")

    { }

    public DbSet<Phone> Phones { get; set; }

}

Собственно инициализатор наследуется от одного из выше рассмотренных классов, который типизируется классом контекста: DropCreateDatabaseAlways<MobileContext>.

Все действия по инициализации происходят в методе Seed, а сама инициализация предполагает простое сохранение данных в бд с помощью контекста данных.

Чтобы инициализатор сработал, надо его вызвать. Один из способов вызова инициализатора предполагет вызов его в статическом конструкторе класса контекста:

1

2

3

4

static MobileContext()

{

    Database.SetInitializer<MobileContext>(new MyContextInitializer());

}

      1. Параллелизм в Entity Framework

При работе с Entity Framework, когда у нас одновременно множесво пользователей имеют доступ к одинаковому набору данных и могут эти данные изменять, мы можем столкнуться с проблемой параллелизма. Например, два пользователя независимо друг от друга начнут редактировать один и тот же объект. И после сохранения объекта первым пользователем второй пользователь уже будет работать с неактуальными данными.

Рассмотрим на примере. Например, в приложении ASP.NET MVC есть стандартное действие для редактирования:

1

2

3

4

5

6

7

8

9

10

11

12

public ActionResult Edit(int id)

{

    Person p = db.People.Find(id);

    return View(p);

}

[HttpPost]

public ActionResult Edit(Person p)

{

    db.Entry(p).State = EntityState.Modified;

    db.SaveChanges();

    return RedirectToAction("Index");

}

Хотя данный код прекрасно будет работать, но если оба пользователя одновременно откроют один и тот же объект на редактирование, то после сохранения изменнного объекта первым пользователем, второй пользователь уже буде работать с устаревшими данными.

Что предлагает Entity Framework для решения этой проблемы?

Прежде всего стоит сказать, что есть два типа параллелизма: оптимистичный и пессимистичный.

При пессимистичном параллелизме (pessimistic concurrency) на базу данных накладываются ограничения по доступу. Например, строки объявляются только для чтения или обновления. Пессимистичный параллелизм предполагает работу на уровне базы данных и предполагает создание сложной программной логики, которая бы отслеживала и управляла правами доступа. В Entity Framework поддержки для пессимистичного параллелизма нет.

При оптимистичном параллелизме (optimistic concurrency) допускается проблема параллельного доступа к данным со стороны разных пользователей, как в выше приведенном случае с обновлением. И для решения оптимистичного параллелизма в Entity Framework есть свои методы.

Одни из методов предполагает, что в модели мы объявляем специальное свойство с атрибутом [Timestamp], которое будет отслеживать модификацию строки в таблице. Например:

1

2

3

4

5

6

7

8

9

10

11

using System.ComponentModel.DataAnnotations;

//..................................

public class Person

{

    public int Id { get; set; }

    public string Name { get; set; }

    public int Age { get; set; }

 

    [Timestamp]

    public byte[] RowVersion { get; set; }

}

Атрибут Timestamp указывает, что значение свойства RowVersion будет включаться в создаваемое Entity Frameworkом SQL-выражение Where при отправке в базу данных команд на обновление и удаление. В качестве типа для свойства используется массив байтов.

Также на представлении для редактирования надо добавить скрытое поле для хранения значения новго свойства:

1

@Html.HiddenFor(model => model.RowVersion)

Теперь, если два пользователя одновременно начнут редактировать одну и ту же модель, то после сохранения модели первым пользователем, второй пользователь получит исключение DbUpdateConcurrencyException (находится в пространстве имен System.Data.Entity.Infrastructure), которое соответственно надо обработать:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

[HttpPost]

public ActionResult Edit(Person p)

{

    try

    {

        db.Entry(p).State = EntityState.Modified;

        db.SaveChanges();

    }

    catch (DbUpdateConcurrencyException ex)

    {

        ViewBag.Error = "Объект ранее уже был изменен";

        return View(p);

    }

    return RedirectToAction("Index");

}

Ну и чтобы дать пользователю знать об ошибке, где-нибудь в представлении выведем ViewBag.Error. И теперь при одовременном редактировании одного и того же объекта второй сохраняющий пользователь получит ошибку, а его обновления не будут сохранены в базу данных.

При чем это касается не только обновления, но и удаления.