前言

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. node.js小结 2

    下载node安装npm什么的就不说了 入门总结 http://www.cnblogs.com/Darren_code/archive/2011/10/31/nodejs.html 进入node_HOM ...

  2. CGFloat Float 互转

    直接上代码吧 var positionX:CGFloat = 10 var positionY:CGFloat = 20 var tmpX:Float = 30 var tmpY:Float = 40 ...

  3. UIImage NSData 相互转化

    //UIImage 转为 NSData NSData *imageData = UIImagePNGRepresentation(aImage); //NSData 转为 UIImage UIImag ...

  4. Kafka入门初探+伪集群部署

    Kafka是目前非常流行的消息队列中间件,常用于做普通的消息队列.网站的活性数据分析(PV.流量.点击量等).日志的搜集(对接大数据存储引擎做离线分析). 全部内容来自网络,可信度有待考证!如有问题, ...

  5. fir.im Weekly - iOS 保持界面流畅的技巧

    生命不息,coding 不止.本期 fir.im Weekly 收集了微博上的热转资源,包含 Android.iOS 开发工具.源码分享,产品 UI 设计的好文章,还有一些程序员成长的 Tips,希望 ...

  6. Asp.net WebApi 项目示例(增删改查)

    1.WebApi是什么 ASP.NET Web API 是一种框架,用于轻松构建可以由多种客户端(包括浏览器和移动设备)访问的 HTTP 服务.ASP.NET Web API 是一种用于在 .NET ...

  7. ASP.NET MVC 异常Exception拦截器Fillter

    异常信息的处理在程序中非常重要, 在asp.net mvc中提供异常属性拦截器进行对异常信息的处理,异常拦截器也没有什么的,只是写一个类,继承另一个类(System.Web.Mvc.FilterAtt ...

  8. 过滤器中的chain.doFilter(request,response)

    Servlet中的过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码.做一些业务逻辑判断等.其工作原理是,只要你在web.xml文件配置好要 ...

  9. 加载的过程中图片变形了? --教你自定义自动适配图片宽高比的RatioLayout

    很多同行在开发中可能会遇到这样的问题,就是在加载图片的时候会出现图片变形的问题.其实这很可能就是你的图片宽高比和图片所在容器的宽高比不匹配造成的.比如说图片的宽为200,高为100.宽高比就是2,那么 ...

  10. JavaScript起点(严格模式深度了解)

    格模式(Strict Mode)是ECMAScript5新增的功能,目前所有的主流浏览器的最新版本——包括IE10与Opera12——都支持严格模式,感兴趣的朋友可以了解下啊,希望本文对你有所帮助 严 ...