前言:前面三篇分享了下DDD里面的两个主要特性:聚合和仓储。领域层的搭建基本完成,当然还涉及到领域事件和领域服务的部分,后面再项目搭建的过程中慢慢引入,博主的思路是先将整个架构走通,然后一步一步来添加相关元素,使架构慢慢变得丰满。这篇打算分享下应用层的搭建。根据DDD的设计原则,应用层不包含任何领域逻辑,它主要的作用是协调任务,或者叫调度任务,维护应用程序状态。根据博主的理解,应用层是用来隔离领域层的,假设没有应用层,那么我们的界面层可以直接调用领域层的逻辑,也就是说可以直接访问领域的model,这样的坏处显而易见:一是领域model不是纯粹的数据model,它含有领域的行为,直接将其传到前台会造成调用的混乱;二是仓储是和数据持久化打交道了,界面直接调用仓储,也就是界面直接和数据打交道,也不符合一般分层的原则。所以我们引入应用层,本文应用层是一个以控制台项目为宿主的WCF服务。我们来看代码设计。

DDD领域驱动设计初探系列文章:

一、WCF简介

WCF(Windows Communication Foundation)是由微软发展的一组数据通信的应用程序开发接口,可以翻译为Windows通讯接口,它是.NET框架的一部分。由 .NET Framework 3.0 开始引入。WCF的最终目标是通过进程或不同的系统、通过本地网络或是通过Internet收发客户和服务之间的消息。关于WCF的理论知识,需要我们了解的是经典的ABC。

  • Address: 每一个WCF的Service都有一个唯一的地址。这个地址给出了Service的地址和传输协议(Transport Protocol)。
  • Binding:绑定制定了服务通过什么形式访问。只要类比传输协议, encoding (text, binary, etc) 以及 WS-* 协议,像transactional支持以及可信任的消息队列。
  • Contract:Contract描述了Service能提供的各种服务。Contract有四种,包括Service Contract, Data Contract, Fault Contract和Message Contract。

关于WCF的理论在此就不再展开,下面结合我们的项目代码我们从零开始一步一步来搭建一个自己的WCF服务吧。

二、WCF代码示例

1、代码结构图

项目按照模块为单位划分服务,比如权限模块,我们就有一个权限的接口契约IPowerManageWCFService。IService文件夹里面放了3个接口,分别对应系统3个模块的接口契约,Service文件夹里面分别对应了3个接口的实现。ServiceAttribute.cs里面定义了两个特性,表示接口是WCF的服务。我们来看看具体的代码。

2、代码示例

2.1 ServiceAttribute.cs文件定义契约接口和实现的特性类:

namespace ESTM.WCF.Service
{
//标记此特性的为WCF服务接口
public class ServiceInterfaceAttribute : Attribute
{
} //标记此特性的为WCF服务接口实现类
public class ServiceClassAttribute : Attribute
{
}
}

2.2 接口契约代码:

    /// <summary>
/// 工厂布局模块接口契约
/// </summary>
[ServiceInterface]
[ServiceContract]
public interface IFactoryLayoutWCFService
{
[OperationContract]
List<DTO_TM_PLANT> GetAllPlant();
}
    /// <summary>
/// 权限管理模块接口契约
/// </summary>
[ServiceContract]
[ServiceInterface]
public interface IPowerManageWCFService
{
[OperationContract]
IList<DTO_TB_DEPARTMENT> GetAllDepartment();
}
    /// <summary>
/// 产品管理模块接口契约
/// </summary>
[ServiceContract]
[ServiceInterface]
public interface IProductWCFService
{
[OperationContract]
IList<DTO_TP_PRODUCT> GetAllProduct();
}

接口契约[ServiceContract]表示该接口遵守接口契约协定,[OperationContract]操作契约,这两个特性都是WCF内置的东西。[ServiceInterface]的用处我们待会说。

2.3 接口实现代码

    [ServiceClass]
public class FactoryLayoutWCFService : IFactoryLayoutWCFService
{
public List<DTO_TM_PLANT> GetAllPlant()
{
throw new NotImplementedException();
}
}
    [ServiceClass]
public class ProductWCFService : IProductWCFService
{
public IList<DTO_TP_PRODUCT> GetAllProduct()
{
throw new NotImplementedException();
}
}
    [ServiceClass]
public class PowerManageWCFService : IPowerManageWCFService
{
public IList<DTO_TB_DEPARTMENT> GetAllDepartment()
{
throw new NotImplementedException();
}
}

[ServiceClass]特性和接口上面的[ServiceInterface]特性对应,用于标记契约和实现。

2.4 Bootstrapper.cs里面定义了服务的启动方法

   public class Bootstrapper
{
private string strBaseServiceUrl = ConfigurationManager.AppSettings["ServiceUrl"].ToString(); //启动所有的服务
public void StartServices()
{
//1.读取此程序集里面的有服务契约的接口和实现类
var assembly = Assembly.Load(typeof(Bootstrapper).Namespace);
var lstType = assembly.GetTypes();
var lstTypeInterface = new List<Type>();
var lstTypeClass = new List<Type>();
foreach (var oType in lstType)
{
//2.通过接口上的特性取到需要的接口和实现类
var lstCustomAttr = oType.CustomAttributes;
if (lstCustomAttr.Count() <= )
{
continue;
}
var oInterfaceServiceAttribute = lstCustomAttr.FirstOrDefault(x => x.AttributeType.Equals(typeof(ServiceInterfaceAttribute)));
if (oInterfaceServiceAttribute != null)
{
lstTypeInterface.Add(oType);
continue;
}
var oClassServiceAttribute = lstCustomAttr.FirstOrDefault(x => x.AttributeType.Equals(typeof(ServiceClassAttribute)));
if (oClassServiceAttribute != null)
{
lstTypeClass.Add(oType);
}
} //3.启动所有服务
foreach (var oInterfaceType in lstTypeInterface)
{
         //通过反射找到接口的实现类,找到配对然后启动服务
var lstTypeClassTmp = lstTypeClass.Where(x => x.GetInterface(oInterfaceType.Name) != null).ToList();
if (lstTypeClassTmp.Count <= )
{
continue;
}
if(lstTypeClassTmp[].GetInterface(oInterfaceType.Name).Equals(oInterfaceType))
{
var oTask = Task.Factory.StartNew(() =>
{
OpenService(strBaseServiceUrl + "/" + oInterfaceType.Name, oInterfaceType, lstTypeClassTmp[]);
});
}
}
} //通过服务接口类型和实现类型启动WCF服务
private void OpenService(string strServiceUrl, Type typeInterface, Type typeclass)
{
Uri httpAddress = new Uri(strServiceUrl);
using (ServiceHost host = new ServiceHost(typeclass))
{
///////////////////////////////////////添加服务节点///////////////////////////////////////////////////
host.AddServiceEndpoint(typeInterface, new WSHttpBinding(), httpAddress);
if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
{
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
behavior.HttpGetUrl = httpAddress;
host.Description.Behaviors.Add(behavior);
}
host.Opened += delegate
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("服务启动成功。服务地址:" + strServiceUrl);
}; host.Open();
while (true)
{
Console.ReadLine();
}
}
}
}

对应的App.Config里面对应的服务的URL

  <appSettings>
<add key="ServiceUrl" value="http://127.0.0.1:1234/MyWCF.Server"/>
</appSettings>

StartServices()方法通过反射和两个特性[ServiceClass]与[ServiceInterface],依次启动三个服务。

然后再Program里面调用

     static void Main(string[] args)
{
var oBootstrapper = new Bootstrapper();
oBootstrapper.StartServices();
Console.ReadLine();
}

得到结果:

我们随便选择一个服务,通过浏览器访问,测试服务是否启动成功。

至此,WCF服务基本完成。

三、DTO说明

DTO,全称Data Transfer Object,数据传输对象。DTO是一个贫血模型,也就是它里面基本没有方法,只有一堆属性,并且所有属性都具有public的getter和setter访问器。为什么需要一个DTO对象?这个问题在C#进阶系列——MEF实现设计上的“松耦合”(终结篇:面向接口编程)这篇里面介绍过,它的作用其实很单一,就是用于数据传递和数据绑定。至于DTO如何设计,博主的项目里,DTO是按照聚合来划分的,也就是一个聚合对应一个DTO,DTO里面属性的定义可以根据项目需求来定。我们来看看代码:

    /// <summary>
/// 所有DTO model的父类,用作泛型约束
/// </summary>
[DataContract]
public class DTO_BASEMODEL
{
}
/// <summary>
/// TB_DEPARTMENT
/// </summary>
[DataContract]
public class DTO_TB_DEPARTMENT : DTO_BASEMODEL
{
[DataMember]
public string DEPARTMENT_ID { get; set; } [DataMember]
public string DEPARTMENT_NAME { get; set; } [DataMember]
public string PARENT_ID { get; set; } [DataMember]
public string DEPARTMENT_LEVEL { get; set; } [DataMember]
public string STATUS { get; set; }
}

其他DTO都和这个类似,就不一一列举了。由于DTO需要由WCF传递到Web前台,所以要求这个对象可以序列化,需要标记[DataContract]和[DataMember]两个特性,DTO_BASEMODEL作为所有DTO的父类, 用作泛型约束和定义DTO的一些公用特性。到此,WCF的搭建基本完成,下篇我们来介绍下Automapper的使用。累死我了,今天先到这吧,也不早了,博主也要安歇了。晚安!

还是附上 源码 吧,要不然园友们要说博主小气呢~~有兴趣可以看看。

C#进阶系列——DDD领域驱动设计初探(四):WCF搭建的更多相关文章

  1. C#进阶系列——DDD领域驱动设计初探(一):聚合

    前言:又有差不多半个月没写点什么了,感觉这样很对不起自己似的.今天看到一篇博文里面写道:越是忙人越有时间写博客.呵呵,似乎有点道理,博主为了证明自己也是忙人,这不就来学习下DDD这么一个听上去高大上的 ...

  2. C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)

    前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...

  3. C#进阶系列——DDD领域驱动设计初探(三):仓储Repository(下)

    前言:上篇介绍了下仓储的代码架构示例以及简单分析了仓储了使用优势.本章还是继续来完善下仓储的设计.上章说了,仓储的最主要作用的分离领域层和具体的技术架构,使得领域层更加专注领域逻辑.那么涉及到具体的实 ...

  4. C#进阶系列——DDD领域驱动设计初探(五):AutoMapper使用

    前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...

  5. C#进阶系列——DDD领域驱动设计初探(六):领域服务

    前言:之前一直在搭建项目架构的代码,有点偏离我们的主题(DDD)了,这篇我们继续来聊聊DDD里面另一个比较重要的知识点:领域服务.关于领域服务的使用,书中也介绍得比较晦涩,在此就根据博主自己的理解谈谈 ...

  6. C#进阶系列——DDD领域驱动设计初探(七):Web层的搭建

    前言:好久没更新博客了,每天被该死的业务缠身,今天正好一个模块完成了,继续来完善我们的代码.之前的六篇完成了领域层.应用层.以及基础结构层的部分代码,这篇打算搭建下UI层的代码. DDD领域驱动设计初 ...

  7. DDD领域驱动设计初探(七):Web层的搭建

    前言:好久没更新博客了,每天被该死的业务缠身,今天正好一个模块完成了,继续来完善我们的代码.之前的六篇完成了领域层.应用层.以及基础结构层的部分代码,这篇打算搭建下UI层的代码. DDD领域驱动设计初 ...

  8. DDD领域驱动设计初探(六):领域服务

    前言:之前一直在搭建项目架构的代码,有点偏离我们的主题(DDD)了,这篇我们继续来聊聊DDD里面另一个比较重要的知识点:领域服务.关于领域服务的使用,书中也介绍得比较晦涩,在此就根据博主自己的理解谈谈 ...

  9. DDD领域驱动设计初探(五):AutoMapper使用

    前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...

随机推荐

  1. ios页面过渡效果插件Kontext

    效果体验:http://keleyi.com/keleyi/phtml/html5/28.htm 以下是代码: <!doctype html> <html> <head& ...

  2. Json.NET读取和写入Json文件

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  3. Ajax表单异步上传(包括文件域)

    起因 做前台页面时,需要调用WebAPI的Post请求,发送一些字段和文件(相当于把表单通过ajax异步发送出去,得到返回结果),然后得到返回值判断是否成功. 尝试 先是尝试了一下 "jQu ...

  4. SharePoint 2013 图文开发系列之事件接收器

    在SharePoint的使用中,我们经常需要在完成一个动作之后,触发一个事件:比如,我们上传一个文档,但是没有标题,我们需要在上传完成之后,触发一个事件把文件名同步到标题,这就需要用到事件接收器. 此 ...

  5. 自定义加载loading view动画组件的使用。

    在github上找的一个有点酷炫的loading动画https://github.com/Fichardu/CircleProgress 我写写使用步骤 自定义view(CircleProgress  ...

  6. EditText 显示明文和密码

    1.效果图 2.布局文件 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xml ...

  7. 【读书笔记】100个Switf必备tips

    声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯伯: http://over140.cnblogs.com 正文 1.Selector 在Swi ...

  8. 【转】Android Studio下加入百度地图的使用 (一)——环境搭建

    最近有学 生要做毕业设计,会使用到定位及地图信息的功能,特此研究了一下,供大家参考,百度定位SDK已经更新到了5.0,地图SDK已经更新到了3.5,但是在 AndroidStudio中使用还是存在一些 ...

  9. Mybatis基于注解的方式访问数据库

    1. 使用方式:在Service层直接调用 package com.disappearwind.service; import org.springframework.beans.factory.an ...

  10. 让我们来谈谈JDBC

    1.JDBC     1)JDBC简介         - JDBC就是Java中连接数据库方式         - 我们可以通过JDBC来执行SQL语句.       2)获取数据库连接   - j ...