前言

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的更多相关文章

  1. WCF:为 SharePoint 2010 Business Connectivity Services 构建 WCF Web 服务(第 1 部分,共 4 部分)

    转:http://msdn.microsoft.com/zh-cn/library/gg318615.aspx 摘要:通过此系列文章(共四部分)了解如何在 Microsoft SharePoint F ...

  2. 从头构建自己的Linux系统

    2012-09-10        在博文“Linux系统启动过程分析”中我们了解了linux系统的启动流程,今天我们就来手动一步一步从头来构建一个最小的linux系统,然后用模拟器将其加载起来.常见 ...

  3. 使用Visual Studio 2013 从头构建Web表单

    在这篇文章中,我将采取VS 2013中特定的模板,也就是没有身份验证的Web表单模板,并说明如何构建这个项目从头开始.在本教程的最后,你会最终有一个模板,内容几乎是一样的使用Web表单模板没有认证(文 ...

  4. 如何一秒钟从头构建一个 ASP.NET Core 中间件

    前言 其实地上本没有路,走的人多了,也便成了路. -- 鲁迅 就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最 ...

  5. 使用Topshelf 5步创建Windows 服务 z

    使用Topshelf创建Windows 服务简要的介绍了创建Windows服务的另一种方法,老外的一篇文章Create a .NET Windows Service in 5 steps with T ...

  6. WCF服务承载(笔记)

    自托管(也做自承载) 承载 WCF 服务最灵活.最便捷的方法就是进行自承载.要能够自承载服务,必须满足两个条件.第一,需要 WCF 运行时:第二,需要可以承载 ServiceHost 的托管 .NET ...

  7. WCF学习资料汇总

    微软官方讲解教程: 跟我一起从零开始学WCF系列课程 http://msdnwebcast.net/webcast/1/2692/ 构建WCF面向服务的应用程序系列课程 http://msdnwebc ...

  8. 三十一、【WCF路由中间件】WCFHosting服务主机的路由器与负载均衡和实现思路

    回<[开源]EFW框架系列文章索引> EFW框架源代码下载V1.3:http://pan.baidu.com/s/1c0dADO0 EFW框架实例源代码下载:http://pan.baid ...

  9. 深入浅出 React Native:使用 JavaScript 构建原生应用

    深入浅出 React Native:使用 JavaScript 构建原生应用 链接:https://zhuanlan.zhihu.com/p/19996445 原文:Introducing React ...

随机推荐

  1. 自制Unity小游戏TankHero-2D(5)声音+爆炸+场景切换+武器弹药

    自制Unity小游戏TankHero-2D(5)声音+爆炸+场景切换+武器弹药 我在做这样一个坦克游戏,是仿照(http://game.kid.qq.com/a/20140221/028931.htm ...

  2. Senparc.Weixin.MP SDK 微信公众平台开发教程(四):Hello World

    =============  以下写于2013-07-20 ============= 这一篇文章其实可以写在很前面,不过我还是希望开发者们尽多地了解清楚原理之后再下手. 通过上一篇Senparc.W ...

  3. [CSS]复选框单选框与文字对齐问题的研究与解决.

    前言:今天碰到的这个问题, 恰好找到一个很好的博文, 在这里转载过来 学习下. 原文地址:复选框单选框与文字对齐问题的研究与解决. 目前中文网站上面的文字,就我的个人感觉而言,绝大多数网站的主流文字大 ...

  4. Mongodb 的基本使用

    一.cmd连接mongodb 服务 进入mongodb的bin目录下:[D:\mongodb3.2.5\bin]$ mongo 127.0.0.1:27017 常用查询: show dbs 查看所有数 ...

  5. vuejs与服务器通信

    vuejs与服务器通信 与服务器通信 Vue 实例的原始数据 $data 能直接用 JSON.stringify() 序列化.社区贡献了一个插件 vue-resource,提供一种容易的方式与 RES ...

  6. initial、inherit、unset、revert和all

    前面的话 在CSS中,有4个关键字理论上可以应用于任何的CSS属性,它们是initial(初始).inherit(继承).unset(未设置).revert(还原).而all的取值只能是以上这4个关键 ...

  7. URL中的特殊字符

    原网址:http://pichcar.iteye.com/blog/676292 URL中的特殊字符 有些符号在URL中是不能直接传递的,如果要在URL中传递这些特殊符号,那么就要使用他们的编码了.编 ...

  8. 推荐10个 CSS3 制作的创意下拉菜单效果

    下拉菜单是一个很常见的效果,在网站设计中被广泛使用.通过使用下拉菜单,设计者不仅可以在网站设计中营造出色的视觉吸引力,但也可以为网站提供了一个有效的导航方案.使用 HTML5 和 CSS3 可以更容易 ...

  9. vs2015使用GIt连接git.oschina.net/

    本文转自:http://www.bubuko.com/infodetail-1066588.html.谢谢作者 先安装Git命令行,下载地址:https://github.com/git-for-wi ...

  10. window、document、html、body、element的事件属性比较

    在分析jQuery的事件的时候有提到绑定事件的方式: Dean Edwards的跨浏览器事件绑定使用的方式是 element["on" + type] = handleEvent; ...