摘要: Blog I: http://hi.baidu.com/freezesoulBlog II: http://blog.csdn.net/freezesoulBlog III: http://freezesoul.cnblogs.com/Blog IV: http://blog.csdn.net/freeze_soul阅读全文
posted @ 2009-03-26 22:57 FreezeSoul 阅读(50) 评论(0) 编辑

        背景需求:对于数据中表A数据字段的变更,需要引发相应业务逻辑,插入或更新相关表或字段。在以往的方式我们多会在数据库端下文章,建立相应触发器,来完 成业务逻辑操作。不过这种方式仅适用于单纯对于数据操作的需求,可是当我们要完成更复杂的业务需求是却不太容易了(虽然sql05已经支持托管代码的使用 了)。可能你会想到我们可以轮询数据库相关表或视图,来发现数据的变化,可是这对于性能和即时性却是个不容易取舍的问题。不过在 SqlServer2005中有了新的方案,那就是查询通知。

        查询通知是在 Microsoft SQL Server 2005 中以及 ADO.NET 2.0 的 System.Data.SqlClient 命名空间中引入的。查询通知建立在 Service Broker 基础结构的基础上,使应用程序可以在数据更改时收到通知。如果应用程序提供数据库中信息的缓存(例如 Web 应用程序),需要在源数据更改时接收通知,此功能特别有用。

通过三种方式可以使用 ADO.NET 实现查询通知:

  1. 低级实现由 SqlNotificationRequest 类提供,该类公开服务器端功能,使您可以对通知请求执行命令。

  2. 高级实现由 SqlDependency 类提供,该类提供源应用程序与 SQL Server 之间通知功能的高级抽象,使您可以使用相关性来检测服务器中的更改。大多数情况下,这是托管客户端应用程序通过适用于 SQL Server 的 .NET Framework 数据提供程序利用 SQL Server 通知功能的最简单、最有效的方法。

  3. 此外,使用 ASP.NET 2.0(或更高版本)构建的 Web 应用程序可以使用 SqlCacheDependency 帮助器类。

        如果应用程序需要通过刷新显示或缓存来响应基础数据中的更改,查询通知非常有用。如果执行相同命令生成的结果集与最初检索到的结果集不同,则 Microsoft SQL Server 可允许 .NET Framework 应用程序向 SQL Server 发送命令和请求通知。服务器上生成的通知通过队列发送,供以后处理。

您可以为 SELECT 和 EXECUTE 语句设置通知。使用 EXECUTE 语句时,SQL Server 会为执行的命令而不是 EXECUTE 语句本身注册通知。该命令必须满足 SELECT 语句的要求和限制。当注册通知的命令包含多个语句时,数据库引擎会为批处理中的每个语句创建一个通知。

使用查询通知的应用程序有一组通用的要求。必须正确配置数据源才能支持 SQL 查询通知,并且用户必须具有正确的客户端和服务器端权限。

要使用查询通知,必须符合下列条件:

1.使用 SQL Server 2005 或 SQL Server 2008。

2.对数据库启用查询通知。

3.确保用于连接数据库的用户 ID 具有必要的权限。

4.使用 SqlCommand 对象执行有效的 SELECT 语句,包含关联的通知对象 — SqlDependency 或 SqlNotificationRequest。

5.提供所监视的数据更改时用于处理通知的代码。

下面就以一个例子来说明使用SqlDependency的整个流程

using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
using System.Windows.Forms;

namespace CaptureWeb
{
    public class SQLServiceBroker
    {

        private string connectionStr = ConfigurationManager.ConnectionStrings["ConnectionString"].ToString();

        private string sqlStr = "";

        private SqlConnection connection = null;

        public delegate void UIDelegate();

        private UIDelegate uidel = null;

        public Form form = null;

        /// <summary>
        ///
        /// </summary>
        /// <param name="TableName"></param>
        /// <param name="ColumnNames"></param>
        public SQLServiceBroker(string TableName, List<string> ColumnNames)
        {
            string columns = "";
            foreach (string str in ColumnNames)
            {
                if (columns != "")
                    columns = columns + ",";
                columns = columns + "[" + str + "]";
            }
            this.sqlStr = string.Format("select {0} From [dbo].[{1}]", columns, TableName);
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="constr"></param>
        /// <param name="TableName"></param>
        /// <param name="ColumnNames"></param>
        public SQLServiceBroker(string constr, string TableName, List<string> ColumnNames)
            : this(TableName, ColumnNames)
        {
            this.connectionStr = ConfigurationManager.ConnectionStrings[constr].ToString();
        }


        /// <summary>
        ///
        /// </summary>
        ~SQLServiceBroker()
        {
            StopDependency();
            connection.Dispose();
        }

        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public bool EnoughPermission()
        {

            SqlClientPermission perm = new SqlClientPermission(System.Security.Permissions.PermissionState.Unrestricted);
            try
            {
                perm.Demand();
                return true;
            }
            catch (System.Exception)
            {
                return false;
            }
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="uidelegate"></param>
        public void InitDependency(UIDelegate uidelegate)
        {

            SqlDependency.Stop(connectionStr);
            SqlDependency.Start(connectionStr);
            if (connection == null)
                connection = new SqlConnection(connectionStr);

            if (!EnoughPermission())
                throw new Exception("没有权限(SqlClientPermission)!");
            if (uidelegate == null)
                throw new Exception("回调方法未指定(UIDelegate)!");
            if (connection == null)
                throw new Exception("未初始化(InitDependency)!");
            this.uidel = uidelegate;

        }

        /// <summary>
        /// 传入窗体对象,以防止委托有需要访问UI层控件是引发的“从不是创建控件的线程访问它”
        /// </summary>
        /// <param name="form1"></param>
        /// <param name="uidelegate"></param>
        public void InitDependency(Form form1, UIDelegate uidelegate)
        {
            InitDependency(uidelegate);
            this.form = form1;
        }

        /// <summary>
        ///
        /// </summary>
        public void StartDependency()
        {
            //这里很奇怪,每次都需要新的command对象
            using (SqlCommand command = new SqlCommand(sqlStr, connection))
            {
                command.Notification = null;
                SqlDependency dependency = new SqlDependency(command);
                dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
                if (connection.State != ConnectionState.Open)
                    connection.Open();
                command.ExecuteNonQuery();
                command.Dispose();
            }
        }

        /// <summary>
        ///
        /// </summary>
        public void StopDependency()
        {
            SqlDependency.Stop(connectionStr);
            if (connection != null)
                connection.Close();
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
        {
            //注销监测事件
            SqlDependency dependency = (SqlDependency)sender;
            dependency.OnChange -= dependency_OnChange;
            //放在移除事件之后又很大必要,防止ui层调用更新相同表时,进入循环出发调用
            //uidel.Invoke();
            //uidel();
            //使用from.Invoke调用防止访问界面控件引发“从不是创建控件的线程访问它”
            if (form != null)
                form.Invoke(uidel);
            else
                uidel();
            //再次启动监听
            StartDependency();
        }

    }
}


调用方式:

SQLServiceBroker broker;
private void button1_Click(object sender, EventArgs e)
{
    //需要监测的列
    List<string> columns = new List<string>();
    columns.Add("test1");
    columns.Add("test2");
    string table = "test";
    broker = new SQLServiceBroker(table, columns);
    //实例化毁掉函数
    SQLServiceBroker.UIDelegate uidel = new SQLServiceBroker.UIDelegate(writeCon);
    //初始化,及传入回调函数
    broker.InitDependency(uidel);
    //初始化,传入窗体对象对于需要委托中访问ui控件的情况
    //broker.InitDependency(this, uidel);
    //启动监听
    broker.StartDependency();
    MessageBox.Show("启动");
}

private void writeCon()
{
    MessageBox.Show("changed");
}


代码比较简单,都有说明,这里有必要注意几点问题:

1.首先需要SQL Server 2005上执行ALTER DATABASE <DatabaseName> SET ENABLE_BROKER;语句让相应的数据库启用监听服务,以便支持SqlDependency特性。


2.对于SqlCommand的cmdText有特殊要求,其中不能用*,不能用top,不能用函数,包括聚合函数,不能用子查询,包括where后的子查询,不能用外连接,自连接,不能用临时表,不能用变量,不能用视图,不能垮库,表名之前必须加类似dbo数据库所有者这样的前缀。


3.其中在使用当中发现SqlConnection对象应该是一直存在的,因此在此示例中升级为属性,如果将它生命在StartDependency方法体中,出现只能调用一次的情况,因为对于SqlDependency需要connection对象的存在。


4.在使用委托传入调用方法是,如果方法有访问界面UI控件的情况,需要传入窗体对象,以form.Invoke(uidel);的方式调用,否则会引发“从不是创建控件的线程访问它”异常。


5.对于回调函数中需要更新正在监听的表时防止循环调用造成死循环,请在调用委托之前先移除onchange事件dependency.OnChange -= dependency_OnChange;


整个项目Demo:http://cid-9601b7b7f2063d42.skydrive.live.com/self.aspx/Code/SQLServiceBroker.rar


相关文章:http://support.microsoft.com/kb/555893/


CodeProject:http://www.codeproject.com/KB/database/chatter.aspx


MSDN: http://msdn.microsoft.com/zh-cn/library/t9x04ed2.aspx

作者:FreezeSoul
出处:http://hi.baidu.com/freezesoul
欢迎转载,但请保留此段声明,且在文章页面明显位置给出原文连接。

From: http://hi.baidu.com/freezesoul/blog/item/148778cbbd892216bf09e643.html

posted @ 2009-08-24 22:40 FreezeSoul 阅读(1014) 评论(0) 编辑
通过WebBrowser+PrintWindow实现了网页截图,内部使用了拼接的方式可以实现保存完整网页,也可保存第一屏,不过这种方式的潜在 bug是窗体不可最小化,否则为黑屏,而且也没有找到webbrowser以内存方式构建截屏和合理方式,无法正确render后通过PrintWindow 方式截获图片,这可能也是通过PrintWindow截取的不完美的地方吧(请大家指点更完美的方式吧),下面看代码吧:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
using System.Drawing.Imaging;
using SHDocVw;
using mshtml;
using System.Net;

namespace WebBrowserCapture
{
    public class WebBrowserCapture
    {
        #region DLLImport

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr FindWindowEx(IntPtr parent /*HWND*/, IntPtr next /*HWND*/, string sClassName, IntPtr sWindowTitle);

        [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
        private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);

        [DllImport("user32.dll")]
        private static extern void GetClassName(int h, StringBuilder s, int nMaxCount);

        [DllImport("user32.dll")]
        private static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

        public const int GW_CHILD = 5;
        public const int GW_HWNDNEXT = 2;

        #endregion

        public System.Windows.Forms.WebBrowser m_browser;

        //我还抓你第一屏幕
        public bool firstScreen = true;

        //测试网络连通状况
        private bool TestInternet(string url)
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(new Uri(url));
            req.AllowAutoRedirect = true;
            req.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; .NET CLR 3.5.21022; .NET CLR 1.0.3705; .NET CLR 1.1.4322)";
            req.Referer = "http://www.google.com";
            req.ContentType = "text/html";
            req.AllowWriteStreamBuffering = true;
            req.AutomaticDecompression = DecompressionMethods.GZip;
            req.Method = "GET";
            req.Proxy = null;
            req.ReadWriteTimeout = 20;

            HttpStatusCode status;
            using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse())
            {
                status = resp.StatusCode;
            }

            if (status == HttpStatusCode.OK || status == HttpStatusCode.Moved)
            {
                return true;
            }
            return false;
        }

        private static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            int j;
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }

        public void GetPageImage(string url, string path)
        {
            //Test Is OK
            if (TestInternet(url))
            {
                bool flag = firstScreen;
                m_browser.Navigate(url);
                //浏览器Dom载入完毕
                while (m_browser.ReadyState != WebBrowserReadyState.Complete)
                {
                    Application.DoEvents();
                }
                mshtml.IHTMLDocument2 myDoc = (mshtml.IHTMLDocument2)m_browser.Document.DomDocument;

                //处理由于webbrowser上、左边框阴影带来的拼接bug
                int URLExtraHeight = 3;
                int URLExtraLeft = 3;

                (myDoc as HTMLDocumentClass).documentElement.setAttribute("scroll", "yes", 0);

                //document完整高度
                int heightsize = (int)(myDoc as HTMLDocumentClass).documentElement.getAttribute("scrollHeight", 0);
                int widthsize = (int)(myDoc as HTMLDocumentClass).documentElement.getAttribute("scrollWidth", 0);

                ////Get Screen Height
                int screenHeight = (int)(myDoc as HTMLDocumentClass).documentElement.getAttribute("clientHeight", 0);
                int screenWidth = (int)(myDoc as HTMLDocumentClass).documentElement.getAttribute("clientWidth", 0);

                IntPtr myIntptr = (IntPtr)m_browser.Handle;

                //寻找IE对象句柄
                IntPtr wbHandle = FindWindowEx(myIntptr, IntPtr.Zero, "Shell Embedding", null);
                wbHandle = FindWindowEx(wbHandle, IntPtr.Zero, "Shell DocObject View", null);
                wbHandle = FindWindowEx(wbHandle, IntPtr.Zero, "Internet Explorer_Server", null);
                IntPtr hwnd = wbHandle;

                Bitmap bm = new Bitmap(screenWidth, screenHeight, System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
                Bitmap bm2 = new Bitmap(widthsize, heightsize, System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
                Graphics g = null;
                Graphics g2 = Graphics.FromImage(bm2);
                //切割用的临时对象
                Bitmap tempbm = null;
                Graphics tempg = null;

                IntPtr hdc;
                Image screenfrag = null;
                Image firstScreenfrag = null;

                #region 拼接
                int brwTop = 0;
                int brwLeft = 0;
                int myPage = 0;

                //Get Screen Height (for bottom up screen drawing)
                while ((myPage * screenHeight) < heightsize)
                {
                    (myDoc as HTMLDocumentClass).documentElement.setAttribute("scrollTop", (screenHeight - 5) * myPage, 0);
                    ++myPage;
                }
                //Rollback the page count by one
                --myPage;

                int myPageWidth = 0;
                //screenWidth+ URLExtraLeft
                while ((myPageWidth * screenWidth) < widthsize)
                {
                    (myDoc as HTMLDocumentClass).documentElement.setAttribute("scrollLeft", (screenWidth - 5) * myPageWidth, 0);
                    brwLeft = (int)(myDoc as HTMLDocumentClass).documentElement.getAttribute("scrollLeft", 0);
                    for (int i = myPage; i >= 0; --i)
                    {
                        //Shoot visible window
                        g = Graphics.FromImage(bm);
                        hdc = g.GetHdc();
                        (myDoc as HTMLDocumentClass).documentElement.setAttribute("scrollTop", (screenHeight - 5) * i, 0);
                        brwTop = (int)(myDoc as HTMLDocumentClass).documentElement.getAttribute("scrollTop", 0);
                        PrintWindow(hwnd, hdc, 0);
                        g.ReleaseHdc(hdc);
                        g.Flush();
                        screenfrag = Image.FromHbitmap(bm.GetHbitmap());

                        //切除URLExtraLeft、URLExtraHeight长度的webbrowser带来的bug
                        tempbm = new Bitmap(screenWidth - URLExtraLeft, screenHeight - URLExtraHeight, System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
                        tempg = Graphics.FromImage(tempbm);
                        tempg.DrawImage(screenfrag, -URLExtraLeft, -URLExtraHeight);

                        //拼接到g2
                        g2.DrawImage(tempbm, brwLeft + URLExtraLeft, brwTop + URLExtraLeft);

                    }

                    //是否得到第一屏
                    if (flag)
                    {
                        firstScreenfrag = (Image)tempbm.Clone();
                        flag = false;
                    }

                    ++myPageWidth;
                }

                int finalWidth = (int)widthsize;
                int finalHeight = (int)heightsize;
                Bitmap finalImage = new Bitmap(finalWidth, finalHeight, System.Drawing.Imaging.PixelFormat.Format16bppRgb555);
                Graphics gFinal = Graphics.FromImage((Image)finalImage);
                gFinal.DrawImage(bm2, 0, 0, finalWidth, finalHeight);

                #endregion

                //Get Time Stamp
                DateTime myTime = DateTime.Now;
                String format = "yyyy_MM_dd_hh_mm_ss";

                //Create Directory to save image to.
                Directory.CreateDirectory(path);

                //Write Image.
                EncoderParameters eps = new EncoderParameters(1);
                long myQuality = Convert.ToInt64(100);
                eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, myQuality);
                ImageCodecInfo ici = GetEncoderInfo("image/jpeg");
                //保存
                finalImage.Save(path + @"\Img_" + myTime.ToString(format) + ".jpg", ici, eps);

                //保存第一屏
                if (firstScreen)
                {
                    firstScreenfrag.Save(path + @"\Img_FirstScreen_" + myTime.ToString(format) + ".jpg", ici, eps);
                    firstScreenfrag.Dispose();
                }

                //Process.Start("explorer.exe", path);

                #region Clean
                //Clean Up.
                myDoc.close();
                myDoc = null;
                g.Dispose();
                g2.Dispose();
                gFinal.Dispose();
                bm.Dispose();
                bm2.Dispose();
                finalImage.Dispose();
                tempbm.Dispose();
                tempg.Dispose();
                #endregion
            }
        }

    }
}



简易的界面:



截取163.com的图片如下:

首屏



全页面(注:页面被上传控制缩小了,保存的图片是完全比例大小的)



整个项目Demo:http://cid-9601b7b7f2063d42.skydrive.live.com/self.aspx/Code/WebBrowserCapture.rar

作者:FreezeSoul
出处:http://hi.baidu.com/freezesoul
欢迎转载,但请保留此段声明,且在文章页面明显位置给出原文连接。
Link:http://hi.baidu.com/freezesoul/blog/item/124db9a18cb3ba824710649d.html
posted @ 2009-08-21 22:27 FreezeSoul 阅读(3057) 评论(5) 编辑
posted @ 2009-03-26 22:57 FreezeSoul 阅读(50) 评论(0) 编辑
摘要: 背景:由于我使用的是百度空间,本身百度空间发帖的富文本编辑器并不支持代码高亮,因此最近搞了一个FireFox下的用户脚本,可以通过快捷键设置代码高亮,效果很不错,并且实现了脱离百度空间以及FireFox的环境。下面就将代码与大家分享(注:此高亮效果即为快捷键设置的结果) // ==UserScript==// @name HighlightCode// @namespace http://hi.b...阅读全文
posted @ 2008-09-27 10:59 FreezeSoul 阅读(1778) 评论(5) 编辑

终于有时间把『百度空间助手』这个软件完善了一下,目前测试来看,可以说是一个相对稳定的版本,因此按原计划将它开源。

首先值得一提的是可以远程存图(包括本地与网络中的图片),有兴趣的可以尝试 

软件截图:

环境:VS2005 SP1

下面对项目代码简单介绍一下:

解决方案共分四个项目,其中
1、BaidDuHelper :发帖助手主程序
2、Design :发帖助手HTML编辑器
3、BDUninstall :卸载模块
4、BaiDuHelperSetup :安装包项目

其中主程序主要模块是BDUser 和BDWebClient:

BDWebClient:处于底层,主要负责与HTTP请求通讯的目的,主要采用了比较简单的WebClient类,如果你有其他复杂需求,可以考虑使用HttpWebRequest和HttpWebResponse类,代码里面也有部分例子。

其类关系图(相对比较简单,相信大家通过名称也可以猜出属性、方法的作用,不多做说明):


BDUser:处于中间层,主要负责UI层与底层BDWebClient衔接用户行为的关键操作。

其类关系图(不多做说明):

另外发帖助手中的HTML编辑器,采用的是一个codeproject上的一个开源项目,用兴趣大家可以看我以前帖子的介绍。(PS:在此特别感谢作者)

最后一点说明:此软件的开源目的,是为了能与大家更好的交流,当然代码也比较简单,我没有作过多注释,相信大家比较容易能看懂,如果大家看到代码中有哪些 不足请与我联系交流(freezesoul@gmail.com),大家共同进步,毕竟C/S的开发做的不多。当然代码你也可以任意修改,作为你的网站后 台C/S发布端。

 

Csdn 下载 :http://download.csdn.net/source/585528


详情访问:http://hi.baidu.com/freezesoul/blog/item/7bd4aa01ba8e02001d9583bf.html


关于这个软件的更多介绍:百度空间发帖助手(C/S版)【支持远程存图】
posted @ 2008-08-27 15:56 FreezeSoul 阅读(1425) 评论(7) 编辑
摘要: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->usingSystem.Web.Security;usingSystem.Configuration.Provider;usingSystem.Collections.Specialized;usingS...阅读全文
posted @ 2007-12-26 18:06 FreezeSoul 阅读(223) 评论(0) 编辑
摘要: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->usingSystem.Web.Security;usingSystem.Configuration.Provider;usingSystem.Collections.Specialized;usingS...阅读全文
posted @ 2007-12-26 18:06 FreezeSoul 阅读(280) 评论(0) 编辑