zoukankan      html  css  js  c++  java
  • 将WCF寄宿在托管的Windows服务中

      在我之前的一篇博客中我介绍了如何发布WCF服务并将该服务寄宿于IIS上,今天我再来介绍一种方式,就是将WCF服务寄宿在Windows服务中,这样做有什么好处呢?当然可以省去部署IIS等一系列的问题,能够让部署更加简单,当然WCF的寄宿方式一般分为以下四种方式,针对每一种方式我来简单介绍以下:

      具体的寄宿方式详细信息请参考MSDN:https://msdn.microsoft.com/zh-cn/library/ms733109(v=vs.100).aspx

      一、WCF服务寄宿方式:

      1):寄宿在IIS上:与经典的webservice托管类似,把服务当成web项目,需要提供svc文件,其缺点是只能使用http协议,也就是说,只能使用单调服务,没有会话状态。IIS还受端口的限制(所有服务必须使用相同的端口),主要优势是:客户端第一次请求是自动启动宿主进程。 

      2):寄宿在WAS上(全称Windows激活服务):WAS 是一个新的进程激活服务,它是使用非 HTTP 传输协议的 Internet 信息服务 (IIS) 功能的一般化。WCF 使用侦听器适配器接口来传递通过 WCF 支持的非 HTTP 协定(例如,TCP、命名管道和消息队列)接收的激活请求。可托管网站,可托管服务,可使用任何协议,可以单独安装和配置,不依赖IIS。需要提供svc文件或在配置文件内提供等价的信息。 

      3):自承载:开发者提供和管理宿主进程生命周期的一种方法。可使用控制台程序,WinForm窗口程序,WPF程序提供宿主服务。可使用任意协议。必须先于客户端启动。可以实现WCF高级特性:服务总线,服务发现,单例服务。 

          4):寄宿在Windows服务上:此方案可通过托管 Windows 服务承载选项启用,此选项是在没有消息激活的安全环境中在 Internet 信息服务 (IIS) 外部承载的、长时间运行的 WCF 服务。服务的生存期改由操作系统控制。此宿主选项在 Windows 的所有版本中都是可用的。可以使用 Microsoft 管理控制台 (MMC) 中的 Microsoft.ManagementConsole.SnapIn 管理 Windows 服务,并且可以将其配置为在系统启动时自动启动。此承载选项包括注册承载 WCF 服务作为托管 Windows 服务的应用程序域,因此服务的进程生存期由 Windows 服务的服务控制管理器 (SCM) 来控制。

      这一篇主要用来介绍第四种即:WCF程序寄宿在托管的Windows服务中。

          1 新建一个WCF服务,并按照相关规则来建立一个完整的WCF程序。

          a:定义服务接口    

    // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IService1”。
        [ServiceContract]
        public interface IBasicService
        {
            [OperationContract]
            string Login(string username, string password, string version);
            [OperationContract]
            Users GetUserInfo(string userName);
    
            [OperationContract]
            bool SaveOption(string option_name, string option_value);
            [OperationContract]
            string GetOptionValue(string option_name);
            [OperationContract]
            bool SaveOptionByUser(string option_name, string option_value, int userid);
            [OperationContract]
            string GetOptionValueByUser(string option_name, int userid);
            [OperationContract]
            string TestSQLConnection();
        
        }
    

      b 实现接口(这里面的和数据库的交互方式为:Linq To Sql)  

        public class BasicService : IBasicService
        {
            #region 用户
            public string Login(string username, string password, string version)
            {
                try
                {
                    using (var db = new dbmls.BasicDataContext())
                    {
                        var entity = (from x in db.Users
                                      where x.Email == username && x.Password == password
                                      select x).SingleOrDefault() ?? null;
                        if (null == entity)
                        {
                            return "用户名或密码错误";
                        }
                        entity.LastLoginTime = DateTime.Now;
                        db.SubmitChanges();
                        Utils.LogUtil.WriteLog(Utils.LogUtil.LogTypes.User, "登录", version, entity.id);
                        return "";
                    }
                }
                catch (Exception ex)
                {
                    return ex.Message;
                }
            }
    
            public Users GetUserInfo(string userName)
            {
                try
                {
                    using (var db = new dbmls.BasicDataContext())
                    {
                        var entity = (from x in db.Users
                                      where x.Email == userName
                                      select x).SingleOrDefault() ?? null;
                        return entity;
                    }
                }
                catch
                {
                    return null;
                }
            }
    
            #endregion
    
    
            #region 数据存储
            public bool SaveOption(string option_name, string option_value)
            {
                try
                {
                    using (dbmls.BasicDataContext db = new dbmls.BasicDataContext())
                    {
                        dbmls.Options option = null;
                        option = (from x in db.Options
                                  where x.OptionName == option_name && x.UserID == 0
                                  select x).SingleOrDefault() ?? null;
                        if (null != option)
                        {
                            option.OptionValue = option_value;
                            option.UpdateTime = DateTime.Now;
                        }
                        else
                        {
                            option = new Options()
                            {
                                OptionName = option_name,
                                OptionValue = option_value,
                                UpdateTime = DateTime.Now,
                                CreateTime = DateTime.Now,
                                UserID = 0,
                            };
                            db.Options.InsertOnSubmit(option);
                        }
                        db.SubmitChanges();
                        return true;
                    }
                }
                catch (Exception ex)
                {
                    return false;
                }
            }
    
            public string GetOptionValue(string option_name)
            {
                try
                {
                    using (dbmls.BasicDataContext db = new dbmls.BasicDataContext())
                    {
                        dbmls.Options option = null;
                        option = (from x in db.Options
                                  where x.OptionName == option_name && x.UserID == 0
                                  select x).SingleOrDefault() ?? null;
                        if (null != option)
                        {
                            return option.OptionValue;
                        }
                    }
                    return "";
                }
                catch (Exception ex)
                {
                    return "";
                }
            }
    
    
            public bool SaveOptionByUser(string option_name, string option_value, int userid)
            {
                try
                {
                    using (dbmls.BasicDataContext db = new dbmls.BasicDataContext())
                    {
                        dbmls.Options option = null;
                        option = (from x in db.Options
                                  where x.OptionName == option_name && x.UserID == userid
                                  select x).SingleOrDefault() ?? null;
                        if (null != option)
                        {
                            option.OptionValue = option_value;
                            option.UpdateTime = DateTime.Now;
                        }
                        else
                        {
                            option = new Options()
                            {
                                OptionName = option_name,
                                OptionValue = option_value,
                                UpdateTime = DateTime.Now,
                                CreateTime = DateTime.Now,
                                UserID = userid
                            };
                            db.Options.InsertOnSubmit(option);
                        }
                        db.SubmitChanges();
                        return true;
                    }
                }
                catch (Exception ex)
                {
                    return false;
                }
            }
    
            public string GetOptionValueByUser(string option_name, int userid)
            {
                try
                {
                    using (dbmls.BasicDataContext db = new dbmls.BasicDataContext())
                    {
                        dbmls.Options option = null;
                        option = (from x in db.Options
                                  where x.OptionName == option_name && x.UserID == userid
                                  select x).SingleOrDefault() ?? null;
                        if (null != option)
                        {
                            return option.OptionValue;
                        }
                    }
                    return "";
                }
                catch (Exception ex)
                {
                    return "";
                }
            }
    
            public string TestSQLConnection()
            {
                dbmls.BasicDataContext db = new dbmls.BasicDataContext();
                try
                {
                    db.Connection.Open();
                    return "";
                }
                catch (Exception ex)
                {
                    return "无法连接SQL Server数据库
    " + ex.Message;
                }
                finally
                {
                    db.Dispose();
                }
            }
            #endregion
        }
    

      由于当前的程序是寄宿在Windows服务中,所以和数据库的交互方式配置在Windows服务中的App.config中,下面会逐一进行说明。

      2 新建一个Windows服务作为当前WCF程序的宿主。

      在我们的Windows服务中,我们写了一个继承自ServiceBase的类CoreService,并在Windows服务的静态Main函数中启动这个服务。  

           #region 开启服务
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]
                {
                    new CoreService()
                };
                ServiceBase.Run(ServicesToRun);
                #endregion
    

      在CoreService.cs中,我们通过重载OnStart和OnStop函数开启和关闭WCF服务。 

    ServiceHost host = new ServiceHost(typeof(Dvap.ServicesLib.BasicService));
    	protected override void OnStart(string[] args)
            {
                try
                {            
                    host.Open();              
                }
                catch (Exception ex)
                {
                     Utils.LoggerHelper.WriteLog(typeof(CoreService), ex);
                }
            } 
     
        	protected override void OnStop()
            {
                host.Close();         
            }
    

      3  配置当前的WCF服务,在当前的Windows服务的App.config配置下面的信息,这里需要着重说明的是,WCF程序Binding的方式有多种,可以是http方式也可以是net.tcp方式,这里我们采用后面的net.tcp具体的优势可以查阅相关资料。

     <connectionStrings>
        <add name="Dvap.ServicesLib.Properties.Settings.DvapConnectionString"
          connectionString="Data Source=LAPTOP-BFFCLBD1SQLEXPRESS;Initial Catalog=Dvap;User ID=sa;Password=XXXX"
          providerName="System.Data.SqlClient" />
      </connectionStrings>
      <system.serviceModel>
        <services>
          <service behaviorConfiguration="BasicServiceBehavior"
            name="Dvap.ServicesLib.BasicService">
            <endpoint address="" binding="netTcpBinding" bindingConfiguration=""
              contract="Dvap.ServicesLib.Interfaces.IBasicService">
              <identity>
                <dns value="127.0.0.1" />
              </identity>
            </endpoint>
            <endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""
              contract="IMetadataExchange" />
            <host>
              <baseAddresses>
                <add baseAddress="net.tcp://127.0.0.1:9000/BasicService.svc"/>
              </baseAddresses>
            </host>
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="BasicServiceBehavior">
              <serviceMetadata httpGetEnabled="false" />
              <serviceDebug includeExceptionDetailInFaults="false" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
        <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" minFreeMemoryPercentageToActivateService="0" />
        <bindings>
          <netTcpBinding>
            <binding name="defaultBinding" maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647">
              <security mode="None">
                <message clientCredentialType="None"/>
                <transport clientCredentialType="None"></transport>
              </security>
              <readerQuotas />
            </binding>
          </netTcpBinding>
        </bindings>
      </system.serviceModel>
    

      4 安装部署Window服务。

      这样就完成了我们的基本需求,另外就是安装和部署Windows服务,这里都是一些常规的操作,首先我们来看一看生成的文件。

    图一 Windows服务安装文件

      安装Windows服务,在我们的生成文件目录下,我们最好写一个安装的bat文件,然后直接运行就可以安装和卸载Windows服务。

    @echo off
    set /p var=是否要安装可视化数据交换服务(Y/N):
    if "%var%" == "y" (goto install) else if "%var%" == "Y" (goto install) else (goto batexit)
    
    :install
    copy C:WindowsMicrosoft.NETFrameworkv4.0.30319InstallUtil.exe  InstallUtil.exe /Y
    call InstallUtil.exe Dvap数据通信服务.exe
    sc start 可视化数据交换服务
    pause
    
    :batexit
    exit
    
    //卸载
    @echo off
    set /p var=是否要卸载可视化数据交换服务(Y/N):
    if "%var%" == "y" (goto uninstall) else (goto batexit)
    
    :uninstall
    copy C:WindowsMicrosoft.NETFrameworkv4.0.30319InstallUtil.exe  InstallUtil.exe /Y
    call InstallUtil.exe /u Dvap数据通信服务.exe
    pause
    
    :batexit
    exit
    

      当然关于Windows的安装文件的一些配置,可以参考下面的内容,这里不再进行赘述,主要都是配置一下属性的值,下面的介绍仅供参考。

      serviceProcessInstaller1控件

      ServiceProcessInstall安装一个可执行文件,该文件包含扩展 ServiceBase 的类。该类由安装实用工具(如 InstallUtil.exe)在安装服务应用程序时调用。

      在这里主要是修改其Account属性。ServiceAccount指定服务的安全上下文,安全上下文定义其登录类型,说白了..就是调整这个系统服务的归属者..如果你想只有某一个系统用户才可以使用这个服务..那你就用User..并且制定用户名和密码。

      具体各参数定义:

      LocalService:充当本地计算机上非特权用户的帐户,该帐户将匿名凭据提供给所有远程服务器。 

      LocalSystem:服务控制管理员使用的帐户,它具有本地计算机上的许多权限并作为网络上的计算机。 

      NetworkService:提供广泛的本地特权的帐户,该帐户将计算机的凭据提供给所有远程服务器。 
      User:由网络上特定的用户定义的帐户。如果为 ServiceProcessInstaller.Account 成员指定 User,则会使系统在安装服务时提示输入有效的用户名和密码,除非您为 ServiceProcessInstaller 实例的 Username 和 Password 这两个属性设置值。  

      serviceInstaller1控件

      ServiceInstaller安装一个类,该类扩展 ServiceBase 来实现服务。在安装服务应用程序时由安装实用工具调用该类。

      具体参数含义

      在这里主要修改其StartType属性。此值指定了服务的启动模式。

      Automatic 指示服务在系统启动时将由(或已由)操作系统启动。如果某个自动启动的服务依赖于某个手动启动的服务,则手动启动的服务也会在系统启动时自动启动。 
      Disabled 指示禁用该服务,以便它无法由用户或应用程序启动。 
      Manual 指示服务只由用户(使用“服务控制管理器”)或应用程序手动启动。  

    还有一些其他的一些属性需要进行配置:

      ServiceName  服务在服务列表里的名字

      Description    服务在服务列表里的描述

      DisplayName  向用户标示的友好名称..没搞懂..一般都跟上边的ServiceName保持一致..

      5 查看当前的WCF服务。

          首先我们来查看我们部署好的Windows服务。

    图二 发布好的Windows服务

      6 引用当前的WCF服务  

          最后贴出相关代码,请点击这里进行下载!

      

  • 相关阅读:
    Spark:大数据的“电光石火”
    Android开发-取消程序标题栏或自定义标题栏
    Android中实现圆角矩形及半透明效果。
    Android中设定背景图片平铺。
    收到的电邮附件为Winmail.dat?
    Runas命令:能让域用户/普通User用户以管理员身份运行指定程序。
    AD域服务器|两台DC无法进行复制同步
    IIS服务器运行一段时间后卡死,且无法打开网站(IIS管理无响应,必须重启电脑)
    Outlook不能打开附件(提示:无法创建文件xx,请右键单击要在其中创建文件的文件夹..)
    点击自动显示/隐藏DIV代码。(简单实用)
  • 原文地址:https://www.cnblogs.com/seekdream/p/6821348.html
Copyright © 2011-2022 走看看