March 2009 - Posts
A new cumulative update for SharePoint (actually one for WSS and another one for MOSS) has been released.
Request the WSS one (http://support.microsoft.com/kb/961755) from http://support.microsoft.com/hotfix/KBHotfix.aspx?kbnum=961755&kbln=en-us and the MOSS one (no details page yet) from http://support.microsoft.com/hotfix/KBHotfix.aspx?kbnum=961756&kbln=en-us. A link will be sent to the e-mail address you indicate.
Requirements are just WSS SP1 and MOSS SP1. These updates include all other updates released since SP1.
You should install first the WSS update and then the MOSS one. Afterwards, run
psconfig -cmd upgrade -inplace b2b –force
When using master pages, the normal page event lifecycle is a little different. Here is the actual order:
- Page.OnPreInit
- MasterPageControl.OnInit (for each control on the master page)
- Control.OnInit (for each contol on the page)
- MasterPage.OnInit
- Page.OnInit
- Page.OnInitComplete
- Page.LoadPageStateFromPersistenceMedium
- Page.LoadViewState
- MasterPage.LoadViewState
- Page.OnPreLoad
- Page.OnLoad
- MasterPage.OnLoad
- MasterPageControl.OnLoad (for each control on the master page)
- Control.OnLoad (for each control on the page)
- OnXXX (control event)
- MasterPage.OnBubbleEvent
- Page.OnBubbleEvent
- Page.OnLoadComplete
- Page.OnPreRender
- MasterPage.OnPreRender
- MasterPageControl.OnPreRender (for each control on the master page)
- Control.OnPreRender (for each control on the page)
- Page.OnPreRenderComplete
- MasterPageControl.SaveControlState (for each control on the master page)
- Control.SaveControlState (for each control on the page)
- Page.SaveViewState
- MasterPage.SaveViewState
- Page.SavePageStateToPersistenceMedium
- Page.OnSaveStateComplete
- MasterPageControl.OnUnload (for each control on the master page)
- Control.OnUnload (for each control on the page)
- MasterPage.OnUnload
- Page.OnUnload
Update: fixed a bug in the attached code.
Enterprise Library comes with several lifetime manager, which you can use to control how your objects are stored and retrieved. The available lifetime managers are:
-
ExternallyControlledLifetimeManager: stores a weak reference to objects which are created elsewhere
-
PerThreadLifetimeManager: stores objects in a thread static dictionary)
-
ContainerControlledLifetimeManager (the default): stores objects in the Unity container
-
TransientLifetimeManager: does not store objects, create a new one every time it is requested
In web applications, we may need additional options, for example:
-
Store objects in the current session
-
Store objects in the current request
-
Store objects in the current application
So, I wrote three new lifetime managers, for each of these requirements: WebSessionLifetimeManager, WebRequestLifetimeManager and WebApplicationLifetimeManager. Another possibility would be a pooled lifetime manager, which will be the subject for a future post.
In order to have dependency injection on your ASP.NET pages, the best way to go is through a customized page handler factory. A page handler factory is a class directly or indirectly derived from PageHandlerFactory who has the responsibility to create a new instance of a Page. What we do is, we call the base class implementation of GetHandler method and then we call IUnityContainer's BuildUp method. Of course, we must also
-
have previously initialized the Unity container, possibly on Application_Start method of Global.asax.cs
-
configure interceptors for the types you want in Web.config. In this example, since I am registering an interface, I will be using InterfaceInterceptor, but you can also use VirtualMethodInterceptor or TransparentProxyInterceptor. I will talk about these in a future post.
-
replace the existing page handler factory with our onw in Web.config
See the attached code, and replace ISomething for your own interface and ConcreteSomething, in Web.config, for the actual implementation of this interface.
Call Handlers are Enterprise Library's extension mechanism. They allow code to be injected before or after a method is called. Enterprise Library, as of version 4.1, comes with the following call handlers and associated handler attributes, in assembly Microsoft.Practices.EnterpriseLibrary.PolicyInjection.CallHandlers:
-
AuthorizationCallHandler / AuthorizationCallHandlerAttribute
-
CachingCallHandler / CachingCallHandlerAttribute
-
ExceptionCallHandler / ExceptionCallHandlerAttribute
-
LogCallHandler / LogCallHandlerAttribute
-
PerformanceCounterCallHandler / PerformanceCounterCallHandlerAttribute
-
ValidationCallHandler / ValidationCallHandlerAttribute
If you know the Enterprise Library, you can easily guess their meaning from their names.
I have written two additional call handlers:
-
TransactionCallHandlerAttribute (implements ICallHandler): wraps the target method in a transaction scope
-
TriesCallHandlerAttribute (implements ICallHandler): tries to invoke the target method a number of times, while an exception is thrown
For simplicity's sake, I have implemented ICallHandler in the attribute class, so the ICallHandler.CreateHandler method only returns this.
Usage is like this:
[TransactionCallHandler(TransactionScopeOption.Required)]
void SomeMethod();
and
[TriesCallHandler(3, Delay = 500)]
void SomeOtherMethod();
Enjoy!
Update: I had forgot to send the code for the synchronous executor. Here it is, as an attachment.
AJAX, standing for Asynchronous JavaScript and XML, is, well, asynchronous by default. Usually, that is exactly what we want, but there may be times when we want to execute some operation synchronously.
The ASP.NET AJAX (previously known as ASP.NET AJAX Extensions) allows us out of the box to call web service methods asynchronously. See this example:
...
<script type="text/javascript">
function onAddSuccess(result, context, functionName)
{
window.alert('Result: ' + result);
}
function onAddFailure(error, context, functionName)
{
window.alert('Error: ' + error);
}
</script>
...
<asp:ScriptManager ID="ScriptManager" runat="server">
<Services>
<asp:ServiceReference Path="~/CalculatorService.asmx" />
</Services>
</asp:ScriptManager>
<
asp:Button runat="server" ID="Button1" OnClientClick="CalculatorService.Add(1, 1, onAddSuccess, onAddFailure, null)" Text="Add 1 + 1" />
...
[ScriptService]
[
WebService(Namespace = "http://tempuri.org/")][WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class CalculatorService : WebService
{
[ScriptMethod]
[
WebMethod]public Int32 Add(Int32 a, Int32 b)
{
return (a + b);
}
}
If all goes well, you would get the string "Result: 2" coming from the onAddSuccess callback function.
But in some cases, for example, an ASP.NET Custom Validator, you must get an answer immediately. Have a look at the following code:
function ExecuteSynchronously(url, method, args)
{
var executor = new Sys.Net.XMLHttpSyncExecutor();
// Instantiate a WebRequest.var request = new Sys.Net.WebRequest();
// Set the request URL.request.set_url(url + '/' + method);
// Set the request verb.request.set_httpVerb('POST');
// Set request header.request.get_headers()['Content-Type'] = 'application/json; charset=utf-8';
// Set the executor.
request.set_executor(executor);
// Serialize argumente into a JSON string.
request.set_body(Sys.Serialization.JavaScriptSerializer.serialize(args));
// Execute the request.
request.invoke();
if (executor.get_responseAvailable())
{
return (executor.get_object());
}
return (false);
}
function add()
{
var result = ExecuteSynchronously('CalculatorService.asmx', 'Add', { a: 1, b: 2 });window.alert('Result: ' + result);
}
<
asp:Button runat="server" ID="Button1" OnClientClick="add()" Text="Add 1 + 1" />
You get the result immediately in the add method.
When using no-compile web user controls (where the CompilationMode directive is set to Never), you cannot just add them to your web page declaratively.
There are two ways to do it:
-
Programmatically, by calling Page.LoadControl, casting the returned value to the appropriate class (UserControl-derived or PartialCachingControl, if the web user control has the OutputCache directive) and set its properties manually
-
Use the aspnet_compiler tool to build your site beforehand (another project), add a reference to this project and then register your web user control as a regular server control, with the Assembly and Namespace properties instead of Src
I built a simple control that allows us to declare a no-compile web user control. It allows us to declare its attributes in the markup (through the implementation of the IAttributeAccessor interface), but we also have to specify the location of its .ascx file. I believe this is much better to have a declarative approach, whenever possible, to a programmatic one.
This is how it should be used:
<someprefix:DynamicUserControl runat="server" VirtualPath="~/SomeWebUserControl.ascx SomeProperty="1" SomeOtherProperty="false"/>
Here is the code:
using
System;
using
System.Data;
using
System.Configuration;
using
System.Web;
using
System.Web.Security;
using
System.Web.UI;
using
System.Web.UI.WebControls;
using
System.Web.UI.WebControls.WebParts;
using
System.Web.UI.HtmlControls;
using
System.ComponentModel;namespace Controls
{
[Themeable(false)]
public class DynamicUserControl: Control, IAttributeAccessor
{
#region Private fields private System.Web.UI.AttributeCollection attributes = null;
private String virtualPath = String.Empty;
#endregion
#region Public constructorpublic DynamicUserControl()
{
this.attributes = new System.Web.UI.AttributeCollection(this.ViewState);
}
#endregion
#region Public properties
[
Browsable(true)][Localizable(false)]
[
Description("")][DefaultValue("")]
[
UrlProperty("*.ascx")]public String VirtualPath
{
get
{
return (this.virtualPath);
}
set
{
this.virtualPath = value ?? String.Empty;
}
}
[Browsable(false)]public System.Web.UI.AttributeCollection Attributes
{
get
{
return (this.attributes);
}
}
#endregion
#region Protected override methodsprotected override void OnLoad(EventArgs e)
{
Control ctrl = this.Page.LoadControl(this.VirtualPath);Control innerCtrl = null;if (ctrl is PartialCachingControl)
{
innerCtrl = (ctrl as PartialCachingControl).CachedControl;
}
else if (ctrl is UserControl)
{
innerCtrl = ctrl;
}
if (innerCtrl != null)
{
foreach (String key in this.Attributes.Keys)
{
PropertyDescriptor prop = TypeDescriptor.GetProperties(innerCtrl)[key];if (prop != null)
{
TypeConverter converter = prop.Converter;if (converter != null)
{
prop.SetValue(innerCtrl, converter.ConvertFrom(this.attributes[key]));
}
else
{
converter = TypeDescriptor.GetConverter(prop.PropertyType);if (converter != null)
{
prop.SetValue(innerCtrl, converter.ConvertFrom(this.attributes[key]));
}
else
{
prop.SetValue(innerCtrl, Convert.ChangeType(this.attributes[key], prop.PropertyType));
}
}
}
}
this.Controls.Add(innerCtrl);
}
base.OnLoad(e);
}
#endregion
#region IAttributeAccessor Memberspublic String GetAttribute(String key)
{
return (this.Attributes[key]);
}
public void SetAttribute(String key, String value)
{
this.attributes[key] = value;
}
#endregion
}
}
Expression builders are a cool subject... they work with no-compile pages and provide a lot of declarative power!
How many times have you had the need to display values that are mostly equal, but slightly different than resources included in you .resx file?
For example, supose you have resource entry with key "User" which maps to value "User", and you also want to be able to display "Users". Normally, you would have to add another entry to the .resx file for the "Users" value. Enter the ConcatExpressionBuilder! See this example:
<%$ Concat:SomeGlobalResourceFile.ResourceKey,'SomeText',SomeOtherGlobalResourceFile.SomeOtherKey,'SomeOtherText',SomeLocalResourceKey %>
As you can guess, it mimics the behavior of the String.Concat method, but it can accept both literal strings (included in '') as well as global and local resource keys.
But what if you wanted to format a long value from a resource key? Enter the FormatExpressionBuilder:
<%$ Format:'Number is {0:2}',SomeGlobalResourceFile.LongResourceKey %>
The first argument before the first ',' is the format expression, and the others are arguments to it. The format expression can also come from a resource key:
<%$ Format:SomeLocalResourceKey,SomeGlobalResourceFile.SomeResourceKey,'SomeText' %>
Basically, it implements the String.Format method.
What about execute some code without code regions? Here is the CodeExpressionBuilder:
<%$ Code:DateTime.Now.Date %>
Here is my full list of general-purpose expression builders:
| ApplicationExpressionBuilder |
Returns a value stored in the application |
| CodeExpressionBuilder |
Executes code |
| ConcatExpressionBuilder |
Concatenates a number of expressions, which may come from resource files |
| CookieExpressionBuilder |
Returns a value stored in a cookie |
| FormatExpressionBuilder |
Formats an expression with any number of arguments, which may come from resource files |
| InRoleExpressionBuilder |
Checks if the current user belongs to a role |
| ProfileExpressionBuilder |
Returns a profile value |
| QueryStringExpressionBuilder |
Returns a value from the query string |
| RequestExpressionBuilder |
Returns a value from the request |
| SessionExpressionBuilder |
Returns a value stored in the session |
In the attached file, you can see all of these expression builders, together with their editors. Enjoy, and let me know if you like them or if you find any problems!
More Posts
« Previous page