ASP.NET 2.0
ASP.NET 2.0
Recently, I posted a question about what happened to the IField, IRow and ITable interfaces that were available in the System.Web.UI.WebControls.WebParts namespace in .NET beta1 and the CTPs. I found it: IField has become IWebPartField, IRow became IWebPartRow, etc...
Interestingly, you are now supposed to pass in a delegate when you retrieve the value from the connection provider. I suppose that this is a mechanism to alert the consumer of changes occurring after the retrieval of the value? Documentation is not available yet.
When you want to create connectable web parts in ASP.NET 2.0, the preferred way to pass information is by specifying an interface with one or more properties. You can read up on how it works in this article by Carlos Aguilar Mares (based on beta 1). The nice thing of using interfaces is that you can have an IStringValue interface that just returns a string value and have an IZipCode interface deriving from IStringValue without adding or overriding any members. Of course, a zip code is a string, but a string is not always a zip code. This way, you can use zip code providers to feed both zip code consumers and consumers of string values.
To allow my connection providers to be connected to your connection consumers, they must be based upon the same interface for passing information. To make it easy for third party developers to connect to each others web parts, in beta 1 Microsoft included a small number of very simple interfaces. The most basic of them all was IField, which allowed you to pass an object (any object). They also included IParameters (set of name-value pairs), IRow (collecion of objects) and ITable (collection of isomorphic IRows). If your part does something very specific, you should specify your own interfaces, to make sure that only parts providing the right kind of info can connect to yours. But if you want your part to work with as many other parts as possible, you want to settle on a widely known interface, preferably from the framework.
Now in beta 2, all of these basic interfaces seem to have disappeared. Why is that? You can easily create these interface yourself, of course, but my interface would then be different from yours and parts from different vendors could never connect (well, you could start writing connection transformers for every combination, but that's another story).
It has been possible for a few years now to include drag-drop functionality in browser-based applications. It is still tricky to make it work cross-platform (meaning IE & FireFox & Safari to me), but libraries to help do this are freely available as well. I personally like the X library from cross-browser.com (drag-drop example).
When you are a javascript enthusiast, that is all fine and sorted out, but what if you prefer to work with ASP.NET and all it's nice server controls, strong-typed programming model? You would really expect this kind of functionality to be available in out-of-the-box, but it isn't. Not in the 1.1 release from 2003 and not in the forthcoming 2.0 release. There is some dragging functionality implemented in the Web Part Framework, but this cannot be used for non-WebPart functionality.
But ASP.NET can always easily be extended, so presumably, someone has already implemented some kind of server control that adds drag-drop to other elements on the page? Well, maybe someone has, but I couldn't find it. I was imagining the following:
- A server control named DragDropManager, that is invisible at runtime
- In VS.NET, the property browser show a DragObjects collection and a DropTargets collection. In DragObjects, you can add references to the other controls on the page or user control. Maybe you can also indicate what subelement of the draggable element is the “handle“. You can also specify a CSS classname that will be added to the style of the element during dragging.
- The DropTargets collection can also be filled with references to elements. DropTargets have a hover-class, which is added to the style while something is dragged over the target that could be accepted by the target
- Drag objects and drop targets could also know the concept of content types. Drag objects of content type X can only be dropped on a drop target that accepts X
- When an object is dropped on a target, an event is fired. This can be a client-side event or a server-side event (as configured on the drop target).
Did anyone ever come across this component? Free or paid? It seems very usefull in implementing web applications with rich features. I do know of some frameworks for Rich Internet Applications that include drag-drop, but these typically don't work nicely with standard existing ASP.NET server controls. Still, I don't see a reason why this couldn't work.
PS. The envisioned DragDropManager would of course be even cooler when implemented as an ExtenderProvider, so you could set all of the drag related proerties on the object-to-drag itself. But it seems that ExtenderProviders do not work well for web forms in VS.NET 2003 (more info and a fix).
In ASP.NET 2.0, the Portal Framework allows users to personalize their page to their liking. The configuration they choose is stored as a BLOB that only the WebPartManager can make sense of. What's more: the WebPartManager only works in the context of the user visiting the page. You cannot use the WebPartManager to change the configuration of other users, nor to read or change the configuration of other pages.
I have created a library that allows you to take the binary info that the WebPartManager stores in the personalization system and read it's content through collections and properties. You can even make changes and add or remove entries and then serialize this information back to the format that the WebPartManager understands.
You could use this functionality to make tools that update certain personalized information for all users in your system. Or to remove any instances of a web part that you offered to the users before, but you don't want to anymore. In that case, you have to programmatically change the personalization blob for all users that added the web part to their page.
The way to work with the library is like this:
The PageSettings class exposes 3 collections of properties. Each holds specific information on a number of controls, but not necessarily the same controls.
| DynamicParts |
These are the controls that must be added to the page dynamically. They are not coded into the ASPX, but are added to the page from a catalog. |
| Locations |
This contains information on the location of web parts. Each entry contains a ZoneID, ZoneIndex. It also contains an IsClosed property that indicates whether the part is visible on the page. |
| WebPartSettings |
The collection contains all of the other properties that can be set on a web part. Normally, these are the public properties of the web part that are marked WebBrowsable with an attribute. |
I haven't had the time for proper testing and documentation, but maybe this is already of use to someone out there. If you make any changes that could benefit others as well, please let me know.
The library and sources can be downloaded here.
This article is about the inner workings of the WebPartManager from ASP.NET 2.0. If you don't know exactly what I'm talking about, check these links:
ASP.NET 2.0
Web Parts
Personalization system
So you probably know that all of the properties you can set on Web Parts in ASP.NET 2.0 are stored inside the personalization system. In this article, you can find out how the storage happens and how you can plug in your own storage provider for personalization. If you have a look at the actual data persisted inside the personalization system (regardless of the provider used), you will find that it is a large BLOB. When you are inside a running Web Part age, this is not something you have to worry about, as the Web Part Infrastructure will set the correct values to your object properties automatically. There are situations, however, where you want to have access to the information stored from other contexts:
when you want to "copy" web parts from one page to another
when you are migrating information from a web patr based system to another (or vice versa)
when you need to update the personalized settings of other users (think of a tool that does certain updates necessary for a new version of a specific control)
So for these cases, we want to be able to access the information stored by the WebPartManager control from outside. Maybe from a console application, a WinForms application or another web page. In this article, I will explain how the WebPartManager stores the information for a Web Part page and I will show code for accessing this information.
We will first have a look at how the information is stored. Essentially, the blob in the personalisation system is the binary representation of an object array. The encoding/decoding of the array to the binary data is performed by a class called System.Web.UI.ObjectStateFormatter. In the resulting object array, each element has a fixed meaning based upon the order in the array:
Repeat | Type | Meaning
___________________________________________________________________
| int | Must have the value 1 if the array is filled
| int | The number of described controls
control | Type | Type of the control (optional)
| | string | Virtual path of UserControl (optional)
| | string | Control ID
| | int | Number of properties
| prop | IndexedString | Key
| - | object | Value
| | int | Number of custom properties
| cust | IndexedString | Key
- - | object | Value
So the first element of the array normally holds an Int32 with value 1. The second element holds the number of controls described. This is the number of times the 'control' block occurs. Within the control block, there are two more repeating sections, here labeld 'prop' and 'cust', each representing an arbitrary number of name-value pairs. Each is preceded by an int indicating the number of blocks we can expect. An IndexedString is a special kind of string the has a better performance when it is used often as a key in dictionary structures.
The array contains a control entry for all Web Parts on the page and the WebPartManager itself. When you cretae a WebBrowsable property on your Web Part, you will find this back in the properties dictionary for your Web Part. If your part implements System.Web.UI.WebControls.WebParts.IPersonalizable, you may also have "Custom Properties". Most Web Parts do not use this facility though. The WebPartManager on the other hand, relies heavily on these custom properties, to store extra information about the page.
The sample project
I created a small library that knows how to decode the binary store of the WebPartManager. It contains a class WebPartBlobEncoder with a static method Decode on it. This method expects a byte array (or base64 encoded string) and returns a Dictionary<string, PersonalizationInfo>. PersonalizationInfo is a simple data holder class that contains properties like these:
Then I created a sample console project to use the library. It decodes some hardcoded base64 strings (from my personalization database) and sends information about the contents to the Console:
The result:
The most important limitation now is that the information inside the WebPartManager's custom properties is still an array with a lot of seemingly unrelated values. I will complete the code to expose this in a strong-typed way as well and I will make sure that the Encoding also works. That will allow you to make changes to the configuration from an outside tool. When this works, I will release the library via this blog.
Disclaimer: this is all tested on Beta 1 code.
When I had the VirtualPathProvider sample from Scott Guthrie working, I wanted to create something useful with this technique. The idea I had was building XInclude support for web.config files. This would allow to split up the several segments of a config file over several physical files. Some ConfigurationSectionHandlers offer some support for reading values from external files (like NameValueFileSectionHandler), but often, this support is not sufficient.
What I would like to do is splitting web.config into parts that are the sole responsibility of the development team (and which are automatically deployed with newer versions) and parts that are the domain and responsibility of the system administrators. The web.config should then be constructed before it was even read by the AppDomain. I figured that by using the VirtualPathProvider model, I could just subclass from the default provider (called MapPathBasedVirtualPathProvider) and change it only for the cases where the GetFile method is called for files that are called web.config. Only in those cases, I would then open the file myself, scan it for any xi:include elements and replace them by the content they refer to. Easy. So I thought.
First, it turns out that MapPathBasedVirtualPathProvider is an internal class, so no subclassing. No harm done: when our provider is initialized, the system passes us the existing provider to allow us to do some delegation to it. So, to start up I built a wrapper class that delegates all of its calls to the previous provider. Code now looks like this:
public class ConfigPathProvider : VirtualPathProvider
{
protected VirtualPathProvider _previous = null;
public ConfigPathProvider(){}
// The system initializes our provider after we have registered it
public override void Initialize(VirtualPathProvider previous)
{
_previous = previous;
}
public override bool DirectoryExists(string virtualDir)
{
return _previous.DirectoryExists(virtualDir);
}
public override bool FileExists(string virtualPath)
{
return _previous.FileExists(virtualPath);
}
public override VirtualDirectory GetDirectory(string virtualDir)
{
return _previous.GetDirectory(virtualDir);
}
public override VirtualFile GetFile(string virtualPath)
{
return _previous.GetFile(virtualPath);
}
/* And some more methods, you get the point */
}
The AppInitialize is set up in a different class, to allow this class to be in a reusable library. Putting up break points on all of the methods now allows us to see how this works. Some findings:
- web.config is not accessed through the provider (boohoo, this immediately kills my mini-project)
- not all of the other file requests go through the provider anyway. This is because we delegate to the MapPathBasedVirtualPathProvider. It turns out that the GetDirectory method returns an instance of MapPathBasedVirtualDirectory. When ASP.NET iterates through the files or directories within that directory, the MapPathBasedVirtualDirectory will return MapPathBasedVirtualFile instances that will directly access the filesystem without using the VirtualPathProvider to instantiate the VirtualFile instance. This is a bug, because it breaks the concept of delegating to the previous provider (MSFT: if you are reading this: the problem is in MapPathBasedVirtualPathEnumerator.Current)
Wrapping it up: VirtualPathProviders can be used for serving code and aspx templates from a custom source, like a database. It will not work for images and other resources that are not mapped to ASP.NET, but it will also not work for the web.config file. This is a pity, because that seemed like a promising application. I think that the debugging experience with code being served from the database may be very special as well. ;)
Disclaimer: all of my findings are on the Beta 1 release
In my previous post, I told about the VirtualPathProvider that is part of the ASP.NET 2.0 architecture. Scott Guthrie told about it in his presentation at the European ASP.NET roadshow. There is a sample online on his site (it's the one from VSLive Orlando). In the sample, a website is created that contains only C# files in the Code directory and an Access database. The only table in the MDB file looks like this:
Note that the database actually contains all of the code for the ASPX pages and the code-behinds that would normally be on the file system. In the Code directory, there is a DBFileProvider.cs class. This class derives from System.Web.Hosting.VirtualPathProvider and whenever the ASP.NET engine needs to read a “file”, it actually calls into this class.
That is all very cool; you could use this kind of functionality very nicely in CMS's (every developer still works on his own CMS that will be better and more user friendly? Of course you are!). Here is the strange part: there is no web.config. So how does the ASP.NET runtime know which class to load? In the sample from Scott, the trick is done in a static method called AppInitialize(). If the Code directory in your project contains a class that has a method with this signature:
public static void AppInitialize()
the engine will call this method when the application is starting up. If you have more than one class with this method, VS.NET 2005 will give a build error. The in this method you can register the provider:
WebConfigXInclude.ConfigPathProvider fileProvider = new WebConfigXInclude.ConfigPathProvider();
HostingEnvironment.RegisterVirtualPathProvider(fileProvider);
After finding out how the registration of the provider works, I concluded that this was done in this way to allow the web.config file to be loaded through the VirtualPathProvider as well. You can't configure the provider for reading the configuration in the configuration. That figures. However, this turns out not to be the case. I will write about my unfruitful attempts to run from a virtual web.config in my next post.
What I really don't understand: why did the very smart guys developing the ASP.NET engine decide to use a “special method name”? They could have used a special interface. What exactly will happen when you have a current .NET 1.1 application that contains several classes with an AppInitialize() method in them? Or only one that expects an HttpContext available?
Today I attended a long presentation of Scott Guthrie on ASP.NET 2.0. I was already familiar with most of the content, but Scott is a great presenter, so it was really enjoyable seeing him do all the slick demoing.
One thing was completely new to me: he mentioned a FileSystemProvider, that ASP.NET uses to access the file system. Think of the possibilities! You could write your own provider that returns content from the database whenever ASP.NET tries to read a file from the file-system. So you could have users create themes by selecting and clicking around and when ASP.NET looks for a App_Theme/username folder, you feed it the skin and css files generated from the database!
Many other uses come to mind: think content management, think versioned ASPX files that appear online after a certain date.
But why can't I find any reference on this in Google that does not eventually point back to a blog post by Scott himself? Odd...
UPDATE: after looking it up in my local setup of Whidbey CTP nov.:
indeed, there seems to be a VirtualPathProvider class in the framework. It all plugs into the classes in the System.Web.Hosting namespace. I will investigate the possibilities and post about it soon.
In reaction on my posting on the UnpersonalizedZone class, someone asked me by personal e-mail how to prevent certain users access from the shared scope. This is indeed necessary information for the UnpersonalizedZone to be useful.
The access to the shared scope is enabled through web.config. It hooks into the new role management application that comes standard with ASP.NET 2.0. Through this application, you can create users and roles and assign users to roles. Using the provider model, you can use a storage backend of your choice (a database, an XML file ActiveDirectory, you name it...).
In web.config, you can specify which users and roles can switch from the Private to the Shared scope.
<webParts>
<personalization >
<authorization>
<allow roles="superusers" verbs="enterSharedScope" />
<deny users="*" verbs="enterSharedScope" />
</authorization>
</personalization>
</webParts>
This will allow only the users in the role superusers to switch to the shared scope. All other users are denied this privilege. Also, the users and roles attributes can contain multiple users and roles.
PS. The only two verbs that I am aware of are enterSharedScope (to view the page in it's unpersonalized form) and modifyState (to be allowed to switch to any mode but Browse).
Some time ago, I published a catalog web part that reads its content from a normal .ASCX file. One of the comments on this posting suggested a better use of generics in the code and I learned a bit more about how the web part creation in the WebPartManager control works, so I cleaned up the code and it got easier to understand along the way. For more explanation, check the original post. Here is the new and improved code:
public class FileCatalogPart : CatalogPart
{
public FileCatalogPart()
{
}
private string _templateLocation;
public string TemplateLocation
{
get
{
return _templateLocation;
}
set
{
_templateLocation = value;
}
}
Dictionary<WebPartDescription, WebPart> webparts = new Dictionary<WebPartDescription, WebPart>();
public override WebPartDescriptionCollection GetAvailableWebPartDescriptions()
{
webparts.Clear();
if (TemplateLocation != null)
{
Control container = Page.LoadControl(TemplateLocation);
if (container != null)
{
// You cannot directy iterate over the Controls collection, an excption
// is thrown that the collection was changed while iterating
ArrayList iterate = new ArrayList(container.Controls);
foreach (Control child in iterate)
{
WebPart wp = null;
if (child is WebPart)
{
wp = (WebPart)child;
}
else
{
if (!( child is LiteralControl))
{
wp = this.WebPartManager.CreateWebPart(child);
}
}
if (wp != null)
{
WebPartDescription desc = new WebPartDescription(wp);
webparts[desc] = wp;
}
}
}
}
return new WebPartDescriptionCollection(webparts.Keys);
}
public override WebPart GetWebPart(WebPartDescription description)
{
return (WebPart)webparts[description];
}
}
Nikhil Kothari has posted an
article describing how the ASP.NET team imagines that
cross-page connections would be implemented building on the web part infrastructure in ASP.NET 2. It is a very interesting read. He also explains (not entirely satisfactory IMHO) why this functionality is not a standard part of the framework.
The november 2004 issue of MSDN Magazine has a nice overview article on using the .NET 2.0 classes in the System.Security.AccessControl namespace. These classes allow you to access and modify the security descriptors on files, directories and other system objects. The article shows how to grant or deny rights on files to users from code, enumerate all ACE's in the ACL, take ownership, all from managed code.
Still, there seems to be no way to check for a specific right (say: write) for a specific SID (say: the current user). It seems to me that any application that allows a user to edit or display security settings should also be able to grey out the 'security' menu item when the current user lacks the privileges to view the security settings.
Am I missing something? Is there some HasAccess() method that I haven't found on msdn2? Am I really supposed to try/catch to know if the current user may edit a file?
Update: a colleague (thanks Emile) pointed me to this article on wrapping the AccessCheck API call from advapi32.dll. Code is in VB.NET and comes with samples. Uses .NET 1.1, so no integration with the new classes in System.Security.AccessControl
Remark up front: an updated version for the code presented below is here. The