NHibernate+Unity学习笔记

最近了解了一下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"/>


欢迎指出其中错误!














评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值