小談需求:
最近工作上接到一個需求,做一個web展示數據的報表,最好能實時更新,不限制所用技術。
第一個問題:web服務器推送給瀏覽器新數據,一開始我想到的最快的最簡單的方法就是 在web頁面上js輪詢了。因為我們的數據更新頻率並不快。 后來覺得這種辦法有點太土了。 或許長輪詢更有效。 當然長輪詢的技術很多了。 java 的dwr,c#的 signalr。c#還可以同過異步請求來自己寫長輪詢。
遇到的第二個問題,就是數據庫如何通知web服務器更新數據,下面便是sql server2008的推送了,通過sql server的觸發器,當數據表有變化時(增,刪,改)就通過tcp請求服務器,服務器會在啟動后開啟端口一直監聽,隨時等待通信請求。當收到請求后,就從數據庫讀取新數據,推送給瀏覽器。整體大概就這樣。
下面是數據庫通知服務器。這是一個 winform的demo ,winfom就相當於我們展示數據的服務器了。
最后demo圖:

現在我插入一條數據:

然后再看那個客戶端:

剛插入的數據已經出現了哦。
客戶端代碼:
winform:
程序啟動后,開啟端口監聽,如果有收到通信,則通知 dataview更新數據。
02 |
using System.Collections.Generic; |
03 |
using System.ComponentModel; |
05 |
using System.Data.SqlClient; |
09 |
using System.Net.Sockets; |
11 |
using System.Windows.Forms; |
12 |
using System.Threading; |
14 |
namespace sql_dependency |
16 |
public partial class Form1 : Form |
20 |
InitializeComponent(); |
23 |
System.Data.SqlClient.SqlConnection conn = null ; |
24 |
string _connstr = "Data Source = 10.6.154.251; database=Temp;user id=sa;pwd=MOcyou0543_" ; |
25 |
System.Data.SqlClient.SqlCommand command = null ; |
27 |
private void Form1_Load( object sender, EventArgs e) |
29 |
conn = new System.Data.SqlClient.SqlConnection(_connstr); |
30 |
command = conn.CreateCommand(); |
31 |
command.CommandText = "select [A],[B],[C] From [Temp].[dbo].[Simple]" ; |
32 |
SqlDependency.Start(_connstr); |
33 |
Thread t = new Thread( new ThreadStart(GetData)); |
38 |
private void GetData() |
42 |
IPAddress localAddr = IPAddress.Parse( "127.0.0.1" ); |
43 |
TcpListener tcplistener = new TcpListener(localAddr, 10010); |
45 |
byte [] btServerReceive = new byte [2048]; |
46 |
string strServerReceive = string .Empty; |
49 |
TcpClient tcp = tcplistener.AcceptTcpClient(); |
50 |
Console.WriteLine( "Connected!" ); |
51 |
NetworkStream ns = tcp.GetStream(); |
52 |
int intReceiveLength = ns.Read(btServerReceive, 0, btServerReceive.Length); |
53 |
strServerReceive = Encoding.ASCII.GetString(btServerReceive, 0, intReceiveLength); |
60 |
private delegate void ChangeDataView(); |
61 |
private void SetData() |
63 |
if ( this .InvokeRequired) |
65 |
this .Invoke( new ChangeDataView(SetData)); |
69 |
using (SqlDataAdapter adapter = new SqlDataAdapter(command)) |
71 |
System.Data.DataSet ds = new DataSet(); |
72 |
adapter.Fill(ds, 0, 100, "Simple" ); |
73 |
dataGridView1.DataSource = ds.Tables[ "Simple" ]; |
81 |
private void Form1_Closed( object sender, FormClosedEventArgs e) |
84 |
SqlDependency.Stop(_connstr); |
91 |
} 數據庫與clr集成,編寫寫dll:SqlDependency.dll,sql server將在可編程性中加載此dll,
04 |
using System.Net.Sockets; |
05 |
using Microsoft.SqlServer.Server; |
07 |
using System.Net.Sockets; |
08 |
namespace SqlDependency |
13 |
[SqlFunction(IsDeterministic = true , DataAccess = DataAccessKind.Read)] |
14 |
public static String WriteStringToFile(String FileFullPath, String Contend) |
17 |
FileInfo Fi = new FileInfo(FileFullPath); |
18 |
if (!Fi.Directory.Exists) |
20 |
Fi.Directory.Create(); |
23 |
using (StreamWriter rw = File.CreateText(FileFullPath)) |
26 |
rw.WriteLine(Contend); |
27 |
TcpClient tcpClient = new TcpClient(); |
31 |
if (tcpClient == null ) |
33 |
tcpClient = new TcpClient(); |
34 |
tcpClient.ReceiveTimeout = 20000; |
36 |
if (tcpClient.Connected == false ) |
38 |
System.Net.IPAddress address = System.Net.IPAddress.Parse(Contend); |
39 |
System.Net.IPHostEntry ipInfor = System.Net.Dns.GetHostByAddress(address); |
40 |
string hostName = ipInfor.HostName; |
41 |
IPEndPoint serverEndPoint = new IPEndPoint(IPAddress.Parse( "127.0.0.1" ), 10010); |
42 |
tcpClient.Connect(serverEndPoint); |
45 |
rw.Write( "連接成功,先發送指令" ); |
47 |
Byte[] data = System.Text.Encoding.ASCII.GetBytes( "new data!" ); |
49 |
NetworkStream stream = tcpClient.GetStream(); |
52 |
stream.Write(data, 0, data.Length); |
接下來,便開始配置sql server啦: 首先開啟sql server的clr支持:
4 |
alter database dbname set TRUSTWORTHY on ; |
接着在sql server 2008中,新建查詢窗口。加載剛才編寫的dll SqlDependency.dll,並注冊方法,然后寫觸發器,當表數據有變化時,觸發函數。:
02 |
create assembly SqlDependency FROM 'D:\SqlDependency.dll' |
03 |
WITH PERMISSION_SET = UNSAFE |
07 |
create function WriteStringToFile(@FileFullName as nvarchar( max ), @FileContend AS nvarchar( max )) |
09 |
with returns null on null input |
10 |
external name [SqlDependency].[SqlDependency.Program].[WriteStringToFile] |
14 |
CREATE TRIGGER [dbo].[UserTableChangedEvent] on [dbo].[Simple] |
15 |
FOR INSERT , DELETE , UPDATE |
19 |
DECLARE @Contend AS VARCHAR (100) |
20 |
DECLARE @FileName AS VARCHAR ( MAX ) |
21 |
SET @FileName = 'D:\\MSG\\' + CONVERT ( varchar (12) , getdate(), 112 )+ '\\' + convert (nvarchar(50), NEWID())+ '.TXT' |
23 |
SET @Contend = '127.0.0.1' ; |
24 |
Select dbo.WriteStringToFile(@FileName, @Contend) |
注意,我的應用程序和 數據庫在一台服務器上,所以地址都是127.0.0.1.可跟據實際填寫正確地址。 再次在sql server中新建一個查詢窗口,插入語句,進行測試吧。 如果過程中有問題,需要更新程序,方便地刪除之上所創建的幾個東
1 |
drop TRIGGER [dbo].[UserTableChangedEvent] |
2 |
drop function WriteStringToFile |
3 |
drop assembly SqlDependency |
抄送:開源中國 http://my.oschina.net/u/867090/blog/122146 |