前言

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. 302 Moved Temporarily

    这个就是表示 重定向!! 不过,302在不同HTTP协议下的状态信息不同. Moved temporarily (redirect) 你所连接的页面进行了Redirect Found 类似于301,但 ...

  2. 鸟哥的Linux私房菜——基础学习篇 —— 笔记2

    at 语法 == 注意,输入at之后便进入命令行模式 ------- 不管怎么样,只会执行一次. [test @test test]# at [-m] TIME (输入工作指令)[test @test ...

  3. Java-字符串练习

    1. 用自己的算法实现startsWith和endsWith功能. String str="dsjhajdl"; Scanner sc=new Scanner(System.in) ...

  4. EF架构~为BulkInsert引入SET IDENTITY_INSERT ON功能

    回到目录 在设计表结构时,我们通常将不是很在乎表现的主键设计成自增长的,大数据量用bigint,一般地用int就可以了,int就是C#里的Int32,它最大可以存储到2147483647,21亿,基本 ...

  5. offsetTop,offsetHeight,clientHeight,scrollHeight,scrollTop区别

    这些高度相信很多同学都搞不清楚吧.这里我通过本地测试,发现了区别. 以聊天窗口为例. 元素(class='content')高度444px,其中上下padding分别是10px,margin为0.距离 ...

  6. CAR

    24.编写一个Car类,具有String类型的属性品牌,具有功能drive: 定义其子类Aodi和Benchi,具有属性:价格.型号:具有功能:变速: 定义主类E,在其main方法中分别创建Aodi和 ...

  7. 每天一个linux命令(29):chgrp命令

    在lunix系统里,文件或目录的权限的掌控以拥有者及所诉群组来管理.可以使用chgrp指令取变更文件与目录所属群组,这种方式采用群组名称或群组识别码都可以.Chgrp命令就是change group的 ...

  8. Java EE开发平台随手记5——Mybatis动态代理接口方式的原生用法

    为了说明后续的Mybatis扩展,插播一篇广告,先来简要说明一下Mybatis的一种原生用法,不过先声明:下面说的只是Mybatis的其中一种用法,如需要更深入了解Mybatis,请参考官方文档,或者 ...

  9. Html与CSS快速入门02-HTML基础应用

    这部分是html细节知识的学习. 快速入门系列--HTML-01简介 快速入门系列--HTML-02基础元素 快速入门系列--HTML-03高级元素和布局 快速入门系列--HTML-04进阶概念 示例 ...

  10. js阻止冒泡及jquery阻止事件冒泡示例介绍

    js阻止冒泡 在阻止冒泡的过程中,W3C和IE采用的不同的方法,那么我们必须做以下兼容. 复制代码 代码如下: function stopPro(evt){ var e = evt || window ...