最近了解了一下Unity,感觉用起来挺方便的,就尝试用其和NHibernate、控制台搭建一个简单的三层架构的开发框架。
IDE用的是Visual Studio 2017
最终测试通过的项目结构
项目只简单的完成了功能的实现,仅供参考使用。
Client——客户端应用程序(这里简单的用了控制台)
NormalServer——服务端控制台程序
Persistent——NHibernate持久层类库
TestBusiness——接口业务实现类库
TestDomain——域对象
TestInterfaceAndValueObject——值对象和接口
首先我们先配置好NHibernate的运行环境,在Persistent持久层中。
hibernate.cfg.xml
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration-x-factories xmlns="urn:nhibernate-configuration-2.2-x-factories">
<session-factory name="Development">
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MySQL5Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
<property name="connection.connection_string">Data Source=192.168.13.180;Port=3306;Initial Catalog=test;user id=root;password=123456;SslMode=none</property>
<property name="show_sql">true</property>
<!--<mapping assembly="MyDomain" />-->
</session-factory>
<session-factory name="Production">
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.MySQL5Dialect</property>
<property name="connection.driver_class">NHibernate.Driver.MySqlDataDriver</property>
<property name="connection.connection_string">Data Source=192.168.13.12;Port=3306;Initial Catalog=test;user id=root;password=123456;SslMode=none</property>
<property name="show_sql">true</property>
<mapping assembly="TestDomain" />
</session-factory>
</hibernate-configuration-x-factories>
此处配置文件直接合并配置了多个MySql数据库,用了NHibernate.XFactories的工具将合并的.cfg.xml文件进行解析,该工具可以自行NuGet。
如果程序将连接并操作多个不同的数据库时,需创建Session工厂。
public class SessionFactories
{
static Dictionary<string, ISessionFactory> _sessionFactorys = new Dictionary<string, ISessionFactory>();
public static Dictionary<string, ISessionFactory> SessionFactorys
{
get
{
if (_sessionFactorys.Count <= 0)
{
IList<string> list = new List<string>();
using (XmlTextReader xml = new XmlTextReader(System.AppDomain.CurrentDomain.BaseDirectory + "hibernate.cfg.xml"))
{
xml.WhitespaceHandling = WhitespaceHandling.None;
while (xml.Read())
{
if (xml.NodeType == XmlNodeType.Element)
{
if (xml.Name == "session-factory")
list.Add(xml.GetAttribute("name"));
}
}
}
foreach (var name in list)
{
NHibernate.Cfg.Configuration config = new NHibernate.Cfg.Configuration();
ISessionFactory session = config
.Configure(System.AppDomain.CurrentDomain.BaseDirectory + "hibernate.cfg.xml", name)
.BuildSessionFactory();
_sessionFactorys.Add(name, session);
}
}
return _sessionFactorys;
}
}
public static ISessionHandle CreateSessionHandle(string factoryName)
{
if (SessionFactorys.ContainsKey(factoryName))
{
ISession session = SessionFactorys[factoryName].OpenSession();
ISessionHandle sessionHandle = new SessionHandle(session);
return sessionHandle;
}
else
{
throw new Exception(string.Format("找不到SessionFactory!"));
}
}
}
建立NHibernateHelper帮助类
public class NHibernateHelper
{
public static ISessionHandle Open(string factoryName)
{
return SessionFactories.CreateSessionHandle(factoryName);
}
}
还需要创建持久层的数据操作接口ISessionHandle
public interface ISessionHandle
{
void Save(object obj);
void Update(object obj);
void Delete(object obj);
T Get<T>(object id);
IList<T> GetAll<T>();
ICriteria CreateCriteria(Type persistentClass);
IQuery CreateQuery(string queryString);
ISQLQuery CreateSQLQuery(string queryString);
ISession Session { get; }
IDbConnection DbConnection { get; }
}
同时实现接口ISessionHandle
public class SessionHandle : ISessionHandle
{
public SessionHandle(ISession session)
{
this.session = session;
}
public void Save(object obj)
{
using (ITransaction transaction = session.BeginTransaction())
{
Session.Save(obj);
transaction.Commit();
}
}
public void Update(object obj)
{
using (ITransaction transaction = session.BeginTransaction())
{
Session.Update(obj);
transaction.Commit();
}
}
public void Delete(object obj)
{
using (ITransaction transaction = session.BeginTransaction())
{
Session.Delete(obj);
transaction.Commit();
}
}
public T Get<T>(object id)
{
return Session.Get<T>(id);
}
public IList<T> GetAll<T>()
{
return Session.CreateQuery(string.Format("from {0}", typeof(T).Name)).List<T>();
}
public ICriteria CreateCriteria(Type persistentClass)
{
return Session.CreateCriteria(persistentClass);
}
public IQuery CreateQuery(string queryString)
{
return Session.CreateQuery(queryString);
}
public ISQLQuery CreateSQLQuery(string queryString)
{
return Session.CreateSQLQuery(queryString);
}
private ISession session;
public ISession Session
{
get { return session; }
}
public IDbConnection DbConnection
{
get
{
return Session.Connection;
}
}
public void Flush()
{
Session.Flush();
}
public void Clear()
{
Session.Clear();
}
public void Close()
{
Session.Close();
}
}
然后创建域对象和其对应的映射配置文件
注意映射配置文件的格式必须是"类名.hbm.xml"否则会报错。
以下是Score.hbm.xml的映射配置文件
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class name="TestDomain.Score,TestDomain" table="t_score">
<id name="Id" column="Id" type="String" length="32">
<generator class="uuid.hex" />
</id>
<property name="Name" column="Name" type="String"/>
<property name="Chinese" column="Chinese" type="int"/>
<property name="Math" column="Math" type="int"/>
<property name="English" column="English" type="int"/>
<property name="History" column="History" type="int"/>
<property name="Politice" column="Politice" type="int"/>
<property name="Science" column="Science" type="int"/>
</class>
</hibernate-mapping>
至此NHibernate基本搞定完了,例如使用
NHibernateHelper.Open("Production").Save(score);
以上语句即可完成对Production数据库的保存操作。
接下来说明Unity的简单使用方法
首先需要做准备工作:创建业务接口、服务、值对象,这里我创建了最简单的接口和其实现
//IScoreSrv.cs
public interface IScoreSrv
{
void Save(ScoreV scoreV);
}
//ScoreSrv.cs
public class ScoreSrv : IScoreSrv
{
public void Save(ScoreV scoreV)
{
Score score = new Score();
new CopyProperties(scoreV, score).CopyXTNameProperties();
NHibernateHelper.Open("Production").Save(score);
}
}
//ScoreV.cs
[Serializable]
public class ScoreV
{
public virtual string Id { get; set; }
public virtual string Name { get; set; }
public virtual int Chinese { get; set; }
public virtual int Math { get; set; }
public virtual int English { get; set; }
public virtual int History { get; set; }
public virtual int Politice { get; set; }
public virtual int Science { get; set; }
}
然后在NormalServer的app.config中configuration节点下添加
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration" />
</configSections>
<unity xmlns="http://schemas.microsoft.com/practces/2010/unity">
<containers>
<!--MyContainer为自定义名称 只需要和调用时名称保持一致即可-->
<container name="MyContainer">
<!--type为对象的名称,mapTo为注入对象的名称 写法为用逗号隔开两部分,一是类的全部,包括命名空间,二是程序集名称-->
<register type="TestInterfaceAndValueObject.IScoreSrv,TestInterfaceAndValueObject" mapTo="TestBusiness.ScoreSrv,TestBusiness" />
</container>
</containers>
</unity>
这些都配置好后,Unity的最简单使用方法:
UnityContainer container = new UnityContainer();
UnityConfigurationSection config = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
config.Configure(container, "MyContainer");
IScoreSrv scoreSrv = container.Resolve<IScoreSrv>();
scoreSrv.Save(new ScoreV());
但这只能让服务和客户端部署在同一台电脑上才能使用,生产环境中用到最多的应该是客户端远程连接服务,所以这里我使用了Remoting。
//Program.cs
class Program
{
static void Main(string[] args)
{
//在端口1079上创建一个新的监听通道
TcpServerChannel channel = new TcpServerChannel(1079);
//注册通道
ChannelServices.RegisterChannel(channel, false);
//注册容器
ServiceBus.config.Configure(ServiceBus.container, "MyContainer");
RemotingConfiguration.RegisterWellKnownServiceType(typeof(CommonHandle), "RemoteOperation", WellKnownObjectMode.Singleton);
Console.WriteLine("成功");
string echo = "当前注册的服务:";
WellKnownServiceTypeEntry[] entries = RemotingConfiguration.GetRegisteredWellKnownServiceTypes();
foreach (WellKnownServiceTypeEntry entry in entries)
{
echo += (Char)10 + string.Format("路径:{0};类型:{1}", entry.ObjectUri, entry.TypeName);
}
Console.WriteLine(echo);
Console.ReadLine();
}
}
//ServiceBus.cs 提供静态UnityContainer和UnityConfigurationSection对象
public class ServiceBus : MarshalByRefObject
{
public static UnityContainer container = new UnityContainer();
public static UnityConfigurationSection config = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
}
这里ServiceBus类继承了MarshalByRefObject,关于MarshalByRefObject的解释是:在支持远程处理的应用程序中,允许跨应用程序域边界访问对象。同样的,我们还需要在ScoreSrv服务中也继承该类:
public class ScoreSrv : MarshalByRefObject, IScoreSrv
创建CommonHandle.cs 该类用于服务端根据远程客户端传进的接口获取对应的服务
//ICommonHandle.cs
public interface ICommonHandle
{
T GetService<T>();
}
public class CommonHandle : MarshalByRefObject, ICommonHandle
{
public T GetService<T>()
{
Type interfaceType = typeof(T);
string serviceName = interfaceType.FullName;
return ServiceBus.container.Resolve<T>();
}
}
至此,服务端已经搭建完成,胜利就在眼前。
接下来编写客户端的测试程序。在客户端,我们首先要连接远程服务端。
//ClientUnityProxy.cs 客户端Unity代理工厂类
public class ClientUnityProxy
{
private static ICommonHandle commonHandle;
public static T GetService<T>()
{
return commonHandle.GetService<T>();
}
public static void InitClientPorxy()
{
commonHandle = (ICommonHandle)Activator.GetObject(typeof(ICommonHandle), "tcp://localhost:1079/RemoteOperation");//此处自行配置url参数
}
}
在客户端控制台的Main方法中编写测试代码
ClientUnityProxy.InitClientPorxy();//初始化服务
IScoreSrv scoreSrv = ClientUnityProxy.GetService<IScoreSrv>();
ScoreV scoreV = new ScoreV() { Name = "Steve", Chinese = 20, English = 33, History = 66, Math = 90, Politice = 70, Science = 55 };//测试数据
scoreSrv.Save(scoreV);
Console.WriteLine("操作成功");
Console.ReadLine();
同时启动服务端和客户端,可以发现客户端请求的scoreSrv.Save(scoreV);方法在远程服务端成功执行。
至此测试项目结束。
Tip:
1. 关于Unity、NHibernate、MySql等运行库,可以使用NuGet安装。
2. 如果在服务中想要调取其它服务中的方法,例如有两个服务分别为"UserSrv"、"TestSrv",想要在“UserSrv”中调用“TestSrv”中的方法,需要采取以下方法:
<register type="Unity.Interface.IUserSrv,Unity.Interface" mapTo="Unity.Test.UserSrv,Unity.Test">
<!--调用其它接口服务,需设置被调用接口的name,dependencyName为被调用接口的重命名-->
<property name="TestSrv" dependencyName="TestSrv"></property>
</register>
<register type="Unity.Interface.ITestSrv,Unity.Interface" mapTo="Unity.Test.TestSrv,Unity.Test" name="TestSrv"/>
欢迎指出其中错误!