Отслеживание внешних изменений в базе данных с помощью LINQ-to-SQL - PullRequest
2 голосов
/ 16 декабря 2009

Есть ли способ заставить SQL Server 2005 перезвонить подключенному приложению, чтобы подключенное приложение узнало, когда запись в таблице имеет поле, измененное другим приложением, использующим ту же базу данных?

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

UPDATE

Спасибо большое за помощь. Я бы никогда не узнал, чтобы искать класс SqlDependency. Я следовал инструкции на этой странице http://msdn.microsoft.com/en-us/a52dhwx7.aspx при создании демонстрационной демонстрации SqlDependency. Однако я не смог заставить это работать. Я никогда не вижу, чтобы вызывалось событие OnChange.

Я также безуспешно пытался изменить свое собственное приложение, используя инструкции в качестве руководства. Я включил код из моего собственного приложения ниже. По сути, таблица Position имеет поле PositionID вместе с полями LocationX и LocationY. Я написал другое приложение, которое позволяет мне обновлять поле LocationX данной строки.

Что мне не хватает? Почему изменения базы данных не вызовут мой четный обработчик?

ОБНОВЛЕНИЕ № 2

Также обратите внимание, что я использую жестко закодированную строку SQL для своей команды. Я бы предпочел не использовать закомментированный оператор LINQ. Считается ли правильным использовать LINQ таким образом для генерации строки SQL, которая будет использоваться для построения команды?

ОБНОВЛЕНИЕ № 3

Так что мне удалось выяснить, что не так с моим кодом ниже. По-видимому, вам нужно выполнить команду один раз, чтобы был кэш данных, иначе сервер не знает, когда вас уведомить? Я добавил в строку для выполнения DataAdapter.Fill () с моим SqlCommand, и теперь событие, кажется, срабатывает, когда ожидается.

Что подводит меня к моей следующей проблеме. Событие SqlDependency.OnChange только сообщает, что что-то изменилось. Как я могу узнать из моего старого DataSet и нового DataSet, каковы построчные изменения?

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

Я могу вызвать DataContext.Refresh () и заставить его выполнять все обновления моих структур данных, но, похоже, это не вызывает никаких событий, генерируемых DataContext OnChanging (). Кажется, что Refresh () фактически разрушает все мои структуры и создает новые. Поэтому я никогда не могу понять, что изменилось.

У кого-нибудь есть рекомендации?

public partial class MainForm : Form
  {
    private ArpPhase2DbContextDataContext db = null;
    private SqlConnection connection = null;
    private SqlCommand command = null;

    public MainForm()
    {
      InitializeComponent();
    }

    private void MainForm_Load(object sender, EventArgs e)
    {
      this.canRequestNotifications();
      this.db = ArpPhase2DbContextDataContext.Instance;
      this.setupSqlDependency();
    }

    private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
    {
      SqlDependency.Stop(this.db.Connection.ConnectionString);

      if (this.connection != null)
      {
        this.connection.Close();
      }

      this.db.SubmitChanges();
    }

    private bool canRequestNotifications()
    {
      try
      {
        SqlClientPermission perm = new SqlClientPermission(PermissionState.Unrestricted);
        perm.Demand();

        return true;
      }
      catch
      {
        return false;
      }
    }

    private void setupSqlDependency()
    {
      // Remove any existing dependency connection, then create a new one.
      SqlDependency.Stop(this.db.Connection.ConnectionString);
      SqlDependency.Start(this.db.Connection.ConnectionString);

      if (this.connection == null)
      {
        this.connection = new SqlConnection(this.db.Connection.ConnectionString);
      }

      if (this.command == null)
      {
        var sql = (from position in this.db.Positions
                   select position);

        //string commandString = sql.ToString();
        string commandString = "SELECT * FROM Positions;";
        this.command = new SqlCommand(commandString, connection);
      }

      this.getData();
    }

    private void getData()
    {
      // Make sure the command object does not already have
      // a notification object associated with it.
      this.command.Notification = null;

      // Create and bind the SqlDependency object
      // to the command object.
      SqlDependency dependency = new SqlDependency(this.command);
      dependency.OnChange += new OnChangeEventHandler(this.dependency_OnChange);
    }

    private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
    {
      // This event will occur on a thread pool thread.
      // Updating the UI from a worker thread is not permitted.
      // The following code checks to see if it is safe to
      // update the UI.
      ISynchronizeInvoke i = (ISynchronizeInvoke)this;

      // If InvokeRequired returns True, the code
      // is executing on a worker thread.
      if (i.InvokeRequired)
      {
        // Create a delegate to perform the thread switch.
        OnChangeEventHandler del = new OnChangeEventHandler(this.dependency_OnChange);

        object[] args = { sender, e };

        // Marshal the data from the worker thread
        // to the UI thread.
        i.BeginInvoke(del, args);

        return;
      }

      // Remove the handler, since it is only good
      // for a single notification.
      SqlDependency dependency = (SqlDependency)sender;

      dependency.OnChange -= this.dependency_OnChange;

      // Add information from the event arguments to the list box
      // for debugging purposes only.
      Console.WriteLine("Info: {0}, Source: {1}, Type: {2}", e.Info.ToString(),
        e.Source.ToString(), e.Type.ToString());

      // Rebind the dependency.
      this.setupSqlDependency();
    }
  }

Ответы [ 3 ]

2 голосов
/ 16 декабря 2009

SQL Server может сделать это с Уведомления о запросах . В L2S нет ничего встроенного для поддержки этого, но также нет ничего, что могло бы помешать вам использовать его вне L2S в том же приложении.

0 голосов
/ 20 декабря 2009

Почему вы хотите это сделать?

Linq-to-SQL был мертв до того, как вы начали его использовать.

Теперь они нажимают EF, WCF-DS и т. Д. (Кто знает, когда они их тоже убьют).

Даже уведомления о запросах больше не являются безопасной ставкой (так как они очень хрупкие, если у вас есть приложение, которое будет работать более нескольких лет).

0 голосов
/ 16 декабря 2009

Query Notifications использует технологию индексированного представления для обнаружения изменений данных и уведомления подписанных запросов, когда результирующий набор, возможно, изменился. Это технология, которая обеспечивает ASP SqlCacheDependency для аннулирования кэша. Вы можете узнать больше о том, как это работает, в Таинственное уведомление .

В .Net Framework наиболее часто используемым компонентом, использующим уведомления о запросах, является SqlDependency. Существуют различные примеры интеграции linq2sql с SqlDependency, например linqtosqlcache .

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...