WCF服务属性注入基础设施

WCF的服务的创建行为:使用默认构造函数创建WCF服务对象。如果我们想要在WCF内使用外部对象,最简单的方式就是把外部对象做成全局对象。然而这样的话会增加全局对象的数量,让代码的耦合度增加了。所以,我们需要突破WCF的默认行为。解决的办法是添加自定义的ServiceHost子类。

首先,添加一个IWCFService泛型接口,WCF服务将继承这个接口,从而拥有外部注入泛型属性的能力。

public interface IWCFService<TDependency>
{
    TDependency Dependency { get; set; }
}

其次,我们需要自定义ServiceHost子类,提供外部注入Dependency的构造函数。

public class WCFServiceHost<Service, TDependency> : ServiceHost
    where Service : IWCFService<TDependency>, new()
{
    public WCFServiceHost(TDependency dependency, Type serviceType, params Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
        if (dependency == null) {
            throw new ArgumentNullException("dependency");
        }
 
        foreach (var cd in ImplementedContracts.Values) {
            cd.Behaviors.Add(new WCFInstanceProvider<Service, TDependency>(dependency));
        }
    }
}

内部用到了WCFInstanceProvider,意味着,我们必须提供自己的InstanceProvider,实现如下:

public class WCFInstanceProvider<Service, TDependency> : IInstanceProvider, IContractBehavior
    where Service : IWCFService<TDependency>, new()
{
    private readonly TDependency _dependency;
 
    public WCFInstanceProvider(TDependency dependency)
    {
        if (dependency == null) {
            throw new ArgumentNullException("dependency");
        }
 
        _dependency = dependency;
    }
 
    #region IInstanceProvider Members
    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return GetInstance(instanceContext);
    }
    public object GetInstance(InstanceContext instanceContext)
    {
        return new Service { Dependency = _dependency };
    }
    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
    }
    #endregion
 
    #region IContractBehavior Members
    public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
    }
    public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
    }
    public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
    {
        dispatchRuntime.InstanceProvider = this;
    }
    public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
    {
    }
    #endregion
}

这样,我们就差不多完成了自定义ServiceHost的定制,紧接着我们提供一个WCF服务启动关闭的基类,简化WCF服务开启和关闭的行为。

public abstract class WCFServiceBase<IChannel, Channel,TDependency> : IDisposable
    where Channel:IWCFService<TDependency>,new ()
{
    private readonly System.Threading.AutoResetEvent _waitor = new System.Threading.AutoResetEvent(false);
    private readonly object _locker = new object();
    private bool _isOpen;
 
    protected abstract string Url { get; }
    protected abstract TDependency Dependency { get; }
 
    public bool IsOpen
    {
        get
        {
            lock (_locker) {
                return _isOpen;
            }
        }
        private set
        {
            lock (_locker) {
                _isOpen = value;
            }
        }
    }
    public void Open()
    {
        System.Threading.ThreadPool.QueueUserWorkItem(o => {
            var namePipeAddress = new Uri(Url);
            var serverType = typeof(Channel);
            using (var host = new WCFServiceHost<Channel,TDependency>(Dependency,serverType, namePipeAddress)) {
                var serverInterfaceType = typeof(IChannel);
                var namePipeBiding = new NetNamedPipeBinding();
                host.AddServiceEndpoint(serverInterfaceType, namePipeBiding, "");
                host.Open();
                IsOpen = true;
                OnOpen();
                _waitor.WaitOne();
                host.Close();
                IsOpen = false;
            }
        });
    }
    public void Close()
    {
        Dispose();
    }
    protected virtual void OnOpen()
    {
 
    }
 
    #region IDisposeable
    private bool disposed;
    ~WCFServiceBase()
    {
        Dispose(false);
    }
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    private void Dispose(bool disposing)
    {
        if (disposed) {
            return;
        }
        if (disposing) {
            // 清理托管资源
        }
 
        // 清理非托管资源
        _waitor.Set();
 
        disposed = true;
    }
    #endregion IDisposeable
}

既然,提供了WCFServiceBase,我们当然应该提供一个WCFClientBase,方便WCF客户端代码的编写。

public abstract class WCFClientBase<IChannel>
{
    protected abstract string Url { get; }
    protected void Query(Action<IChannel> query, Action<Exception> error = null)
    {
        if (query == null) return;
 
        System.Threading.ThreadPool.QueueUserWorkItem(o => {
            try {
                var namePipeBiding = new NetNamedPipeBinding();
                var namePipeAddress = new EndpointAddress(Url);
                using (var client = new ChannelFactory<IChannel>(namePipeBiding, namePipeAddress)) {
                    var updatorChannel = client.CreateChannel();
                    query(updatorChannel);
                }
            } catch (Exception e) {
                if (error != null) error(e);
            }
        });
    }
    protected void Query(Action<IChannel> query,Action @finally,Action<Exception> error=null)
    {
        if (query == null) return;
 
        System.Threading.ThreadPool.QueueUserWorkItem(o => {
            try{
                var namePipeBiding=new NetNamedPipeBinding();
                var namePipeAddress=new EndpointAddress(Url);
                using(var client=new ChannelFactory<IChannel>(namePipeBiding,namePipeAddress)){
                    var updatorChannel=client.CreateChannel();
                    query(updatorChannel);
                }
            } catch(Exception e){
                if(error!=null) error(e);
            } finally{
                if(@finally!=null) @finally();
            }
        });
    }
    protected void QuerySync(Action<IChannel> query, Action<Exception> error = null)
    {
        if (query == null) return;
 
        try {
            var namePipeBiding = new NetNamedPipeBinding();
            var namePipeAddress = new EndpointAddress(Url);
            using (var client = new ChannelFactory<IChannel>(namePipeBiding, namePipeAddress)) {
                var updatorChannel = client.CreateChannel();
                query(updatorChannel);
            }
        } catch (Exception e) {
            if (error != null) error(e);
        }
    }
    protected void QuerySync(Action<IChannel> query, Action @finally, Action<Exception> error = null)
    {
        if (query == null) return;
 
        try {
            var namePipeBiding = new NetNamedPipeBinding();
            var namePipeAddress = new EndpointAddress(Url);
            using (var client = new ChannelFactory<IChannel>(namePipeBiding, namePipeAddress)) {
                var updatorChannel = client.CreateChannel();
                query(updatorChannel);
            }
        } catch (Exception e) {
            if (error != null) error(e);
        } finally {
            if (@finally != null) @finally();
        }
    }
}

以上,就是所有基础设施的构建。但是,我们的目标是用上述基础设施代码简化WCF服务和客户代码的开发。我们以提供一个WCF计算服务为例说明如何使用上述基础设施。首先是WCF接口代码:

[ServiceContract(Namespace = "LambdaClient")]
public interface ILambdaChannel
{
    [OperationContract]
    int Add(int i, int j);
}

很简单,只是一个Add服务API。我们来实现服务端代码:

public class LambdaChannel:ILambdaChannel,IWCFService<LambdaProvider>
{
    public int Add(int i, int j)
    {
        return Dependency.Add(i, j);
    }
 
    public LambdaProvider Dependency { get; set; }
}
public class LambdaProvider
{
    public Func<int, int, int> Add;
}
public class LambdaChannelService:WCFServiceBase<ILambdaChannel,LambdaChannel,LambdaProvider>
{
    protected override string Url
    {
        get { return @"net.pipe://localhost/lambda"; }
    }
 
    protected override LambdaProvider Dependency
    {
        get {
            return new LambdaProvider{
                    Add = (i, j) => i + j
            };
        }
    }
}
class Program
{
    static void Main(string[] args)
    {
        var lambdaService = new LambdaChannelService();
        lambdaService.Open();
        Console.WriteLine("Lambda计算服务已开启。");
        Console.Read();
    }
}

最后,在客户端使用上述WCF计算服务:

public class LambdaChannelClient:WCFClientBase<ILambdaChannel>
{
    protected override string Url
    {
        get { return @"net.pipe://localhost/lambda"; }
    }
 
    public int Add(int i, int j)
    {
        int result = 0;
        QuerySync(channel => result=channel.Add(i, j));
        return result;
    }
}
class Program
{
    static void Main(string[] args)
    {
        var lambdaChannelClient = new LambdaChannelClient();
        var result =lambdaChannelClient.Add(2, 3);
        Console.WriteLine("{0}+{1}={2}",2,3,result);
        Console.Read();
    }
}

实际跑一下,测试下我们的成果。

1、启动服务端。

2、启动客户端。

实验结束,测试完毕。

谢谢阅读。

 
 
 
标签: wcf

WCF服务属性注入基础设施的更多相关文章

  1. 使用NetTcpBinding,WCF服务未能被激活

    我的WCF采用的是NetTcpBinding,使用时就会报错,换成BasicHttpBinding,就一切正常 The requested service, 'net.tcp://wcf.xxxxx. ...

  2. autofac 注入普通服务和WCF服务

    using Autofac;using Autofac.Builder;using Autofac.Core; //实现Autofac扩展 public static AutofacRegisterW ...

  3. WCF服务中,[DataMember]属性标记的属性一定要有set访问器

    WCF服务中,如果实体类中,包含有[DataMember]属性标记时,该属性一定要有set访问器.当系统必须调用到[DataMember]标记的属性时,如果该属性没有set访问器,则会出错.

  4. net core天马行空系列: 一个接口多个实现类,利用mixin技术通过自定义服务名,实现精准属性注入

    系列目录 1.net core天马行空系列:原生DI+AOP实现spring boot注解式编程 2.net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作 哈哈哈哈,大家好,我 ...

  5. Entity Framework 6 Recipes 2nd Edition(9-7)译->在WCF服务中序列化代理

    9-7. 在WCF服务中序列化代理 问题 从一个查询里返回一个动态代理对象,想要把它序列为一个POCO(Plain-Old CLR Objects)对象. 实现基于POCO实体对象, 在运行时,EF会 ...

  6. WCF服务编程 读书笔记——第1章 WCF基础(1)

    第1章 WCF基础 本章主要介绍WCF的基本概念.构建模块以及WCF体系架构,以指导读者构建一个简单的WCF服务.从本章的内容中,我们可以了解到WCF的基本术语,包括地址(Address).绑定(Bi ...

  7. 翻译:使用 CoreWCF 升级 WCF 服务到 .NET 6

    翻译:使用 CoreWCF 升级 WCF 服务到 .NET 6 原文地址:https://devblogs.microsoft.com/dotnet/upgrading-a-wcf-service-t ...

  8. Autofac - 属性注入

    属性注入不同于通过构造函数方式传入参数. 这里是通过注入的方式, 在类创建完毕之后, 资源释放之前, 给属性赋值. 这里, 我重新弄一些类来演示这一篇吧. public class ClassA { ...

  9. WCF学习之旅—WCF服务部署到IIS7.5(九)

    上接   WCF学习之旅—WCF寄宿前的准备(八) 四.WCF服务部署到IIS7.5 我们把WCF寄宿在IIS之上,在IIS中宿主一个服务的主要优点是在发生客户端请求时宿主进程会被自动启动,并且你可以 ...

随机推荐

  1. 又一次拾起C语言的威严

    自从用了C++,他的方便快捷一直用着屡试不爽,可是越用越认为程序不够清晰, 项目使用DSP,不得不把C++重写成C 速度没得说,很快 记录下看到的文章 少走弯路,学好C语言的推荐途径

  2. C--运算符,表达式和语句实例

    //第五章 运算符,表达式和语句 #include<stdio.h> //引入头文件 #include<math.h> #define ADJUST 7.64 //定义常量 # ...

  3. vim插件管理器vundle

    安装:  git clone http://github.com/gmarik/vundle.git ~/.vim/bundle/vundle set nocompatible " be i ...

  4. 苹果新的编程语言 Swift 语言进阶(十二)--选项链

    选项链是使用选项来查询和调用其属性.方法或下标的一个过程,假设选项包括一个值,则属性.方法.下标的查询和调用成功,否则,调用返回nil. 选项链能用在不论什么类型的选项来检查对其一个属性.方法.下标的 ...

  5. java设计模式之五原型模式(Prototype)

    原型模式虽然是创建型的模式,但是与工程模式没有关系,从名字即可看出,该模式的思想就是将一个对象作为原型,对其进行复制.克隆,产生一个和原对象类似的新对象.本小结会通过对象的复制,进行讲解.在Java中 ...

  6. 查询DBlink创建

    DBlink创建 查询 博客分类: Oracle   当用户要跨本地数据库,访问另外一个数据库表中的数据时,本地数据库中必须创建了远程数据库的dblink,通过dblink本地数据库可以像访问本地数据 ...

  7. AngularJS html5Mode与ASP.NET MVC路由

    AngularJS html5Mode与ASP.NET MVC路由共存 前言 很久之前便听说AngularJS,非常酷,最近也比较火,我也在持续关注这个技术,只是没有认真投入学习.前不久公司找我们部门 ...

  8. Windows 7硬盘安装CentOS 6.4 双系统 (WIN7硬盘安装Linux(Fedora 16,CentOS 6.2,Ubuntu 12.04))

     WIN7下硬盘安装Linux(Fedora 16,CentOS 6.2.Ubuntu 12.04) 近期在看<鸟哥私房菜:基础学习篇>.认为非常不错,想要用U盘装个windows 7 和 ...

  9. Sql Server中如何快速修正SQL 语句错误

    本文将和大家讨论一些关于找SQL 错误的问题. 现在的系统基本都是需要用到数据库的,既然用到数据库我们就要写SQL 脚本,常用的做法是现在Microsoft Sql Server Management ...

  10. sql 事务日志传输

    原文:sql 事务日志传输 概述 可以使用日志传送将事务日志不间断地从一个数据库(主数据库)发送到另一个数据库(辅助数据库).不间断地备份主数据库中的事务日志,然后将它们复制并还原到辅助数据库,这将使 ...