摘要: 首先,很高兴大家对我上篇文章的关注与批评,让我理解了很多专业术语,比如框架,架构以及模块等。同时,也大大加深了我对三层架构的理解。在此,表示感谢!! 下面我主要分析下十个模块中的G模块的工作原理以及实现方式。项目是使用Microsoft® .NET Remoting框架来实现分布式开发的需求。对于.NET Remoting机制,想要了解更多的可以参看博客园里的这篇文章,我觉得写得很好。 http://www.cnblogs.com/wayfarer/archive/2004/07/30/28723.html 由于是具体的项目,为了达到功能的复用、维护的方便以及安全性等方面的要求,因此阅读全文
posted @ 2012-02-08 16:15 菜鸟起步 阅读(1461) 评论(1) 编辑

   首先,很高兴大家对我上篇文章的关注与批评,让我理解了很多专业术语,比如框架,架构以及模块等。同时,也大大加深了我对三层架构的理解。在此,表示感谢!!

   下面我主要分析下十个模块中的G模块的工作原理以及实现方式。项目是使用Microsoft® .NET Remoting框架来实现分布式开发的需求。对于.NET Remoting机制,想要了解更多的可以参看博客园里的这篇文章,我觉得写得很好。

    http://www.cnblogs.com/wayfarer/archive/2004/07/30/28723.html

   由于是具体的项目,为了达到功能的复用、维护的方便以及安全性等方面的要求,因此,封装了很多共通类和接口等。同时也隐藏了很多实现的细节。下面我要讲的代码 都是经过提取的,省去了共通类的层层调用。

    需要用到的配置文件有两个分别是Service.config和client.config。前者用来配置需要注册的服务器对象的信息,后者是用来为客户端提供远程服务器注册通道的信息。内容如下:

Service.config
1 <configuration> 
2 <service>
3 <wellknown mode="Singleton" assemblyName="THRSEC01008P" typeName="ThreeHigh.Application.Remoting.Bussiness.EmployeeInfoDB" objectUri="MyEmployeeInfo" />
4 <wellknown mode="Singleton" assemblyName="THRSEC01108P" typeName="ThreeHigh.Application.Remoting.Bussiness.MenuRoleSetDB" objectUri="MyMenuRoleSet" />
5
6 </service>
7 </configuration>
Client.config
1 <?xml version="1.0" encoding="utf-8"?>
2 <configuration>
3 <application>
4 <channels>
5 <channel ref="HTTP" ip="172.168.1.105" port="3000" />
6 </channels>
7 </application>
8 </configuration>

    服务端:分为开发阶段的 Console控制台启动程序,和window Service服务程序两个版本。主要分析Console控制台程序。

    这部分采用的是单例模式开发,获取需要被注册并激活的服务器对象信类ServiceConfig来读取配置信息中存储的信息。使用单例模式是为了确保所有的客户程序的请求都只有一个实例来处理。当然实现单例模式的方式有很多种,这里主要采用的是静态初始化方式。

ServiceConfig.cs
using System;
using System.IO;
using System.Xml;
using System.Text;
using System.Reflection;
using System.Collections.Generic;
using System.Runtime.Remoting;

namespace DETConsoleService
{
//*********************************************
//* 负责获取Service.config配置文件信息的。
//*********************************************
public class ServiceConfig
{
private static ServiceConfig _Instance = null;

private List<WellKnownServiceTypeEntry> _ServiceEntryList = new List<WellKnownServiceTypeEntry>();

public static ServiceConfig GetInstance()
{
if (_Instance == null)
{
_Instance = new ServiceConfig();
_Instance.Initialize();
}

return _Instance;
}
//*******************************************
//* 需要被注册并激活的对象信息列表。
//*******************************************
public List<WellKnownServiceTypeEntry> ServiceEntryList
{
get
{
return _ServiceEntryList;
}
}


//************************************
//* 读取配置文件,获得需要注册的对象信息。
//************************************
public void Initialize()
{
try
{
string appRoot = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
String appFileName = appRoot + Path.DirectorySeparatorChar + "Config" + Path.DirectorySeparatorChar + "Service.config";

if (File.Exists(appFileName))
{
XmlDocument document = new XmlDocument();
document.Load(appFileName);

XmlNodeList serviceNodes = document.SelectNodes("configuration/service/wellknown");

if (serviceNodes != null && serviceNodes.Count > 0 )
{
foreach (XmlNode node in serviceNodes)
{
string typeName = node.Attributes["typeName"].Value;
string assemblyName = node.Attributes["assemblyName"].Value;
string mode = node.Attributes["mode"].Value;
string objectUri = node.Attributes["objectUri"].Value;

WellKnownObjectMode wMode = WellKnownObjectMode.Singleton;

if ("SingleCall".Equals(mode))
{
wMode = WellKnownObjectMode.SingleCall;
}
WellKnownServiceTypeEntry ServiceEntry = new WellKnownServiceTypeEntry(typeName, assemblyName, objectUri, wMode);

_ServiceEntryList.Add(ServiceEntry);
}

}
}
}
catch
{
//抓错误 写日志。
}
}

}
}

 

   注册并激活对象,提供给客户端调用的工作是由主控制台程序Program.cs来执行的。Remoting的通道主要有Tcp和Http两种。本例子中使用Http方式。

Program.cs
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Http;

namespace DETConsoleService
{
class Program
{
private static HttpChannel _HttpChannel;

static void Main(string[] args)
{
try
{
_HttpChannel = new httpChannel(Convert.ToInt32(3000));
//这里是使用ServerConfig类去配置文件中取ChannelPort的。为了简便,我直接写了3000.
ChannelServices.RegisterChannel(_HttpChannel, false);

List<WellKnownServiceTypeEntry> entryList = ServiceConfig.GetInstance().ServiceEntryList;
foreach (WellKnownServiceTypeEntry entry in entryList)
{
RemotingConfiguration.RegisterWellKnownServiceType(entry);
}

Console.WriteLine("服务正常启动");

}
catch (Exception ex)
{
Console.WriteLine("启动错误:" + ex.Message);
}

Console.ReadLine();
//注销通道
UnRegisterChannel();
}
//注销通道
private static bool UnRegisterChannel()
{
IChannel[] channels = ChannelServices.RegisteredChannels;
try
{
foreach (IChannel eachChannel in channels)
{
ChannelServices.UnregisterChannel(eachChannel);
}
return true;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
return false;
}

}
}
}

    至此,服务器端工作基本结束。以后我们增加了功能模块,只需要在service.config配置文件中添加相应配置项即可。

   客户端:即每个每本程序的G模块。A模块需要下层P模块的对象,只需要调用G模块 去获得即可。代码如下。

GlobalSettingInfoMediation.cs
using System.Reflection;

namespace ThreeHigh.Application.Remoting.Mediation
{
public class GlobalSettingInfoMediation
{
public GlobalSettingInfoMediation()
{

}

/// <summary>
/// 取得远程对象的引用。
/// </summary>
/// <returns></returns>
public static IGlobalSettingInfoDB GetGlobalSettingInfoDB()
{
try
{
IGlobalSettingInfoDB globalSettingInfoDB = (IGlobalSettingInfoDB)GetRemoteObject(typeof(IGlobalSettingInfoDB), "MyGlobalSettingInfo");

return globalSettingInfoDB;
}
catch (Exception ex)
{
string msg = ex.Message;
return null;
}
}

/// <summary>
/// 返回O模块的接口类型。
/// </summary>
/// <param name="type">返回值的类型</param>
/// <param name="p">远程对象的名字,与Service.config中的objectUri值对应。</param>
/// <returns></returns>
private static IGlobalSettingInfoDB GetRemoteObject(Type type, string p)
{
try
{
//读取client.config文件。
ClientConfig config = ClientConfig.GetInstance();
////http: //172.168.1.38:3000/MyEmployee
string remoteUrl = String.Format("{0}://{1}:{2}/{3}", config.ChannelRef, config.ChannelIP, config.ChannelPort, uri);
//获得远程激活对象。
object obj = Activator.GetObject(type, remoteUrl);

return obj;
}
catch (Exception ex)
{
throw ex;
}
}
}
}

      经过这样处理 之后,我们就只需要将自己开发的O模块 P模块 D模块以及R模块的程序集dll, 放到相应的提供AP服务器上面,配置好service.config 文件即可。整个系统的这部分模块的更新,也不需要每个客户端都同步更新,只要更新下提供AP服务器上的dll。

posted @ 2012-02-08 16:15 菜鸟起步 阅读(1461) 评论(1) 编辑

凡是接触.net开发的,首选要理解的就是三层架构的重要思想。关于三层架构的概述 我就不多说了,相信大家都早已明白。

三层架构(3-tier application) 就是将整个业务应用划分为:表现层(UI)、业务逻辑层(BLL)、数据访问层(DAL)。

区分层次的目的即为了“高内聚,低耦合”的思想。

1、表现层(UI):通俗讲就是展现给用户的界面,即用户在使用一个系统的时候他的所见所得。

2、业务逻辑层(BLL):针对具体问题的操作,也可以说是对数据层的操作,对数据业务逻辑处理。

3、数据访问层(DAL):该层是直接操作数据库的,针对数据的增添、删除、修改、更新、查找等。

各层关系如下图所示:

image123

       我进入公司之后,所接触的项目都是建立在十个模块的基础之上。所谓十个模块 其实就是三层架构的具体应用。

增加了网络通讯模块,实现远程调用。服务端运行着Windows service 来产生数据访问模块的对象提供给客户端请求调用。

这样处理,使得整个C/S模式中,一旦底层方法发生变动时,只需要更新服务器上相应的dll,而不需要客户端主机同步更新,

减少了维护成本。

下面说下十个模块中,每个模块的作用以及模块与模块之间的关系。

  0L模块:即启动模块。用来控制画面模块中主画面的启动。

              namespace CompanyName.Application.UI。

1U模块:即画面UI模块。创建用户界面。namespace CompanyName.Application.UI。

2C模块:即画面逻辑模块。处理与画面相关的逻辑控制,封装下层方法供UI模块使用。

             namespace CompanyName.Application.Controller。

3E模块:即画面实体类模块。根据画面设计相应的实体类或者枚举类型。

           namespace CompanyName.Application.Entity。

4A模块:即业务逻辑模块。业务逻辑的处理,以及上下模块之间数据的转换。属于中间模块。

           namespace CompanyName.Application.Access。

5G模块:即网络模块,也称远程调用模块。获取远程服务器上的对象,进行远程调用。

           namespace CompanyName.Application.Remoting.Mediation。

6O模块:即接口模块。定义接口,用来规范数据访问层的方法设计。可以对子类进行封箱和拆箱,也便于网络传输。

          namespace CompanyName.Application.Remoting.Interface。

7D模块:即数据库实体类。这一模块所定义的类,是完全按照数据库中表结构来定义属性的。与3E模块不同在于,

            3E模块类中具有一些类方法,比如Clone();Compare();Equals();等。而D模块则除了get {}set{} 方法之外的额外方法。

          namespace CompanyName.Application.Remoting.ParamData。

8P模块:即数据库逻辑模块。继承O模块接口,封装R模块方法。处理一些数据序列化转换。

          namespace ThreeHigh.Application.Remoting.Bussiness。

9R模块:即数据库访问模块。直接访问数据库,针对数据的增添、删除、修改、更新、查找等。

         namespace CompanyName.Application.Remoting.DAO。

具体项目中一本程序的框架如下图所示。

image

   顺便强调下各个模块之间的引用关系:

    L—>U 即L模块参照引用U模块程序集。以下相似。

  U—>2C 3E.

  C—>3E 4A.

  E—>不参照其他模块。

  A—>3E 5G 6O 7D.

  G—>6O.

  O 和D 不参照其他模块。

  P—>6O 7D 9R.

  R—>7D.

   因为公司的程序都是与公司的数据库有关的,而且有调用了很多共通的类与方法,没办法单独运行,所以程序源代码就不再上传了。

我觉得关键的技术主要在远程调用对象和服务器生成对象。下一篇文章 我将会重点阐述,G层以及服务器端Service的工作原理。

   由于这是本人第一次发表文章,不足之处,还望大家多指正,多批评。

posted @ 2012-02-02 14:33 菜鸟起步 阅读(4968) 评论(67) 编辑