Friday, February 2, 2007

Display image in a Web Page

I 've took a lot of times to surf the web to find out the best resolution to display image and scale image with the best quality.

1. Scale image by .NET to produce the best quality
Dim fileName As String = albumlist.SelectedValue & "_" & currentDate & fileExtension
' Create image object from upload stream
Dim img As System.Drawing.Bitmap = System.Drawing.Bitmap.FromStream(.InputStream)
Dim imageFormat = img.RawFormat

Dim thumbSize As New Size
thumbSize = NewthumbSize(img.Width, img.Height)
'Thumb-nail the image to the new size
Dim imgOutput As New Bitmap(img, thumbSize.Width, thumbSize.Height)

'**************THIS HAD TO BE ADDED!!*****************
Dim myresizer As Graphics
myresizer = Graphics.FromImage(imgOutput)
myresizer.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
myresizer.DrawImage(img, 0, 0, thumbSize.Width, thumbSize.Height)
'****************************************************

'Save original image
imgOutput.Save(Path.Combine(Server.MapPath("../photo/images"), fileName), imageFormat)

Note the bold code above, It will produces the best quality image when you try to Zoom or Scale to smaller image.

Beside, I found another way to scale smaller image but, using CSS and HTML only. THe quality is good.

Thursday, January 4, 2007

Removing HTML from the text in ASP
By Konstantin Vasserman.
(Capture link: http://www.codeproject.com/asp/removehtml.asp )
Exploring the options of removing HTML tags from the text in ASP.

Why remove HTML tags?

There could be a number of reasons why you as a developer want to remove HTML tags from the text. The most common situation is when you are going to display some text on the web page and the text was submitted by an unknown user or it came from some other source that you have no control over. You don't have any idea what the content of the text is: it could contain some damaging script or some HTML formatting that will completely mess up the look of your site. It could be that you just don't want any HTML tags in the text because of some application restrictions. You might want to limit the use of HTML to some simple text formatting tags, but restrict the users from using links and inserting images. Whether you have a good reason for that or you just want HTML out of your text because you are a member of "HTML Hatred Club" - you have to find the way to get those tags out of the text. This article will look into the options you have when it comes to removing HTML tags from the text in ASP.

First Option - Disable HTML

First and probably the easiest solution is to just disable HTML tags in the text without removing them. You can do it with Replace() function. For example, if you want to disable all the SCRIPT tags you could do this:

strText = Replace(strText, ", "<script", 1, -1, 1)
or to make sure that all HTML tags are disabled:
strText = Replace(strText, "<", "<")

No opening brackets - no valid HTML tags - no problem. Right?

It is a good (quick) security measure to prevent people from embedding damaging client-side scripts within the text they submit, but it is hardly a user-friendly feature.

The problem with this approach is that all the HTML tags are now shown as well as the rest of the text and it is very hard to read. It's kind of like displaying the HTML source to the user - not a very nice thing to do.

Second Option - Use the brackets

How to make HTML tags disappear from the text? Well, we can just remove them. We can just take everything between opening bracket "<" and closing bracket ">" of HTML tags and remove it. It sounds easy ...

Well, it is easier said than done, especially in VBScript. :-)

People who code in Perl or Java Scripts can actually tell you that it is a piece of cake. They are absolutely right. For example, JavaScript function that removes everything between the brackets could look like this:

function RemoveHTML( strText )
{
var regEx = /<[^>]*>/g;
return strText.replace(regEx, "");
}

For those of you who doesn't know what all of these "/<[^>]*>/g" mean - it's called Regular Expression. "Regular expressions are patterns used to match character combinations in strings." You can learn more about them by following this link: http://developer.netscape.com/docs/manuals/js/client/jsguide/regexp.htm.

Back in VBScript world, for those of us who runs Scripting Engine 5.0 or later (you can check you version by calling ScriptEngineMajorVersion and ScriptEngineMinorVersion functions) we can use RegExp object as well. RemoveHTML function could look like this:

Function RemoveHTML( strText )
Dim RegEx

Set RegEx = New RegExp

RegEx.Pattern = "<[^>]*>"
RegEx.Global = True

RemoveHTML = RegEx.Replace(strText, "")
End Function

It doesn't look too complicated, does it? Providing that you know how to build those patterns ... ;-)

For the rest of VBScript people (who has an older Scripting Engine or doesn't want to mess with the Regular Expressions) writing of your own little parser is the way to go. Below is an example of such a function. My friend Chris Coursey and I used this function in one of our projects a couple of years ago:

Function RemoveHTML( strText )
Dim nPos1
Dim nPos2

nPos1 = InStr(strText, "<")
Do While nPos1 > 0
nPos2 = InStr(nPos1 + 1, strText, ">")
If nPos2 > 0 Then
strText = Left(strText, nPos1 - 1) & Mid(strText, nPos2 + 1)
Else
Exit Do
End If
nPos1 = InStr(strText, "<")
Loop

RemoveHTML = strText
End Function

While all of the above solutions work and do exactly what they were meant to do (remove everything between the brackets), there are at least a couple of problems with this approach:

First of all, because these functions are only take into an account the bracket characters - any brackets within the body of the text that were never meant to be HTML tags will be removed. They will be removed together with any text that happens to be within those brackets. In other words, any attempt by a user to include "<" or ">" characters in the text might cause these functions to produce unpredictable and at the time very ugly results.

On the other hand, these functions remove all the HTML tags unconditionally. You cannot control which tags are removed and which are kept untouched. That is the problem when you want to let your users to enter some harmless HTML tags like "" and "", but remove the other tags.

Third Option - Use IE and other tools

The only way to overcome both of the previously discussed problems is to make your code aware of specific HTML tags that you want to be removed. I am currently unaware of any third-party ASP components that would do the job for you, but they might very well be out there. I did however attempted to write one myself based on MSHTML Library and I've seen that somebody has used Internet Explorer's Application object to produce the desired results of striping HTML tags. Both of these solutions seemed to work, but with IE solution you will most likely get a huge performance hit and both of them don't seem to be very safe things to do according to MSKB:

"It may be desirable to parse HTML files inside a Web server process in response to a browser page request. However, the WebBrowser control, DHTML Editing Control, MSHTML, and other Internet Explorer components may not function properly in an Active Server Pages (ASP) page or other application run in a Web server application." (http://support.microsoft.com/support/kb/articles/Q244/0/85.ASP?LN=EN-US&SD=gn&FR=0)

In other words - think twice before using any IE components on the server side.

Fourth Option - Another VBScript attempt

Having explored all of the above options I have taking a challenge of writing an ASP function in VBScript that would both be intelligent enough to remove only known HTML tags and at the same time would provide the developer with ability to control which tags to remove. Following is the result of this attempt.

A few words about the function:

  • List of the HTML tags to be removed controlled by adding or removing tags from the TAGLIST constant. For example, to leave all tags in the text you must remove B from the TAGLIST. Current list contains every tag listed in index of HTML tags of MSDN Library with the addition of the LAYER tag. Please note that every tag must be surrounded by semi-colons (";") in order for this function to work properly.
  • Both the start and the end tags will be removed. For example, both and tags will be removed.
  • If tag is present in both TAGLIST and BLOCKTAGLIST constants this function will remove everything between the start and the end tag. For example, if SCRIPT tag is included in both TAGLIST and BLOCKTAGLIST everything between tags will be removed.
  • Tags without a closing bracket will not be considered a valid HTML tags and therefore will not be removed. That in compliance with the HTML standard as far as I know.
  • Block tags that does not have an end tag will cause the entire portion of the text from the start tag to the end of the text to be removed. For example, if is missing - everything from to the end of the text will be removed.
  • I've done some performance testing on this function just to get an idea about its speed. It removed a 1000 tags from 24K text string in one second. 2300 tags were removed from 60K text string in about 4.5 seconds. Relatively short string with a few tags - very fast. :-)

Usage of the function is simple:



strPlainText = RemoveHTML(strTextWithHTML)

And here is the function (VB .NET):

Collapse
Function RemoveHTML( strText )
Dim TAGLIST
TAGLIST = ";!--;!DOCTYPE;A;ACRONYM;ADDRESS;APPLET;AREA;B;BASE;BASEFONT;" &_
"BGSOUND;BIG;BLOCKQUOTE;BODY;BR;BUTTON;CAPTION;CENTER;CITE;CODE;" &_
"COL;COLGROUP;COMMENT;DD;DEL;DFN;DIR;DIV;DL;DT;EM;EMBED;FIELDSET;" &_
"FONT;FORM;FRAME;FRAMESET;HEAD;H1;H2;H3;H4;H5;H6;HR;HTML;I;IFRAME;IMG;" &_
"INPUT;INS;ISINDEX;KBD;LABEL;LAYER;LAGEND;LI;LINK;LISTING;MAP;MARQUEE;" &_
"MENU;META;NOBR;NOFRAMES;NOSCRIPT;OBJECT;OL;OPTION;P;PARAM;PLAINTEXT;" &_
"PRE;Q;S;SAMP;SCRIPT;SELECT;SMALL;SPAN;STRIKE;STRONG;STYLE;SUB;SUP;" &_
"TABLE;TBODY;TD;TEXTAREA;TFOOT;TH;THEAD;TITLE;TR;TT;U;UL;VAR;WBR;XMP;"

Const BLOCKTAGLIST = ";APPLET;EMBED;FRAMESET;HEAD;NOFRAMES;NOSCRIPT;OBJECT;SCRIPT;STYLE;"

Dim nPos1
Dim nPos2
Dim nPos3
Dim strResult
Dim strTagName
Dim bRemove
Dim bSearchForBlock

nPos1 = InStr(strText, "<")
Do While nPos1 > 0
nPos2 = InStr(nPos1 + 1, strText, ">")
If nPos2 > 0 Then
strTagName = Mid(strText, nPos1 + 1, nPos2 - nPos1 - 1)
strTagName = Replace(Replace(strTagName, vbCr, " "), vbLf, " ")

nPos3 = InStr(strTagName, " ")
If nPos3 > 0 Then
strTagName = Left(strTagName, nPos3 - 1)
End If

If Left(strTagName, 1) = "/" Then
strTagName = Mid(strTagName, 2)
bSearchForBlock = False
Else
bSearchForBlock = True
End If

If InStr(1, TAGLIST, ";" & strTagName & ";", vbTextCompare) > 0 Then
bRemove = True
If bSearchForBlock Then
If InStr(1, BLOCKTAGLIST, ";" & strTagName & ";", vbTextCompare) > 0 Then
nPos2 = Len(strText)
nPos3 = InStr(nPos1 + 1, strText, " & strTagName, vbTextCompare)
If nPos3 > 0 Then
nPos3 = InStr(nPos3 + 1, strText, ">")
End If

If nPos3 > 0 Then
nPos2 = nPos3
End If
End If
End If
Else
bRemove = False
End If

If bRemove Then
strResult = strResult & Left(strText, nPos1 - 1)
strText = Mid(strText, nPos2 + 1)
Else
strResult = strResult & Left(strText, nPos1)
strText = Mid(strText, nPos1 + 1)
End If
Else
strResult = strResult & strText
strText = ""
End If

nPos1 = InStr(strText, "<")
Loop
strResult = strResult & strText

RemoveHTML = strResult
End Function

Konstantin Vasserman



private string RemoveHTML(string strText)
{

string TAGLIST
= ";!--;!DOCTYPE;A;ACRONYM;ADDRESS;APPLET;AREA;B;BASE;BASEFONT;" +
"BGSOUND;BIG;BLOCKQUOTE;BODY;BR;BUTTON;CAPTION;CENTER;CITE;CODE;" +
"COL;COLGROUP;COMMENT;DD;DEL;DFN;DIR;DIV;DL;DT;EM;EMBED;FIELDSET;" +
"FONT;FORM;FRAME;FRAMESET;HEAD;H1;H2;H3;H4;H5;H6;HR;HTML;I;IFRAME;IMG;" +
"INPUT;INS;ISINDEX;KBD;LABEL;LAYER;LAGEND;LI;LINK;LISTING;MAP;MARQUEE;" +
"MENU;META;NOBR;NOFRAMES;NOSCRIPT;OBJECT;OL;OPTION;P;PARAM;PLAINTEXT;" +
"PRE;Q;S;SAMP;SCRIPT;SELECT;SMALL;SPAN;STRIKE;STRONG;STYLE;SUB;SUP;" +
"TABLE;TBODY;TD;TEXTAREA;TFOOT;TH;THEAD;TITLE;TR;TT;U;UL;VAR;WBR;XMP;";

const string BLOCKTAGLIST = ";APPLET;EMBED;FRAMESET;HEAD;NOFRAMES;NOSCRIPT;OBJECT;SCRIPT;STYLE;";

int nPos1 = 0;
int nPos2 = 0;
int nPos3 = 0;
string strResult = "";
string strTagName = "";
bool bRemove;
bool bSearchForBlock;

nPos1 = strText.IndexOf("<"); while (nPos1 >= 0)
{
nPos2 = strText.IndexOf(">", nPos1 + 1);
if (nPos2 >= 0)
{
strTagName = strText.Substring(nPos1 + 1, nPos2 - nPos1 - 1);

strTagName = strTagName.Replace("r", " ").Replace("n", " ");

nPos3 = strTagName.IndexOf(" ");

if (nPos3 > 0) strTagName = strTagName.Substring(0, nPos3);

if (strTagName.Substring(0, 1) == "/")
{
strTagName = strTagName.Substring(1);
bSearchForBlock = false;
}
else bSearchForBlock = true;

if (TAGLIST.IndexOf(";" + strTagName.ToUpper() + ";", 0) >= 0)
{
bRemove = true;
if (bSearchForBlock)
{
if (BLOCKTAGLIST.IndexOf(";" + strTagName.ToUpper() + ";") >= 0)
{
nPos2 = strText.Length;
nPos3 = strText.IndexOf(" if (nPos3 > 0) nPos3 = strText.IndexOf(">", nPos3 + 1);
if (nPos3 > 0) nPos2 = nPos3;
}
}
}
else bRemove = false;

if (bRemove)
{
strResult = strResult + strText.Substring(0, nPos1);
strText = strText.Substring(nPos2 + 1);
}
else
{
strResult = strResult + strText.Substring(nPos1);
strText = strText.Substring(nPos1 + 1);
}
}
else
{
strResult = strResult + strText;
strText = "";
}

nPos1 = strText.IndexOf("<"); } strResult = strResult + strText; strResult = strResult.Replace("rnrn", "rn"); return strResult; }

Friday, December 15, 2006


SqlCacheDependency Class

Technical Concept



Author: Minh Nguyen


Version 0.1

Status: new

Author

AUTHOR

FIRM/DEPARTMENT

Minh Nguyen

Software Development

HISTORY

VERSION

DATE

CHANGES

0.1

14.09.2006

First draft

OPEN ISSUES

From

Issues

Date

Status





Content

1 Introduction 4

2 How to use 4

2.1 Web.config 4

Above is the a piece configuration in Web.config file. 5

2.2 Enable Service Broker in SQL Server 2005 5

2.2.1 Create Service Broker endpoint 5

2.2.2 Enable Service Broker for a database 5

2.3 Enable Query Notification in SQL Server 2005 6

2.3.1 Enable Query Notification in SQL Server 2005 by Command Line 6

2.3.2 Enable Query Notification in SQL Server 2005 by Programming 6

2.4 Code using 7

3 Some issues 7

3.1 Performance 7

3.2 SQL Command 7

3.3 Supported SELECT Statements 8

3.4 Duplicate Subscriptions 9

4 Reference 9

In ASP.NET 2.0, caching has been improved in a couple of notable ways. Probably the most interesting feature is the introduction of database-triggered cache invalidation. In ASP.NET 1.x, you can invalidate a cached item based on some pre-defined conditions such as change in an XML file or change in another cache item. Using this feature, you can remove or invalidate an item from the cache when the data or another cached item changes. However, the ASP.NET 1.x Cache API does not allow you to invalidate an item in the cache when data in a SQL Server database changes. This is a very common capability most applications will require. ASP.NET 2.0 addresses this by providing the database triggered cache invalidation capability to ensure that the items in the cache are kept up-to-date with the changes in the database. You can accomplish this using any one of the following methods.

· Declarative Output caching - This is similar to declarative output caching in ASP.NET 1.x, wherein you configure caching by specifying the OutputCache directive and their related attributes.

· Programmatic Output caching - In this method, you will use the SqlCacheDependency object programmatically to specify the items to be cached and set their attributes.

· Cache API - In this option, you will use the static methods of the Cache class such as Insert, Remove, Add and so on to add or remove items from the ASP.NET cache, while still using the SqlCacheDependency object to trigger the cache invalidation.

Another important caching feature in ASP.NET 2.0 is the ability to create custom cache dependencies, which is not possible with ASP.NET 1.x Cache API. To accomplish this, you need to inherit from the CacheDependency class. Since the CacheDependency is a sealed class in ASP.NET 1.x, you can't inherit and extend it. However, in ASP.NET 2.0, this is no longer the case. You can inherit from CacheDependency class and create your own custom cache dependencies. This opens up a world of opportunities where you can roll your own custom cache dependencies required for a particular class of applications. For example, you can create a StockPriceCacheDependency class that automatically invalidates the cached data when the stock price changes.

This document will mention to SQL Server 2005 only, the DBMS that use in the project.

The main points are:

· How to use: explain some important features, notices and example coding to use SqlCacheDependency.

· Some issues: answer some technical question about this class.

To use this class, you need to configure some setting on SQL Server 2005; this is optional step, Web Application configuration and use code as below sample.

2.1 Web.config

In order to let the Web application understand and run the SqlCacheDependency modules, below configuration need to add to Configuration file (Web.config, app.config).

...

<connectionStrings>

<add name="LP_Shop_Instance" connectionString="Database=LP_Shop_Test;Server=172.16.6.3SQL2005;uid=lp_shop;pwd=lp_shop;" providerName="System.Data.SqlClient"/>

connectionStrings>

...

<system.web>

<caching>

<sqlCacheDependency enabled="true" pollTime="1000">

<databases>

<add name="LP_Shop_Instance" connectionStringName="LP_Shop_Instance" pollTime="1000"/>

databases>

sqlCacheDependency>

caching>

....

system.web>

Above is the a piece configuration in Web.config file.

  • ConnectionString: represent the connection string to database
  • pollTime: the interval polling in millisecond, if no invalidation occurs
  • connectionStringName: the name of ConnectionString declare in ConnectionString section

2.2 Enable Service Broker in SQL Server 2005

To use the SqlCacheDependency correctly, the Service broker must be enable.

2.2.1 Create Service Broker endpoint

Use SQL Server Management Studio or a similar tool to execute the following command to create a Service Broker endpoint:

USE master;

GO

CREATE ENDPOINT BrokerEndpoint

STATE = STARTED

AS TCP ( LISTENER_PORT = 4037 )

FOR SERVICE_BROKER ( AUTHENTICATION = WINDOWS );

GO

2.2.2 Enable Service Broker for a database

Enable Service Broker for a given database (for example, LP_WebShopLabel_DB) with this command:

ALTER DATABASE LP_WebShopLabel_DB SET ENABLE_BROKER;

GO

Now you can use SQL cache dependencies against the LP_WebShopLabel_DB database.

2.3 Enable Query Notification in SQL Server 2005

Once you configure the table to send notifications, any time data in the table changes, it notifies ASP.NET to invalidate the specific item in the cache. For the purposes of this article, consider the aspnet_regsqlcache utility to configure the tables. Basically this utility creates an extra table named AspNet_SqlCacheTablesForChangeNotification that is used to keep track of the changes to all the monitored tables in the database. It also creates a number of triggers and stored procedures to enable this capability.

There are 2 ways to enable the Query Notification in SQL Server 2005:

  • By aspnet_regsqlcache utility
  • By programming

2.3.1 Enable Query Notification in SQL Server 2005 by Command Line

To run the aspnet_regsqlcache utility, open up the Visual Studio .NET command prompt and enter the command shown in the following screenshot.

In the above command:

S - Name of the Server

U - User ID to use to connect to the SQL Server

P - Password to use to connect to the SQL Server

d - Specifies the name of the database

t - Table to configure

et - enables the tables for SQL Server database triggered invalidation

With SQL Server 2005, the above configurations are not necessary. Moreover the cache invalidating mechanism works through a highly efficient notification model, wherein the Notification Delivery Service component of SQL Server directly notifies IIS using TCP Port 80 when the data in a SQL Server changes.

2.3.2 Enable Query Notification in SQL Server 2005 by Programming

To enable the Query notification in SQL Server 2005, in the code line, add the following code:

SqlCacheDependencyAdmin.EnableNotifications(DB.CurrentConnectionString); SqlCacheDependencyAdmin.EnableTableForNotifications(DB.CurrentConnectionString, "CMRC_Products");

In the above example,

  • The SqlCacheDependency is the member of namespace: System.Web.Caching
  • DB.CurrentConnectionString represents the ConnectionString to Database.
  • “CMRC_Products” is the name of the table need to be enable Query notification.

This code of line will enable the table CMRC_products for any change will be notified to the web application.

We also can get the list of table has notification enabled by:

SqlCacheDependencyAdmin.GetTablesEnabledForNotifications();

2.4 Code using

After enable the table need to be notify, we can use it in the code.

Example: on Page_Load event:

DataSet ds;

ds = (DataSet)Cache["Customer"];

if (ds == null)

{

System.Data.SqlClient.SqlCommand sqlCmd = new

System.Data.SqlClient.SqlCommand();

sqlCmd.CommandText = "Select ProductID, Product From dbo.CMRC_Products";

sqlCmd.Connection = new

System.Data.SqlClient.SqlConnection(DB.CurrentConnectionString);

SqlCacheDependencyAdmin.EnableNotifications(DB.CurrentConnectionString); SqlCacheDependencyAdmin.EnableTableForNotifications(DB.CurrentConnectionString, "CMRC_Products");

SqlCacheDependency dependency = new SqlCacheDependency(sqlCmd);

Database db = DatabaseFactory.CreateDatabase(DB.DatabaseInstance);

DbCommand dbCommand = db.GetStoredProcCommand(sqlCmd.CommandText

ds = db.ExecuteDataSet(dbCommand);

this.Cache.Insert("Products", ds, dependency);

Label1.Text = "Page created on: " + DateTime.Now.ToString();

}

else

Label1.Text = "
Get from cache"
;

GridView1.DataSource = ds;

GridView1.DataBind();

3.1 Performance

SqlCacheDependency improves the performance of the ASP .NET website a lot. Especially the feature notification help the site update the latest version of data from the Database immediately.

When try to run the Query Profiler to monitor the traffic between the Database Server and the application using the SqlCacheDependency and with the application without using SqlCacheDependency, the performance is not increase so much, but the traditional caching method will not update the data immediately but after an interval.

Question: If in 1000 records of data, one record change while the data is in cache, the SqlCacheDependency is enough intelligent to replace only the record changed in the cache or all of data?

Answer: All the data will be pulled out. But the duration will be reduced a lot.

3.2 SQL Command

In general, you can request notification for any query that can be used to create an indexed view. You can set up notifications for the following statements:

  • SELECT
    For requirements and limitations specific to SELECT, see "Supported SELECT Statements" below. For more information on the SELECT statement.
  • EXECUTE
    In this case, SQL Server registers a notification for the command executed rather than the EXECUTE statement itself. The command must meet the requirements and limitations for a SELECT statement. For more information on the EXECUTE statement.

When a command that registers a notification contains more than one statement, the Database Engine creates a notification for each statement in the batch.

If a subscription request is made for a batch or stored procedure, a separate subscription request is made for each statement executed within the batch or stored procedure.

EXECUTE statements will not register a notification, but will flow the notification request to the executed command. If it is a batch, the context will be applied to the executed statements and the same rules described above apply.

But, when I run the SQL Profiler, I found that when registering Execute notification, I found it sometime does not works until the Cachce expired.

3.3 Supported SELECT Statements

Query notifications are supported for SELECT statements that meet the following requirements:

  • The projected columns in the SELECT statement must be explicitly stated, and table names must be qualified with two-part names. Notice that this means that all tables referenced in the statement must be in the same database.
  • The statement may not use the asterisk (*) or table_name.* syntax to specify columns.
  • The statement may not use unnamed columns or duplicate column names.
  • The statement must reference a base table.
  • The statement must not reference tables with computed columns.
  • The projected columns in the SELECT statement may not contain aggregate expressions unless the statement uses a GROUP BY expression. When a GROUP BY expression is provided, the select list may contain the aggregate functions COUNT_BIG() or SUM(). However, SUM() may not be specified for a nullable column. The statement may not specify HAVING, CUBE, or ROLLUP.
  • A projected column in the SELECT statement that is used as a simple expression must not appear more than once.
  • The statement must not include PIVOT or UNPIVOT operators.
  • The statement must not include the INTERSECT or EXCEPT operators.
  • The statement must not reference a view.
  • The statement must not contain any of the following: DISTINCT, COMPUTE or COMPUTE BY, or INTO.
  • The statement must not reference server global variables (@@variable_name).
  • The statement must not reference derived tables, temporary tables, or table variables.
  • The statement must not reference tables or views from other databases or servers.
  • The statement must not contain subqueries, outer joins, or self-joins.
  • The statement must not reference the large object types: text, ntext, and image.
  • The statement must not use the CONTAINS or FREETEXT full-text predicates.
  • The statement must not use rowset functions, including OPENROWSET and OPENQUERY.
  • The statement must not use any of the following aggregate functions: AVG, COUNT(*), MAX, MIN, STDEV, STDEVP, VAR, or VARP.
  • The statement must not use any nondeterministic functions, including ranking and windowing functions.
  • The statement must not contain user-defined aggregates.
  • The statement must not reference system tables or views, including catalog views and dynamic management views.
  • The statement must not include FOR BROWSE information.
  • The statement must not reference a queue.
  • The statement must not contain conditional statements that cannot change and cannot return results (for example, WHERE 1=0).
  • The statement can not specify READPAST locking hint.
  • The statement must not reference any Service Broker QUEUE.
  • The statement must not reference synonyms.
  • The statement must not have comparison or expression based on double/real data types.

3.4 Duplicate Subscriptions

Submitting a duplicate of an active subscription causes the existing subscription to be renewed using the new specified time-out value. A duplicate subscription is one that meets the following conditions:

  • The query is submitted by the same user under the same database context.
  • The same template, parameter values, notification ID, and delivery location are used.

This means that if a notification is requested for identical queries, only one notification is sent. This applies to a query duplicated in a batch, or to a query in a stored procedure that is called multiple times.