将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数据库\r" + 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-BFFCLBD1\SQLEXPRESS;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:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.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:\Windows\Microsoft.NET\Framework\v4.0.30319\InstallUtil.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服务 
最后贴出相关代码,请点击这里进行下载!
将WCF寄宿在托管的Windows服务中的更多相关文章
- windows 服务中托管asp.net core
在windows 服务中托管asp.net core SDK 2.1.300 官方示例 1.添加运行标识符 xml <PropertyGroup> <TargetFramework& ...
- asp.net core 托管到windows服务,并用iis做反向代理
使用NSSM把.Net Core部署至 Windows 服务 为什么部署至Windows Services 在很多情况下,很少会把.Net Core项目部署至Windows服务中,特别是Asp.n ...
- 解决vista和win7在windows服务中交互桌面权限问题:穿透Session 0 隔离
在某国外大型汽车公司BI项目中,有一个子项目,需要通过大屏幕展示销售报表,程序需要自动启动和关闭.开发人员在开发过程中,发现在Win7的service中不能直接操作UI进程,调查过程中,发现如 ...
- c# 在windows服务中 使用定时器
由于最近做自动执行的程序,开始做windows服务程序, 在windows服务中如何使用定时器的时候一直失效, 以前是直接拖入timer控件,但是不能直接运行,后来在网上找了一段程序,好使了. //开 ...
- 添加 MySql 服务、Tomcat服务到windows服务中
添加 MySql 服务到windows服务中: cmd --> F:\MySql\MySqlServer5.1\bin\mysqld --install 这样用默认的 MySQL 为名称添加一个 ...
- 在windows服务中使用定时器
在windows服务中,利用winform中直接拖动timer控件的方式使用定时器是不可以的,启动服务后会发现定时器并没有执行.那么在windows服务中如何使用定时器呢? 不使用直接拖动控件的方式 ...
- 创建寄宿在Windows服务中的WCF服务
1.创建Windows服务项目 2.Server1改名为你想要的名称,比如WinServer 3.在项目中新建一个WCF文件夹,用于存放wcf服务文件. 注:在WcfServer类的上面还要添加 [S ...
- 在 Windows 服务中托管 ASP.NET Core
众所周知,ASP.NET Core采用了和传统ASP.NET不同的托管和HTTP处理方式,即把服务器和托管环境完全解耦.ASP.NET Core内置了两个HTTP服务器实现,一个是基于libuv实现的 ...
- ASP.NET Core Web程序托管到Windows 服务
前言 在 .NET Core 3.1和WorkerServices构建Windows服务 我们也看到了,如何将workerservices构建成服务,那么本篇文章我们再来看看如何将web应用程序托管到 ...
随机推荐
- P1678 烦恼的高考志愿(二分)
emmmm,我感觉我在解题的过程中还是有点吃亏的,因为,我知道是二分,只是大概知道怎么分,没有管这道到底是需要怎样的二分.然后在题上卡了很久. 思路:要找到填报学校的录取线x和自己的分数y的绝对值最小 ...
- 【css】max-height,min-height,height一起使用时,优先级问题
MDN说法: max-height 这个属性会阻止 height 属性的设置值变得比 max-height 更大. max-height 属性用来设置给定元素的最大高度. 如果height 属性设置的 ...
- hyperledge工具-configtxgen
参考http://www.blockchainbrother.com/article/1339 configtxgen是Hyperledger Fabric提供的用于通道配置的实用程序,主要生成以下3 ...
- 在使用 Spring Boot 和 MyBatis 动态切换数据源时遇到的问题以及解决方法
相关项目地址:https://github.com/helloworlde/SpringBoot-DynamicDataSource 1. org.apache.ibatis.binding.Bind ...
- P2251 质量检测--洛谷luogu
传送门 题目描述 为了检测生产流水线上总共N件产品的质量,我们首先给每一件产品打一个分数A表示其品质,然后统计前M件产品中质量最差的产品的分值Q[m] = min{A1, A2, ... Am},以及 ...
- PAT A1095 Cars on Campus (30 分)——排序,时序,从头遍历会超时
Zhejiang University has 8 campuses and a lot of gates. From each gate we can collect the in/out time ...
- 【C#复习总结】细说匿名方法
1 前言 本系列会将[委托] [匿名方法][Lambda表达式] [泛型委托] [表达式树] [事件]等基础知识总结一下.(本人小白一枚,有错误的地方希望大佬指正) 系类1:细说委托 系类2:细说匿名 ...
- 分享一个公众号h5裂变吸粉源码工具
这次我是分享我本人制作的一个恶搞程序,说白了就是一个公众号裂变吸粉工具,市面上有很多引流方法,例如最常见的就是色流,哈哈,今天我跟大家分享的方法是有趣的,好玩的,恶搞的.这个程序上线一天已经收获了61 ...
- [C#] LINQ之Join与GroupJoin
声明:本文为www.cnc6.cn原创,转载时请注明出处,谢谢! 一.编写Person与City类,如下: class Person { public int CityID { set; get; } ...
- java OOM还在看log日志,兄弟你错的的很严重,正确方式是分析dump文件
目录 OOM异常--intsmaze 正确姿势dump文件分析--intsmaze 正确的姿势--intsmaze dump丢失打印--intsmaze 哪些内存溢出会产生dump文件--intsma ...