导航

聚合

«2008年11月»
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

Blog统计

新闻/公告

存档

随笔分类

文章分类

相册

An implementation of ASP.NET 2.0's VirtualPathProvider

A question came up on the Developmentor ADVANCED-DOTNET list about the new VirtualPathProvider in ASP.NET 2.0.  (Scott Guthrie talked about the VirtualPathProvider a while ago.)  I've implemented just such a virtual provider for Alchemy and was asked to share the working code, so I figured this blog would be a good place to post it.  I couldn't just post the files to the list because it doesn't accept attachments, and besides, this allows me to share the syntax-highlighted version and maybe get a few Google hits in the process.
 
The idea of this VirtualPathProvider is simple.  There are 'Projects' and 'People', and each of these have their own sets of ASPX pages in special folders, _alchemy/project and _alchemy/personal.  Requests to, say, /people/geoff/Default.aspx get mapped, via the VirtualPathProvider, on to _alchemy/personal/Default.aspx.  Requests to /projects/SomeProject/Default.aspx get mapped on to _alchemy/project/Default.aspx.
 
The handling of valid/invalid project and user names isn't shown here, but it's just as straightforward as you'd expect.  I haven't included the AlchemyUri class here (which does this project and person handling) because it would just muddy the waters.
 
There are three classes here that do the virtual path work: VirtualAlchemyFile, VirtualAlchemyDirectory, and AlchemyPathProvider.  Let's get the first two out of the way because they're quite straightforward:
 

//====================================================================

//

// COPYRIGHT (C) 2006 OPINIONATEDGEEK LTD.

//

// The contents of this file are subject to License from OpinionatedGeek;

// you may not use this file except in compliance with the License.

// You may obtain a License from OpinionatedGeek Ltd. Software distributed

// under the License is distributed "as is" and without any warranty

// as to merchantability or fitness for a particular purpose or any

// other warranties either expressed or implied. The author will

// not be liable for data loss, damages, loss of profits or any

// other kind of loss while using or misusing this software.

//

// For more information visit http://www.opinionatedgeek.com/

//

//====================================================================

//

//    File name:        $RCSfile: VirtualAlchemyFile.cs,v $

//

//    CVS File:        $Source: C://CVSRoot/Alchemy/Core/Modules/VirtualAlchemyFile.cs,v $

//

//    Last Modified:    $Date: 2006/03/08 14:16:51 $

//

//    Author:            $Author: Geoff $

//

//    Version:        $Revision: 1.3 $

//

//====================================================================

 

using System;

using System.IO;

using System.Web;

using System.Web.Util;

using System.Web.Hosting;

 

namespace OpinionatedGeek.Applications.Alchemy

{

    [SourceVersion ("$RCSfile: VirtualAlchemyFile.cs,v $", "$Source: C://CVSRoot/Alchemy/Core/Modules/VirtualAlchemyFile.cs,v $", "$Date: 2006/03/08 14:16:51 $", "$Author: Geoff $", "$Revision: 1.3 $", "")]

    public class VirtualAlchemyFile : VirtualFile

    {

        private string _fullPath;

 

        public VirtualAlchemyFile (string virtualPath, string fullPath)

            : base (virtualPath)

        {

            _fullPath = fullPath;

            if (!File.Exists (_fullPath))

            {

                if (Directory.Exists (_fullPath))

                {

                    _fullPath = Path.Combine (_fullPath, "default.aspx");

                }

            }

 

            return;

        }

 

        public bool Exists

        {

            get

            {

                return File.Exists (_fullPath);

            }

        }

 

        public override Stream Open ()

        {

            return File.OpenRead (_fullPath);

        }

    }

}

 

//====================================================================

//

// COPYRIGHT (C) 2006 OPINIONATEDGEEK LTD.

//

// The contents of this file are subject to License from OpinionatedGeek;

// you may not use this file except in compliance with the License.

// You may obtain a License from OpinionatedGeek Ltd. Software distributed

// under the License is distributed "as is" and without any warranty

// as to merchantability or fitness for a particular purpose or any

// other warranties either expressed or implied. The author will

// not be liable for data loss, damages, loss of profits or any

// other kind of loss while using or misusing this software.

//

// For more information visit http://www.opinionatedgeek.com/

//

//====================================================================

//

//    File name:        $RCSfile: VirtualAlchemyDirectory.cs,v $

//

//    CVS File:        $Source: C://CVSRoot/Alchemy/Core/Modules/VirtualAlchemyDirectory.cs,v $

//

//    Last Modified:    $Date: 2006/03/08 14:16:51 $

//

//    Author:            $Author: Geoff $

//

//    Version:        $Revision: 1.2 $

//

//====================================================================

 

using System;

using System.Collections;

using System.Collections.Generic;

using System.IO;

using System.Web;

using System.Web.Util;

using System.Web.Hosting;

 

namespace OpinionatedGeek.Applications.Alchemy

{

    [SourceVersion ("$RCSfile: VirtualAlchemyDirectory.cs,v $", "$Source: C://CVSRoot/Alchemy/Core/Modules/VirtualAlchemyDirectory.cs,v $", "$Date: 2006/03/08 14:16:51 $", "$Author: Geoff $", "$Revision: 1.2 $", "")]

    public class VirtualAlchemyDirectory : VirtualDirectory

    {

        private string _fullPath;

 

        public VirtualAlchemyDirectory (string virtualPath, string fullPath)

            : base (virtualPath)

        {

            _fullPath = fullPath;

 

            return;

        }

 

        public override IEnumerable Children

        {

            get

            {

                string [] childDirectories = Directory.GetDirectories (_fullPath);

                string [] childFiles = Directory.GetFiles (_fullPath);

 

                System.Collections.Generic.List<string> children = new System.Collections.Generic.List<string> ();

                children.AddRange (childDirectories);

                children.AddRange (childFiles);

 

                return children;

            }

        }

 

        public override IEnumerable Directories

        {

            get

            {

                return Directory.GetDirectories (_fullPath);

            }

        }

 

        public bool Exists

        {

            get

            {

                return Directory.Exists (_fullPath);

            }

        }

 

        public override string Name

        {

            get

            {

                return _fullPath;

            }

        }

 

        public override IEnumerable Files

        {

            get

            {

                return Directory.GetFiles (_fullPath);

            }

        }

    }

}

 
Although they're pretty simple here, they could be a lot more complicated if necessary.  If you needed to retrieve the ASPX source from a database, your VirtualFile is where you'd do it.
 
The final class is the AlchemyPathProvider, and it's what does most of the work.  It has overrides for four important methods: FileExists (), DirectoryExists (), GetFile () and GetDirectory ().  In the AlchemyPathProvider class, these do some checking on the request and then either map it on to one of our own virtual files, or pass it on to the default path provider (by calling the base method).
 
Here's the full source to the AlchemyPathProvider:
 

//====================================================================

//

// COPYRIGHT (C) 2006 OPINIONATEDGEEK LTD.

//

// The contents of this file are subject to License from OpinionatedGeek;

// you may not use this file except in compliance with the License.

// You may obtain a License from OpinionatedGeek Ltd. Software distributed

// under the License is distributed "as is" and without any warranty

// as to merchantability or fitness for a particular purpose or any

// other warranties either expressed or implied. The author will

// not be liable for data loss, damages, loss of profits or any

// other kind of loss while using or misusing this software.

//

// For more information visit http://www.opinionatedgeek.com/

//

//====================================================================

//

//    File name:        $RCSfile: AlchemyPathProvider.cs,v $

//

//    CVS File:        $Source: C://CVSRoot/Alchemy/Core/Modules/AlchemyPathProvider.cs,v $

//

//    Last Modified:    $Date: 2006/03/08 14:16:51 $

//

//    Author:            $Author: Geoff $

//

//    Version:        $Revision: 1.3 $

//

//====================================================================

 

using System;

using System.Collections;

using System.IO;

using System.Web;

using System.Web.Caching;

using System.Web.Util;

using System.Web.Hosting;

 

namespace OpinionatedGeek.Applications.Alchemy

{

    [SourceVersion ("$RCSfile: AlchemyPathProvider.cs,v $", "$Source: C://CVSRoot/Alchemy/Core/Modules/AlchemyPathProvider.cs,v $", "$Date: 2006/03/08 14:16:51 $", "$Author: Geoff $", "$Revision: 1.3 $", "")]

    public class AlchemyPathProvider : VirtualPathProvider

    {

        private const string ProjectsPrefix = "_alchemy\\project\\";

        private const string PeoplePrefix = "_alchemy\\personal\\";

 

        private string _baseProjectLocation = null;

        private string _basePeopleLocation = null;

 

        public AlchemyPathProvider ()

        {

            string applicationLocation = HostingEnvironment.ApplicationPhysicalPath;

            _baseProjectLocation = Path.Combine (applicationLocation, ProjectsPrefix);

            _basePeopleLocation = Path.Combine (applicationLocation, PeoplePrefix);

 

            return;

        }

 

        public override bool FileExists (string virtualPath)

        {

            bool exists = false;

            Uri fullUri = new Uri (HttpContext.Current.Request.Url, virtualPath);

            AlchemyUri alchemyUrl = new AlchemyUri (fullUri);

            if ((alchemyUrl.IsProject) || (alchemyUrl.IsPerson))

            {

                VirtualAlchemyFile file = new VirtualAlchemyFile (virtualPath, this.GetPhysicalName (alchemyUrl));

                exists = file.Exists;

            }

            else

            {

                exists = base.FileExists (virtualPath);

            }

 

            return exists;

        }

 

        public override bool DirectoryExists (string virtualPath)

        {

            bool exists = false;

            Uri fullUri = new Uri (HttpContext.Current.Request.Url, virtualPath);

            AlchemyUri alchemyUrl = new AlchemyUri (fullUri);

            if ((alchemyUrl.IsProject) || (alchemyUrl.IsPerson))

            {

                VirtualAlchemyDirectory dir = new VirtualAlchemyDirectory (virtualPath, this.GetPhysicalName (alchemyUrl));

                exists = dir.Exists;

            }

            else

            {

                exists = base.DirectoryExists (virtualPath);

            }

 

            return exists;

        }

 

        public override VirtualFile GetFile (string virtualPath)

        {

            VirtualFile file = null;

            Uri fullUri = new Uri (HttpContext.Current.Request.Url, virtualPath);

            AlchemyUri alchemyUrl = new AlchemyUri (fullUri);

            if ((alchemyUrl.IsProject) || (alchemyUrl.IsPerson))

            {

                file = new VirtualAlchemyFile (virtualPath, this.GetPhysicalName (alchemyUrl));

            }

            else

            {

                file = base.GetFile (virtualPath);

            }

 

            return file;

        }

 

        public override VirtualDirectory GetDirectory (string virtualPath)

        {

            VirtualDirectory directory = null;

            Uri fullUri = new Uri (HttpContext.Current.Request.Url, virtualPath);

            AlchemyUri alchemyUrl = new AlchemyUri (fullUri);

            if ((alchemyUrl.IsProject) || (alchemyUrl.IsPerson))

            {

                directory = new VirtualAlchemyDirectory (virtualPath, this.GetPhysicalName (alchemyUrl));

            }

            else

            {

                directory = base.GetDirectory (virtualPath);

            }

 

            return directory;

        }

 

        public override CacheDependency GetCacheDependency (string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)

        {

            Uri fullUri = new Uri (HttpContext.Current.Request.Url, virtualPath);

            AlchemyUri alchemyUrl = new AlchemyUri (fullUri);

            CacheDependency dependency = null;

            if ((alchemyUrl.IsProject) || (alchemyUrl.IsPerson))

            {

                dependency = new CacheDependency (this.GetPhysicalName (alchemyUrl));

            }

            else

            {

                dependency = base.GetCacheDependency (virtualPath, virtualPathDependencies, utcStart);

            }

 

            return dependency;

        }

 

        private string GetPhysicalName (AlchemyUri url)

        {

            string physicalName = url.Path;

            if ((url.IsPerson) || (url.IsProject))

            {

                string trimmedPath;

                string baseLocation;

                if (url.IsPerson)

                {

                    trimmedPath = url.Path.Substring (AlchemyUri.PeoplePrefix.Length);

                    baseLocation = _basePeopleLocation;

                }

                else

                {

                    trimmedPath = url.Path.Substring (AlchemyUri.ProjectsPrefix.Length);

                    baseLocation = _baseProjectLocation;

                }

 

                trimmedPath = trimmedPath.Substring (url.Space.Length);

                trimmedPath = trimmedPath.TrimStart ('/');

                trimmedPath = trimmedPath.Replace ('/', '\\');

 

                physicalName = Path.Combine (baseLocation, trimmedPath);

            }

 

            return physicalName;

        }

    }

}

 
As usual, let me know if you've any thoughts or comments (or spot any bugs!)

打印 | 发表于 2007年6月21日 3:15

评论

# Rey

http://5efe6b5b128ae4297360e3a508d72c73-t.rfmsjv.org 5efe6b5b128ae4297360e3a508d72c73 [url]http://5efe6b5b128ae4297360e3a508d72c73-b1.rfmsjv.org[/url] [url=http://5efe6b5b128ae4297360e3a508d72c73-b2.rfmsjv.org]5efe6b5b128ae4297360e3a508d72c73[/url] [u]http://5efe6b5b128ae4297360e3a508d72c73-b3.rfmsjv.org[/u] 51db5f58e300383915b4ea83c7fc983b
2007/8/26 18:21 | Harold

发表评论

标题  
姓名  
Email
主页
评论内容