Tuesday, 25 September 2012

ASP.NET Web Forms Application Paths Related

This article is going to list all ASP.NET Web Forms application paths related properties and their values on the Request object. Below the test result are based on this scenario: local hosted IIS site named VirtualSite, first we access to http://localhost/VirtualSite/Redirect/dummy.aspx and there is a HttpModule to redirect the request to http://localhost/VirtualSite/Redirect/Redirect.aspx. The request has parameters "a=1&b=2&c=3". So here it is:

Request Property Value
ApplicationPath /VirtualSite
AppRelativeCurrentExecutionFilePath ~/Redirect/Redirect.aspx
CurrentExecutionFilePath /VirtualSite/Redirect/Redirect.aspx
FilePath /VirtualSite/Redirect/Redirect.aspx
Path /VirtualSite/Redirect/Redirect.aspx
QueryString {a=1&b=2&c=3}
RawUrl /VirtualSite/Redirect/Dummy.aspx?a=1&b=2&c=3
Url.AbsolutePath /VirtualSite/Redirect/Redirect.aspx
Url.AbsoluteUrl http://localhost:25739/VitualSite/Redirect/Redirect.aspx?a=1&b=2&c=3
Url.Authority localhost:25739
Url.DnsSafeHost localhost
Url.Host localhost
Url.LocalPath /VitualSite/Redirect/Redirect.aspx
Url.OriginalString http://localhost:25739/VitualSite/Redirect/Redirect.aspx?a=1&b=2&c=3
Url.PathAndQuery /VitualSite/Redirect/Redirect.aspx?a=1&b=2&c=3
Url.Port 25739
Url.Query ?a=1&b=2&c=3
Url.Scheme http

Since there is a redirect, as you can see, only the RawUrl contains the very original request, all other values are for the redirected Url, event the Request.Url.OriginalString

Thursday, 6 September 2012

Do not mix using HtmlTextWriter and WebControl itself to set its attributes

Have a look the simplified code below first. The code is inside a ButtonControl class (assuming there is no null reference exception for CssClass property).


What does it do? First in the Render method we set the class to "Disabled" if Enabled property is false. Later when AddAttributesToRender method fired during base.Render() it will attach more CSS classes on the web control. All look good, right? No. It won’t work when Enabled == false.

Why? Check what the base.AddAttributesToRender() in line 16 does first. The AddAttributesToRender method will copy every single attribute from the web control into the HtmlTextWriter for page rendering later. If the attribute name has already existed in the HtmlTextWriter then the value of this attribute is abandoned to avoid value overwritten. This rule doesn't apply for Style attribute. For Style attribute, the value will be appended at the end of existing style string.

Ok, in our case when Enabled == false, since we have already set the Class attribute equal to "Disabled" then no matter how many other CssClass values assign to the web control directly those values will be abandoned when base. AddAttributesToRender() called.

So here is my simple advice: see the title of this article. Since we can always set attributes value on web control level it is unnecessary to touch the HtmlTextWriter under any condition.

Friday, 20 April 2012

Uhuru Cloud - My First App

I recently had a chance to test a newly release cloud service provided by Uhuru. The service is named Uhuru .NET Services for Cloud Foundry and you can find all information about it by accessing Products page of Uhuru website.

Uhuru Cloud support JAVA, .NET, php and other web applications but I'd like to start with the simplest one, my company website, a single html/js webpage.

To deploy app to Uhuru Cloud you will need either install a MMC (Microsoft Management Console) or Uhuru Visual Studio plugin. I choose MMC because the multi page user guide for installation of Visual Studio plugin is quite scary. Uhuru gives a very detailed PDF doc to help deploying app through MMC and the whole process is very straight forward.

One thing worth mentioning, when pushing your app to Uhuru CloudFoundry, it will automatically identify the suitable framework and runtime version for your app. In my case, since it is just a html/js site, CloudFoundry treat it as a php app (fair enough, compare with JAVA and IIS, php host cost less).  While after couple of mouse clicking and filling one small windows form, my first app is officially online now. See here: wandj.uhurucloud.net

Saturday, 24 March 2012

Cheat Sheets for Web Development you must have

The original article is from: Cheat Sheets for Web Development you must have. Here I only copy those I am familiar with as a back up.

Monday, 2 January 2012

Tips of securing your ASP.NET MVC3 application

Tip 1, Always encode user input content before showing it on web page. You can do this simply by using Html.Encode() for innerHTML, Html.AttributeEncode() for attribute values and Url.Encode() for href link values.

You should know code expressions in Razor are implicitly HTML encoded, therefore in cases you do need to render some HTML value that should not be HTML encoded you can use the Html.Raw() method to ensure that the value is not encoded. BUT by doing so you have thrown open the door to evil.

Tip 2, Always JavaScript Encode user input values in your JavaScript code. You can use Ajax.JavaScriptStringEncode() to do this. The reason behind this is the html encode methods mentioned in Tip 1 cannot fully stop the XSS attacks because the html encoding cannot handle hex escape string but DOM does. Therefore the hacker may use your JS code to pass hex escape string into DOM object and the DOM object will convert the string back to JavaScript code and hurt you. Have a look the code below:

   1:  <div id="test">Text here is suppose to be replaced</div>
   2:  <script type="text/javascript">
   3:      $(function () {
   4:          $("#test").html(
   5:              '@Html.Encode("\\x3cscript\\x3e alert(\\x27oops\\x27) \\x3c/script\\x3e")');
   6:      });
   7:  </script>

Copy the code into your razor page and run it you will see an alert message says “oops”.

Tip 3, Follow the listed principles below to prevent CSRF attacks (if you don’t know what CSRF is please google it).

  • First thing first, you need follow Tip1 and 2 to make you site XSS proof
  • Always use POST instead of GET to submit data changes
  • Validate HTTPReferrer by checking the HttpContext.Request.UrlReferrer to see if the client that posted the data was indeed from your site page
  • Put token Verification in your form. You can use Html.AntiForgeryToken() to create a hidden token value in your form and in the related controller use ValidateAntiforgeryToken attribute to check if the token is matched

Tip 4, Protect your open redirection URL. The ASP.NET MVC 3 template includes code to protect against open redirection attacks in its AccountController after user successfully log on. You need also do similar thing in other places where a potential URL redirection may occur.

Tip 5, Use the AuthrizeAttribute to require a login access or role membership access. AuthrizeAttribute can be used on Action level or Controller level. You can use FormsAuthentication.SetAuthCookie() to keep the user login info into http cookie and it can be read by calling User.Identity.IsAuthenticated() in the requested Controller.

Tip 6, Use ValidationAttibute to do view-model level data value validation automatically. Most of the ValidationAttributes support client side validation if related jQuery lib file included which is a huge benefit for us developers.

Tip 7, Prevent over posting data through ASP.NET MVC3 model binding feature which can allow your attacker an opportunity to populate model properties you didn’t even put on your input forms. You can use BindAttribute to specify the name of properties which allow auto model binding. Alternatively you can do a manual data binding by calling UpdateModel() or TryUpdateModel() methods in the controller.

Still another way to deal with over-posting is to avoid binding directly to the data model. You can
do this by define a View-Model class that holds only the properties you want to allow the user to set.

Sunday, 16 October 2011

Dynamic HttpModule registration in .NET Framework 4.0

This article could be useful for the scenario that you are developing HttpModule handling related functionality for a web application and you do not have the right to or not intend to modify the Global.asax file to register your HttpModule handler.

So here we go. Step 1, before register a HttpModule handler dynamically the first thing is to create the handler. See code below:

public class MyDynamicModule : IHttpModule
{
    void IHttpModule.Dispose()
    {
    }
 
    void IHttpModule.Init(HttpApplication context)
    {
        context.BeginRequest += new EventHandler(context_BeginRequest);
        context.EndRequest += new EventHandler(context_EndRequest);
    }
    //... more code ignored
}

Step 2, to register this HttpModule handler, we need the reference to Microsoft.Web.Infrastructure assemble and use the contained method DynamicModuleUtility.RegisterModule. See sample code below:

DynamicModuleUtility.RegisterModule(typeof(MyDynamicModule));

But where we call this line of code? As you know HttpModule registration has to be done before the instance of HttpAppliction start. To achieve that we need the newly introduced attribute class System.Web.PreApplicationStartMethodAttribute in .NET framework 4.0. Note this attribute is assembly level attribute. See the code below, as step 3 of our example, of how to use the attribute:

[assembly: PreApplicationStartMethod(typeof(DynamicHttpModule), "RegisterMyModule")]

The code above basically says I want the web application to do something in its pre-start event, which is, to execute the static method DynamicHttpModule.RegisterMyModule, see below the code of the DynamicHttpModule class:

public static class DynamicHttpModule
{
    public static void RegisterMyModule()
    {
        DynamicModuleUtility.RegisterModule(typeof(MyDynamicModule)); //code from step 2
    }
}

And that is it.

Wednesday, 12 October 2011

Controlling XML Serialization Using Attributes

The article is mainly focus on mapping the object type name with the xml file node name when De-serializing the xml file and when the node name is not a proper C# type name

Long story short, here are some brief ideas:

  • Mapping xml root element name with class name: XmlRootAttribute
  • Mapping element name other than the root with class property/field name: XmlElementAttribute
  • Mapping List of elements underneath the parent node, normally to a ArrayList (or generic List<T>): XmlArrayAttribute and XmlArrayItemAttribute
  • Mapping a xml attribute to a C# class property/field: XmlAttribute

More details you can check the Microsoft article: Controlling XML Serialization Using Attributes

Friday, 7 October 2011

Control.BeginInvoke() vs Control.Invoke()

The BeginInvoke() method has a IAsyncResult type return value and the Invoke() method simply return a object. This makes a big difference between them which is the BeginInvoke() is making a asynchronous callback to the thread created the handle of the control.

Assuming the main thread is A and currently executing code is in thread B, if call Invoke() from thread B then thread B has to wait for the callback to thread A being finished and then continue the following process.

Tuesday, 7 June 2011

Ways of passing data between WP7 pages

The book Programming Windows Phone 7 (MS Press) introduced 4 ways:
  1. add a public propery on App class to retain the data during current lifecycle;
  2. add a public propery on the target page, and then in the prev page's OnNavigatedFrom event set the value of the property for the next page;
  3. use the property State from the application's current PhoneApplicationService instance, which is a type of generic dictionary. But the object been stored has to be serializable (the reason is when app goes to deactivated it will serialize the objects to isolated storage);
  4. isolate storage. BUT obviously it is not wise to frequently access isolate storage during application running. Here is a great article of compairing the performance of using isolate storage http://blogs.claritycon.com/kevinmarshall/2010/11/03/wp7-serialization-comparison/

Friday, 18 February 2011

How Secure Are Query Strings Over HTTPS?

The original article is from HttpWatch's blog, a very good post. So, to make a long story short, the conclusions are: At the network level, HTTPS URL parameters are secure, but there are some other ways in which URL based data can leak:
  1. URLs are stored in web server logs,
  2. URLs are stored in the browser history,
  3. URLs are passed in Referrer headers

Monday, 19 July 2010

Developing on SharePoint 2010 (Day 2)

a) SharePoint Server Architecture

server

b) SharePoint Object Hierarchy

object

So as you can see the SharePoint Object model is relatively straightforward. Here we have a very simple slip of code which start from reaching all services of the Farm and navigate through to the item and file level objects.

   1:  foreach (SPService svc in SPFarm.Local.Services)
   2:  {
   3:      if (svc is SPWebService)
   4:      {
   5:          foreach (SPWebApplication webApp in ((SPWebService)svc).WebApplications)
   6:          {
   7:              foreach (SPSite site in webApp.Sites)
   8:              {
   9:                  foreach (SPWeb web in site.AllWebs)
  10:                  {
  11:                      foreach (SPList list in web.Lists)
  12:                      {
  13:                          foreach (SPListItem i in list.Items)
  14:                          {
  15:                              // do something
  16:                          }
  17:                      }
  18:   
  19:                      foreach (SPFile f in web.Files)
  20:                      {
  21:                          // do something
  22:                      }
  23:                  }
  24:              }
  25:          }
  26:      }
  27:  }

Friday, 9 July 2010

Oracle Procedures (version 11.1)

General
Related Data Dictionary Objects
error$ source$  
DBA ALL USER
dba_arguments all_arguments user_arguments
dba_errors all_errors user_errors
dba_object_size all_object_size user_object_size
dba_procedures all_procedures user_procedures
dba_source all_source user_source
System Privileges Related To Procedures
create procedure alter any procedure
  create any procedure
  debug any procedure
  drop any procedure
  execute any procedure
Object Privileges GRANT execute ON <procedure_name>;

Privileges to tables and views granted through roles may not be valid within a procedure. See the section on AUTHID.
GRANT execute ON testproc TO uwclass;
 
Stored Procedure

No Parameters
CREATE OR REPLACE PROCEDURE <procedure_name> IS

BEGIN
  <code here>
END <procedure_name>;
/
CREATE OR REPLACE PROCEDURE no_param IS
BEGIN
  dbms_output.put_line('No Params');
END no_param;
/

set serveroutput on

exec no_param;

Single IN Parameter
CREATE OR REPLACE PROCEDURE <procedure_name> (
<parameter_name> IN <data_type>)
IS

BEGIN
  <code here>
END <procedure_name>;
/
CREATE OR REPLACE PROCEDURE in_param (mesg VARCHAR2) IS
BEGIN
  dbms_output.put_line(mesg);
END in_param;
/

set serveroutput on

exec in_param('Single IN Parameter');

OUT Parameter
CREATE OR REPLACE PROCEDURE <procedure_name> (
<parameter_name> OUT <data_type>)
IS

BEGIN
  <code here>
END <procedure_name>;
/
CREATE OR REPLACE PROCEDURE out_param(mesg OUT VARCHAR2) IS
BEGIN
  mesg := 'Single OUT Parameter';
END out_param;
/

set serveroutput on

DECLARE
s VARCHAR2(50);
BEGIN
  out_param(s);
  dbms_output.put_line(s);
END;
/
CREATE OR REPLACE PROCEDURE out_param (mesg OUT VARCHAR2) IS
BEGIN
  mesg := 'Single OUT Parameter';
END out_param;
/

SQL> var x VARCHAR2(30)
SQL> exec out_param(:x)
SQL> print x

IN OUT Parameter
CREATE OR REPLACE PROCEDURE <procedure_name> (
<parameter_name> IN OUT <data_type>)
IS

BEGIN
  <code here>
END <procedure_name>;
/
CREATE OR REPLACE PROCEDURE inout_param (mesg IN OUT VARCHAR2) IS
BEGIN
  mesg := mesg || ' an IN OUT Parameter';
END inout_param;
/

set serveroutput on

DECLARE
s VARCHAR2(50) := 'This procedure uses';
BEGIN
  inout_param(s);
  dbms_output.put_line(s);
END;
/

Multiple Parameters
CREATE OR REPLACE PROCEDURE <procedure_name> (
<parameter_name> IN <data_type>,
<parameter_name> OUT <data_type>,
<parameter_name> IN OUT <data_type>)
IS

BEGIN
  <code here>
END <procedure_name>;
/
CREATE OR REPLACE PROCEDURE many_params (
mesg1 IN     VARCHAR2,
mesg2 OUT   VARCHAR2,
mesg3 IN OUT VARCHAR2) IS
BEGIN
  mesg2 := mesg1 || 'Parameter As The OUT';
  mesg3 := mesg3 || 'Returned';
END many_params;
/

set serveroutput on

DECLARE
iparm  VARCHAR2(50) := 'This is the IN ';
oparm  VARCHAR2(50);
ioparm VARCHAR2(50) := 'And This is the IN OUT ';
BEGIN
  many_params(iparm, oparm, ioparm);
  dbms_output.put_line(oparm || ' ' || ioparm);
END;
/
 
Parameter DEFAULT

Procedure Without Default
CREATE OR REPLACE PROCEDURE <procedure_name> (
<parameter_name> <data_type> <default_value>) IS

BEGIN
  <code>
END;
/
CREATE OR REPLACE PROCEDURE no_default(num_rows PLS_INTEGER) IS

BEGIN
  FOR r IN (SELECT object_name FROM all_objects
            WHERE rownum < num_rows+1)
  LOOP
    dbms_output.put_line(r.object_name);
  END LOOP;
END no_default;
/

set serveroutput on

exec no_default

exec no_default(5);

Procedure With Default
CREATE OR REPLACE PROCEDURE <procedure_name> (
<parameter_name> <data_type> DEFAULT <default_value>) IS

BEGIN
  <code>
END;
/
CREATE OR REPLACE PROCEDURE with_default (
num_rows PLS_INTEGER DEFAULT 20) IS
BEGIN
  FOR r IN (SELECT object_name FROM all_objects
            WHERE rownum < num_rows+1)
  LOOP
    dbms_output.put_line(r.object_name);
  END LOOP;
END with_default;
/

set serveroutput on

exec with_default

exec with_default(5);
 
Parameter Notations

Positional Notation
exec <procedure_name> (<parameter>,<parameter>);
CREATE OR REPLACE PROCEDURE positional (
min_nr PLS_INTEGER DEFAULT 100,
max_nr PLS_INTEGER DEFAULT 1000) IS
BEGIN
  FOR r IN (SELECT table_name FROM user_tables
            WHERE num_rows BETWEEN min_nr AND max_nr)
  LOOP
    dbms_output.put_line(r.table_name);
  END LOOP;
END positional;
/

set serveroutput on

exec positional;

exec positional(1);

exec positional(1000, 500000);

Named Notation
exec <procedure_name> (<parameter_name> => <parameter>);
exec positional;

exec positional(min_nr => 1);

exec positional(max_nr => 500);

exec positional(max_nr => 5000);

exec positional(max_nr => 10000);

exec positional(max_nr => 50000);

exec positional(max_nr => 999999);
Mixed Notation exec <procedure_name> (<parameter>, <parameter_name> => <parameter>);
exec positional(10, max_nr => 1000);

exec dbms_stats(USER, cascade=>TRUE);
 
Parameter NOCOPY
Note: NOCOPY is a hint ... not a directive ... and may be ignored.

NOCOPY Performance Demo
CREATE OR REPLACE PROCEDURE <procedure_name> (
<parameter_name> NOCOPY <data_type> <default_value>) IS

BEGIN
  <code>
END;
/
-- Note: This first demo shows an example of NOCOPY not working

conn / as sysdba

GRANT execute ON dbms_crypto TO uwclass;

conn uwclass/uwclass

CREATE OR REPLACE PROCEDURE default_out (retval OUT VARCHAR2) AS
BEGIN
  retval := dbms_crypto.randombytes(32);
END default_out;
/

CREATE OR REPLACE PROCEDURE nocopy_out (retval OUT NOCOPY VARCHAR2) AS
BEGIN
  retval := dbms_crypto.randombytes(32);
END nocopy_out;
/

CREATE OR REPLACE PROCEDURE nocopy_test (reps IN PLS_INTEGER) AS
bt1 TIMESTAMP(9);
et1 TIMESTAMP(9);

bt2 TIMESTAMP(9);
et2 TIMESTAMP(9);

outval VARCHAR2(64);
BEGIN
  bt1 := SYSTIMESTAMP;
  FOR i IN 1..reps LOOP
    default_out(outval);
  END LOOP;
  et1 := SYSTIMESTAMP;

  bt2 := SYSTIMESTAMP;
  FOR i IN 1..reps LOOP
    nocopy_out(outval);
  END LOOP;
  et2 := SYSTIMESTAMP;

  dbms_output.put_line('Default: ' || TO_CHAR(et1-bt1));
  dbms_output.put_line('No Copy: ' || TO_CHAR(et2-bt2));
END nocopy_test;
/

-- Note: Tried this with REF CURSORS returning 40K rows with, again, 
-- no measurable difference. But now watch what happens when used in a 
-- demo received from Tom Kyte on 2/9/2007.

CREATE OR REPLACE PROCEDURE p3(x IN OUT dbms_sql.varchar2s) AS
BEGIN
  FOR i IN 1 .. 2000000 LOOP
    x(i) := RPAD('*', 255, '*');
  END LOOP;
  RAISE PROGRAM_ERROR;
END;
/

CREATE OR REPLACE PROCEDURE p4(x IN OUT NOCOPY dbms_sql.varchar2s) AS
BEGIN
  FOR i IN 1 .. 2000000 LOOP
    x(i) := RPAD( '*', 255, '*' );
  END LOOP;
  RAISE PROGRAM_ERROR;
END;
/

set serveroutput on
set timing on

DECLARE
l_x dbms_sql.varchar2s;
BEGIN
  FOR i IN 1 .. 1000000 LOOP
    l_x(i) := RPAD('*', 250, '*');
  END LOOP;
  p3(l_x);
EXCEPTION
  WHEN OTHERS THEN
    dbms_output.put_line('count = ' || l_x.count);
END;
/

DECLARE
  l_x dbms_sql.varchar2s;
BEGIN
  FOR i IN 1 .. 1000000 LOOP
    l_x(i) := RPAD( '*', 250, '*' );
  END LOOP;
  p4(l_x);
EXCEPTION
  WHEN OTHERS THEN
    dbms_output.put_line('count = ' || l_x.count);
END;
/

Note: If a subprogram exists with an unhandled exception, the value assigned to its OUT and IN OUT formal parameters are not copied into the corresponding actual parameters, the changes appear to roll back. However, when you specify NOCOPY, assignments to the formal parameters immediately affect the actual parameters. So, if the subprogram exits with an unhandled exception, the (possibly unfinished) changes are not "rolled back."

Error Handling With NOCOPY
CREATE OR REPLACE PROCEDURE raise_error (
p_Raise BOOLEAN,
p_ParameterA OUT NOCOPY NUMBER) AS
BEGIN
  p_ParameterA := 7;
  IF p_Raise THEN
    RAISE DUP_VAL_ON_INDEX;
  ELSE
    RETURN;
  END IF;
EXCEPTION
  WHEN OTHERS THEN
    RETURN;
END raise_error;
/

set serveroutput on

DECLARE
p_B BOOLEAN := TRUE;
n NUMBER;
BEGIN
  raise_error(p_B, n);
  dbms_output.put_line(n);
END;
/

CREATE OR REPLACE PROCEDURE raise_error (
p_Raise      BOOLEAN,
p_ParameterA IN OUT NOCOPY NUMBER) AS
BEGIN
  IF p_Raise THEN
    RAISE DUP_VAL_ON_INDEX;
  ELSE
    p_ParameterA := 999;
  END IF;
EXCEPTION
  WHEN OTHERS THEN
    NULL;
END raise_error;
/

DECLARE
p_B BOOLEAN := FALSE;
n NUMBER := 100;
BEGIN
  raise_error(p_B, n);
  dbms_output.put_line(n);
END;
/

DECLARE
  p_B BOOLEAN := TRUE;
  n NUMBER := 100;
BEGIN
  raise_error(p_B, n);
  dbms_output.put_line(n);
END;
/
 
AUTHID
Note: For DEFINER RIGHTS objects execute privileges must be granted directly to the user; not to a role. With CURRENT USER rights can be granted to a role. AUTHID DEFINER (default) stored procedures are executed in environment equivalent to the one you get after SET ROLE NONE. In other words, roles are disabled for PL/SQL and any privileges granted via roles do not apply unless you created the procedure with AUTHID CURRENT_USER, in which case role privileges do apply (but executing such procedures is a bit more expensive because Oracle has to evaluate the privileges on every call).

AUTHID Demo
AUTHID DEFINER (the default)
run the procedure with the rights of the procedure's owner.

AUTHID CURRENT_USER
run the procedure with the rights of the executing schema.
conn / as sysdba

CREATE USER abc
IDENTIFIED BY abc
DEFAULT TABLESPACE uwdata
TEMPORARY TABLESPACE temp
QUOTA 10M ON uwdata;

GRANT create session, create table, create procedure to abc;

conn abc/abc

CREATE TABLE t1 (
mycol VARCHAR2(20));

CREATE TABLE t2 (
yourcol NUMBER(10,2));

CREATE TABLE t3 (
ourcol DATE);

conn uwclass/uwclass

CREATE OR REPLACE PROCEDURE definer_test AUTHID DEFINER IS
BEGIN
  FOR rec IN (SELECT table_name FROM user_tables)
  LOOP
    dbms_output.put_line(rec.table_name);
  END LOOP;
END definer_test;
/

CREATE OR REPLACE PROCEDURE cu_test AUTHID CURRENT_USER IS
BEGIN
  FOR rec IN (SELECT table_name FROM user_tables)
  LOOP
    dbms_output.put_line(rec.table_name);
  END LOOP;
END cu_test;
/

set serveroutput on

exec definer_test;

exec cu_test;

GRANT execute on definer_test TO abc;
GRANT execute on cu_test TO abc;

conn abc/abc

set serveroutput on

exec uwclass.definer_test;

exec uwclass.cu_test;

--======================================

conn abc/abc

CREATE OR REPLACE PROCEDURE definer_test AUTHID DEFINER IS

BEGIN
  FOR rec IN (SELECT srvr_id FROM servers)
  LOOP
    dbms_output.put_line(rec.srvr_id);
  END LOOP;
END definer_test;
/

CREATE OR REPLACE PROCEDURE cu_test AUTHID CURRENT_USER IS

BEGIN
  FOR rec IN (SELECT srvr_id FROM servers)
  LOOP
    dbms_output.put_line(rec.srvr_id);
  END LOOP;
END cu_test;
/

set serveroutput on

exec definer_test;

exec cu_test;

GRANT execute on definer_test TO uwclass;
GRANT execute on cu_test TO uwclass;

conn uwclass/uwclass

set serveroutput on

exec abc.definer_test;

exec abc.cu_test;
 
Procedure Demos

Calculate Business Days
CREATE TABLE daterange (
beg_date DATE,
end_date DATE,
biz_days NUMBER(5));

INSERT INTO daterange VALUES (SYSDATE-10, SYSDATE+10, NULL);
INSERT INTO daterange VALUES (SYSDATE-17, SYSDATE+10, NULL);
INSERT INTO daterange VALUES (SYSDATE-22, SYSDATE+12, NULL);

CREATE OR REPLACE PROCEDURE bizdays AUTHID CURRENT_USER IS

CURSOR bd_cur IS
SELECT beg_date, end_date, 0
FROM daterange;

TYPE bDate_tab IS TABLE OF DATE
INDEX BY BINARY_INTEGER;
bdate bDate_tab;

TYPE eDate_tab IS TABLE OF DATE
INDEX BY BINARY_INTEGER;
edate eDate_tab;

TYPE bDay_tab IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;
bDays bDay_tab;

NumDays PLS_INTEGER;
TestDate DATE;

BEGIN
  OPEN bd_cur;
  LOOP
    FETCH bd_cur BULK COLLECT INTO bDate, eDate, bDays LIMIT 100;
    EXIT WHEN bd_cur%NOTFOUND;


    FOR i IN bdate.FIRST .. bdate.LAST LOOP
      TestDate := bdate(i);
      NumDays := 0;

      FOR j IN 1 .. (edate(i) - bdate(i) + 1) LOOP
        IF TO_CHAR(TestDate, 'D') BETWEEN '2' AND '6' THEN
          NumDays := NumDays+1;
        END IF;

        TestDate := TestDate + 1;
      END LOOP;


      bDays(i) := NumDays;

      UPDATE daterange
      SET biz_days = bDays(i)
      WHERE beg_date = bDate(i)
      AND end_date = eDate(i);
    END LOOP;

  END LOOP;
  COMMIT;
  CLOSE bd_cur;
END bizdays;
/

Temporary Tables vs. Table Variables

  Temp Table Table Variable
record transaction logs? Yes No
inside transaction scope? Yes No
cross execution batch? Global temp table No way
support index? Yes No
where to store? Tempdb Memory

Thursday, 8 July 2010

Developing on SharePoint 2010 (Day 1)

This article is talking about developing web parts for SharePoint 2010 using Visual Studio 2010. If you are looking for articles for older version of SharePoint or Visual Studio I recommend this URL: http://www.aspfree.com/c/a/Windows-Scripting/Beginning-SharePoint-Web-Part-Development/

First of all, there are two types of web parts VS 2010 supports: Standard Web Part (which in VS 2010 Item Template list named Web Part) and Visual Web Part.

A standard web part is just a class inherited from abstract class WebPart. Below is the Inheritance Hierarchy of WebPart class.

   1:  WebControl
   2:  |---Panel
   3:      |---Part
   4:          |---WebPart

To add contents into standard web part we need override the CreateChildControls method and inside put code logic to create sub controls manually and add them into the Controls collection. See the code below:

   1:  protected override void CreateChildControls()
   2:  {
   3:      LiteralControl msg = new LiteralControl("test");
   4:      this.Controls.Add(msg);
   5:  }

The visual web part, in the other hand, give you a visual design UI to drag existed web control in. But if you check the source code, a visual web part is just a standard web part plus a Web.UI.UserControl. And in the overrided CreateChildControls method used a trick like this:

this.Controls.Add(Page.LoadControl(_controlPath));

One more thing for visual web part: the value _controlPath is the physical URL of the user control and the value of it is maintained by VS 2010.

Monday, 10 May 2010

How to convert UTF-8 to urlencoded string

As you know, System.Web.HttpUtility.UrlEncode can easily convert UTF-8 string into the format of %xx%xx so it can be used in a URL string. Now imagine you are not working on a ASP.NET project and you don’t want to add reference of System.Web.dll in your project, then how can you do the same thing? Below is one solution I found:

   1: private static string GetEncodedString(string username)
   2: {
   3:     StringBuilder sb = new StringBuilder();
   4:     byte[] btyes = System.Text.Encoding.Default.GetBytes(username);
   5:     for (int i = 0; i < btyes.Length; i++)
   6:     {
   7:         sb.Append(@"%" + Convert.ToString(btyes[i], 16));
   8:     }
   9:  
  10:     return sb.ToString();
  11: }

Now if you pass in chinese string 你好 it will turn out %c4%e3%ba%c3, which is exactly what I want. The reason I create this method is because I am trying to use HttpWebRequest component to directly post data to a web page and I need a way to submit UTF-8 string.

BTW, the only difference between my method and UrlEncode is my method will convert all characters in the passed in string to %xx format even it is ASCII but UrlEncode won’t do that.

Monday, 12 April 2010

Star Schema, Snowflake Schema and others

The star/snowflake schema is a way to implement multi-dimensional database (MDDB) functionality using a mainstream relational database.

Star schema resembles a star, one or more fact tables are surrounded by single level dimension tables (one completely de-normalized table per relationship). Dimension tables aren't normalized - that means even if you have repeating fields such as name or category no extra table is added to remove the redundancy.

Snowflake schema resembles a snowflake because comparing with Star Schema the dimension tables are further normalized or have parent tables.

Either way the fact tables are usually designed in third normal form because all data depends on either one dimension or all of them, not on combinations of a few dimensions.

Star Schema vs. Snowflake Schema

The reason for using a star schema is its simplicity from the users' point of view: queries are never complex because the only joins and conditions involve a fact table and a single level of dimension tables, without the indirect dependencies to other tables that are possible in a better normalized snowflake schema.

Snowflake schema generates more joins than a star schema during, which translates into longer queries. Therefore it is normally recommended to choose the star schema design over the snowflake schema for optimal performance.

Snowflake schema does have an advantage of providing more flexibility.

Disadvantage of Star Schema

A well designed database has to be in 3NF whereas the star schema's dimensions are de-normalized. Normally a huge number of redundant data existed in dimension tables.

For improving query performance many pre-processes (sum, grouping, resort etc) have been done on the dimension’s purpose and saved in the dimension table. When business logic changed to modify the related pre-processes are always hard.

When create new dimension table and build relationship with fact table, it can cause geometrically fact table size increasing.

How to Index Star/Snowflake Schema Data
Declare a primary key in each dimension table.
For star dimensions, declare foreign key (FK) relationships between each dimension table and the corresponding fact table. Additionally, for snowflake dimensions, declare FK relationships between each secondary dimension table and the primary dimension table that it augments.
Make sure that there is a primary key associated with the fact table(s), even if you have to use an IDENTITY field.
Declare indexes on each of the primary keys in the dimension tables and in the fact table(s).
Declare indexes on each of the foreign keys in the fact table(s).

Sunday, 14 March 2010

WTF: UAC and rsAccessDenied error in Windows 2008 R2 (x64)

Here is today’s WTF, imaging you spend whole day installed SSRS and finally start creating your first report service, but when you are trying to deploy it you keep receiving error message regarding denied accessing, it is really confused me.

This is not the worst part yet, if you google the error type [rsAccessDenied] you can find the solution is accessing http://localhost/reports/ and assign proper user roles to the report service login user. But when login http://localhost/reports/ in Windows 2008 R2, you can see only a blank home page without any settings you can work with. This is because UAC blocked the dynamic content in this page. Even I added the URL to trusted site list, you can still only see a blank page, which really piss me off.

To overcome this issue, you have to turn off UAC and restart the computer, and then access http://localhost/reports/ you can see all the functionality you need.

Followed are some tips installing SSRS 2008 x64 on Windows 2008 R2 x64

First thing first I strongly suggest installing VS2008, SQL SERVER 2008 in the default path, otherwise you will suffer installation failure soon or later, especially for the Business Intelligence Development Studio.

Another thing you need know is Report Service is no longer relying on IIS. Even you stopped IIS service you can still access Report Service from URL http://localhost/Reports and http://localhost/ReportService.

Assuming you have already installed SSRS 2008 with SQLSERVER 2008, next step is configure database connection. To do this go to Microsoft SQL Server 2008/Configuration Tools/Reporting Service Configuration Manager, then click database page and connect the database server you want. The reporting service manager will create two databases ReportServer and ReportServerTempDB for itself.

Another thing you may need to know is the SSRS report web service asmx URLs are:
http://localhost/ReportServer/ReportService2005.asmx (to manage the service)
http://localhost/ReportServer/ReportExecution2005.asmx (to create reports)
Even you installed MSSQL2008 the services are still named 2005, isn't it kind of WTF. :)

Monday, 8 March 2010

SILVER - An easy way to remember asp.net lifecycle

S – Start
I – Initialize
L – Load
V – Validate
E – Event Handling
R – Render

Haha,really easy to remember.

Wednesday, 24 February 2010

TransactionScopeOption.Suppress

As you may know, Suppress scope will suppress the current ambient transaction, and if there is no ambient transaction present, it will not create any transaction at all.

But there is one thing you may not know, which is, if there is another TransactionScope further down the stack it will react as if there was no ambient transaction (and create one if necessary).

So if you have 3 scopes as follows: .Required -> .Suppress -> .Required: Then 2 transactions will be created. The innermost TransactionScope with .Required will see that there is no ambient transaction and will creates one for itself.

One thing to remember, to keep the code consistence, always call scope.Complete() at the end of your suppress TransactionScope even you know logically there will be no transaction created. There will be no any overhead caused by doing that.