ASP.NET MVC开发专题博客

ASP.NET MVC开发专题博客,为您精选ASP.NET MVC开发教程,助您开发愉快!

公告信息
欢迎光临ASP.NET MVC开发专题博客,祝您开发愉快!
文章档案
最新评论

ASP.NET MVC NVelocity模板引擎

ASP.NET MVC的世界里,又多一种可用之物:MVC NVelocity

NVelocity是一种简单实用的模板引擎,语法简洁,且API可扩展性强。

今天,我们就来介绍一下这个物,虽然有MVC Razor可用,不过介绍下也无妨。

NVelocityViewEngine

在之前的实现中,直接实现了IViewEngine这个接口,查找View的路径是通过实现IViewLocator来定位。在MVC2当中,修改了这部分的实现,MVC内部提供了VirtualPathProviderViewEngine这个模板方法类,在子类当中,我们中需要设置一下我们要查找的路径格式,其它的事件就可以交给模板方法类来完成,这样一方面可以简化我们的实现,另一方面还可以和默认的路径查找方式统一。

同时,由于我使用Nvelocity内置的相对文件路径的方式来查找模板,而使用VirtualPath的风格,因此在找到VirtualPath后,我们需要转换成实际的物理路径,直接通过物理路径来加载模板内容,而内置的FileResourceLoader并不支持从物理路径加载模板,所以我们还要额外实现一下FileResourceLoader,让支持从物理路径的加载方法。这两个类的代码如下:

public class FileResourceLoaderEx : FileResourceLoader
{
    public FileResourceLoaderEx() : base() { }
    private Stream FindTemplate(string filePath)
    {
        try
        {
            FileInfo file = new FileInfo(filePath);
            return new BufferedStream(file.OpenRead());
        }
        catch (Exception exception)
        {
            base.runtimeServices.Debug(string.Format("FileResourceLoader : {0}", exception.Message));
            return null;
        }
    }


    public override long GetLastModified(global::NVelocity.Runtime.Resource.Resource resource)
    {
        if (File.Exists(resource.Name))
        {
            FileInfo file = new FileInfo(resource.Name);
            return file.LastWriteTime.Ticks;
        }
        return base.GetLastModified(resource);
    }
    public override Stream GetResourceStream(string templateName)
    {
        if (File.Exists(templateName))
        {
            return FindTemplate(templateName);
        }
        return base.GetResourceStream(templateName);
    }
    public override bool IsSourceModified(global::NVelocity.Runtime.Resource.Resource resource)
    {
        if (File.Exists(resource.Name))
        {
            FileInfo file = new FileInfo(resource.Name);
            return (!file.Exists || (file.LastWriteTime.Ticks != resource.LastModified));
        }
        return base.IsSourceModified(resource);
    }
}
public class NVelocityViewEngine : VirtualPathProviderViewEngine, IViewEngine
{
    public static NVelocityViewEngine Default = null;

    private static readonly IDictionary DEFAULT_PROPERTIES = new Hashtable();
    private readonly VelocityEngine _engine;

    static NVelocityViewEngine()
    {
        string targetViewFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "views");
        //DEFAULT_PROPERTIES.Add(RuntimeConstants.RESOURCE_LOADER, "file");
        DEFAULT_PROPERTIES.Add(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, targetViewFolder);
        DEFAULT_PROPERTIES.Add("file.resource.loader.class", "NVelocityEngine.FileResourceLoaderEx\\,NVelocityEngine");


        Default = new NVelocityViewEngine();
    }

    public NVelocityViewEngine()
        : this(DEFAULT_PROPERTIES)
    {
    }

    public NVelocityViewEngine(IDictionary properties)
    {
        base.MasterLocationFormats = new string[] { "~/Views/{1}/{0}.vm", "~/Views/Shared/{0}.vm" };
        base.AreaMasterLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.vm", "~/Areas/{2}/Views/Shared/{0}.vm" };
        base.ViewLocationFormats = new string[] { "~/Views/{1}/{0}.vm", "~/Views/Shared/{0}.vm" };
        base.AreaViewLocationFormats = new string[] { "~/Areas/{2}/Views/{1}/{0}.vm", "~/Areas/{2}/Views/Shared/{0}.vm" };
        base.PartialViewLocationFormats = base.ViewLocationFormats;
        base.AreaPartialViewLocationFormats = base.AreaViewLocationFormats;
        base.FileExtensions = new string[] { "vm" };


        if (properties == null) properties = DEFAULT_PROPERTIES;

        ExtendedProperties props = new ExtendedProperties();
        foreach (string key in properties.Keys)
        {
            props.AddProperty(key, properties[key]);
        }

        _engine = new VelocityEngine();
        _engine.Init(props);
    }

    protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
    {
        Template viewTemplate = GetTemplate(viewPath);
        Template masterTemplate = GetTemplate(masterPath);
        NVelocityView view = new NVelocityView(controllerContext, viewTemplate, masterTemplate);
        return view;
    }
    protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
    {
        Template viewTemplate = GetTemplate(partialPath);
        NVelocityView view = new NVelocityView(controllerContext, viewTemplate, null);
        return view;
    }
    public Template GetTemplate(string viewPath)
    {
        if (string.IsNullOrEmpty(viewPath))
        {
            return null;
        }
        return _engine.GetTemplate(System.Web.Hosting.HostingEnvironment.MapPath(viewPath));
    }
    
}

NVelocityView

主要实现IView接口,实现Render方法来将模板和当前的上下文结合之后输出出来。这个类还实现了,IViewDataContainer好像不是特别必要。NVelocity的Render也很简单,只是把所需要的对像塞到NVelocity执行的上下文当中,然后调用一下Merge方法就OK了。这里要特别说明的是,在NVelocity模板上面,我们可以调用上下文对象的中的任何属性和方法,但是没有办法调用到对象上的扩展方法,这时候,我们就需要借助NVelocity所提供的IDuck这个接口来提供扩展方法的支持,如下代码的:new HtmlExtensionDuck(context, this); 。完全代码如下:

public class NVelocityView : IViewDataContainer, IView
{
    private ControllerContext _controllerContext;
    private readonly Template _masterTemplate;
    private readonly Template _viewTemplate;

    public NVelocityView(ControllerContext controllerContext, string viewPath, string masterPath)
        : this(controllerContext, NVelocityViewEngine.Default.GetTemplate(viewPath), NVelocityViewEngine.Default.GetTemplate(masterPath))
    {

    }
    public NVelocityView(ControllerContext controllerContext, Template viewTemplate, Template masterTemplate)
    {
        _controllerContext = controllerContext;
        _viewTemplate = viewTemplate;
        _masterTemplate = masterTemplate;
    }

    public Template ViewTemplate
    {
        get { return _viewTemplate; }
    }

    public Template MasterTemplate
    {
        get { return _masterTemplate; }
    }

    private VelocityContext CreateContext(ViewContext context)
    {
        Hashtable entries = new Hashtable(StringComparer.InvariantCultureIgnoreCase);
        if (context.ViewData != null)
        {
            foreach (var pair in context.ViewData)
            {
                entries[pair.Key] = pair.Value;
            }
        }
        entries["viewdata"] = context.ViewData;
        entries["tempdata"] = context.TempData;
        entries["routedata"] = context.RouteData;
        entries["controller"] = context.Controller;
        entries["httpcontext"] = context.HttpContext;
        entries["viewbag"] = context.ViewData;
        CreateAndAddHelpers(entries, context);

        return new VelocityContext(entries);
    }

    private void CreateAndAddHelpers(Hashtable entries, ViewContext context)
    {
        entries["html"] = entries["htmlhelper"] = new HtmlExtensionDuck(context, this);
        entries["url"] = entries["urlhelper"] = new UrlHelper(context.RequestContext);
        entries["ajax"] = entries["ajaxhelper"] = new AjaxHelper(context, this);
    }

    public void Render(ViewContext viewContext, TextWriter writer)
    {
        this.ViewData = viewContext.ViewData;

        bool hasLayout = _masterTemplate != null;

        VelocityContext context = CreateContext(viewContext);

        if (hasLayout)
        {
            StringWriter sw = new StringWriter();
            _viewTemplate.Merge(context, sw);

            context.Put("childContent", sw.GetStringBuilder().ToString());

            _masterTemplate.Merge(context, writer);
        }
        else
        {
            _viewTemplate.Merge(context, writer);
        }
    }

    private ViewDataDictionary _viewData;
    public ViewDataDictionary ViewData
    {
        get
        {
            if (_viewData == null)
            {
                return _controllerContext.Controller.ViewData;
            }
            return _viewData;
        }
        set
        {
            _viewData = value;
        }
    }
}

ExtensionDuck

ExtensionDuck就是对IDuck接口的实现,它是我们需要提供扩展方法支持的Duck对象的基类。所有需要接供扩展方法的对象,通过继承该方法可以简化大部分的工作:

public class ExtensionDuck : IDuck
{
    private readonly object _instance;
    private readonly Type _instanceType;
    private readonly Type[] _extensionTypes;
    private Introspector _introspector;

    public ExtensionDuck(object instance)
        : this(instance, Type.EmptyTypes)
    {
    }

    public ExtensionDuck(object instance, params Type[] extentionTypes)
    {
        if(instance == null) throw new ArgumentNullException("instance");

        _instance = instance;
        _instanceType = _instance.GetType();
        _extensionTypes = extentionTypes;
    }

    public Introspector Introspector
    {
        get
        {
            if(_introspector == null)
            {
                _introspector = RuntimeSingleton.Introspector;
            }
            return _introspector;
        }
        set { _introspector = value; }
    }

    public object GetInvoke(string propName)
    {
        throw new NotSupportedException();
    }

    public void SetInvoke(string propName, object value)
    {
        throw new NotSupportedException();
    }

    public object Invoke(string method, params object[] args)
    {
        if(string.IsNullOrEmpty(method)) return null;

        MethodInfo methodInfo = Introspector.GetMethod(_instanceType, method, args);
        if(methodInfo != null)
        {
            return methodInfo.Invoke(_instance, args);
        }

        object[] extensionArgs = new object[args.Length + 1];
        extensionArgs[0] = _instance;
        Array.Copy(args, 0, extensionArgs, 1, args.Length);

        foreach(Type extensionType in _extensionTypes)
        {
            methodInfo = Introspector.GetMethod(extensionType, method, extensionArgs);
            if(methodInfo != null)
            {
                return methodInfo.Invoke(null, extensionArgs);
            }
        }

        return null;
    }
}

接下,我们就可以来实现一个HtmlExtensionDuck,指定一下,View中可以调用到HtmlHelper的哪些扩展方法,需要被开放的扩展方法可以在HTML_EXTENSION_TYPES中提供扩展方法所在的静态类名:

public class HtmlExtensionDuck : ExtensionDuck
{
    public static readonly Type[] HTML_EXTENSION_TYPES =
        new Type[]
            {
                typeof(DisplayExtensions),
                typeof(DisplayTextExtensions),
                typeof(EditorExtensions),
                typeof(FormExtensions), 
                typeof(InputExtensions), 
                typeof(LabelExtensions),
                typeof(LinkExtensions), 
                typeof(MvcForm),
                typeof(PartialExtensions),
                typeof(RenderPartialExtensions),
                typeof(SelectExtensions),
                typeof(TextAreaExtensions),
                typeof(ValidationExtensions)
            };

    public HtmlExtensionDuck(ViewContext viewContext, IViewDataContainer container)
        : this(new HtmlHelper(viewContext, container))
    {
    }

    public HtmlExtensionDuck(HtmlHelper htmlHelper)
        : this(htmlHelper, HTML_EXTENSION_TYPES)
    {
    }

    public HtmlExtensionDuck(HtmlHelper htmlHelper, params Type[] extentionTypes)
        : base(htmlHelper, extentionTypes)
    {
    }
}

完整的NVelocity for ASP.NET MVC的实现就是以上几个类就可以完成。然后,我们就可以直接注册到系统中来。我们不需要重写任何Conroller,我们直接把ViewEngine注册到MVC中来就可以被用到,也可以支持一个程序支持多种视图引擎共存的和谐场面。简单的注册代码放在Global.asax文件中:

ViewEngines.Engines.Add(NVelocityViewEngine.Default);

源码下载:下载附件

本文ASP.NET MVC NVelocity模板引擎就为您介绍到此了。

如果涉及数据库操作,推荐一款配套的ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"

2011/9/8 23:24:15 | ASP.NET MVC教程 | |

#2ロレックス 銀座 オーバーホール[45.174.204.*]2024/2/29 3:37:29
ブランドコピー
日本に一番人気のブランドコピーヴィトンバッグ,ヴィトン生地コピー,ヴィトンコピー財布..
気のブランドコピー新作は定時に更新し、ご満足に添うようにいたします
ブランドコピーブランド,偽物ブランド,ブランドコピーバッグ,
ブランドコピー財布,ブランドコピー時計,ブランドコピー品
#1日本最大級のブランド時計・バッグ・財布N級品専門店[143.255.245.*]2018/9/10 15:18:17
ロレックス 通販専門店

★経営理念:
1.信用第一,品質保証,最も合理的な価格で商品を消費者に提供致します.
2.弊社の商品品数大目で、商品は安めです!★商品現物写真★
3.数量制限無し、一個の注文も、OKです.
4.1個も1万個も問わず、誠心誠意対応します.
5.不良品の場合、弊社が無償で交換します.


営業種目:
高級腕時計,スーパーコピー時計(N級品),財布(N級品)
バッグ(N級品),靴(N品),指輪(N級品),ベルト(N級品),マフラー(N級品)
ロレックス,カルティエ,IWC,オメガ,パネライ,ブランド時計等も豊富に取り揃えております

価格が特恵を与えて、信用の第1、品質の100%は保証します
  • 发表评论