zoukankan      html  css  js  c++  java
  • Topshelf + ServiceModelEx + Nlog 从头构建WCF

    前言

    Topshelf可以很方便的构建windows service,而且在本地开发时也可以构建Console宿主,因此很方便WCF的开发。

    ServiceModelEx则提供了很多便利的方法来配置wcf的behavior。

    Nlog是.NET中记录日志类库和log4net提供的功能一样。

    构建solution

    好了,现在开始从头构建解决方案:

    以上的Host和Client为Console,Contract和Service为class librariy。

    构建Contract

    Contract里面定义的是wcf对外提供之服务,这里将其分为一下三个部分:

    • 请求Request
    • 回复Response
    • 行为Action

    其中的Request和Response可以看作为Data Transfer Object也就是我们说的DTO,这里就将其放置与DTO的文件夹下面。

    现在构建Request:

    [DataContract]
    public class JulyLuoRequest
    {
        [DataMember]
        public string Greeting { get; set; }
    
        [DataMember]
        public string Name { get; set; }
    }
    

     构建Response:

    [DataContract]
    public class JulyLuoResponse
    {
        [DataMember]
        public string Greeting { get; set; }
    
        [DataMember]
        public string ClientName { get; set; }
    
        [DataMember]
        public string ServiceName { get; set; }
    }
    

     最后构建我们的接口:

    [ServiceContract]
    public interface IJulyLuoIntroduce
    {
    [OperationContract] JulyLuoResponse Introduce(JulyLuoRequest request); }

     整个的Contract工程如下:

     构建Service

    Service是最终实现接口的地方,因此其需要引用Contract project,这里就简单的实现:

    public class JulyLuoIntroduce : IJulyLuoIntroduce
    {
        public JulyLuoResponse Introduce(JulyLuoRequest request)
        {
            return new JulyLuoResponse()
            {
                Greeting = request.Greeting,
                ClientName = request.Name,
                ServiceName = "JulyLuo"
            };
        }
    }
    

     构建Host

    Host需要引用以上的Contract和Service工程。

    Host这里我们就需要TopShelf和Nlog的第三方类库,可以在NuGet上获取:

     最后的引用如下:

    Topshelf的最新版本网上提到不支持.NET 4.0, .NET4.5,因此用Nuget的时候可能不成功,解决办法就是使用低版本的Topshelf,或者从Topshelf官网下载对应的dll直接引用。

    ServiceModeEx的类库在Nuget上获取不到,大家可以在网上下载自己再添加引用。

    现在构建一个WcfHost类封装wcf提供的服务:

    public class WcfHost
    {
        private ServiceHost<JulyLuoIntroduce> _service;
    
        internal WcfHost()
        {
            _service = new ServiceHost<JulyLuoIntroduce>(new Uri[] { });
        }
    
        public void Start()
        {
            _service.Open();
        }
    
        public void Stop()
        {
            try
            {
                if (_service != null)
                {
                    if (_service.State == CommunicationState.Opened)
                    {
                        _service.Close();
                    }
                }
    
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
    

     最后通过TopShelf 配置Nlog,并宿主刚刚定义的WcfHost:

    class Program
    {
        private static Logger logger = LogManager.GetLogger("JulyLuo.Host");
    
        public static readonly LogFactory Instance = new LogFactory(new XmlLoggingConfiguration(GetNLogConfigFilePath()));
    
        private static string GetNLogConfigFilePath()
        {
            return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "NLog.config");
        }
    
        static void Main(string[] args)
        {
            try
            {
                const string name = "JulyLuo-Service";
                const string description = "JulyLuo-Introduce";
                var host = HostFactory.New(configuration =>
                {
                    configuration.UseNLog(Instance);
    
                    configuration.Service<WcfHost>(callback =>
                    {
                        callback.ConstructUsing(s => new WcfHost());
                        callback.WhenStarted(service => service.Start());
                        callback.WhenStopped(service => service.Stop());
                    });
                    configuration.SetDisplayName(name);
                    configuration.SetServiceName(name);
                    configuration.SetDescription(description);
                    configuration.RunAsLocalService();
                });
                host.Run();
            }
            catch (Exception ex)
            {
                logger.Error("Pdf Generator Service fatal exception. " + ex.Message);
            }
    
        }
    }
    

     配置Config

    现在要配置Nlog的配置文件,以及Host控制台的wcf配置。

     Nlog这里需要配置两个target,一个是作为Console时Nlog写入Console,一个是作为windows service是Nlog写入本地的文件

    <target xsi:type="Console" layout="${longdate}[${level}]${message}" name="Console"/>
    
    <target name="TopShelfCSV" xsi:type="File" fileName="${basedir}/Logs/TopShelf-${shortdate}.csv"
            archiveFileName="${basedir}/Archive/TopShelf-{#}.csv"
            archiveNumbering="Date"
            archiveEvery="Day"
            maxArchiveFiles="7"
            archiveDateFormat="yyyy-MM-dd" >
      <layout xsi:type="CsvLayout">
        <column name="time" layout="${longdate}" />
        <column name="message" layout="${message} ${exception:format=tostring}" />
        <column name="logger" layout="${logger}"/>
        <column name="level" layout="${level}"/>
      </layout>
    </target>
    

     Nlog的rule配置在Console时配置如下:

    <logger name="*" minlevel="Debug" writeTo="Console" />
    

     在widows service的配置如下:

    <logger name="*" minlevel="Debug" writeTo="TopShelfCSV" />
    

     现在在Host控制台下添加app.config文件,并配置wcf service节点:

    <system.serviceModel>
      <services>
        <service name="JulyLuo.Service.JulyLuoIntroduce" behaviorConfiguration="mexServiceBehavior">
          <host>
            <baseAddresses>
              <add baseAddress="net.tcp://localhost:50129/PdfGenerator" />
            </baseAddresses>
          </host>
          <endpoint address="" binding="netTcpBinding" contract="JulyLuo.Contract.IJulyLuoIntroduce" />
          <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
        </service>
      </services>
      <behaviors>
        <serviceBehaviors>
          <behavior name="mexServiceBehavior">
            <serviceMetadata />
          </behavior>
        </serviceBehaviors>
      </behaviors>
    </system.serviceModel>
    

     这里的wcf用的是Tcp的绑定,要主要就是service中name和 endpoint中的contract,其对应的就是以上创建的service和contract的名称。

    一切设置完毕之后,设置Host为启动项目,现在可以直接运行,因为传入的参数为空,topshelf将设置为Console,界面如下:

     构建Client端

     这里的Client端只需要应用Contract工程即可,引用添加之后新创建一个类封装调用wcf:

    public class WcfProxy<TContract> : IDisposable
        where TContract : class
    {
        public TContract Service { get; private set; }
    
        public WcfProxy()
        {
            try
            {
                var factory = new ChannelFactory<TContract>(typeof(TContract).Name + "_Endpoint");
                factory.Open();
                Service = factory.CreateChannel();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Could not create proxy: {0}", ex.Message);
                Service = null;
            }
        }
    
        public void Dispose()
        {
            if (Service != null)
            {
                var internalProxy = Service as ICommunicationObject;
    
                try
                {
                    if (internalProxy != null)
                    {
                        if (internalProxy.State != CommunicationState.Closed && internalProxy.State != CommunicationState.Faulted)
                            internalProxy.Close();
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Could not close proxy: {0}", ex.Message);
                    try
                    {
                        if (internalProxy != null)
                            internalProxy.Abort();
                    }
                    catch (Exception exInternal)
                    {
                        Console.WriteLine("Could not abort proxy: {0}", exInternal.Message);
                    }
                }
    
                if (internalProxy is IDisposable)
                {
                    try
                    {
                        if (internalProxy.State != CommunicationState.Faulted)
                            (internalProxy as IDisposable).Dispose();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Could not dispose proxy: ", ex.Message);
                    }
                }
            }
        }
    }
    

     因为是调用wcf,这里的Client端也需要添加app.config并设置如下:

    <system.serviceModel>
      <client>
        <endpoint address="net.tcp://localhost:50129/Introduce"
            binding="netTcpBinding"
            contract="JulyLuo.Contract.IJulyLuoIntroduce"
            name="IJulyLuoIntroduce_Endpoint">
        </endpoint>
      </client>
    </system.serviceModel>
    

     所有的准备完毕之后,我们就可以在Client端开始编写代码调用wcf:

    Console.WriteLine("Press enter to send the introduction request");
    Console.ReadLine();
    using (var proxy = new WcfProxy<IJulyLuoIntroduce>())
    {
        Console.ForegroundColor = ConsoleColor.Blue;
        var request = new JulyLuo.Contract.DTO.JulyLuoRequest
        {
            Greeting = "Hello",
            Name = "world"
        };
        Console.WriteLine("Sending: {0}", request);
    
        Console.ForegroundColor = ConsoleColor.Green;
        var response = proxy.Service.Introduce(request);
        Console.WriteLine("Received: {0} {1} {2}", response.Greeting, response.ClientName, response.ServiceName);
    }
    Console.ForegroundColor = ConsoleColor.White;
    Console.WriteLine("Press enter to exit");
    Console.ReadLine();
    

     然后设置整个solution将client和host 工程都设置为启动项目:

    最后的运行结果如下:

    总结

    通过以上的步骤,我们成功的整合了几个类库来开发wcf,topshelf还可以宿主为windows service,这样的文章园子里面有很多,这里就不说明了。

  • 相关阅读:
    《计算机网络 自顶向下方法》 第1章 计算机网络和因特网
    记一次代码优化
    不要刻意寻求局部最优解
    Eclipse Jetty插件安装
    Jetty的工作原理
    log4g 使用教程
    有用资料的网址
    Java 编程 订单、支付、退款、发货、退货等编号主动生成类
    Spring框架
    Eclipse常用快捷键大全1
  • 原文地址:https://www.cnblogs.com/julyluo/p/6217150.html
Copyright © 2011-2022 走看看