小谈需求:
最近工作上接到一个需求,做一个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 |
|