2009-12-15 78 views
2

有没有办法让SQL Server 2005回调到连接的应用程序,这样连接的应用程序就会知道表中的记录何时修改了字段由另一个应用程序使用相同的数据库使用LINQ到SQL跟踪数据库的外部更改

一个简单的例子是同一个应用程序连接到同一个数据库中的同一个表的两个实例。当应用程序的一个实例对表进行更改时,另一个实例会收到通知,指出某些内容已更改,并且能够查询数据库以查找更改。

UPDATE

感谢这么多的帮助至今。我甚至从来不知道要查找SqlDependency类。我已经按照此页上的说明http://msdn.microsoft.com/en-us/a52dhwx7.aspx创建了SqlDependency测试演示。但是,我无法得到这个工作。我从来没有看到OnChange事件被调用。

我也试图修改我自己的应用程序使用说明作为指导,没有运气。我在下面列出了我自己的应用程序中的代码。基本上,位置表具有PositionID字段以及LocationX和LocationY字段。我编写了另一个应用程序,允许我更新给定行的LocationX字段。

我错过了什么?为什么数据库更改不会触发我的偶数处理程序?

更新#2

另外请注意,我用我的命令硬编码的SQL字符串。我宁愿不使用注释掉的LINQ语句。以这种方式使用LINQ来生成将用于构建命令的SQL字符串是否可以使用?

更新#3

所以我设法弄清楚什么是错的我下面的代码。显然你必须执行一次这样的命令,这样会有一个数据缓存,否则服务器不知道何时通知你?我在一行中添加了一个DataAdapter.Fill()和我的SqlCommand,现在事件似乎在预期时触发。

这给我带来了下一个问题。 SqlDependency.OnChange事件只能让你知道事情已经改变。我怎样才能从我的旧数据集和新的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(); 
    } 
    } 

回答

2

SQL Server可以使用Query Notifications来做到这一点。 L2S没有任何内容支持这一点,但也没有什么能阻止你在同一个应用程序中在L2S之外使用它。

0

查询通知使用索引视图技术来检测数据更改并在结果集可能发生更改时通知订阅的查询。这是ASP SqlCacheDependency缓存失效的技术。您可以通过The Mysterious Notification了解更多信息。

在.Net Framework中,利用查询通知最常用的组件是SqlDependency。有关如何将linq2sql与SqlDependency集成的各种示例,如linqtosqlcache

您不应该使用此技术来监视频繁更改的数据,但仅适用于值得高速缓存的目录参考数据。设置和发送通知的成本很高。

-4

你为什么想这样做?

Linq-to-SQL在你开始使用它之前就已经死了。

现在他们推EF,WCF-DS等(谁知道他们什么时候也会杀了他们)。

即使查询通知不再是安全的赌注(因为如果你的应用程序持续超过几年,它们就非常脆弱)。

+0

我不知道这是如何相关。 LINQ到SQL可能已经有几年了,但似乎微软仍在为此开发。至于EF和WCF-DS,我不能说这个。谈到微软技术时,我倾向于落后几年。但我的经验是,微软支持他们的旧技术。 – thebeav 2009-12-21 14:45:56

+1

Linq to SQL并未死亡。 VS2010添加了新功能。以下是许多文章之一:http://reddevnews.com/blogs/data-driver/2008/12/microsoft-says-linq-to-sql-not-dead.aspx 没有功能添加到ADO.NET从何时起? ADO.NET死了吗? – fuzzbone 2010-02-12 21:55:57