Переопределение операторов модификации базы данных

56

»» В ДАННОЙ СТАТЬЕ ИСПОЛЬЗУЕТСЯ ИСХОДНЫЙ КОД ДЛЯ ПРИМЕРОВ

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

Переопределение кода, вызываемого для вставки, обновления и удаления, сводится просто к определению соответственно именованного частичного метода с соответствующей сигнатурой. Выполнив такое переопределение, вы заставляете процессор изменений DataContext вызывать вашу реализацию частичного метода для обновления, вставки и удаления записей базы данных. Это еще одно из преимуществ частичных методов, использованное Microsoft. Вы получаете возможность вмешаться в код без ка-ких-либо накладных расходов, если вы ею не воспользуетесь.

Следует помнить, однако, что принимая этот подход, вы берете на себя ответственность за обнаружение конфликтов параллельного доступа.

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

Переопределение метода Insert

Для переопределения метода, вызываемого для вставки записи в базу данных, реализуется частичный метод со следующим прототипом:

partial void Insert[EntityClassName] (T instance)

Здесь [EntityClassName] — имя сущностного класса, метод вставки которого переопределяется, а T — тип сущностного класса.

Вот пример переопределяемого прототипа метода вставки для сущностного класса Snipper:

partial void InsertShipper(Shipper instance)
Переопределение метода Update

Для переопределения метода, вызываемого для обновления записи в базе данных, реализуется частичный метод со следующим прототипом:

partial void Update[EntityClassName](T instance)

Здесь [EntityClassName] — имя сущностного класса, метод обновления которого переопределяется, а T — тип сущностного класса. Вот пример переопределяемого прототипа метода обновления для сущностного класса Snipper:

partial void UpdateShipper(Shipper instance)
Переопределение метода Delete

Для переопределения метода, вызываемого для удаления записи в базе данных, реализуется частичный метод со следующим прототипом:

partial void Delete[EntityClassName](T instance)

Здесь [EntityClassName] — имя сущностного класса, метод удаления которого переопределяется, а T — тип сущностного класса. Вот пример переопределяемого прототипа метода удаления для сущностного класса Snipper:

partial void DeleteShipper(Shipper instance)

Пример

Для примера, демонстрирующего переопределение методов вставки, обновления и удаления, вместо модификации сгенерированного файла сущностных классов будет создан новый файл, специально для переопределенных частичных методов, чтобы в случае, если придется повторно генерировать файл сущностных классов, переопределения частичных методов не были потеряны. Этот файл называется NorthwindExtended.cs и имеет следующее содержимое:

using System;
using System.Data.Linq;

namespace nwind
{
  public partial class Northwind : DataContext
  {
    partial void InsertShipper(Shipper instance)
    {
      Console.WriteLine("Для грузоотправителя {0} вызван переопределенный метод вставки.",
        instance.CompanyName);
    }

    partial void UpdateShipper(Shipper instance)
    {
        Console.WriteLine("Для грузоотправителя {0} вызван переопределенный метод обновления.",
        instance.CompanyName);
    }

    partial void DeleteShipper(Shipper instance)
    {
        Console.WriteLine("Для грузоотправителя {0} вызван переопределенный метод удаления.",
        instance.CompanyName);
    }
  }
}

Первое, что следует отметить относительно переопределенного кода — это тот факт, что переопределенные методы являются частичными методами, определенными на уровне DataContext. Они не определены в сущностном классе, к которому относятся.

Как видите, переопределенные методы не делают ничего помимо того, что уведомляют о том, что они были вызваны. Во многих ситуациях переопределение нужно для вызова хранимых процедур, хотя это оставлено на усмотрении разработчика.

Теперь давайте взглянем на пример, который содержит код, вызывающий переопределенные методы (для данного примера используйте консольное приложение, а не проект на WPF):

Northwind db = new Northwind(@"Data Source=MICROSOF-1EA29E\SQLEXPRESS;
                                           AttachDbFilename=C:\Northwind.mdf;
                                           Integrated Security=True");
                  
Shipper ship = (from s in db.Shippers
                      where s.ShipperID == 1
                      select s).Single<Shipper>();

      ship.CompanyName = "Jiffy Shipping";

      Shipper newShip =
        new Shipper
        {
          ShipperID = 4,
          CompanyName = "Vickey Rattz Shipping",
          Phone = "(800) SHIP-NOW"
        };

      db.Shippers.InsertOnSubmit(newShip);

      Shipper deletedShip = (from s in db.Shippers
                             where s.ShipperID == 3
                             select s).Single<Shipper>();

      db.Shippers.DeleteOnSubmit(deletedShip);

      db.SubmitChanges();

В этом коде сначала извлекается грузоотправитель, у которого ShipperID равен 1, и обновляется поле. Затем вставляется другой грузоотправитель — Vickey Rattz Shipping, и еще один удаляется — тот, у которого ShipperID равно 3. Естественно, поскольку вызываются переопределенные методы, которые только выводят сообщение на консоль, ни одно из изменений в базе данных не сохраняется. Вот как выглядит результат:

Пример с переопределенными методами обновления, вставки и удаления

В этом результате можно видеть, что вызывался каждый из переопределенных методов. Теперь возникает вопрос: а как переопределить методы вставки, обновления и удаления, не отменяя поведения по умолчанию?

Поскольку необходимый код вступил бы в конфликт с частичными методами из предыдущего примера, работающий пример этого приводиться не будет, а будут даны только объяснения, как это сделать. Чтобы получить поведение по умолчанию, в реализациях частичных методов для вставки, обновления и удаления необходимо вызывать методы DataContext.ExecuteDynamicInsert, DataContext.ExecuteDynamicUpdate и DataContext.ExecuteDynamicDelete.

Например, если бы в предыдущем примере нужно было сначала вызвать протоколирование сообщений, а затем нормальный код LINQ to SQL, чтобы сохранить изменения в базе данных, понадобилось бы изменить реализацию частичного метода следующим образом:

using System;
using System.Data.Linq;

namespace nwind
{
  public partial class Northwind : DataContext
  {
    partial void InsertShipper(Shipper instance)
    {
      Console.WriteLine("Для грузоотправителя {0} вызван переопределенный метод вставки.",
        instance.CompanyName);
        this.ExecuteDynamicInsert(instance);
    }

    partial void UpdateShipper(Shipper instance)
    {
        Console.WriteLine("Для грузоотправителя {0} вызван переопределенный метод обновления.",
        instance.CompanyName);
        this.ExecuteDynamicUpdate(instance);
    }

    partial void DeleteShipper(Shipper instance)
    {
        Console.WriteLine("Для грузоотправителя {0} вызван переопределенный метод удаления.",
        instance.CompanyName);
        this.ExecuteDynamicDelete(instance);
    }
  }
}

Обратите внимание, что в каждом из частичных методов вызывается соответствующий метод ExecuteDynamicInsert, ExecuteDynamicUpdate или ExecuteDynamicDelete. Теперь можно расширить поведение при вызове сущностного класса, модифицировать его или даже создать оболочку для существующего поведения по умолчанию.

Пройди тесты
Лучший чат для C# программистов