【WCF】自动加载WCF Library (IIS Host)

在之前这篇 Blog ( 【WCF】自动加载WCF Library) 中介绍了如何在一个desktop应用中自动加载 WCF Library 的简单实现。后来我就想到如果能部署到 IIS 上,用 IIS Host 实现不就更方便嘛。正好最近学习 ASP.NET MVC 碰到这个类: VirtualPathProvider 类 (它提供了一组方法,使 Web 应用程序可以从虚拟文件系统中检索资源。) ,一下子豁然开朗:通过这个类,可以将 svc 请求通过动态生成 .svc 服务文件的方式动态加载 WCF Library 提供服务。其中最关键的是:通过 VirtualPathProvider.GetFile 来返回 虚拟的 WCF 服务文件。

先上图:

上传 assembly

自动加载并迁移到 help 页面:


上传 dll 先放在一边,通过反射分析,我会将 url 跳转到类似: http://localhost/WcfLibs/WcfServiceLibrary1.Service1.svc 这个路径上。
作为规则:WcfServiceLibrary1 是上传的 dll 的名称,也是 Service1 的 namespace,而只有请求路径是指向 WcfLibs 这个目录上的,
才会进入我自定义的 VirtualPathProvider 上进行处理:


1. WCF虚拟路径解析(WcfVirtualPathProvider.cs)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Hosting;
using System.Web.Caching;
using System.Collections;

namespace DynamicWcfApp
{
    public class WcfVirtualPathProvider : VirtualPathProvider
    {
        public override bool FileExists(string virtualPath)
        {
            var appRelativeVirtualPath = ToAppRelativeVirtualPath(virtualPath);

            if (IsVirtualFile(appRelativeVirtualPath))
            {
                return true;
            }
            else
            {
                return Previous.FileExists(virtualPath);
            }
        }

        public override System.Web.Hosting.VirtualFile GetFile(string virtualPath)
        {
            var appRelativeVirtualPath = ToAppRelativeVirtualPath(virtualPath);
            if (IsVirtualFile(appRelativeVirtualPath))
            {
                var servicePath = VirtualPathUtility.MakeRelative(Constants.VirtualWcfDirectoryName + "/", virtualPath);
                if (servicePath.EndsWith(".svc"))
                    servicePath = servicePath.Substring(0, servicePath.IndexOf(".svc"));
                // check
                if (!servicePath.Contains("."))
                    return Previous.GetFile(virtualPath);
                
                var assemblyLocation = System.IO.Path.Combine(Constants.AbsolutePath, servicePath.Split('.')[0] + ".dll");
                if (!System.IO.File.Exists(assemblyLocation))
                    return Previous.GetFile(virtualPath);

                return new WcfVirtualFile(virtualPath, servicePath, typeof(WcfVirtualServiceHostFactory).FullName);
            }
            else
            {
                return Previous.GetFile(virtualPath);
            }
        
        }

        public override CacheDependency GetCacheDependency(string virtualPath, IEnumerable virtualPathDependencies, DateTime utcStart)
        {
            var appRelativeVirtualPath = ToAppRelativeVirtualPath(virtualPath);

            if (IsVirtualFile(appRelativeVirtualPath) || IsVirtualDirectory(appRelativeVirtualPath))
            {
                return null;
            }
            else
            {
                return Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
            }
        }


        private bool IsVirtualFile(string appRelativeVirtualPath)
        {
            if (appRelativeVirtualPath.StartsWith(Constants.VirtualWcfDirectoryName + "/", StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
            return false;
        }

        private bool IsVirtualDirectory(string appRelativeVirtualPath)
        {
            return appRelativeVirtualPath.Equals(Constants.VirtualWcfDirectoryName, StringComparison.OrdinalIgnoreCase);
        }

        private string ToAppRelativeVirtualPath(string virtualPath)
        {
            var appRelativeVirtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
            if (!appRelativeVirtualPath.StartsWith("~/"))
            {
                throw new HttpException("Unexpectedly does not start with ~.");
            }
            return appRelativeVirtualPath;
        }
    }

    public class Constants
    {
        public static readonly string VirtualWcfDirectoryName = "~/WcfLibs";
        public static readonly string AbsolutePath = HttpContext.Current.Server.MapPath("~/WcfLibs");
    }
}
另外提醒各位看官注意:在 Provider 里 无法直接使用 HttpContext.Current.Server.MapPath 来寻找绝对路径(HttpContext.Current 是 null),因此提前用 Constants 在启动时保存了绝对路径的信息。

2. 映射虚拟WCF服务文件(WcfVirtualFile.cs)
在 WCF IIS Host 的时候,.net 是通过对 .svc (markup)文件映射到WCF Host 上进行处理的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Hosting;
using System.IO;
using System.Globalization;
using System.Web;

namespace DynamicWcfApp
{
    public class WcfVirtualFile : VirtualFile
    {
        private string _service;
        private string _factory;

        public WcfVirtualFile(string vp, string service, string factory)
            : base(vp)
        {
            _service = service;
            _factory = factory;
        }

        public override Stream Open()
        {
            var ms = new MemoryStream();
            var tw = new StreamWriter(ms);
            tw.Write(string.Format(CultureInfo.InvariantCulture, 
                "<%@ServiceHost language=c# Debug=\"true\" Service=\"{0}\" Factory=\"{1}\"%>",
                HttpUtility.HtmlEncode(_service), HttpUtility.HtmlEncode(_factory)));

            tw.Flush();
            ms.Position = 0;
            return ms;
        }
    }
}

3. 提供自动加载的 WCF Host Service Factory (WcfVirtualServiceHostFactory.cs)
constructorString 是唯一的信息来源( 来自 Wcf 服务文件中 Markup 中的 "Service"),结合之前的 Constants.AbsolutePath ,利用 Assembly.LoadFile(LoadFrom) 来动态加载 Wcf Library Assembly。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Reflection;
using System.ServiceModel.Activation;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace DynamicWcfApp
{
    public class WcfVirtualServiceHostFactory : ServiceHostFactory
    {
        public override System.ServiceModel.ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
        {
            var assmblyName = constructorString.Split('.')[0] + ".dll";
            var serviceName = constructorString;
            assmblyName = System.IO.Path.Combine(Constants.AbsolutePath, assmblyName);
            var assembly = Assembly.LoadFile(assmblyName);
            var serviceType = assembly.GetType(serviceName);
            var host = new ServiceHost(serviceType, baseAddresses);

            foreach (var iface in serviceType.GetInterfaces())
            {
                var attr = (ServiceContractAttribute)Attribute.GetCustomAttribute(iface, typeof(ServiceContractAttribute));
                if (attr != null)
                    host.AddServiceEndpoint(iface, new BasicHttpBinding(), "");
            }
            var metadataBehavior = new ServiceMetadataBehavior();
            metadataBehavior.HttpGetEnabled = true;
            host.Description.Behaviors.Add(metadataBehavior);
            return host;
        }
    }
}

最后,别忘记要在 Global.asax 的 Application_Start 里对自定义的 VirtualPathProvider 注册。
HostingEnvironment.RegisterVirtualPathProvider(new WcfVirtualPathProvider()); 
  protected void Application_Start()
  {
      AreaRegistration.RegisterAllAreas();

      RegisterGlobalFilters(GlobalFilters.Filters);
      RegisterRoutes(RouteTable.Routes); 
      HostingEnvironment.RegisterVirtualPathProvider(new WcfVirtualPathProvider()); 
  }



  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值