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,这样的文章园子里面有很多,这里就不说明了。
Topshelf + ServiceModelEx + Nlog 从头构建WCF的更多相关文章
- WCF:为 SharePoint 2010 Business Connectivity Services 构建 WCF Web 服务(第 1 部分,共 4 部分)
转:http://msdn.microsoft.com/zh-cn/library/gg318615.aspx 摘要:通过此系列文章(共四部分)了解如何在 Microsoft SharePoint F ...
- 从头构建自己的Linux系统
2012-09-10 在博文“Linux系统启动过程分析”中我们了解了linux系统的启动流程,今天我们就来手动一步一步从头来构建一个最小的linux系统,然后用模拟器将其加载起来.常见 ...
- 使用Visual Studio 2013 从头构建Web表单
在这篇文章中,我将采取VS 2013中特定的模板,也就是没有身份验证的Web表单模板,并说明如何构建这个项目从头开始.在本教程的最后,你会最终有一个模板,内容几乎是一样的使用Web表单模板没有认证(文 ...
- 如何一秒钟从头构建一个 ASP.NET Core 中间件
前言 其实地上本没有路,走的人多了,也便成了路. -- 鲁迅 就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最 ...
- 使用Topshelf 5步创建Windows 服务 z
使用Topshelf创建Windows 服务简要的介绍了创建Windows服务的另一种方法,老外的一篇文章Create a .NET Windows Service in 5 steps with T ...
- WCF服务承载(笔记)
自托管(也做自承载) 承载 WCF 服务最灵活.最便捷的方法就是进行自承载.要能够自承载服务,必须满足两个条件.第一,需要 WCF 运行时:第二,需要可以承载 ServiceHost 的托管 .NET ...
- WCF学习资料汇总
微软官方讲解教程: 跟我一起从零开始学WCF系列课程 http://msdnwebcast.net/webcast/1/2692/ 构建WCF面向服务的应用程序系列课程 http://msdnwebc ...
- 三十一、【WCF路由中间件】WCFHosting服务主机的路由器与负载均衡和实现思路
回<[开源]EFW框架系列文章索引> EFW框架源代码下载V1.3:http://pan.baidu.com/s/1c0dADO0 EFW框架实例源代码下载:http://pan.baid ...
- 深入浅出 React Native:使用 JavaScript 构建原生应用
深入浅出 React Native:使用 JavaScript 构建原生应用 链接:https://zhuanlan.zhihu.com/p/19996445 原文:Introducing React ...
随机推荐
- Azure China (9) 在Azure China配置CDN服务
<Windows Azure Platform 系列文章目录> 本文介绍的是国内由世纪互联运维的Azure China Update 2015-11-20:Azure China CDN服 ...
- 翻译:AKKA笔记 - 介绍Actors
任何以前做过多线程的人都不会否认管理多线程程序是困难并且痛苦的. 我说管理是因为它开始很容易而且当你看到性能提升时会很兴奋.但是,当你看到你没法从子线程的错误中恢复 或者 这些僵尸bug很难重现 或者 ...
- Springlake-02 权限&文档设置&Role设置&Folder设置&登录
1. 权限 有3个默认的权限用户: 1.System Owner so 管理员权限全部:Type Setup; Group Setup; Form Setup; Role Setup; Share R ...
- EXCEL的导出
具体实现代码: protected void EXCEL_Click(object sender, EventArgs e) { ToExcel(GridView1); } public void T ...
- KnockoutJS 3.X API 第四章 表单绑定(10) textInput、hasFocus、checked绑定
textInput绑定目的 textInput绑定主要用于<input>或者<textarea>元素.他提供了DOM和viewmodel的双向更新.不同于value绑定,tex ...
- Git Sophisticated Commands
Compare two branches: branch_1 and branch_2: git diff branch_1…branch_2 Merge specified files of one ...
- MVVM模式下,ViewModel和View,Model有什么区别
摘自正美的5群 Model:很简单,就是业务逻辑相关的数据对象,通常从数据库映射而来,我们可以说是与数据库对应的model. View:也很简单,就是展现出来的用户界面. 基本上,绝大多数软件所做的工 ...
- 手动为php安装memcached扩展模块
最近公司需要新部署几台服务器,主要就是lnmp平台,这几台服务器需要部署公司的系统,由于本屌刚入职时间不长,加上又是新手,所以对公司的架构一头雾水,前前后后折腾了一个月时间,终于磕磕绊绊的将系统服务器 ...
- (4) PIVOT 和 UPIVOT 的使用
最近项目中用到了行转列,使用SQL SERVER 提供的PIVOT实现起来非常容易. 官方解释:详见这里 可以使用 PIVOT 和 UNPIVOT 关系运算符将表值表达式更改为另一个表. PIVOT ...
- timus_1007_bfs
图像编码 题目描述: 有这样一副图,它有黑白像素,黑像素的坐标在1-10之间.有很多种方法来编码这个图.例如下面的图: 一种表示方法是只描述黑像素,并按x坐标的增序描述,如果x相同,则按y的增序描述, ...