Thursday, December 18, 2008

How to deploy a VS Database Project GDR using vsdbcmd

With the release of Visual Studio Team System 2008 Database Edition GDR we now have the possibility to use a standalone command for deploying a database project. Unfortunately the documentation for the new vsdbcmd is so full of errors that you cannot use it, see for example “Command-line Reference for VSDBCMD (Deployment and Schema Import)” and “How to: Prepare a Database for Deployment From a Command Prompt by Using VSDBCMD ”. Seems to be written for a previous version.

Properties

The documentation states that you should specify properties using this syntax:

/p:PropertyName:PropertyValue    INCORRECT!

The correct syntax is:

/p:PropertyName=PropertyValue 

Verbose and quiet

It also states that there exists a /verbose or /v option. This option do not exist. It has been removed. It’s verbose by default and you can use the undocumented /quiet or /q to turn of verbosity [source].

Invalid property names

The common deployment properties list isn’t right either. For example the TargetDatabaseName is in fact called TargetDatabase. Have a look in the .sqldeployment and .deploymanifest files for proper naming. These files exists in the directory created when building the project, for example MyDbProject/sql/debug/.

Deploy-example

Below is an example of how to deploy a database project that has been built by Visual Studio or Team Server Foundation (TFS).

Start a command prompt and change directory to the directory that was created when the project was built (for example MyDbProject/sql/debug/) and execute the command below (on a single line).

"%ProgramFiles%\Microsoft Visual Studio 9.0\VSTSDB\deploy\vsdbcmd"
/a:Deploy
/ConnectionString:"Data Source=MyServer;Integrated Security=True;"
/dsp:SQL
/manifest:MyDbProject.deploymanifest
/p:TargetDatabase=MyDb
/dd

This will create a .sql file and deploy it, i.e. execute it on the server. If you remove the /dd option the .sql will be created but not deployed.

Sunday, July 27, 2008

How to include scripts after asp.net ajax framework's

When registering script includes using ScriptManager.RegisterClientScriptInclude("myFile.js") the files are always included before the frameworks. This causes errors if your code depends on the ajax framework's. Errors like "Type is not defined" are common since the first line in the included script file often tries to register a namespace: Type.registerNamespace('My.Namespace');

On the other hand, files registered as ScriptReferences on the ScriptManagerProxy will be included AFTER the framework's includes.

<asp:ScriptManagerProxy runat="server" id="ScriptManagerProxy1">
    <Scripts><asp:ScriptReference Path="~/anotherFile.js" /></Scripts>
</asp:ScriptManagerProxy>

Not exactly the expected behavior and certainly not what we want.

When calling ScriptManager.RegisterClientScriptInclude() it delegates to the class ClientScriptManager. This class holds, internally, a list of registered scripts, and new entries ends up at the end. So the call ScriptManager.RegisterClientScriptInclude("myFile.js") will put myFile.js at the end of that list. Like this:

ClientScriptManagerClientScripts = { ..., "~/myFile.js" }

The references in the ScriptManagerProxies are handled a bit differently. The ScriptManager will, on the event PagePreRenderComplete, collect all references from the proxies in a list, and first in that list put the ajax framework's files.

List in ScriptManager = { ajax framework files, "~/anotherFile.js" }

After being collected the files are registered with the ClientScriptManager and are appended at the end of it's list. Since we registered myFile.js before PagePreRenderComplete (which occurs late in the asp.net page's life cycle), myFile.js is before the framework's files:

ClientScriptManagerClientScripts = { ..., "~/myFile.js", ..., ajax framework files, "~/anotherFile.js"}

These are rendered to the page in this order and it explains why anotherFile.js may use the framework directly and myFile.js not.

Solution 1. Use a proxy

The simplest solution is to put a ScriptManagerProxy on the page (or user control, or MasterPage) and either, as shown above declare a ScriptReference, or add one by code:

ScriptManagerProxy1.Scripts.Add(new ScriptReference("~/myFile.js"));

Solution 2. Register the file after PagePreRenderComplete

If you somehow manage to register myFile.js after PagePreRenderComplete (i.e. after ScriptControl has added all files) it will end up after the framework's files. But you'll need to do it before the rendering takes place. Between PreRender and Render phases is the SaveViewState phase. If we override SaveViewState on a control (Page is also a control) and registers myFile.js there it will be added to the list after the framework's files. This is sort of a hack and it's not guaranteed to work when new versions of the framework are released, but: It works.

protected override object SaveViewState()
{
    ScriptManager.RegisterClientScriptInclude(this,GetType(),"myFile","~/myFile.js");
    return base.SaveViewState();
}

Solution 3. Your own ScriptManager

This is, at least to me, the most elegant solution. Basically: you create the class MyScriptManager, which derives from ScriptManager; you replace the ScriptManager on the page (or user control, or MasterPage) with MyScriptManager; add the method RegisterClientScriptInclude(string url) to MyScriptManager and by some magic inside MyScriptManager makes sure that files registered thru the new method will be rendered after the ajax framework's includes.

When the ScriptManager collects all the ScriptReferences from the proxies (as explained above) it also collects ScriptReferences from all controls that have been registered with the ScriptManager as ScriptControls, by calling the method GetScriptReferences, that ScriptControls must implement. These files will be added after the proxies' files.

This is what we'll do: The new RegisterClientScriptInclude method will only add the url to a list, and not register it anywhere else. We let MyScriptManager be a ScriptControl (i.e. implement IScriptControl) and register itself as such. In the method GetScriptReferences we will, when requested, supply the list of registered files, and they will end up after the framework's.

Not much code is needed.

public class MyScriptManager : ScriptManager, IScriptControl
{
    private List<string> _registeredScripts = new List<string>();

    public virtual void RegisterClientScriptInclude(string url)
    {
        _registeredScripts.Add(url);
    }

    protected override void OnPreRender(EventArgs e)
    {
        //Register this instance as a ScriptControl.
        RegisterScriptControl(this);
        base.OnPreRender(e);
    }

    public new static MyScriptManager GetCurrent(Page page)
    {
        return (MyScriptManager) ScriptManager.GetCurrent(page);
    }

    #region IScriptControl Members
    IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()
    {
        return null;
    }

    IEnumerable<ScriptReference> IScriptControl.GetScriptReferences()
    {
        //For each element in _registeredScripts create a
        //ScriptReference and return the IEnumerable
        return _registeredScripts.ConvertAll(s => new ScriptReference(s));
    }
    #endregion
}

Please note that I've simplified the code in order to show the concept. In production code you'll want to encapsulate the list in a public property that creates the list if needed. And you want two protected virtual versions of GetScriptDescriptors and GetScriptReferences that the existing ones will call.

To register a file to be included after the framework's files you use:

MyScriptManager.GetCurrent(Page).RegisterClientScriptInclude("~/myFile.js");

If you're using AjaxControlToolkit change the inheritance to ToolkitScriptManager.

Note the order the files are included:

  1. ScriptManager.RegisterClientScriptInclude registered files
  2. Ajax framework's files
  3. All ScriptManagerProxy ScriptReferences
  4. MyScriptManager.RegisterClientScriptInclude registered files

If you want your files to be included between 2 and three you're in deep water. It's probably doable but far from trivial, since ScriptManager is pretty closed for extension. It would be nice if Microsoft adhered the Open/Closed-principle a bit more. :)

Friday, July 27, 2007

AutoEventWireup=true

If you specify the attribute AutoEventWireup=True on a Web User Control or a Page, methods like Page_Init() och Page_Load() will be automatically routed to the page's Init- and Load-events.

This is good, sometimes. It simplifies for you since you don't have to hook up events to event handlers. But it is something that's done during runtime, and therefore adds to the executiion time and it may cause event handlers to be called many times.

Example: Event handler beeing called twice.

protected void Page_Init()
{
    Page.Load += Page_Load;
}
protected void Page_Load(object sender, EventArgs e)
{
    //This code will execute twice
}

Page_Load in the example above will be called twice. Once since you mannually hooked the event to the Page_Load handler. Another time since AutoEventWireup=true and the method has a name causing the event to automatically be hooked to it.

AutoEventWireup works on TemplateControl which both Page och Web User Controls derives from.

K Scott Allen's blog talkes about this. I'll go more into depths.

SupportAutoEvents

What happens when AutoEventWireup=true? A TemplateControl has a property SupportAutoEvents. This is true by default (and will change to false by the parser/control builderif you set the attribute AutoEventWireup=false).

HookUpAutomaticHandlers

TemplateControl.HookUpAutomaticHandlers (which is called during the OnInit) will, if AutoEventWireup=true try to find methods with these names:

  • If the control is a Page:
    • Page_PreInit
    • Page_PreLoad
    • Page_LoadComplete
    • Page_PreRenderComplete
    • Page_InitComplete
    • Page_SaveStateComplete
  • For all TemplateControls:
    • Page_Init
    • Page_Load
    • Page_DataBind
    • Page_PreRender
    • Page_Unload
    • Page_Error
    • Page_AbortTransaction  or if it does not exist; OnTransactionAbort
    • Page_CommitTransaction  or if it does not exist; OnTransactionCommit

This is carried out in TemplateControl.GetDelegateInformationWithNoAssert().

32 attempts

As K Scott Allen writes two attempts will be made for every name. First it will try to find an EventHandler with the specified name, i.e. a method with the signature:

void EventHandler(object sender, EventArgs e)

Exemple: A method that is an EventHandler

void Page_PreInit(object sender, EventArgs e)

If that fails a VoidMethod is searched for, i.e. a method with the signature:

void VoidMethod()

Exemple: A method that is a VoidMethod

void Page_PreInit()

Although no methods matching the list have been added to the Page, 32 attempts to find a method will be made (K Scott Allen got 28 but he must have forgotten about OnTransactionAbort and OnTransactionCount).

Ok, the result will be cached but the search will be made at least once, at that's for every TemplateControl.

Hook up the event to the handler

For all matching methods on the control TemplateControl.HookUpAutomaticHandlers() will hook up the event to the corresponding event handler, unless the method already has been added to the event since HookUpAutomaticHandlers searches the list of event handlers for the event. If it finds the method, it will not add the method again.

But wait. In the first example Page_Load() was obviously added twice to the event.
Yes, but that's because HookUpAutomaticHandlers already had hooked up the Page.Load event to the Page_Load handler when we in Page_Init did it. Remember that HookUpAutomaticHandlers is called in the Page's OnInit, which is called before our Page_Init method. If we hook up the event to the handler before the page's OnInit we will do it before the HookUpAutomaticHandlers has ben called and therefore Page_Load will only be called once.

Exemple: Page_Load is only called once.

public partial class _Default : System.Web.UI.Page
{
    public _Default()
    {
        Page.Load += Page_Load;
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        //This code will execute once
    }
}

Conclusion: AutoEventWireup=false

Consider setting AutoEventWireup=false and manually hook up the events to the handlers. You gain control over when things are called and you don't have to pay for the overhead TemplateControl.HookUpAutomaticHandlers causes.

Microsoft recommends setting it to false if performance is a key consideration.

More on AutoEventWireup on MSDN

http://msdn2.microsoft.com/en-us/library/system.web.configuration.pagessection.autoeventwireup.aspx
http://support.microsoft.com/default.aspx/kb/324151

Monday, May 21, 2007

Insert Do-nothing comments

Always insert a //Do nothing comment in places where the intention is that nothing should be done. Let Me show you why by an example. Assume you have a constructor for a Control that inherits WebControl and all you want to do is changing the default tag (which is Span).
public MyControl()
   :base(HtmlTextWriterTag.Div)
{
}
When someone else reads this code (or you in a month or so when you've forgotten all about it) they might wonder if this code has been finished or if there is something missing in the constructor. By adding a comment, you show for anyone reading the code that there shouldn't really be any code inside the constructor.
public MyControl()
   :base(HtmlTextWriterTag.Div)
{
   //Do nothing
}
I have a code snippet for this so I just need to type don and hit tab twice and //Do nothing is inserted.

Friday, May 18, 2007

Passing values to behavior properties

Typically a property in a AjaxControlToolkit Extender looks like this:
[ExtenderControlProperty]
[DefaultValue("")]
public string MyProperty
{
  get { ... }
  set { ... }
}
Which will match a property in the behavior:
get_MyProperty : function() { ... },
set_MyProperty : function(value) { ... }
Values for MyProperty on the server side will end up in the behavior. Sometimes you want to pass values to the behavior without creating a property in the Extender control. To do this, simply override RenderScriptAttributes() and add the properties using AddProperty().
protected override void RenderScriptAttributes(ScriptBehaviorDescriptor descriptor)
{
  base.RenderScriptAttributes(descriptor);
  string horizontalAlignment=GetHorizontalAlignment();
  descriptor.AddProperty("HorizontalAlignment", horizontalAlignment);
}
And voilà, the HorizontalAlignment property in the beahvior will be set.

Property names & Extenders

Properties for extenders inheriting ExtenderControlBase typically looks like this:
[ExtenderControlProperty]
[DefaultValue("")]
public string MyProperty
{
  get
  {
    return GetPropertyValue("MyProperty", "");
  }
  set
  {
    SetPropertyValue("MyProperty", value);
  }
}
You must have a property with the same name in the behavior on the client side:
get_MyProperty : function() {
  return this._myProperty;
},
set_MyProperty : function(value) {
  this._myProperty = value;
}
But what if you want it to have different name on the client side? Easy. Add the ClientPropertyName attribute to the server side property. If we want MyProperty to be called myProp instead this is how it's done:
[ExtenderControlProperty]
[DefaultValue("")]
[ClientPropertyName("myProp")]
public string MyProperty
{
  get
  {
    return GetPropertyValue("MyProperty", "");
  }
  set
  {
    SetPropertyValue("MyProperty", value);
  }
}
On the client side we get:
get_myProp : function() {
  return this._myProperty;
},
set_myProp : function(value) {
  this._myProperty = value;
}
More on attributes: http://ajax.asp.net/ajaxtoolkit/Walkthrough/ExtenderClasses.aspx

Thursday, May 10, 2007

OOP in Javascript

MSDN Magazine May 2007 edition has an interesting article on how to use object oriented techniques in Javascript. A must-read if you are using Microsoft AJAX since the entire client script library is written using these techniques. http://msdn.microsoft.com/msdnmag/issues/07/05/JavaScript/default.aspx

Thursday, March 22, 2007

Validators not working with UpdatePanel??

The validators don't work very well sometimes when put in an UpdatePanel. But they used to... The reason is that in the 1.0 release of Ajax asp.net the validators that were used in place of the the ordinary asp.net validators have been removed. There was supposed to be an update available for the System.Web namespace to overcome this problem but it will take some time before we have the update. In the meantime there is a fix available. Have a look here: http://blogs.msdn.com/mattgi/archive/2007/01/23/asp-net-ajax-validators.aspx

Friday, February 16, 2007

Shortcuts and Cheat Sheets

Keyboard shortcuts for VS 20005: http://blogs.msdn.com/robcaron/archive/2007/01/29/1552795.aspx Regular Expressions cheat sheet: http://www.ilovejackdaniels.com/cheat-sheets/regular-expressions-cheat-sheet/ CSS cheat sheet http://www.ilovejackdaniels.com/cheat-sheets/css-cheat-sheet/ JavaScript cheat sheet http://www.ilovejackdaniels.com/cheat-sheets/javascript-cheat-sheet/ Asp.net Lifecycle: http://john-sheehan.com/blog/index.php/net-cheat-sheets/ AJAX asp.net http://aspnetresources.com/blog/ms_ajax_cheat_sheets_batch2.aspx

Thursday, February 8, 2007

Don't access the property ClientId to early

The ClientId property on a Control is correct only after the control has been added to a Parent, i.e. added to a Controls collection. The parent must also be connected to a parent, and it's parent, and so on up al the way to the Page control. If you try to get ClientId to early, not only will you get something unusable, it will be cached so all subsequent calls will get the same. It will also prevent postback events from working. Big No-no:
public class MyControl : CompositeControl
{
 protected override void CreateChildControls()
 {
  Button myButton = new Button();
  myButton.Text = "Unclicked";
  myButton.Click += new EventHandler(myButton_Click);
  Controls.Add(myButton);
  
  //MyControl has no parent yet. The following code will prevent
  //the myButton_Click from beeing called when the button is clicked.
  //Remove the line and it will be called.
  string id = myButton.ClientID; 
 }
 
 void myButton_Click(object sender, EventArgs e)
 {
  Button button = (Button)sender;
  button.Text = "clicked";
 }
}
Another example: http://aspadvice.com/blogs/joteke/archive/2007/01/28/Accessing-ClientID-or-UniqueID-too-early-can-cause-issues.aspx