May 22, 2008

The Constant Fact : Const Vs Static ReadOnly field

One fact about constant, that we all know, is it's value can never be changed.
Here goes another one.... The constant field is statically replaced by the compiler at the compile time wherever it is found.

Means,


double
circleArea = System.Math.PI * 2 * r;
compiled to
double circleArea = 6.28318 * r;

Said that, suppose you have following expression in your one assembly "Lib"

public const int DataGridPageSize = 50;

And you are referring this assembly in your application "App". Now if you compile the solution the DataGridPageSize will be replaced by 50 in your application. But, if you change the DataGridPageSize value to 100 and compile only the assembly "Lib" and forgot to compile the application "App" then your application still has the old value 50 and not the 100 unless you recompile the "App" application.

But if you have used readonly static field you are safe in above scenario as it is not statically replaced at compile time as the constants are.

Submit this story to DotNetKicks

May 17, 2008

How To Embed Resource And Use It With WebResource Handler

Before you go deeper into this please look into my previous post Why WebResources for good reasons for using WebResources.

Three simple steps for this
.
Embed a resource in the assembly : Select your resource (javascript or image or any other) and goto properties and select "Embedded Resource" as Build Action.



Mention in your AssemblyInfo.cs file :
Mention above this resource in the AssemblyInfo file with the following syntax.
[assembly: WebResource("TestWeb.JavaScripts.common.js", "text/javascript",PerformSubstitution=true)]
Here the first parameter is the path as fully qualified name from the root namespace, like TestWeb is the root namespace and JavaScripts is the directory and then common.js is the filename. Second parameter is the MIME type for the resource being called. Third parameter tells .net that this resource contains some expression that need to be parsed and replaced with real values. We will discuss this third parameter at the end of the topic.

Call the GetWebResourceUrl() : To get the URL that can be used to call the embedded resource. The Url will be in the format
/PathFromRoot/WebResource.axd?d=P6z37LRwQoHAo_Le8MfO3Q2&t=63338169731478
Where d specify the assembly key and t specify the last time the assembly is written (compiled).

syntax of GetWebResourceUrl method is
GetWebResourceUrl(Type type, string resourceName);

I have used the following syntax to register the WebResource given javascript :
string scriptUrl = ClientScript.GetWebResourceUrl(typeof(_Default), "TestWeb.JavaScripts.common.js");
ClientScript.RegisterClientScriptInclude("Common", scriptUrl);

So we are done with how to use WebResource handler.

Now let's examine the third parameter of the WebResource attribute we have used above.
Imagine that we want to use another resource (an image) within the JavaScript file we are calling using the WebResource handler. Suppose the javascript is setting "src" of an image and that image is again a WebResource. So we will write something like following in our javascript file

document.getElementById('logo') = <%= WebResource('logo.gif')%>;


Now if you have mentioned PerformSubstitution=true for the javascript resource then all expressions like above one in your javascript will be replaced by the WebResource handler given url when your javascript will be served like

document.getElementById('logo') = "/PathFromRoot/WebResource.axd?d=P6z37LRwQoHAo_Le8MfO3Q2&t=63338169731478"

Submit this story to DotNetKicks

May 13, 2008

Why WebResources

Before we digg into "HOW TO"s about this topic let's think of "WHY WebResources?" .

Have you ever developed a control that uses some JavaScript? or image?. If not then let's exmine a case where you need to have a TextBox (web custom control) that only accepts digits means you are looking for developing one numeric text box. The quickest thing you will do is you write a class that inherits System.Web.UI.WebControl.TextBox and in that control's life cycle event you will add an HTML attribute to your control like this.Attributes.Add("onkeypress","javascript : return NumericValidationFunction()") (think that you are developing this control not only for a single web application you are working with but to provide to others if anyone needs or to sell).

Now the JavaScript function mentioned above will be written and stored in a string variable and then registered as a javascript in the PreRender method as

protected override void OnPreRender(EventArgs e) {
string numericValidationJs = "<script type='text/javascript'>function NumericValidationFunction() { your custom logic ...}</script>";
this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "NumericValidationJs", numericValidationJs ,true);
}
One disadvantage (not to just supporting this topic but it really is) is that it will be rendered as part of your HTML, embedded in the page HTML itself. It increases your page size (HTML that is being rendered to the browser) and also is HeadAche for search engine spiders. Also if you have set of controls within a single library you, for maintainance purpose, need to have a single place to write this JavaScript instead of writing within each control.

Now, based on above reasons you decided to write a ".js" file. The question is to ship this file along with your control library. And if you might have used any image file, for indication that the user has not entered a number, you might also have thought of shipping that image file along with the ".js" file with this assembly.

ASP.NET 2.0 provides a very good way to achieve this and it's recognised as "WebResource handler". Where you can have your resources (like javascript and image and other resources) embedded within your assembly and then use a URL that calls a file with ".axd" extension which is being served by an HTTPHander that respond you with the resource within the assembly. So you need to ship just one ".dll" file that contains your resources within as embedded resource.

Good enough discussion on "WHY" now let's take a look into HOW TO EMBED RESOURCE AND USE IT WITH WEBRESOURCE HANDLER.

Submit this story to DotNetKicks

May 12, 2008

ASP.NET HTML Optimization..

Addition to the ViewState Optimization that we already discussed, our further target could be the extra spaces within the HTML that is being rendered for an aspx page.

No, do not even think of removing all the spaces as that will make the page content unreadablelikethislastwordandthatwedonotwant. But the spaces between the two successive HTML tags (when one ends and another starts) like </div> <div> can be safely removed and also the \n and \r characters are not useful for the browser.

We can take care of this spaces while designing the .aspx page itself but if we remove the spaces at design time then further modification to the page will be a nightmare.

Rather, trap the HTML before it is being sent to the browser and remove those unwanted stuff, and as you might have caught it is the Render method that can be overridden.

The following code can be used to achieve the above desire...

protected override void Render(HtmlTextWriter writer) {
StringWriter stringWriter = new StringWriter();
HtmlTextWriter interceptedHtmlWriter = new HtmlTextWriter(stringWriter, " ");
base.Render(interceptedHtmlWriter);
string interceptedHtml = stringWriter.ToString();
//// Remove the newline and tab characters to decrease the filesize interceptedHtml = interceptedHtml.Replace("\n", "").Replace("\t", "").Replace("\r", "");
//// removing spaces will result in deformatted text on the page so //// only remove the spaces between the HTML tags like <a> </a> which is fine.
interceptedHtml = Regex.Replace(interceptedHtml, ">\\s+<", "><");
writer.Write(interceptedHtml);
}
Put the above method in a class (say BasePage) that inherit the System.Web.UI.Page and then inherit your all other pages from this BasePage
.

IMPORTANT : Make sure your page do not contain embedded javascript with single
line comment. As after removing new line character the following lines to the
line having single line commnent will all be considered as commented lines.


Enjoy........

Submit this story to DotNetKicks

May 10, 2008

Using Custom Assembly in Sql Server Reporting Services (SSRS).

There are times when one needs to have something that is not already available in Sql Server Reporting Services.

I too had one requirement to have custom Localization implementation for the static labels in a report. I had to fetch Localized data based on the UserID parameter (which is MUST for every report I had as all the reports were User specific.).

Let's look into details how I did that.

Give it a thought First !!

I did the same, I knew I had to use expression so I assumed I would use something like

=Code.RptUtils.GetValue("LabelKey",Parameters!UserID.Value,"DefaultText").

WHERE

Code = Globally defined member through which Instance methods from our assembly are available. (We would not have used this keyword if we had written static methods.)

RptUtils = Instance of the class in our assembly.(This one also is there because we had not written static method).

GetValue = method about which I mentioned above. ( Additional to it, this function returns a string)

LabelKey = Database field value. My database contains a Key, CountryID, LanguageID, Value pattern to have multilingual values.

Parameters!UserID.Value = UserID from the Report parameter.

DefaultText = If the value is not found in database then show this value.

Let's Start :

Create a class library project and name it as you desire. I named it ReportUtilities. And add a class that is going to help us getting multilingual values that is MLHelper.

Create a method that looks like following.

/// <summary>
///
This method is used to fetch the Resource value from the database.
/// The method will be used by the SSRS report.
/// </summary>
/// <param name="key">
Key of the resource</param>
/// <param name="userId">
specific UserID to fetch the resource
/// value for.</param>
/// <returns>
Resource value as string if found, Key enclosed
/// in square brackets otherwise.</returns>
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
public string GetValue(string key, string userId, string defaultText)
{
// Set initial value for the resource .
string returnValue = defaultText;
try
{
using (
SqlConnection
_con = new SqlConnection(
DatabaseHelper.ConnectionString
)
)
{
// DatabaseHelper is a class containing the constant string that holds the Sql connection string.
using (
SqlCommand _cmd = new SqlCommand(
"[Internationalization].[GetResourceValue]",
_con)
)
{
SqlParameter[] _params = new SqlParameter[2];
_params[0] =
new SqlParameter();
_params[0].ParameterName =
"@Key";
if (!string.IsNullOrEmpty(key))
{
_params[0].Value = key;
}
else
throw new
Exception("The Key must not be blank.");
_params[1] =
new SqlParameter();
_params[1].ParameterName =
"@UserID";
if (!string.IsNullOrEmpty(userId))
{
_params[1].Value = System.Data.SqlTypes.
SqlGuid.Parse(userId);
}
else
throw new
Exception("The UserID must not be blank.");
_cmd.Parameters.Add(_params[0]);
_cmd.Parameters.Add(_params[1]);
_con.Open();


// I have a stored proc to get multilingual value from the database
// based on the UserID and the Key passed to it.

_cmd.CommandType = System.Data.
CommandType.StoredProcedure;
object objResourceValue = _cmd.ExecuteScalar();
if (objResourceValue != null)
{
returnValue = objResourceValue.ToString();
}
_con.Close();
}
}
}
catch
{
// if anything goes wrong....the default text will be set on the label.
// Or you may want to handle it differently.
}
return returnValue;
}
Compile it to an assembly and it is ready to be hosted.

Host It

Hosting is just copy the DLL from your class library's Bin to
  • C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies ( for the developement machine on which you are designing and testing the report).

  • For SSRS 2005 (Reporting Host machine), the default is C:\Program Files\Microsoft SQL Server\MSSQL.n\Reporting Services\ReportServer\bin.

Use It

Goto ReportProperties >> References and select the assembly



rpt1



After the assembly is added we can use it in two ways depending upon the type of member we are using.


  • If we are using static member (if our method GetValue is static) we can directly access it using the full qualified member name from the root namespace like

=ReportUtilities.MLHelper.GetValue(.....)

  • For the Instance (non static) members we need to alias the class. For this look at the classes section in above image. Mention the fully qualified class name into the Class name section and alias it in the InstanceName section. And then it can be used as

=Code.RptUtils.GetValue(.....)

As we have Instance method "GetValue" we will follow the later one from above.

Right click the label you want to write expression for and put the following text

=Code.RptUtils.GetValue("LabelKeyYouHaveDefined",Parameters!UserID.Value,"First Name")

Does it really work ??

  • Yes, when you run the report in the Visual Studio's Reporting project ( means in the design environment).
  • No, on the report server when you host the assembly in the
  • C:\Program Files\Microsoft SQL Server\MSSQL.n\Reporting Services\ReportServer\bin. and it gives "#Error" instead of what you wanted.

Why?? Because of the security. When you run the report within the report designer (Visual Studio), the Visual Studio runs under the logged in user's context and as the logged in user has the rights it runs properly.

On the Reporting server the report is running under the ASP.NET's account so it requires the permission.

If the custom assembly needs permissions in addition to the Execution permission we need to mention it in the ReportServer's rssrvpolicy.config file located at C:\Program Files\Microsoft SQL Server\MSSQL.4\Reporting Services\ReportServer\. Open the file and put the following CodeGroup stuff

<CodeGroup class="UnionCodeGroup"
version
="1"
PermissionSetName
="FullTrust"
Name
="YourCustomCodeGroup"
Description
="For the Custom assembly of Demo">
<IMembershipCondition
class="UrlMembershipCondition"
version
="1"
Url
="C:\Program Files\Microsoft SQL Server\MSSQL.4\Reporting Services\ReportServer\bin\ReportUtilities.dll"
/>
</CodeGroup>

Now you are ready to go...!! Congratulations you have done it....!!!
Find more on SSRS on
Nirav Patel's Weblog

Submit this story to DotNetKicks

May 03, 2008

Export ReportViewer Report to PDF, Excel, HTML

The following code helps exporting the Report into PDF and Excel formats using the Render method of the ServerReport. Use below code after the report is loaded (refresh() is called on the reportviewer control).

1 string mimeType;
2 string encoding;
3 string fileNameExtension;
4 Warning[] warnings;
5 string[] streamids;
6 byte[] exportBytes = reportViewer.ServerReport.Render("PDF", null, out mimeType, out encoding, out fileNameExtension, out streamids, out warnings);
7 HttpContext.Current.Response.Buffer = true;
8 HttpContext.Current.Response.Clear();
9 HttpContext.Current.Response.ContentType = mimeType;
10 HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=ExportedReport." + fileNameExtension);
11 HttpContext.Current.Response.BinaryWrite(exportBytes);
12 HttpContext.Current.Response.Flush();
13 HttpContext.Current.Response.End();


To get Excel file use "Excel" as the parameter in the Render method.

Sometimes you might need to send the Report content into email. To Achieve this you can use the above code where you need to pass "HTML4.0" as the first parameter to the Render method. After you get the byte array from the Render method you can convert it to string as below


1 string mimeType;
2 string encoding;
3 Warning[] warnings;
4 string[] streamids;
5 string fileNameExtension;
6 byte[] htmlBytes = MyReportViewer.ServerReport.Render("HTML4.0", null, out mimeType, out encoding, out fileNameExtension, out streamids, out warnings);
7 string reportHtml = System.Text.Encoding.UTF8.GetString(htmlBytes);
8 return reportHtml;

Enjoy......

Submit this story to DotNetKicks

May 01, 2008

Save HTTP 404

Once I had a call from my boss saying "Can you tell me if we have used any Favicon file in our website in any page or somewhere? As if we have used it, the file is not in place. Look at this IIS log it is full of 404s for the same file.". And I was absolutely blank about this name. I opened the project and fired "Find in solution" but did not get any FavIcon reference.

Huh, a file that is not referenced anywhere in your website and still your IIS has 404 enteries? Later on I came to know that this is the file that is being shown into the address bar and in the favourites list in most browsers. More information about FavIcon can be found
here. And be sure to keep the Favicon file small in size to avoid high bandwidth usage, check this out on Scott's Blog

But keep this file atleast to save 404 in your IIS log. One other file is
Robots.txt that, I am not pretty sure, puts 404 in your IIS log when not found by the crawlers. You can check it out.

Submit this story to DotNetKicks

Safer Email Address

Direct email addresses used in the website are supposed to be caught by the mail bots that capture the email addresses from the website. But as the browsers support ascii display (&#ASCII CODE) we can use them so if the mail capturing program captures this encoded address (which is also difficult for them as they try to find mailto: which they won't get) it will not be useful to them unless they decode it. There are mail capture programs that know this alternative but most of them are not written considering this case.
To see how ascii character display is supported just paste the following code to any html or aspx page and browse it


<a href="&#109&#97&#105&#108&#116&#111&#58&#97&#98&#99
&#64&#97&#98&#99&#46&#99&#111&#109"> &#97&#98&#99&#64&#97&#98&#99&#46&#99&#111&#109< /a>


you can even try to click the link to send email it will get the correct email address to the mail client. And also view the page source for double check the email address is ascii encoded.
So this conversion code (just loop through the characters in email address and build a string consisting of ["&#" + ascii code of character] that is "&#97" = "a" of email address.) can also be put in the overridden method PreRender to secure the emails in the page if it has any.

This is for the text emails otherwise the image emails are good and there are lot of online tools that makes the image email addresses for you.

Happy Programming...!!!

Submit this story to DotNetKicks